mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
HID: intel_ish-hid: ISH Transport layer
The ISH transport layer (ishtp) is a bi-directional protocol implemented on the top of PCI based inter processor communication layer. This layer offers: - Connection management - Flow control with the firmware - Multiple client sessions - Client message transfer - Client message reception - DMA for RX and TX for fast data transfer Refer to Documentation/hid/intel-ish-hid.txt for overview of the functionality implemented in this layer. Original-author: Daniel Drubin <daniel.drubin@intel.com> Reviewed-and-tested-by: Ooi, Joyce <joyce.ooi@intel.com> Tested-by: Grant Likely <grant.likely@secretlab.ca> Tested-by: Rann Bar-On <rb6@duke.edu> Tested-by: Atri Bhattacharya <badshah400@aim.com> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
17e2adf2a7
commit
3703f53b99
@ -967,4 +967,6 @@ source "drivers/hid/usbhid/Kconfig"
|
||||
|
||||
source "drivers/hid/i2c-hid/Kconfig"
|
||||
|
||||
source "drivers/hid/intel-ish-hid/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -113,3 +113,5 @@ obj-$(CONFIG_USB_MOUSE) += usbhid/
|
||||
obj-$(CONFIG_USB_KBD) += usbhid/
|
||||
|
||||
obj-$(CONFIG_I2C_HID) += i2c-hid/
|
||||
|
||||
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
|
||||
|
17
drivers/hid/intel-ish-hid/Kconfig
Normal file
17
drivers/hid/intel-ish-hid/Kconfig
Normal file
@ -0,0 +1,17 @@
|
||||
menu "Intel ISH HID support"
|
||||
depends on X86_64 && PCI
|
||||
|
||||
config INTEL_ISH_HID
|
||||
tristate "Intel Integrated Sensor Hub"
|
||||
default n
|
||||
select HID
|
||||
help
|
||||
The Integrated Sensor Hub (ISH) enables the ability to offload
|
||||
sensor polling and algorithm processing to a dedicated low power
|
||||
processor in the chipset. This allows the core processor to go into
|
||||
low power modes more often, resulting in the increased battery life.
|
||||
The current processors that support ISH are: Cherrytrail, Skylake,
|
||||
Broxton and Kaby Lake.
|
||||
|
||||
Say Y here if you want to support Intel ISH. If unsure, say N.
|
||||
endmenu
|
12
drivers/hid/intel-ish-hid/Makefile
Normal file
12
drivers/hid/intel-ish-hid/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
#
|
||||
# Makefile - Intel ISH HID drivers
|
||||
# Copyright (c) 2014-2016, Intel Corporation.
|
||||
#
|
||||
#
|
||||
obj-$(CONFIG_INTEL_ISH_HID) += intel-ishtp.o
|
||||
intel-ishtp-objs := ishtp/init.o
|
||||
intel-ishtp-objs += ishtp/hbm.o
|
||||
intel-ishtp-objs += ishtp/client.o
|
||||
intel-ishtp-objs += ishtp/bus.o
|
||||
intel-ishtp-objs += ishtp/dma-if.o
|
||||
intel-ishtp-objs += ishtp/client-buffers.o
|
791
drivers/hid/intel-ish-hid/ishtp/bus.c
Normal file
791
drivers/hid/intel-ish-hid/ishtp/bus.c
Normal file
@ -0,0 +1,791 @@
|
||||
/*
|
||||
* ISHTP bus driver
|
||||
*
|
||||
* Copyright (c) 2012-2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include "bus.h"
|
||||
#include "ishtp-dev.h"
|
||||
#include "client.h"
|
||||
#include "hbm.h"
|
||||
|
||||
static int ishtp_use_dma;
|
||||
module_param_named(ishtp_use_dma, ishtp_use_dma, int, 0600);
|
||||
MODULE_PARM_DESC(ishtp_use_dma, "Use DMA to send messages");
|
||||
|
||||
#define to_ishtp_cl_driver(d) container_of(d, struct ishtp_cl_driver, driver)
|
||||
#define to_ishtp_cl_device(d) container_of(d, struct ishtp_cl_device, dev)
|
||||
static bool ishtp_device_ready;
|
||||
|
||||
/**
|
||||
* ishtp_recv() - process ishtp message
|
||||
* @dev: ishtp device
|
||||
*
|
||||
* If a message with valid header and size is received, then
|
||||
* this function calls appropriate handler. The host or firmware
|
||||
* address is zero, then they are host bus management message,
|
||||
* otherwise they are message fo clients.
|
||||
*/
|
||||
void ishtp_recv(struct ishtp_device *dev)
|
||||
{
|
||||
uint32_t msg_hdr;
|
||||
struct ishtp_msg_hdr *ishtp_hdr;
|
||||
|
||||
/* Read ISHTP header dword */
|
||||
msg_hdr = dev->ops->ishtp_read_hdr(dev);
|
||||
if (!msg_hdr)
|
||||
return;
|
||||
|
||||
dev->ops->sync_fw_clock(dev);
|
||||
|
||||
ishtp_hdr = (struct ishtp_msg_hdr *)&msg_hdr;
|
||||
dev->ishtp_msg_hdr = msg_hdr;
|
||||
|
||||
/* Sanity check: ISHTP frag. length in header */
|
||||
if (ishtp_hdr->length > dev->mtu) {
|
||||
dev_err(dev->devc,
|
||||
"ISHTP hdr - bad length: %u; dropped [%08X]\n",
|
||||
(unsigned int)ishtp_hdr->length, msg_hdr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ISHTP bus message */
|
||||
if (!ishtp_hdr->host_addr && !ishtp_hdr->fw_addr)
|
||||
recv_hbm(dev, ishtp_hdr);
|
||||
/* ISHTP fixed-client message */
|
||||
else if (!ishtp_hdr->host_addr)
|
||||
recv_fixed_cl_msg(dev, ishtp_hdr);
|
||||
else
|
||||
/* ISHTP client message */
|
||||
recv_ishtp_cl_msg(dev, ishtp_hdr);
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_recv);
|
||||
|
||||
/**
|
||||
* ishtp_send_msg() - Send ishtp message
|
||||
* @dev: ishtp device
|
||||
* @hdr: Message header
|
||||
* @msg: Message contents
|
||||
* @ipc_send_compl: completion callback
|
||||
* @ipc_send_compl_prm: completion callback parameter
|
||||
*
|
||||
* Send a multi fragment message via IPC. After sending the first fragment
|
||||
* the completion callback is called to schedule transmit of next fragment.
|
||||
*
|
||||
* Return: This returns IPC send message status.
|
||||
*/
|
||||
int ishtp_send_msg(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr,
|
||||
void *msg, void(*ipc_send_compl)(void *),
|
||||
void *ipc_send_compl_prm)
|
||||
{
|
||||
unsigned char ipc_msg[IPC_FULL_MSG_SIZE];
|
||||
uint32_t drbl_val;
|
||||
|
||||
drbl_val = dev->ops->ipc_get_header(dev, hdr->length +
|
||||
sizeof(struct ishtp_msg_hdr),
|
||||
1);
|
||||
|
||||
memcpy(ipc_msg, &drbl_val, sizeof(uint32_t));
|
||||
memcpy(ipc_msg + sizeof(uint32_t), hdr, sizeof(uint32_t));
|
||||
memcpy(ipc_msg + 2 * sizeof(uint32_t), msg, hdr->length);
|
||||
return dev->ops->write(dev, ipc_send_compl, ipc_send_compl_prm,
|
||||
ipc_msg, 2 * sizeof(uint32_t) + hdr->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_write_message() - Send ishtp single fragment message
|
||||
* @dev: ishtp device
|
||||
* @hdr: Message header
|
||||
* @buf: message data
|
||||
*
|
||||
* Send a single fragment message via IPC. This returns IPC send message
|
||||
* status.
|
||||
*
|
||||
* Return: This returns IPC send message status.
|
||||
*/
|
||||
int ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr,
|
||||
unsigned char *buf)
|
||||
{
|
||||
return ishtp_send_msg(dev, hdr, buf, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_fw_cl_by_uuid() - locate index of fw client
|
||||
* @dev: ishtp device
|
||||
* @uuid: uuid of the client to search
|
||||
*
|
||||
* Search firmware client using UUID.
|
||||
*
|
||||
* Return: fw client index or -ENOENT if not found
|
||||
*/
|
||||
int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *uuid)
|
||||
{
|
||||
int i, res = -ENOENT;
|
||||
|
||||
for (i = 0; i < dev->fw_clients_num; ++i) {
|
||||
if (uuid_le_cmp(*uuid, dev->fw_clients[i].props.protocol_name)
|
||||
== 0) {
|
||||
res = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_fw_cl_by_uuid);
|
||||
|
||||
/**
|
||||
* ishtp_fw_cl_by_id() - return index to fw_clients for client_id
|
||||
* @dev: the ishtp device structure
|
||||
* @client_id: fw client id to search
|
||||
*
|
||||
* Search firmware client using client id.
|
||||
*
|
||||
* Return: index on success, -ENOENT on failure.
|
||||
*/
|
||||
int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id)
|
||||
{
|
||||
int i, res = -ENOENT;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->fw_clients_lock, flags);
|
||||
for (i = 0; i < dev->fw_clients_num; i++) {
|
||||
if (dev->fw_clients[i].client_id == client_id) {
|
||||
res = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_device_probe() - Bus probe() callback
|
||||
* @dev: the device structure
|
||||
*
|
||||
* This is a bus probe callback and calls the drive probe function.
|
||||
*
|
||||
* Return: Return value from driver probe() call.
|
||||
*/
|
||||
static int ishtp_cl_device_probe(struct device *dev)
|
||||
{
|
||||
struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
|
||||
struct ishtp_cl_driver *driver;
|
||||
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
driver = to_ishtp_cl_driver(dev->driver);
|
||||
if (!driver || !driver->probe)
|
||||
return -ENODEV;
|
||||
|
||||
return driver->probe(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_device_remove() - Bus remove() callback
|
||||
* @dev: the device structure
|
||||
*
|
||||
* This is a bus remove callback and calls the drive remove function.
|
||||
* Since the ISH driver model supports only built in, this is
|
||||
* primarily can be called during pci driver init failure.
|
||||
*
|
||||
* Return: Return value from driver remove() call.
|
||||
*/
|
||||
static int ishtp_cl_device_remove(struct device *dev)
|
||||
{
|
||||
struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
|
||||
struct ishtp_cl_driver *driver;
|
||||
|
||||
if (!device || !dev->driver)
|
||||
return 0;
|
||||
|
||||
if (device->event_cb) {
|
||||
device->event_cb = NULL;
|
||||
cancel_work_sync(&device->event_work);
|
||||
}
|
||||
|
||||
driver = to_ishtp_cl_driver(dev->driver);
|
||||
if (!driver->remove) {
|
||||
dev->driver = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return driver->remove(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_device_suspend() - Bus suspend callback
|
||||
* @dev: device
|
||||
*
|
||||
* Called during device suspend process.
|
||||
*
|
||||
* Return: Return value from driver suspend() call.
|
||||
*/
|
||||
static int ishtp_cl_device_suspend(struct device *dev)
|
||||
{
|
||||
struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
|
||||
struct ishtp_cl_driver *driver;
|
||||
int ret = 0;
|
||||
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
driver = to_ishtp_cl_driver(dev->driver);
|
||||
if (driver && driver->driver.pm) {
|
||||
if (driver->driver.pm->suspend)
|
||||
ret = driver->driver.pm->suspend(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_device_resume() - Bus resume callback
|
||||
* @dev: device
|
||||
*
|
||||
* Called during device resume process.
|
||||
*
|
||||
* Return: Return value from driver resume() call.
|
||||
*/
|
||||
static int ishtp_cl_device_resume(struct device *dev)
|
||||
{
|
||||
struct ishtp_cl_device *device = to_ishtp_cl_device(dev);
|
||||
struct ishtp_cl_driver *driver;
|
||||
int ret = 0;
|
||||
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* When ISH needs hard reset, it is done asynchrnously, hence bus
|
||||
* resume will be called before full ISH resume
|
||||
*/
|
||||
if (device->ishtp_dev->resume_flag)
|
||||
return 0;
|
||||
|
||||
driver = to_ishtp_cl_driver(dev->driver);
|
||||
if (driver && driver->driver.pm) {
|
||||
if (driver->driver.pm->resume)
|
||||
ret = driver->driver.pm->resume(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_device_reset() - Reset callback
|
||||
* @device: ishtp client device instance
|
||||
*
|
||||
* This is a callback when HW reset is done and the device need
|
||||
* reinit.
|
||||
*
|
||||
* Return: Return value from driver reset() call.
|
||||
*/
|
||||
static int ishtp_cl_device_reset(struct ishtp_cl_device *device)
|
||||
{
|
||||
struct ishtp_cl_driver *driver;
|
||||
int ret = 0;
|
||||
|
||||
device->event_cb = NULL;
|
||||
cancel_work_sync(&device->event_work);
|
||||
|
||||
driver = to_ishtp_cl_driver(device->dev.driver);
|
||||
if (driver && driver->reset)
|
||||
ret = driver->reset(device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
|
||||
char *buf)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = snprintf(buf, PAGE_SIZE, "ishtp:%s\n", dev_name(dev));
|
||||
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
|
||||
}
|
||||
|
||||
static struct device_attribute ishtp_cl_dev_attrs[] = {
|
||||
__ATTR_RO(modalias),
|
||||
__ATTR_NULL,
|
||||
};
|
||||
|
||||
static int ishtp_cl_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
if (add_uevent_var(env, "MODALIAS=ishtp:%s", dev_name(dev)))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ishtp_cl_bus_dev_pm_ops = {
|
||||
/* Suspend callbacks */
|
||||
.suspend = ishtp_cl_device_suspend,
|
||||
.resume = ishtp_cl_device_resume,
|
||||
/* Hibernate callbacks */
|
||||
.freeze = ishtp_cl_device_suspend,
|
||||
.thaw = ishtp_cl_device_resume,
|
||||
.restore = ishtp_cl_device_resume,
|
||||
};
|
||||
|
||||
static struct bus_type ishtp_cl_bus_type = {
|
||||
.name = "ishtp",
|
||||
.dev_attrs = ishtp_cl_dev_attrs,
|
||||
.probe = ishtp_cl_device_probe,
|
||||
.remove = ishtp_cl_device_remove,
|
||||
.pm = &ishtp_cl_bus_dev_pm_ops,
|
||||
.uevent = ishtp_cl_uevent,
|
||||
};
|
||||
|
||||
static void ishtp_cl_dev_release(struct device *dev)
|
||||
{
|
||||
kfree(to_ishtp_cl_device(dev));
|
||||
}
|
||||
|
||||
static struct device_type ishtp_cl_device_type = {
|
||||
.release = ishtp_cl_dev_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* ishtp_bus_add_device() - Function to create device on bus
|
||||
* @dev: ishtp device
|
||||
* @uuid: uuid of the client
|
||||
* @name: Name of the client
|
||||
*
|
||||
* Allocate ISHTP bus client device, attach it to uuid
|
||||
* and register with ISHTP bus.
|
||||
*
|
||||
* Return: ishtp_cl_device pointer or NULL on failure
|
||||
*/
|
||||
static struct ishtp_cl_device *ishtp_bus_add_device(struct ishtp_device *dev,
|
||||
uuid_le uuid, char *name)
|
||||
{
|
||||
struct ishtp_cl_device *device;
|
||||
int status;
|
||||
unsigned long flags;
|
||||
struct list_head *pos;
|
||||
|
||||
spin_lock_irqsave(&dev->device_list_lock, flags);
|
||||
list_for_each(pos, &dev->device_list) {
|
||||
device = list_entry(pos, struct ishtp_cl_device, device_link);
|
||||
if (!strcmp(name, dev_name(&device->dev))) {
|
||||
device->fw_client = &dev->fw_clients[
|
||||
dev->fw_client_presentation_num - 1];
|
||||
spin_unlock_irqrestore(&dev->device_list_lock, flags);
|
||||
ishtp_cl_device_reset(device);
|
||||
return device;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->device_list_lock, flags);
|
||||
|
||||
device = kzalloc(sizeof(struct ishtp_cl_device), GFP_KERNEL);
|
||||
if (!device)
|
||||
return NULL;
|
||||
|
||||
device->dev.parent = dev->devc;
|
||||
device->dev.bus = &ishtp_cl_bus_type;
|
||||
device->dev.type = &ishtp_cl_device_type;
|
||||
device->ishtp_dev = dev;
|
||||
|
||||
device->fw_client =
|
||||
&dev->fw_clients[dev->fw_client_presentation_num - 1];
|
||||
|
||||
dev_set_name(&device->dev, "%s", name);
|
||||
|
||||
spin_lock_irqsave(&dev->device_list_lock, flags);
|
||||
list_add_tail(&device->device_link, &dev->device_list);
|
||||
spin_unlock_irqrestore(&dev->device_list_lock, flags);
|
||||
|
||||
status = device_register(&device->dev);
|
||||
if (status) {
|
||||
spin_lock_irqsave(&dev->device_list_lock, flags);
|
||||
list_del(&device->device_link);
|
||||
spin_unlock_irqrestore(&dev->device_list_lock, flags);
|
||||
dev_err(dev->devc, "Failed to register ISHTP client device\n");
|
||||
kfree(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ishtp_device_ready = true;
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_bus_remove_device() - Function to relase device on bus
|
||||
* @device: client device instance
|
||||
*
|
||||
* This is a counterpart of ishtp_bus_add_device.
|
||||
* Device is unregistered.
|
||||
* the device structure is freed in 'ishtp_cl_dev_release' function
|
||||
* Called only during error in pci driver init path.
|
||||
*/
|
||||
static void ishtp_bus_remove_device(struct ishtp_cl_device *device)
|
||||
{
|
||||
device_unregister(&device->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* __ishtp_cl_driver_register() - Client driver register
|
||||
* @driver: the client driver instance
|
||||
* @owner: Owner of this driver module
|
||||
*
|
||||
* Once a client driver is probed, it created a client
|
||||
* instance and registers with the bus.
|
||||
*
|
||||
* Return: Return value of driver_register or -ENODEV if not ready
|
||||
*/
|
||||
int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
|
||||
struct module *owner)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!ishtp_device_ready)
|
||||
return -ENODEV;
|
||||
|
||||
driver->driver.name = driver->name;
|
||||
driver->driver.owner = owner;
|
||||
driver->driver.bus = &ishtp_cl_bus_type;
|
||||
|
||||
err = driver_register(&driver->driver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(__ishtp_cl_driver_register);
|
||||
|
||||
/**
|
||||
* ishtp_cl_driver_unregister() - Client driver unregister
|
||||
* @driver: the client driver instance
|
||||
*
|
||||
* Unregister client during device removal process.
|
||||
*/
|
||||
void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver)
|
||||
{
|
||||
driver_unregister(&driver->driver);
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_cl_driver_unregister);
|
||||
|
||||
/**
|
||||
* ishtp_bus_event_work() - event work function
|
||||
* @work: work struct pointer
|
||||
*
|
||||
* Once an event is received for a client this work
|
||||
* function is called. If the device has registered a
|
||||
* callback then the callback is called.
|
||||
*/
|
||||
static void ishtp_bus_event_work(struct work_struct *work)
|
||||
{
|
||||
struct ishtp_cl_device *device;
|
||||
|
||||
device = container_of(work, struct ishtp_cl_device, event_work);
|
||||
|
||||
if (device->event_cb)
|
||||
device->event_cb(device);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_bus_rx_event() - schedule event work
|
||||
* @device: client device instance
|
||||
*
|
||||
* Once an event is received for a client this schedules
|
||||
* a work function to process.
|
||||
*/
|
||||
void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device)
|
||||
{
|
||||
if (!device || !device->event_cb)
|
||||
return;
|
||||
|
||||
if (device->event_cb)
|
||||
schedule_work(&device->event_work);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_register_event_cb() - Register callback
|
||||
* @device: client device instance
|
||||
* @event_cb: Event processor for an client
|
||||
*
|
||||
* Register a callback for events, called from client driver
|
||||
*
|
||||
* Return: Return 0 or -EALREADY if already registered
|
||||
*/
|
||||
int ishtp_register_event_cb(struct ishtp_cl_device *device,
|
||||
void (*event_cb)(struct ishtp_cl_device *))
|
||||
{
|
||||
if (device->event_cb)
|
||||
return -EALREADY;
|
||||
|
||||
device->event_cb = event_cb;
|
||||
INIT_WORK(&device->event_work, ishtp_bus_event_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_register_event_cb);
|
||||
|
||||
/**
|
||||
* ishtp_get_device() - update usage count for the device
|
||||
* @cl_device: client device instance
|
||||
*
|
||||
* Increment the usage count. The device can't be deleted
|
||||
*/
|
||||
void ishtp_get_device(struct ishtp_cl_device *cl_device)
|
||||
{
|
||||
cl_device->reference_count++;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_get_device);
|
||||
|
||||
/**
|
||||
* ishtp_put_device() - decrement usage count for the device
|
||||
* @cl_device: client device instance
|
||||
*
|
||||
* Decrement the usage count. The device can be deleted is count = 0
|
||||
*/
|
||||
void ishtp_put_device(struct ishtp_cl_device *cl_device)
|
||||
{
|
||||
cl_device->reference_count--;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_put_device);
|
||||
|
||||
/**
|
||||
* ishtp_bus_new_client() - Create a new client
|
||||
* @dev: ISHTP device instance
|
||||
*
|
||||
* Once bus protocol enumerates a client, this is called
|
||||
* to add a device for the client.
|
||||
*
|
||||
* Return: 0 on success or error code on failure
|
||||
*/
|
||||
int ishtp_bus_new_client(struct ishtp_device *dev)
|
||||
{
|
||||
int i;
|
||||
char *dev_name;
|
||||
struct ishtp_cl_device *cl_device;
|
||||
uuid_le device_uuid;
|
||||
|
||||
/*
|
||||
* For all reported clients, create an unconnected client and add its
|
||||
* device to ISHTP bus.
|
||||
* If appropriate driver has loaded, this will trigger its probe().
|
||||
* Otherwise, probe() will be called when driver is loaded
|
||||
*/
|
||||
i = dev->fw_client_presentation_num - 1;
|
||||
device_uuid = dev->fw_clients[i].props.protocol_name;
|
||||
dev_name = kasprintf(GFP_KERNEL,
|
||||
"{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
||||
device_uuid.b[3], device_uuid.b[2], device_uuid.b[1],
|
||||
device_uuid.b[0], device_uuid.b[5], device_uuid.b[4],
|
||||
device_uuid.b[7], device_uuid.b[6], device_uuid.b[8],
|
||||
device_uuid.b[9], device_uuid.b[10], device_uuid.b[11],
|
||||
device_uuid.b[12], device_uuid.b[13], device_uuid.b[14],
|
||||
device_uuid.b[15]);
|
||||
if (!dev_name)
|
||||
return -ENOMEM;
|
||||
|
||||
cl_device = ishtp_bus_add_device(dev, device_uuid, dev_name);
|
||||
if (!cl_device) {
|
||||
kfree(dev_name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
kfree(dev_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_device_bind() - bind a device
|
||||
* @cl: ishtp client device
|
||||
*
|
||||
* Binds connected ishtp_cl to ISHTP bus device
|
||||
*
|
||||
* Return: 0 on success or fault code
|
||||
*/
|
||||
int ishtp_cl_device_bind(struct ishtp_cl *cl)
|
||||
{
|
||||
struct ishtp_cl_device *cl_device;
|
||||
unsigned long flags;
|
||||
int rv;
|
||||
|
||||
if (!cl->fw_client_id || cl->state != ISHTP_CL_CONNECTED)
|
||||
return -EFAULT;
|
||||
|
||||
rv = -ENOENT;
|
||||
spin_lock_irqsave(&cl->dev->device_list_lock, flags);
|
||||
list_for_each_entry(cl_device, &cl->dev->device_list,
|
||||
device_link) {
|
||||
if (cl_device->fw_client->client_id == cl->fw_client_id) {
|
||||
cl->device = cl_device;
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&cl->dev->device_list_lock, flags);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_bus_remove_all_clients() - Remove all clients
|
||||
* @ishtp_dev: ishtp device
|
||||
* @warm_reset: Reset due to FW reset dure to errors or S3 suspend
|
||||
*
|
||||
* This is part of reset/remove flow. This function the main processing
|
||||
* only targets error processing, if the FW has forced reset or
|
||||
* error to remove connected clients. When warm reset the client devices are
|
||||
* not removed.
|
||||
*/
|
||||
void ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev,
|
||||
bool warm_reset)
|
||||
{
|
||||
struct ishtp_cl_device *cl_device, *n;
|
||||
struct ishtp_cl *cl;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ishtp_dev->cl_list_lock, flags);
|
||||
list_for_each_entry(cl, &ishtp_dev->cl_list, link) {
|
||||
cl->state = ISHTP_CL_DISCONNECTED;
|
||||
|
||||
/*
|
||||
* Wake any pending process. The waiter would check dev->state
|
||||
* and determine that it's not enabled already,
|
||||
* and will return error to its caller
|
||||
*/
|
||||
wake_up_interruptible(&cl->wait_ctrl_res);
|
||||
|
||||
/* Disband any pending read/write requests and free rb */
|
||||
ishtp_cl_flush_queues(cl);
|
||||
|
||||
/* Remove all free and in_process rings, both Rx and Tx */
|
||||
ishtp_cl_free_rx_ring(cl);
|
||||
ishtp_cl_free_tx_ring(cl);
|
||||
|
||||
/*
|
||||
* Free client and ISHTP bus client device structures
|
||||
* don't free host client because it is part of the OS fd
|
||||
* structure
|
||||
*/
|
||||
}
|
||||
spin_unlock_irqrestore(&ishtp_dev->cl_list_lock, flags);
|
||||
|
||||
/* Release DMA buffers for client messages */
|
||||
ishtp_cl_free_dma_buf(ishtp_dev);
|
||||
|
||||
/* remove bus clients */
|
||||
spin_lock_irqsave(&ishtp_dev->device_list_lock, flags);
|
||||
list_for_each_entry_safe(cl_device, n, &ishtp_dev->device_list,
|
||||
device_link) {
|
||||
if (warm_reset && cl_device->reference_count)
|
||||
continue;
|
||||
|
||||
list_del(&cl_device->device_link);
|
||||
spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags);
|
||||
ishtp_bus_remove_device(cl_device);
|
||||
spin_lock_irqsave(&ishtp_dev->device_list_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags);
|
||||
|
||||
/* Free all client structures */
|
||||
spin_lock_irqsave(&ishtp_dev->fw_clients_lock, flags);
|
||||
kfree(ishtp_dev->fw_clients);
|
||||
ishtp_dev->fw_clients = NULL;
|
||||
ishtp_dev->fw_clients_num = 0;
|
||||
ishtp_dev->fw_client_presentation_num = 0;
|
||||
ishtp_dev->fw_client_index = 0;
|
||||
bitmap_zero(ishtp_dev->fw_clients_map, ISHTP_CLIENTS_MAX);
|
||||
spin_unlock_irqrestore(&ishtp_dev->fw_clients_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_bus_remove_all_clients);
|
||||
|
||||
/**
|
||||
* ishtp_reset_handler() - IPC reset handler
|
||||
* @dev: ishtp device
|
||||
*
|
||||
* ISHTP Handler for IPC_RESET notification
|
||||
*/
|
||||
void ishtp_reset_handler(struct ishtp_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* Handle FW-initiated reset */
|
||||
dev->dev_state = ISHTP_DEV_RESETTING;
|
||||
|
||||
/* Clear BH processing queue - no further HBMs */
|
||||
spin_lock_irqsave(&dev->rd_msg_spinlock, flags);
|
||||
dev->rd_msg_fifo_head = dev->rd_msg_fifo_tail = 0;
|
||||
spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags);
|
||||
|
||||
/* Handle ISH FW reset against upper layers */
|
||||
ishtp_bus_remove_all_clients(dev, true);
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_reset_handler);
|
||||
|
||||
/**
|
||||
* ishtp_reset_compl_handler() - Reset completion handler
|
||||
* @dev: ishtp device
|
||||
*
|
||||
* ISHTP handler for IPC_RESET sequence completion to start
|
||||
* host message bus start protocol sequence.
|
||||
*/
|
||||
void ishtp_reset_compl_handler(struct ishtp_device *dev)
|
||||
{
|
||||
dev->dev_state = ISHTP_DEV_INIT_CLIENTS;
|
||||
dev->hbm_state = ISHTP_HBM_START;
|
||||
ishtp_hbm_start_req(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_reset_compl_handler);
|
||||
|
||||
/**
|
||||
* ishtp_use_dma_transfer() - Function to use DMA
|
||||
*
|
||||
* This interface is used to enable usage of DMA
|
||||
*
|
||||
* Return non zero if DMA can be enabled
|
||||
*/
|
||||
int ishtp_use_dma_transfer(void)
|
||||
{
|
||||
return ishtp_use_dma;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_bus_register() - Function to register bus
|
||||
*
|
||||
* This register ishtp bus
|
||||
*
|
||||
* Return: Return output of bus_register
|
||||
*/
|
||||
static int __init ishtp_bus_register(void)
|
||||
{
|
||||
return bus_register(&ishtp_cl_bus_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_bus_unregister() - Function to unregister bus
|
||||
*
|
||||
* This unregister ishtp bus
|
||||
*/
|
||||
static void __exit ishtp_bus_unregister(void)
|
||||
{
|
||||
bus_unregister(&ishtp_cl_bus_type);
|
||||
}
|
||||
|
||||
module_init(ishtp_bus_register);
|
||||
module_exit(ishtp_bus_unregister);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
114
drivers/hid/intel-ish-hid/ishtp/bus.h
Normal file
114
drivers/hid/intel-ish-hid/ishtp/bus.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* ISHTP bus definitions
|
||||
*
|
||||
* Copyright (c) 2014-2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
#ifndef _LINUX_ISHTP_CL_BUS_H
|
||||
#define _LINUX_ISHTP_CL_BUS_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
struct ishtp_cl;
|
||||
struct ishtp_cl_device;
|
||||
struct ishtp_device;
|
||||
struct ishtp_msg_hdr;
|
||||
|
||||
/**
|
||||
* struct ishtp_cl_device - ISHTP device handle
|
||||
* @dev: device pointer
|
||||
* @ishtp_dev: pointer to ishtp device structure to primarily to access
|
||||
* hw device operation callbacks and properties
|
||||
* @fw_client: fw_client pointer to get fw information like protocol name
|
||||
* max message length etc.
|
||||
* @device_link: Link to next client in the list on a bus
|
||||
* @event_work: Used to schedule rx event for client
|
||||
* @driver_data: Storage driver private data
|
||||
* @reference_count: Used for get/put device
|
||||
* @event_cb: Callback to driver to send events
|
||||
*
|
||||
* An ishtp_cl_device pointer is returned from ishtp_add_device()
|
||||
* and links ISHTP bus clients to their actual host client pointer.
|
||||
* Drivers for ISHTP devices will get an ishtp_cl_device pointer
|
||||
* when being probed and shall use it for doing bus I/O.
|
||||
*/
|
||||
struct ishtp_cl_device {
|
||||
struct device dev;
|
||||
struct ishtp_device *ishtp_dev;
|
||||
struct ishtp_fw_client *fw_client;
|
||||
struct list_head device_link;
|
||||
struct work_struct event_work;
|
||||
void *driver_data;
|
||||
int reference_count;
|
||||
void (*event_cb)(struct ishtp_cl_device *device);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ishtp_cl_device - ISHTP device handle
|
||||
* @driver: driver instance on a bus
|
||||
* @name: Name of the device for probe
|
||||
* @probe: driver callback for device probe
|
||||
* @remove: driver callback on device removal
|
||||
*
|
||||
* Client drivers defines to get probed/removed for ISHTP client device.
|
||||
*/
|
||||
struct ishtp_cl_driver {
|
||||
struct device_driver driver;
|
||||
const char *name;
|
||||
int (*probe)(struct ishtp_cl_device *dev);
|
||||
int (*remove)(struct ishtp_cl_device *dev);
|
||||
int (*reset)(struct ishtp_cl_device *dev);
|
||||
const struct dev_pm_ops *pm;
|
||||
};
|
||||
|
||||
|
||||
int ishtp_bus_new_client(struct ishtp_device *dev);
|
||||
void ishtp_remove_all_clients(struct ishtp_device *dev);
|
||||
int ishtp_cl_device_bind(struct ishtp_cl *cl);
|
||||
void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device);
|
||||
|
||||
/* Write a multi-fragment message */
|
||||
int ishtp_send_msg(struct ishtp_device *dev,
|
||||
struct ishtp_msg_hdr *hdr, void *msg,
|
||||
void (*ipc_send_compl)(void *),
|
||||
void *ipc_send_compl_prm);
|
||||
|
||||
/* Write a single-fragment message */
|
||||
int ishtp_write_message(struct ishtp_device *dev,
|
||||
struct ishtp_msg_hdr *hdr,
|
||||
unsigned char *buf);
|
||||
|
||||
/* Use DMA to send/receive messages */
|
||||
int ishtp_use_dma_transfer(void);
|
||||
|
||||
/* Exported functions */
|
||||
void ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev,
|
||||
bool warm_reset);
|
||||
|
||||
void ishtp_recv(struct ishtp_device *dev);
|
||||
void ishtp_reset_handler(struct ishtp_device *dev);
|
||||
void ishtp_reset_compl_handler(struct ishtp_device *dev);
|
||||
|
||||
void ishtp_put_device(struct ishtp_cl_device *);
|
||||
void ishtp_get_device(struct ishtp_cl_device *);
|
||||
|
||||
int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
|
||||
struct module *owner);
|
||||
#define ishtp_cl_driver_register(driver) \
|
||||
__ishtp_cl_driver_register(driver, THIS_MODULE)
|
||||
void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver);
|
||||
|
||||
int ishtp_register_event_cb(struct ishtp_cl_device *device,
|
||||
void (*read_cb)(struct ishtp_cl_device *));
|
||||
int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *cuuid);
|
||||
|
||||
#endif /* _LINUX_ISHTP_CL_BUS_H */
|
258
drivers/hid/intel-ish-hid/ishtp/client-buffers.c
Normal file
258
drivers/hid/intel-ish-hid/ishtp/client-buffers.c
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* ISHTP Ring Buffers
|
||||
*
|
||||
* Copyright (c) 2003-2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "client.h"
|
||||
|
||||
/**
|
||||
* ishtp_cl_alloc_rx_ring() - Allocate RX ring buffers
|
||||
* @cl: client device instance
|
||||
*
|
||||
* Allocate and initialize RX ring buffers
|
||||
*
|
||||
* Return: 0 on success else -ENOMEM
|
||||
*/
|
||||
int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl)
|
||||
{
|
||||
size_t len = cl->device->fw_client->props.max_msg_length;
|
||||
int j;
|
||||
struct ishtp_cl_rb *rb;
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
for (j = 0; j < cl->rx_ring_size; ++j) {
|
||||
rb = ishtp_io_rb_init(cl);
|
||||
if (!rb) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ret = ishtp_io_rb_alloc_buf(rb, len);
|
||||
if (ret)
|
||||
goto out;
|
||||
spin_lock_irqsave(&cl->free_list_spinlock, flags);
|
||||
list_add_tail(&rb->list, &cl->free_rb_list.list);
|
||||
spin_unlock_irqrestore(&cl->free_list_spinlock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
dev_err(&cl->device->dev, "error in allocating Rx buffers\n");
|
||||
ishtp_cl_free_rx_ring(cl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_alloc_tx_ring() - Allocate TX ring buffers
|
||||
* @cl: client device instance
|
||||
*
|
||||
* Allocate and initialize TX ring buffers
|
||||
*
|
||||
* Return: 0 on success else -ENOMEM
|
||||
*/
|
||||
int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl)
|
||||
{
|
||||
size_t len = cl->device->fw_client->props.max_msg_length;
|
||||
int j;
|
||||
unsigned long flags;
|
||||
|
||||
/* Allocate pool to free Tx bufs */
|
||||
for (j = 0; j < cl->tx_ring_size; ++j) {
|
||||
struct ishtp_cl_tx_ring *tx_buf;
|
||||
|
||||
tx_buf = kmalloc(sizeof(struct ishtp_cl_tx_ring), GFP_KERNEL);
|
||||
if (!tx_buf)
|
||||
goto out;
|
||||
|
||||
memset(tx_buf, 0, sizeof(struct ishtp_cl_tx_ring));
|
||||
tx_buf->send_buf.data = kmalloc(len, GFP_KERNEL);
|
||||
if (!tx_buf->send_buf.data) {
|
||||
kfree(tx_buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&cl->tx_free_list_spinlock, flags);
|
||||
list_add_tail(&tx_buf->list, &cl->tx_free_list.list);
|
||||
spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags);
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
dev_err(&cl->device->dev, "error in allocating Tx pool\n");
|
||||
ishtp_cl_free_rx_ring(cl);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_free_rx_ring() - Free RX ring buffers
|
||||
* @cl: client device instance
|
||||
*
|
||||
* Free RX ring buffers
|
||||
*/
|
||||
void ishtp_cl_free_rx_ring(struct ishtp_cl *cl)
|
||||
{
|
||||
struct ishtp_cl_rb *rb;
|
||||
unsigned long flags;
|
||||
|
||||
/* release allocated memory - pass over free_rb_list */
|
||||
spin_lock_irqsave(&cl->free_list_spinlock, flags);
|
||||
while (!list_empty(&cl->free_rb_list.list)) {
|
||||
rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb,
|
||||
list);
|
||||
list_del(&rb->list);
|
||||
kfree(rb->buffer.data);
|
||||
kfree(rb);
|
||||
}
|
||||
spin_unlock_irqrestore(&cl->free_list_spinlock, flags);
|
||||
/* release allocated memory - pass over in_process_list */
|
||||
spin_lock_irqsave(&cl->in_process_spinlock, flags);
|
||||
while (!list_empty(&cl->in_process_list.list)) {
|
||||
rb = list_entry(cl->in_process_list.list.next,
|
||||
struct ishtp_cl_rb, list);
|
||||
list_del(&rb->list);
|
||||
kfree(rb->buffer.data);
|
||||
kfree(rb);
|
||||
}
|
||||
spin_unlock_irqrestore(&cl->in_process_spinlock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_free_tx_ring() - Free TX ring buffers
|
||||
* @cl: client device instance
|
||||
*
|
||||
* Free TX ring buffers
|
||||
*/
|
||||
void ishtp_cl_free_tx_ring(struct ishtp_cl *cl)
|
||||
{
|
||||
struct ishtp_cl_tx_ring *tx_buf;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&cl->tx_free_list_spinlock, flags);
|
||||
/* release allocated memory - pass over tx_free_list */
|
||||
while (!list_empty(&cl->tx_free_list.list)) {
|
||||
tx_buf = list_entry(cl->tx_free_list.list.next,
|
||||
struct ishtp_cl_tx_ring, list);
|
||||
list_del(&tx_buf->list);
|
||||
kfree(tx_buf->send_buf.data);
|
||||
kfree(tx_buf);
|
||||
}
|
||||
spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags);
|
||||
|
||||
spin_lock_irqsave(&cl->tx_list_spinlock, flags);
|
||||
/* release allocated memory - pass over tx_list */
|
||||
while (!list_empty(&cl->tx_list.list)) {
|
||||
tx_buf = list_entry(cl->tx_list.list.next,
|
||||
struct ishtp_cl_tx_ring, list);
|
||||
list_del(&tx_buf->list);
|
||||
kfree(tx_buf->send_buf.data);
|
||||
kfree(tx_buf);
|
||||
}
|
||||
spin_unlock_irqrestore(&cl->tx_list_spinlock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_io_rb_free() - Free IO request block
|
||||
* @rb: IO request block
|
||||
*
|
||||
* Free io request block memory
|
||||
*/
|
||||
void ishtp_io_rb_free(struct ishtp_cl_rb *rb)
|
||||
{
|
||||
if (rb == NULL)
|
||||
return;
|
||||
|
||||
kfree(rb->buffer.data);
|
||||
kfree(rb);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_io_rb_init() - Allocate and init IO request block
|
||||
* @cl: client device instance
|
||||
*
|
||||
* Allocate and initialize request block
|
||||
*
|
||||
* Return: Allocted IO request block pointer
|
||||
*/
|
||||
struct ishtp_cl_rb *ishtp_io_rb_init(struct ishtp_cl *cl)
|
||||
{
|
||||
struct ishtp_cl_rb *rb;
|
||||
|
||||
rb = kzalloc(sizeof(struct ishtp_cl_rb), GFP_KERNEL);
|
||||
if (!rb)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&rb->list);
|
||||
rb->cl = cl;
|
||||
rb->buf_idx = 0;
|
||||
return rb;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_io_rb_alloc_buf() - Allocate and init response buffer
|
||||
* @rb: IO request block
|
||||
* @length: length of response buffer
|
||||
*
|
||||
* Allocate respose buffer
|
||||
*
|
||||
* Return: 0 on success else -ENOMEM
|
||||
*/
|
||||
int ishtp_io_rb_alloc_buf(struct ishtp_cl_rb *rb, size_t length)
|
||||
{
|
||||
if (!rb)
|
||||
return -EINVAL;
|
||||
|
||||
if (length == 0)
|
||||
return 0;
|
||||
|
||||
rb->buffer.data = kmalloc(length, GFP_KERNEL);
|
||||
if (!rb->buffer.data)
|
||||
return -ENOMEM;
|
||||
|
||||
rb->buffer.size = length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_io_rb_recycle() - Recycle IO request blocks
|
||||
* @rb: IO request block
|
||||
*
|
||||
* Re-append rb to its client's free list and send flow control if needed
|
||||
*
|
||||
* Return: 0 on success else -EFAULT
|
||||
*/
|
||||
int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb)
|
||||
{
|
||||
struct ishtp_cl *cl;
|
||||
int rets = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (!rb || !rb->cl)
|
||||
return -EFAULT;
|
||||
|
||||
cl = rb->cl;
|
||||
spin_lock_irqsave(&cl->free_list_spinlock, flags);
|
||||
list_add_tail(&rb->list, &cl->free_rb_list.list);
|
||||
spin_unlock_irqrestore(&cl->free_list_spinlock, flags);
|
||||
|
||||
/*
|
||||
* If we returned the first buffer to empty 'free' list,
|
||||
* send flow control
|
||||
*/
|
||||
if (!cl->out_flow_ctrl_creds)
|
||||
rets = ishtp_cl_read_start(cl);
|
||||
|
||||
return rets;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_cl_io_rb_recycle);
|
1054
drivers/hid/intel-ish-hid/ishtp/client.c
Normal file
1054
drivers/hid/intel-ish-hid/ishtp/client.c
Normal file
File diff suppressed because it is too large
Load Diff
182
drivers/hid/intel-ish-hid/ishtp/client.h
Normal file
182
drivers/hid/intel-ish-hid/ishtp/client.h
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* ISHTP client logic
|
||||
*
|
||||
* Copyright (c) 2003-2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifndef _ISHTP_CLIENT_H_
|
||||
#define _ISHTP_CLIENT_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "ishtp-dev.h"
|
||||
|
||||
/* Client state */
|
||||
enum cl_state {
|
||||
ISHTP_CL_INITIALIZING = 0,
|
||||
ISHTP_CL_CONNECTING,
|
||||
ISHTP_CL_CONNECTED,
|
||||
ISHTP_CL_DISCONNECTING,
|
||||
ISHTP_CL_DISCONNECTED
|
||||
};
|
||||
|
||||
/* Tx and Rx ring size */
|
||||
#define CL_DEF_RX_RING_SIZE 2
|
||||
#define CL_DEF_TX_RING_SIZE 2
|
||||
#define CL_MAX_RX_RING_SIZE 32
|
||||
#define CL_MAX_TX_RING_SIZE 32
|
||||
|
||||
#define DMA_SLOT_SIZE 4096
|
||||
/* Number of IPC fragments after which it's worth sending via DMA */
|
||||
#define DMA_WORTH_THRESHOLD 3
|
||||
|
||||
/* DMA/IPC Tx paths. Other the default means enforcement */
|
||||
#define CL_TX_PATH_DEFAULT 0
|
||||
#define CL_TX_PATH_IPC 1
|
||||
#define CL_TX_PATH_DMA 2
|
||||
|
||||
/* Client Tx buffer list entry */
|
||||
struct ishtp_cl_tx_ring {
|
||||
struct list_head list;
|
||||
struct ishtp_msg_data send_buf;
|
||||
};
|
||||
|
||||
/* ISHTP client instance */
|
||||
struct ishtp_cl {
|
||||
struct list_head link;
|
||||
struct ishtp_device *dev;
|
||||
enum cl_state state;
|
||||
int status;
|
||||
|
||||
/* Link to ISHTP bus device */
|
||||
struct ishtp_cl_device *device;
|
||||
|
||||
/* ID of client connected */
|
||||
uint8_t host_client_id;
|
||||
uint8_t fw_client_id;
|
||||
uint8_t ishtp_flow_ctrl_creds;
|
||||
uint8_t out_flow_ctrl_creds;
|
||||
|
||||
/* dma */
|
||||
int last_tx_path;
|
||||
/* 0: ack wasn't received,1:ack was received */
|
||||
int last_dma_acked;
|
||||
unsigned char *last_dma_addr;
|
||||
/* 0: ack wasn't received,1:ack was received */
|
||||
int last_ipc_acked;
|
||||
|
||||
/* Rx ring buffer pool */
|
||||
unsigned int rx_ring_size;
|
||||
struct ishtp_cl_rb free_rb_list;
|
||||
spinlock_t free_list_spinlock;
|
||||
/* Rx in-process list */
|
||||
struct ishtp_cl_rb in_process_list;
|
||||
spinlock_t in_process_spinlock;
|
||||
|
||||
/* Client Tx buffers list */
|
||||
unsigned int tx_ring_size;
|
||||
struct ishtp_cl_tx_ring tx_list, tx_free_list;
|
||||
spinlock_t tx_list_spinlock;
|
||||
spinlock_t tx_free_list_spinlock;
|
||||
size_t tx_offs; /* Offset in buffer at head of 'tx_list' */
|
||||
|
||||
/**
|
||||
* if we get a FC, and the list is not empty, we must know whether we
|
||||
* are at the middle of sending.
|
||||
* if so -need to increase FC counter, otherwise, need to start sending
|
||||
* the first msg in list
|
||||
* (!)This is for counting-FC implementation only. Within single-FC the
|
||||
* other party may NOT send FC until it receives complete message
|
||||
*/
|
||||
int sending;
|
||||
|
||||
/* Send FC spinlock */
|
||||
spinlock_t fc_spinlock;
|
||||
|
||||
/* wait queue for connect and disconnect response from FW */
|
||||
wait_queue_head_t wait_ctrl_res;
|
||||
|
||||
/* Error stats */
|
||||
unsigned int err_send_msg;
|
||||
unsigned int err_send_fc;
|
||||
|
||||
/* Send/recv stats */
|
||||
unsigned int send_msg_cnt_ipc;
|
||||
unsigned int send_msg_cnt_dma;
|
||||
unsigned int recv_msg_cnt_ipc;
|
||||
unsigned int recv_msg_cnt_dma;
|
||||
unsigned int recv_msg_num_frags;
|
||||
unsigned int ishtp_flow_ctrl_cnt;
|
||||
unsigned int out_flow_ctrl_cnt;
|
||||
|
||||
/* Rx msg ... out FC timing */
|
||||
struct timespec ts_rx;
|
||||
struct timespec ts_out_fc;
|
||||
struct timespec ts_max_fc_delay;
|
||||
void *client_data;
|
||||
};
|
||||
|
||||
/* Client connection managenment internal functions */
|
||||
int ishtp_can_client_connect(struct ishtp_device *ishtp_dev, uuid_le *uuid);
|
||||
int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id);
|
||||
void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl);
|
||||
void recv_ishtp_cl_msg(struct ishtp_device *dev,
|
||||
struct ishtp_msg_hdr *ishtp_hdr);
|
||||
int ishtp_cl_read_start(struct ishtp_cl *cl);
|
||||
|
||||
/* Ring Buffer I/F */
|
||||
int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl);
|
||||
int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl);
|
||||
void ishtp_cl_free_rx_ring(struct ishtp_cl *cl);
|
||||
void ishtp_cl_free_tx_ring(struct ishtp_cl *cl);
|
||||
|
||||
/* DMA I/F functions */
|
||||
void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg,
|
||||
struct dma_xfer_hbm *hbm);
|
||||
void ishtp_cl_alloc_dma_buf(struct ishtp_device *dev);
|
||||
void ishtp_cl_free_dma_buf(struct ishtp_device *dev);
|
||||
void *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev,
|
||||
uint32_t size);
|
||||
void ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev,
|
||||
void *msg_addr,
|
||||
uint8_t size);
|
||||
|
||||
/* Request blocks alloc/free I/F */
|
||||
struct ishtp_cl_rb *ishtp_io_rb_init(struct ishtp_cl *cl);
|
||||
void ishtp_io_rb_free(struct ishtp_cl_rb *priv_rb);
|
||||
int ishtp_io_rb_alloc_buf(struct ishtp_cl_rb *rb, size_t length);
|
||||
|
||||
/**
|
||||
* ishtp_cl_cmp_id - tells if file private data have same id
|
||||
* returns true - if ids are the same and not NULL
|
||||
*/
|
||||
static inline bool ishtp_cl_cmp_id(const struct ishtp_cl *cl1,
|
||||
const struct ishtp_cl *cl2)
|
||||
{
|
||||
return cl1 && cl2 &&
|
||||
(cl1->host_client_id == cl2->host_client_id) &&
|
||||
(cl1->fw_client_id == cl2->fw_client_id);
|
||||
}
|
||||
|
||||
/* exported functions from ISHTP under client management scope */
|
||||
struct ishtp_cl *ishtp_cl_allocate(struct ishtp_device *dev);
|
||||
void ishtp_cl_free(struct ishtp_cl *cl);
|
||||
int ishtp_cl_link(struct ishtp_cl *cl, int id);
|
||||
void ishtp_cl_unlink(struct ishtp_cl *cl);
|
||||
int ishtp_cl_disconnect(struct ishtp_cl *cl);
|
||||
int ishtp_cl_connect(struct ishtp_cl *cl);
|
||||
int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length);
|
||||
int ishtp_cl_flush_queues(struct ishtp_cl *cl);
|
||||
|
||||
/* exported functions from ISHTP client buffer management scope */
|
||||
int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb);
|
||||
|
||||
#endif /* _ISHTP_CLIENT_H_ */
|
175
drivers/hid/intel-ish-hid/ishtp/dma-if.c
Normal file
175
drivers/hid/intel-ish-hid/ishtp/dma-if.c
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* ISHTP DMA I/F functions
|
||||
*
|
||||
* Copyright (c) 2003-2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include "ishtp-dev.h"
|
||||
#include "client.h"
|
||||
|
||||
/**
|
||||
* ishtp_cl_alloc_dma_buf() - Allocate DMA RX and TX buffer
|
||||
* @dev: ishtp device
|
||||
*
|
||||
* Allocate RX and TX DMA buffer once during bus setup.
|
||||
* It allocates 1MB, RX and TX DMA buffer, which are divided
|
||||
* into slots.
|
||||
*/
|
||||
void ishtp_cl_alloc_dma_buf(struct ishtp_device *dev)
|
||||
{
|
||||
dma_addr_t h;
|
||||
|
||||
if (dev->ishtp_host_dma_tx_buf)
|
||||
return;
|
||||
|
||||
dev->ishtp_host_dma_tx_buf_size = 1024*1024;
|
||||
dev->ishtp_host_dma_rx_buf_size = 1024*1024;
|
||||
|
||||
/* Allocate Tx buffer and init usage bitmap */
|
||||
dev->ishtp_host_dma_tx_buf = dma_alloc_coherent(dev->devc,
|
||||
dev->ishtp_host_dma_tx_buf_size,
|
||||
&h, GFP_KERNEL);
|
||||
if (dev->ishtp_host_dma_tx_buf)
|
||||
dev->ishtp_host_dma_tx_buf_phys = h;
|
||||
|
||||
dev->ishtp_dma_num_slots = dev->ishtp_host_dma_tx_buf_size /
|
||||
DMA_SLOT_SIZE;
|
||||
|
||||
dev->ishtp_dma_tx_map = kcalloc(dev->ishtp_dma_num_slots,
|
||||
sizeof(uint8_t),
|
||||
GFP_KERNEL);
|
||||
spin_lock_init(&dev->ishtp_dma_tx_lock);
|
||||
|
||||
/* Allocate Rx buffer */
|
||||
dev->ishtp_host_dma_rx_buf = dma_alloc_coherent(dev->devc,
|
||||
dev->ishtp_host_dma_rx_buf_size,
|
||||
&h, GFP_KERNEL);
|
||||
|
||||
if (dev->ishtp_host_dma_rx_buf)
|
||||
dev->ishtp_host_dma_rx_buf_phys = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_cl_free_dma_buf() - Free DMA RX and TX buffer
|
||||
* @dev: ishtp device
|
||||
*
|
||||
* Free DMA buffer when all clients are released. This is
|
||||
* only happens during error path in ISH built in driver
|
||||
* model
|
||||
*/
|
||||
void ishtp_cl_free_dma_buf(struct ishtp_device *dev)
|
||||
{
|
||||
dma_addr_t h;
|
||||
|
||||
if (dev->ishtp_host_dma_tx_buf) {
|
||||
h = dev->ishtp_host_dma_tx_buf_phys;
|
||||
dma_free_coherent(dev->devc, dev->ishtp_host_dma_tx_buf_size,
|
||||
dev->ishtp_host_dma_tx_buf, h);
|
||||
}
|
||||
|
||||
if (dev->ishtp_host_dma_rx_buf) {
|
||||
h = dev->ishtp_host_dma_rx_buf_phys;
|
||||
dma_free_coherent(dev->devc, dev->ishtp_host_dma_rx_buf_size,
|
||||
dev->ishtp_host_dma_rx_buf, h);
|
||||
}
|
||||
|
||||
kfree(dev->ishtp_dma_tx_map);
|
||||
dev->ishtp_host_dma_tx_buf = NULL;
|
||||
dev->ishtp_host_dma_rx_buf = NULL;
|
||||
dev->ishtp_dma_tx_map = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* ishtp_cl_get_dma_send_buf() - Get a DMA memory slot
|
||||
* @dev: ishtp device
|
||||
* @size: Size of memory to get
|
||||
*
|
||||
* Find and return free address of "size" bytes in dma tx buffer.
|
||||
* the function will mark this address as "in-used" memory.
|
||||
*
|
||||
* Return: NULL when no free buffer else a buffer to copy
|
||||
*/
|
||||
void *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev,
|
||||
uint32_t size)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i, j, free;
|
||||
/* additional slot is needed if there is rem */
|
||||
int required_slots = (size / DMA_SLOT_SIZE)
|
||||
+ 1 * (size % DMA_SLOT_SIZE != 0);
|
||||
|
||||
spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags);
|
||||
for (i = 0; i <= (dev->ishtp_dma_num_slots - required_slots); i++) {
|
||||
free = 1;
|
||||
for (j = 0; j < required_slots; j++)
|
||||
if (dev->ishtp_dma_tx_map[i+j]) {
|
||||
free = 0;
|
||||
i += j;
|
||||
break;
|
||||
}
|
||||
if (free) {
|
||||
/* mark memory as "caught" */
|
||||
for (j = 0; j < required_slots; j++)
|
||||
dev->ishtp_dma_tx_map[i+j] = 1;
|
||||
spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
|
||||
return (i * DMA_SLOT_SIZE) +
|
||||
(unsigned char *)dev->ishtp_host_dma_tx_buf;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
|
||||
dev_err(dev->devc, "No free DMA buffer to send msg\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* ishtp_cl_release_dma_acked_mem() - Release DMA memory slot
|
||||
* @dev: ishtp device
|
||||
* @msg_addr: message address of slot
|
||||
* @size: Size of memory to get
|
||||
*
|
||||
* Release_dma_acked_mem - returnes the acked memory to free list.
|
||||
* (from msg_addr, size bytes long)
|
||||
*/
|
||||
void ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev,
|
||||
void *msg_addr,
|
||||
uint8_t size)
|
||||
{
|
||||
unsigned long flags;
|
||||
int acked_slots = (size / DMA_SLOT_SIZE)
|
||||
+ 1 * (size % DMA_SLOT_SIZE != 0);
|
||||
int i, j;
|
||||
|
||||
if ((msg_addr - dev->ishtp_host_dma_tx_buf) % DMA_SLOT_SIZE) {
|
||||
dev_err(dev->devc, "Bad DMA Tx ack address\n");
|
||||
return;
|
||||
}
|
||||
|
||||
i = (msg_addr - dev->ishtp_host_dma_tx_buf) / DMA_SLOT_SIZE;
|
||||
spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags);
|
||||
for (j = 0; j < acked_slots; j++) {
|
||||
if ((i + j) >= dev->ishtp_dma_num_slots ||
|
||||
!dev->ishtp_dma_tx_map[i+j]) {
|
||||
/* no such slot, or memory is already free */
|
||||
spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
|
||||
dev_err(dev->devc, "Bad DMA Tx ack address\n");
|
||||
return;
|
||||
}
|
||||
dev->ishtp_dma_tx_map[i+j] = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
|
||||
}
|
1032
drivers/hid/intel-ish-hid/ishtp/hbm.c
Normal file
1032
drivers/hid/intel-ish-hid/ishtp/hbm.c
Normal file
File diff suppressed because it is too large
Load Diff
321
drivers/hid/intel-ish-hid/ishtp/hbm.h
Normal file
321
drivers/hid/intel-ish-hid/ishtp/hbm.h
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* ISHTP bus layer messages handling
|
||||
*
|
||||
* Copyright (c) 2003-2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifndef _ISHTP_HBM_H_
|
||||
#define _ISHTP_HBM_H_
|
||||
|
||||
#include <linux/uuid.h>
|
||||
|
||||
struct ishtp_device;
|
||||
struct ishtp_msg_hdr;
|
||||
struct ishtp_cl;
|
||||
|
||||
/*
|
||||
* Timeouts in Seconds
|
||||
*/
|
||||
#define ISHTP_INTEROP_TIMEOUT 7 /* Timeout on ready message */
|
||||
|
||||
#define ISHTP_CL_CONNECT_TIMEOUT 15 /* HPS: Client Connect Timeout */
|
||||
|
||||
/*
|
||||
* ISHTP Version
|
||||
*/
|
||||
#define HBM_MINOR_VERSION 0
|
||||
#define HBM_MAJOR_VERSION 1
|
||||
|
||||
/* Host bus message command opcode */
|
||||
#define ISHTP_HBM_CMD_OP_MSK 0x7f
|
||||
/* Host bus message command RESPONSE */
|
||||
#define ISHTP_HBM_CMD_RES_MSK 0x80
|
||||
|
||||
/*
|
||||
* ISHTP Bus Message Command IDs
|
||||
*/
|
||||
#define HOST_START_REQ_CMD 0x01
|
||||
#define HOST_START_RES_CMD 0x81
|
||||
|
||||
#define HOST_STOP_REQ_CMD 0x02
|
||||
#define HOST_STOP_RES_CMD 0x82
|
||||
|
||||
#define FW_STOP_REQ_CMD 0x03
|
||||
|
||||
#define HOST_ENUM_REQ_CMD 0x04
|
||||
#define HOST_ENUM_RES_CMD 0x84
|
||||
|
||||
#define HOST_CLIENT_PROPERTIES_REQ_CMD 0x05
|
||||
#define HOST_CLIENT_PROPERTIES_RES_CMD 0x85
|
||||
|
||||
#define CLIENT_CONNECT_REQ_CMD 0x06
|
||||
#define CLIENT_CONNECT_RES_CMD 0x86
|
||||
|
||||
#define CLIENT_DISCONNECT_REQ_CMD 0x07
|
||||
#define CLIENT_DISCONNECT_RES_CMD 0x87
|
||||
|
||||
#define ISHTP_FLOW_CONTROL_CMD 0x08
|
||||
|
||||
#define DMA_BUFFER_ALLOC_NOTIFY 0x11
|
||||
#define DMA_BUFFER_ALLOC_RESPONSE 0x91
|
||||
|
||||
#define DMA_XFER 0x12
|
||||
#define DMA_XFER_ACK 0x92
|
||||
|
||||
/*
|
||||
* ISHTP Stop Reason
|
||||
* used by hbm_host_stop_request.reason
|
||||
*/
|
||||
#define DRIVER_STOP_REQUEST 0x00
|
||||
|
||||
/*
|
||||
* ISHTP BUS Interface Section
|
||||
*/
|
||||
struct ishtp_msg_hdr {
|
||||
uint32_t fw_addr:8;
|
||||
uint32_t host_addr:8;
|
||||
uint32_t length:9;
|
||||
uint32_t reserved:6;
|
||||
uint32_t msg_complete:1;
|
||||
} __packed;
|
||||
|
||||
struct ishtp_bus_message {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t data[0];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct hbm_cl_cmd - client specific host bus command
|
||||
* CONNECT, DISCONNECT, and FlOW CONTROL
|
||||
*
|
||||
* @hbm_cmd - bus message command header
|
||||
* @fw_addr - address of the fw client
|
||||
* @host_addr - address of the client in the driver
|
||||
* @data
|
||||
*/
|
||||
struct ishtp_hbm_cl_cmd {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t fw_addr;
|
||||
uint8_t host_addr;
|
||||
uint8_t data;
|
||||
};
|
||||
|
||||
struct hbm_version {
|
||||
uint8_t minor_version;
|
||||
uint8_t major_version;
|
||||
} __packed;
|
||||
|
||||
struct hbm_host_version_request {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t reserved;
|
||||
struct hbm_version host_version;
|
||||
} __packed;
|
||||
|
||||
struct hbm_host_version_response {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t host_version_supported;
|
||||
struct hbm_version fw_max_version;
|
||||
} __packed;
|
||||
|
||||
struct hbm_host_stop_request {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t reason;
|
||||
uint8_t reserved[2];
|
||||
} __packed;
|
||||
|
||||
struct hbm_host_stop_response {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t reserved[3];
|
||||
} __packed;
|
||||
|
||||
struct hbm_host_enum_request {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t reserved[3];
|
||||
} __packed;
|
||||
|
||||
struct hbm_host_enum_response {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t reserved[3];
|
||||
uint8_t valid_addresses[32];
|
||||
} __packed;
|
||||
|
||||
struct ishtp_client_properties {
|
||||
uuid_le protocol_name;
|
||||
uint8_t protocol_version;
|
||||
uint8_t max_number_of_connections;
|
||||
uint8_t fixed_address;
|
||||
uint8_t single_recv_buf;
|
||||
uint32_t max_msg_length;
|
||||
uint8_t dma_hdr_len;
|
||||
#define ISHTP_CLIENT_DMA_ENABLED 0x80
|
||||
uint8_t reserved4;
|
||||
uint8_t reserved5;
|
||||
uint8_t reserved6;
|
||||
} __packed;
|
||||
|
||||
struct hbm_props_request {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t address;
|
||||
uint8_t reserved[2];
|
||||
} __packed;
|
||||
|
||||
struct hbm_props_response {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t address;
|
||||
uint8_t status;
|
||||
uint8_t reserved[1];
|
||||
struct ishtp_client_properties client_properties;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct hbm_client_connect_request - connect/disconnect request
|
||||
*
|
||||
* @hbm_cmd - bus message command header
|
||||
* @fw_addr - address of the fw client
|
||||
* @host_addr - address of the client in the driver
|
||||
* @reserved
|
||||
*/
|
||||
struct hbm_client_connect_request {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t fw_addr;
|
||||
uint8_t host_addr;
|
||||
uint8_t reserved;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct hbm_client_connect_response - connect/disconnect response
|
||||
*
|
||||
* @hbm_cmd - bus message command header
|
||||
* @fw_addr - address of the fw client
|
||||
* @host_addr - address of the client in the driver
|
||||
* @status - status of the request
|
||||
*/
|
||||
struct hbm_client_connect_response {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t fw_addr;
|
||||
uint8_t host_addr;
|
||||
uint8_t status;
|
||||
} __packed;
|
||||
|
||||
|
||||
#define ISHTP_FC_MESSAGE_RESERVED_LENGTH 5
|
||||
|
||||
struct hbm_flow_control {
|
||||
uint8_t hbm_cmd;
|
||||
uint8_t fw_addr;
|
||||
uint8_t host_addr;
|
||||
uint8_t reserved[ISHTP_FC_MESSAGE_RESERVED_LENGTH];
|
||||
} __packed;
|
||||
|
||||
struct dma_alloc_notify {
|
||||
uint8_t hbm;
|
||||
uint8_t status;
|
||||
uint8_t reserved[2];
|
||||
uint32_t buf_size;
|
||||
uint64_t buf_address;
|
||||
/* [...] May come more size/address pairs */
|
||||
} __packed;
|
||||
|
||||
struct dma_xfer_hbm {
|
||||
uint8_t hbm;
|
||||
uint8_t fw_client_id;
|
||||
uint8_t host_client_id;
|
||||
uint8_t reserved;
|
||||
uint64_t msg_addr;
|
||||
uint32_t msg_length;
|
||||
uint32_t reserved2;
|
||||
} __packed;
|
||||
|
||||
/* System state */
|
||||
#define ISHTP_SYSTEM_STATE_CLIENT_ADDR 13
|
||||
|
||||
#define SYSTEM_STATE_SUBSCRIBE 0x1
|
||||
#define SYSTEM_STATE_STATUS 0x2
|
||||
#define SYSTEM_STATE_QUERY_SUBSCRIBERS 0x3
|
||||
#define SYSTEM_STATE_STATE_CHANGE_REQ 0x4
|
||||
/*indicates suspend and resume states*/
|
||||
#define SUSPEND_STATE_BIT (1<<1)
|
||||
|
||||
struct ish_system_states_header {
|
||||
uint32_t cmd;
|
||||
uint32_t cmd_status; /*responses will have this set*/
|
||||
} __packed;
|
||||
|
||||
struct ish_system_states_subscribe {
|
||||
struct ish_system_states_header hdr;
|
||||
uint32_t states;
|
||||
} __packed;
|
||||
|
||||
struct ish_system_states_status {
|
||||
struct ish_system_states_header hdr;
|
||||
uint32_t supported_states;
|
||||
uint32_t states_status;
|
||||
} __packed;
|
||||
|
||||
struct ish_system_states_query_subscribers {
|
||||
struct ish_system_states_header hdr;
|
||||
} __packed;
|
||||
|
||||
struct ish_system_states_state_change_req {
|
||||
struct ish_system_states_header hdr;
|
||||
uint32_t requested_states;
|
||||
uint32_t states_status;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* enum ishtp_hbm_state - host bus message protocol state
|
||||
*
|
||||
* @ISHTP_HBM_IDLE : protocol not started
|
||||
* @ISHTP_HBM_START : start request message was sent
|
||||
* @ISHTP_HBM_ENUM_CLIENTS : enumeration request was sent
|
||||
* @ISHTP_HBM_CLIENT_PROPERTIES : acquiring clients properties
|
||||
*/
|
||||
enum ishtp_hbm_state {
|
||||
ISHTP_HBM_IDLE = 0,
|
||||
ISHTP_HBM_START,
|
||||
ISHTP_HBM_STARTED,
|
||||
ISHTP_HBM_ENUM_CLIENTS,
|
||||
ISHTP_HBM_CLIENT_PROPERTIES,
|
||||
ISHTP_HBM_WORKING,
|
||||
ISHTP_HBM_STOPPED,
|
||||
};
|
||||
|
||||
static inline void ishtp_hbm_hdr(struct ishtp_msg_hdr *hdr, size_t length)
|
||||
{
|
||||
hdr->host_addr = 0;
|
||||
hdr->fw_addr = 0;
|
||||
hdr->length = length;
|
||||
hdr->msg_complete = 1;
|
||||
hdr->reserved = 0;
|
||||
}
|
||||
|
||||
int ishtp_hbm_start_req(struct ishtp_device *dev);
|
||||
int ishtp_hbm_start_wait(struct ishtp_device *dev);
|
||||
int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev,
|
||||
struct ishtp_cl *cl);
|
||||
int ishtp_hbm_cl_disconnect_req(struct ishtp_device *dev, struct ishtp_cl *cl);
|
||||
int ishtp_hbm_cl_connect_req(struct ishtp_device *dev, struct ishtp_cl *cl);
|
||||
void ishtp_hbm_enum_clients_req(struct ishtp_device *dev);
|
||||
void bh_hbm_work_fn(struct work_struct *work);
|
||||
void recv_hbm(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr);
|
||||
void recv_fixed_cl_msg(struct ishtp_device *dev,
|
||||
struct ishtp_msg_hdr *ishtp_hdr);
|
||||
void ishtp_hbm_dispatch(struct ishtp_device *dev,
|
||||
struct ishtp_bus_message *hdr);
|
||||
|
||||
void ishtp_query_subscribers(struct ishtp_device *dev);
|
||||
|
||||
/* Exported I/F */
|
||||
void ishtp_send_suspend(struct ishtp_device *dev);
|
||||
void ishtp_send_resume(struct ishtp_device *dev);
|
||||
|
||||
#endif /* _ISHTP_HBM_H_ */
|
115
drivers/hid/intel-ish-hid/ishtp/init.c
Normal file
115
drivers/hid/intel-ish-hid/ishtp/init.c
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Initialization protocol for ISHTP driver
|
||||
*
|
||||
* Copyright (c) 2003-2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include "ishtp-dev.h"
|
||||
#include "hbm.h"
|
||||
#include "client.h"
|
||||
|
||||
/**
|
||||
* ishtp_dev_state_str() -Convert to string format
|
||||
* @state: state to convert
|
||||
*
|
||||
* Convert state to string for prints
|
||||
*
|
||||
* Return: character pointer to converted string
|
||||
*/
|
||||
const char *ishtp_dev_state_str(int state)
|
||||
{
|
||||
switch (state) {
|
||||
case ISHTP_DEV_INITIALIZING:
|
||||
return "INITIALIZING";
|
||||
case ISHTP_DEV_INIT_CLIENTS:
|
||||
return "INIT_CLIENTS";
|
||||
case ISHTP_DEV_ENABLED:
|
||||
return "ENABLED";
|
||||
case ISHTP_DEV_RESETTING:
|
||||
return "RESETTING";
|
||||
case ISHTP_DEV_DISABLED:
|
||||
return "DISABLED";
|
||||
case ISHTP_DEV_POWER_DOWN:
|
||||
return "POWER_DOWN";
|
||||
case ISHTP_DEV_POWER_UP:
|
||||
return "POWER_UP";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_device_init() - ishtp device init
|
||||
* @dev: ISHTP device instance
|
||||
*
|
||||
* After ISHTP device is alloacted, this function is used to initialize
|
||||
* each field which includes spin lock, work struct and lists
|
||||
*/
|
||||
void ishtp_device_init(struct ishtp_device *dev)
|
||||
{
|
||||
dev->dev_state = ISHTP_DEV_INITIALIZING;
|
||||
INIT_LIST_HEAD(&dev->cl_list);
|
||||
INIT_LIST_HEAD(&dev->device_list);
|
||||
dev->rd_msg_fifo_head = 0;
|
||||
dev->rd_msg_fifo_tail = 0;
|
||||
spin_lock_init(&dev->rd_msg_spinlock);
|
||||
|
||||
init_waitqueue_head(&dev->wait_hbm_recvd_msg);
|
||||
spin_lock_init(&dev->read_list_spinlock);
|
||||
spin_lock_init(&dev->device_lock);
|
||||
spin_lock_init(&dev->device_list_lock);
|
||||
spin_lock_init(&dev->cl_list_lock);
|
||||
spin_lock_init(&dev->fw_clients_lock);
|
||||
INIT_WORK(&dev->bh_hbm_work, bh_hbm_work_fn);
|
||||
|
||||
bitmap_zero(dev->host_clients_map, ISHTP_CLIENTS_MAX);
|
||||
dev->open_handle_count = 0;
|
||||
|
||||
/*
|
||||
* Reserving client ID 0 for ISHTP Bus Message communications
|
||||
*/
|
||||
bitmap_set(dev->host_clients_map, 0, 1);
|
||||
|
||||
INIT_LIST_HEAD(&dev->read_list.list);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_device_init);
|
||||
|
||||
/**
|
||||
* ishtp_start() - Start ISH processing
|
||||
* @dev: ISHTP device instance
|
||||
*
|
||||
* Start ISHTP processing by sending query subscriber message
|
||||
*
|
||||
* Return: 0 on success else -ENODEV
|
||||
*/
|
||||
int ishtp_start(struct ishtp_device *dev)
|
||||
{
|
||||
if (ishtp_hbm_start_wait(dev)) {
|
||||
dev_err(dev->devc, "HBM haven't started");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* suspend & resume notification - send QUERY_SUBSCRIBERS msg */
|
||||
ishtp_query_subscribers(dev);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_err(dev->devc, "link layer initialization failed.\n");
|
||||
dev->dev_state = ISHTP_DEV_DISABLED;
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(ishtp_start);
|
277
drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
Normal file
277
drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
Normal file
@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Most ISHTP provider device and ISHTP logic declarations
|
||||
*
|
||||
* Copyright (c) 2003-2016, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifndef _ISHTP_DEV_H_
|
||||
#define _ISHTP_DEV_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include "bus.h"
|
||||
#include "hbm.h"
|
||||
|
||||
#define IPC_PAYLOAD_SIZE 128
|
||||
#define ISHTP_RD_MSG_BUF_SIZE IPC_PAYLOAD_SIZE
|
||||
#define IPC_FULL_MSG_SIZE 132
|
||||
|
||||
/* Number of messages to be held in ISR->BH FIFO */
|
||||
#define RD_INT_FIFO_SIZE 64
|
||||
|
||||
/*
|
||||
* Number of IPC messages to be held in Tx FIFO, to be sent by ISR -
|
||||
* Tx complete interrupt or RX_COMPLETE handler
|
||||
*/
|
||||
#define IPC_TX_FIFO_SIZE 512
|
||||
|
||||
/*
|
||||
* Number of Maximum ISHTP Clients
|
||||
*/
|
||||
#define ISHTP_CLIENTS_MAX 256
|
||||
|
||||
/*
|
||||
* Number of File descriptors/handles
|
||||
* that can be opened to the driver.
|
||||
*
|
||||
* Limit to 255: 256 Total Clients
|
||||
* minus internal client for ISHTP Bus Messages
|
||||
*/
|
||||
#define ISHTP_MAX_OPEN_HANDLE_COUNT (ISHTP_CLIENTS_MAX - 1)
|
||||
|
||||
/* Internal Clients Number */
|
||||
#define ISHTP_HOST_CLIENT_ID_ANY (-1)
|
||||
#define ISHTP_HBM_HOST_CLIENT_ID 0
|
||||
|
||||
#define MAX_DMA_DELAY 20
|
||||
|
||||
/* ISHTP device states */
|
||||
enum ishtp_dev_state {
|
||||
ISHTP_DEV_INITIALIZING = 0,
|
||||
ISHTP_DEV_INIT_CLIENTS,
|
||||
ISHTP_DEV_ENABLED,
|
||||
ISHTP_DEV_RESETTING,
|
||||
ISHTP_DEV_DISABLED,
|
||||
ISHTP_DEV_POWER_DOWN,
|
||||
ISHTP_DEV_POWER_UP
|
||||
};
|
||||
const char *ishtp_dev_state_str(int state);
|
||||
|
||||
struct ishtp_cl;
|
||||
|
||||
/**
|
||||
* struct ishtp_fw_client - representation of fw client
|
||||
*
|
||||
* @props - client properties
|
||||
* @client_id - fw client id
|
||||
*/
|
||||
struct ishtp_fw_client {
|
||||
struct ishtp_client_properties props;
|
||||
uint8_t client_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ishtp_msg_data - ISHTP message data struct
|
||||
* @size: Size of data in the *data
|
||||
* @data: Pointer to data
|
||||
*/
|
||||
struct ishtp_msg_data {
|
||||
uint32_t size;
|
||||
unsigned char *data;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct ishtp_cl_rb - request block structure
|
||||
* @list: Link to list members
|
||||
* @cl: ISHTP client instance
|
||||
* @buffer: message header
|
||||
* @buf_idx: Index into buffer
|
||||
* @read_time: unused at this time
|
||||
*/
|
||||
struct ishtp_cl_rb {
|
||||
struct list_head list;
|
||||
struct ishtp_cl *cl;
|
||||
struct ishtp_msg_data buffer;
|
||||
unsigned long buf_idx;
|
||||
unsigned long read_time;
|
||||
};
|
||||
|
||||
/*
|
||||
* Control info for IPC messages ISHTP/IPC sending FIFO -
|
||||
* list with inline data buffer
|
||||
* This structure will be filled with parameters submitted
|
||||
* by the caller glue layer
|
||||
* 'buf' may be pointing to the external buffer or to 'inline_data'
|
||||
* 'offset' will be initialized to 0 by submitting
|
||||
*
|
||||
* 'ipc_send_compl' is intended for use by clients that send fragmented
|
||||
* messages. When a fragment is sent down to IPC msg regs,
|
||||
* it will be called.
|
||||
* If it has more fragments to send, it will do it. With last fragment
|
||||
* it will send appropriate ISHTP "message-complete" flag.
|
||||
* It will remove the outstanding message
|
||||
* (mark outstanding buffer as available).
|
||||
* If counting flow control is in work and there are more flow control
|
||||
* credits, it can put the next client message queued in cl.
|
||||
* structure for IPC processing.
|
||||
*
|
||||
*/
|
||||
struct wr_msg_ctl_info {
|
||||
/* Will be called with 'ipc_send_compl_prm' as parameter */
|
||||
void (*ipc_send_compl)(void *);
|
||||
|
||||
void *ipc_send_compl_prm;
|
||||
size_t length;
|
||||
struct list_head link;
|
||||
unsigned char inline_data[IPC_FULL_MSG_SIZE];
|
||||
};
|
||||
|
||||
/*
|
||||
* The ISHTP layer talks to hardware IPC message using the following
|
||||
* callbacks
|
||||
*/
|
||||
struct ishtp_hw_ops {
|
||||
int (*hw_reset)(struct ishtp_device *dev);
|
||||
int (*ipc_reset)(struct ishtp_device *dev);
|
||||
uint32_t (*ipc_get_header)(struct ishtp_device *dev, int length,
|
||||
int busy);
|
||||
int (*write)(struct ishtp_device *dev,
|
||||
void (*ipc_send_compl)(void *), void *ipc_send_compl_prm,
|
||||
unsigned char *msg, int length);
|
||||
uint32_t (*ishtp_read_hdr)(const struct ishtp_device *dev);
|
||||
int (*ishtp_read)(struct ishtp_device *dev, unsigned char *buffer,
|
||||
unsigned long buffer_length);
|
||||
uint32_t (*get_fw_status)(struct ishtp_device *dev);
|
||||
void (*sync_fw_clock)(struct ishtp_device *dev);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ishtp_device - ISHTP private device struct
|
||||
*/
|
||||
struct ishtp_device {
|
||||
struct device *devc; /* pointer to lowest device */
|
||||
struct pci_dev *pdev; /* PCI device to get device ids */
|
||||
|
||||
/* waitq for waiting for suspend response */
|
||||
wait_queue_head_t suspend_wait;
|
||||
bool suspend_flag; /* Suspend is active */
|
||||
|
||||
/* waitq for waiting for resume response */
|
||||
wait_queue_head_t resume_wait;
|
||||
bool resume_flag; /*Resume is active */
|
||||
|
||||
/*
|
||||
* lock for the device, for everything that doesn't have
|
||||
* a dedicated spinlock
|
||||
*/
|
||||
spinlock_t device_lock;
|
||||
|
||||
bool recvd_hw_ready;
|
||||
struct hbm_version version;
|
||||
int transfer_path; /* Choice of transfer path: IPC or DMA */
|
||||
|
||||
/* ishtp device states */
|
||||
enum ishtp_dev_state dev_state;
|
||||
enum ishtp_hbm_state hbm_state;
|
||||
|
||||
/* driver read queue */
|
||||
struct ishtp_cl_rb read_list;
|
||||
spinlock_t read_list_spinlock;
|
||||
|
||||
/* list of ishtp_cl's */
|
||||
struct list_head cl_list;
|
||||
spinlock_t cl_list_lock;
|
||||
long open_handle_count;
|
||||
|
||||
/* List of bus devices */
|
||||
struct list_head device_list;
|
||||
spinlock_t device_list_lock;
|
||||
|
||||
/* waiting queues for receive message from FW */
|
||||
wait_queue_head_t wait_hw_ready;
|
||||
wait_queue_head_t wait_hbm_recvd_msg;
|
||||
|
||||
/* FIFO for input messages for BH processing */
|
||||
unsigned char rd_msg_fifo[RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE];
|
||||
unsigned int rd_msg_fifo_head, rd_msg_fifo_tail;
|
||||
spinlock_t rd_msg_spinlock;
|
||||
struct work_struct bh_hbm_work;
|
||||
|
||||
/* IPC write queue */
|
||||
struct wr_msg_ctl_info wr_processing_list_head, wr_free_list_head;
|
||||
/* For both processing list and free list */
|
||||
spinlock_t wr_processing_spinlock;
|
||||
|
||||
spinlock_t out_ipc_spinlock;
|
||||
|
||||
struct ishtp_fw_client *fw_clients; /*Note:memory has to be allocated*/
|
||||
DECLARE_BITMAP(fw_clients_map, ISHTP_CLIENTS_MAX);
|
||||
DECLARE_BITMAP(host_clients_map, ISHTP_CLIENTS_MAX);
|
||||
uint8_t fw_clients_num;
|
||||
uint8_t fw_client_presentation_num;
|
||||
uint8_t fw_client_index;
|
||||
spinlock_t fw_clients_lock;
|
||||
|
||||
/* TX DMA buffers and slots */
|
||||
int ishtp_host_dma_enabled;
|
||||
void *ishtp_host_dma_tx_buf;
|
||||
unsigned int ishtp_host_dma_tx_buf_size;
|
||||
uint64_t ishtp_host_dma_tx_buf_phys;
|
||||
int ishtp_dma_num_slots;
|
||||
|
||||
/* map of 4k blocks in Tx dma buf: 0-free, 1-used */
|
||||
uint8_t *ishtp_dma_tx_map;
|
||||
spinlock_t ishtp_dma_tx_lock;
|
||||
|
||||
/* RX DMA buffers and slots */
|
||||
void *ishtp_host_dma_rx_buf;
|
||||
unsigned int ishtp_host_dma_rx_buf_size;
|
||||
uint64_t ishtp_host_dma_rx_buf_phys;
|
||||
|
||||
/* Dump to trace buffers if enabled*/
|
||||
void (*print_log)(struct ishtp_device *dev, char *format, ...);
|
||||
|
||||
/* Debug stats */
|
||||
unsigned int ipc_rx_cnt;
|
||||
unsigned long long ipc_rx_bytes_cnt;
|
||||
unsigned int ipc_tx_cnt;
|
||||
unsigned long long ipc_tx_bytes_cnt;
|
||||
|
||||
const struct ishtp_hw_ops *ops;
|
||||
size_t mtu;
|
||||
uint32_t ishtp_msg_hdr;
|
||||
char hw[0] __aligned(sizeof(void *));
|
||||
};
|
||||
|
||||
static inline unsigned long ishtp_secs_to_jiffies(unsigned long sec)
|
||||
{
|
||||
return msecs_to_jiffies(sec * MSEC_PER_SEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Register Access Function
|
||||
*/
|
||||
static inline int ish_ipc_reset(struct ishtp_device *dev)
|
||||
{
|
||||
return dev->ops->ipc_reset(dev);
|
||||
}
|
||||
|
||||
static inline int ish_hw_reset(struct ishtp_device *dev)
|
||||
{
|
||||
return dev->ops->hw_reset(dev);
|
||||
}
|
||||
|
||||
/* Exported function */
|
||||
void ishtp_device_init(struct ishtp_device *dev);
|
||||
int ishtp_start(struct ishtp_device *dev);
|
||||
|
||||
#endif /*_ISHTP_DEV_H_*/
|
Loading…
Reference in New Issue
Block a user