diff --git a/Documentation/hid/hid-sensor.txt b/Documentation/hid/hid-sensor.txt new file mode 100755 index 000000000000..948b0989c433 --- /dev/null +++ b/Documentation/hid/hid-sensor.txt @@ -0,0 +1,140 @@ + +HID Sensors Framework +====================== +HID sensor framework provides necessary interfaces to implement sensor drivers, +which are connected to a sensor hub. The sensor hub is a HID device and it provides +a report descriptor conforming to HID 1.12 sensor usage tables. + +Description from the HID 1.12 "HID Sensor Usages" specification: +"Standardization of HID usages for sensors would allow (but not require) sensor +hardware vendors to provide a consistent Plug And Play interface at the USB boundary, +thereby enabling some operating systems to incorporate common device drivers that +could be reused between vendors, alleviating any need for the vendors to provide +the drivers themselves." + +This specification describes many usage IDs, which describe the type of sensor +and also the individual data fields. Each sensor can have variable number of +data fields. The length and order is specified in the report descriptor. For +example a part of report descriptor can look like: + + INPUT(1)[INPUT] + .. + Field(2) + Physical(0020.0073) + Usage(1) + 0020.045f + Logical Minimum(-32767) + Logical Maximum(32767) + Report Size(8) + Report Count(1) + Report Offset(16) + Flags(Variable Absolute) +.. +.. + +The report is indicating "sensor page (0x20)" contains an accelerometer-3D (0x73). +This accelerometer-3D has some fields. Here for example field 2 is motion intensity +(0x045f) with a logical minimum value of -32767 and logical maximum of 32767. The +order of fields and length of each field is important as the input event raw +data will use this format. + + +Implementation +================= + +This specification defines many different types of sensors with different sets of +data fields. It is difficult to have a common input event to user space applications, +for different sensors. For example an accelerometer can send X,Y and Z data, whereas +an ambient light sensor can send illumination data. +So the implementation has two parts: +- Core hid driver +- Individual sensor processing part (sensor drivers) + +Core driver +----------- +The core driver registers (hid-sensor-hub) registers as a HID driver. It parses +report descriptors and identifies all the sensors present. It adds an MFD device +with name HID-SENSOR-xxxx (where xxxx is usage id from the specification). +For example +HID-SENSOR-200073 is registered for an Accelerometer 3D driver. +So if any driver with this name is inserted, then the probe routine for that +function will be called. So an accelerometer processing driver can register +with this name and will be probed if there is an accelerometer-3D detected. + +The core driver provides a set of APIs which can be used by the processing +drivers to register and get events for that usage id. Also it provides parsing +functions, which get and set each input/feature/output report. + +Individual sensor processing part (sensor drivers) +----------- +The processing driver will use an interface provided by the core driver to parse +the report and get the indexes of the fields and also can get events. This driver +can use IIO interface to use the standard ABI defined for a type of sensor. + + +Core driver Interface +===================== + +Callback structure: +Each processing driver can use this structure to set some callbacks. + int (*suspend)(..): Callback when HID suspend is received + int (*resume)(..): Callback when HID resume is received + int (*capture_sample)(..): Capture a sample for one of its data fields + int (*send_event)(..): One complete event is received which can have + multiple data fields. + +Registration functions: +int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct hid_sensor_hub_callbacks *usage_callback): + +Registers callbacks for an usage id. The callback functions are not allowed +to sleep. + + +int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id): + +Removes callbacks for an usage id. + + +Parsing function: +int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, + u8 type, + u32 usage_id, u32 attr_usage_id, + struct hid_sensor_hub_attribute_info *info); + +A processing driver can look for some field of interest and check if it exists +in a report descriptor. If it exists it will store necessary information +so that fields can be set or get individually. +These indexes avoid searching every time and getting field index to get or set. + + +Set Feature report +int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 value); + +This interface is used to set a value for a field in feature report. For example +if there is a field report_interval, which is parsed by a call to +sensor_hub_input_get_attribute_info before, then it can directly set that individual +field. + + +int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 *value); + +This interface is used to get a value for a field in input report. For example +if there is a field report_interval, which is parsed by a call to +sensor_hub_input_get_attribute_info before, then it can directly get that individual +field value. + + +int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + u32 attr_usage_id, u32 report_id); + +This is used to get a particular field value through input reports. For example +accelerometer wants to poll X axis value, then it can call this function with +the usage id of X axis. HID sensors can provide events, so this is not necessary +to poll for any field. If there is some new sample, the core driver will call +registered callback function to process the sample. diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fbf49503508d..f8d314060853 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -690,6 +690,20 @@ config HID_ZYDACRON ---help--- Support for Zydacron remote control. +config HID_SENSOR_HUB + tristate "HID Sensors framework support" + depends on USB_HID + select MFD_CORE + default n + -- help--- + Support for HID Sensor framework. This creates a MFD instance + for a sensor hub and identifies all the sensors connected to it. + Each sensor is registered as a MFD cell, so that sensor specific + processing can be done in a separate driver. Each sensor + drivers can use the service provided by this driver to register + for events and handle data streams. Each sensor driver can format + data and present to user mode using input or IIO interface. + endmenu endif # HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index f975485f88b2..1173d7af20e8 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o obj-$(CONFIG_HID_WALTOP) += hid-waltop.o obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o +obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 60ea284407ce..15ffc0aaf178 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1550,6 +1550,10 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_1020) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_09FA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_1020) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_09FA) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, @@ -1642,6 +1646,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_SENSOR_HUB_7014) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 1dcb76ff51e3..8a270a38cfa1 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -419,6 +419,11 @@ #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 +#define USB_VENDOR_ID_INTEL_8086 0x8086 +#define USB_VENDOR_ID_INTEL_8087 0x8087 +#define USB_DEVICE_ID_SENSOR_HUB_1020 0x1020 +#define USB_DEVICE_ID_SENSOR_HUB_09FA 0x09FA + #define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615 #define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070 @@ -695,6 +700,7 @@ #define USB_VENDOR_ID_STANTUM_STM 0x0483 #define USB_DEVICE_ID_MTP_STM 0x3261 +#define USB_DEVICE_ID_SENSOR_HUB_7014 0x7014 #define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403 #define USB_DEVICE_ID_MTP_SITRONIX 0x5001 diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c new file mode 100644 index 000000000000..34a35ba95fc1 --- /dev/null +++ b/drivers/hid/hid-sensor-hub.c @@ -0,0 +1,695 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#include +#include +#include +#include "usbhid/usbhid.h" +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" + +/** + * struct sensor_hub_pending - Synchronous read pending information + * @status: Pending status true/false. + * @ready: Completion synchronization data. + * @usage_id: Usage id for physical device, E.g. Gyro usage id. + * @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro. + * @raw_size: Response size for a read request. + * @raw_data: Place holder for received response. + */ +struct sensor_hub_pending { + bool status; + struct completion ready; + u32 usage_id; + u32 attr_usage_id; + int raw_size; + u8 *raw_data; +}; + +/** + * struct sensor_hub_data - Hold a instance data for a HID hub device + * @hsdev: Stored hid instance for current hub device. + * @mutex: Mutex to serialize synchronous request. + * @lock: Spin lock to protect pending request structure. + * @pending: Holds information of pending sync read request. + * @dyn_callback_list: Holds callback function + * @dyn_callback_lock: spin lock to protect callback list + * @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance. + * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached). + */ +struct sensor_hub_data { + struct hid_sensor_hub_device *hsdev; + struct mutex mutex; + spinlock_t lock; + struct sensor_hub_pending pending; + struct list_head dyn_callback_list; + spinlock_t dyn_callback_lock; + struct mfd_cell *hid_sensor_hub_client_devs; + int hid_sensor_client_cnt; +}; + +/** + * struct hid_sensor_hub_callbacks_list - Stores callback list + * @list: list head. + * @usage_id: usage id for a physical device. + * @usage_callback: Stores registered callback functions. + * @priv: Private data for a physical device. + */ +struct hid_sensor_hub_callbacks_list { + struct list_head list; + u32 usage_id; + struct hid_sensor_hub_callbacks *usage_callback; + void *priv; +}; + +static int sensor_hub_check_for_sensor_page(struct hid_device *hdev) +{ + int i; + int ret = -EINVAL; + + for (i = 0; i < hdev->maxcollection; i++) { + struct hid_collection *col = &hdev->collection[i]; + if (col->type == HID_COLLECTION_PHYSICAL && + (col->usage & HID_USAGE_PAGE) == HID_UP_SENSOR) { + ret = 0; + break; + } + } + + return ret; +} + +static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev, + int dir) +{ + struct hid_report *report; + + list_for_each_entry(report, &hdev->report_enum[dir].report_list, list) { + if (report->id == id) + return report; + } + hid_warn(hdev, "No report with id 0x%x found\n", id); + + return NULL; +} + +static int sensor_hub_get_physical_device_count( + struct hid_report_enum *report_enum) +{ + struct hid_report *report; + struct hid_field *field; + int cnt = 0; + + list_for_each_entry(report, &report_enum->report_list, list) { + field = report->field[0]; + if (report->maxfield && field && + field->physical) + cnt++; + } + + return cnt; +} + +static void sensor_hub_fill_attr_info( + struct hid_sensor_hub_attribute_info *info, + s32 index, s32 report_id, s32 units, s32 unit_expo, s32 size) +{ + info->index = index; + info->report_id = report_id; + info->units = units; + info->unit_expo = unit_expo; + info->size = size/8; +} + +static struct hid_sensor_hub_callbacks *sensor_hub_get_callback( + struct hid_device *hdev, + u32 usage_id, void **priv) +{ + struct hid_sensor_hub_callbacks_list *callback; + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) + if (callback->usage_id == usage_id) { + *priv = callback->priv; + spin_unlock(&pdata->dyn_callback_lock); + return callback->usage_callback; + } + spin_unlock(&pdata->dyn_callback_lock); + + return NULL; +} + +int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct hid_sensor_hub_callbacks *usage_callback) +{ + struct hid_sensor_hub_callbacks_list *callback; + struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); + + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) + if (callback->usage_id == usage_id) { + spin_unlock(&pdata->dyn_callback_lock); + return -EINVAL; + } + callback = kzalloc(sizeof(*callback), GFP_KERNEL); + if (!callback) { + spin_unlock(&pdata->dyn_callback_lock); + return -ENOMEM; + } + callback->usage_callback = usage_callback; + callback->usage_id = usage_id; + callback->priv = NULL; + list_add_tail(&callback->list, &pdata->dyn_callback_list); + spin_unlock(&pdata->dyn_callback_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(sensor_hub_register_callback); + +int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id) +{ + struct hid_sensor_hub_callbacks_list *callback; + struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); + + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) + if (callback->usage_id == usage_id) { + list_del(&callback->list); + kfree(callback); + break; + } + spin_unlock(&pdata->dyn_callback_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(sensor_hub_remove_callback); + +int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 value) +{ + struct hid_report *report; + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + int ret = 0; + + if (report_id < 0) + return -EINVAL; + + mutex_lock(&data->mutex); + report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); + if (!report || (field_index >= report->maxfield)) { + ret = -EINVAL; + goto done_proc; + } + hid_set_field(report->field[field_index], 0, value); + usbhid_submit_report(hsdev->hdev, report, USB_DIR_OUT); + usbhid_wait_io(hsdev->hdev); + +done_proc: + mutex_unlock(&data->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_set_feature); + +int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 *value) +{ + struct hid_report *report; + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + int ret = 0; + + if (report_id < 0) + return -EINVAL; + + mutex_lock(&data->mutex); + report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); + if (!report || (field_index >= report->maxfield)) { + ret = -EINVAL; + goto done_proc; + } + usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); + usbhid_wait_io(hsdev->hdev); + *value = report->field[field_index]->value[0]; + +done_proc: + mutex_unlock(&data->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_get_feature); + + +int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + u32 attr_usage_id, u32 report_id) +{ + struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + unsigned long flags; + struct hid_report *report; + int ret_val = 0; + + if (report_id < 0) + return -EINVAL; + + mutex_lock(&data->mutex); + memset(&data->pending, 0, sizeof(data->pending)); + init_completion(&data->pending.ready); + data->pending.usage_id = usage_id; + data->pending.attr_usage_id = attr_usage_id; + data->pending.raw_size = 0; + + spin_lock_irqsave(&data->lock, flags); + data->pending.status = true; + report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); + if (!report) { + spin_unlock_irqrestore(&data->lock, flags); + goto err_free; + } + usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); + spin_unlock_irqrestore(&data->lock, flags); + wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5); + switch (data->pending.raw_size) { + case 1: + ret_val = *(u8 *)data->pending.raw_data; + break; + case 2: + ret_val = *(u16 *)data->pending.raw_data; + break; + case 4: + ret_val = *(u32 *)data->pending.raw_data; + break; + default: + ret_val = 0; + } + kfree(data->pending.raw_data); + +err_free: + data->pending.status = false; + mutex_unlock(&data->mutex); + + return ret_val; +} +EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value); + +int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, + u8 type, + u32 usage_id, + u32 attr_usage_id, + struct hid_sensor_hub_attribute_info *info) +{ + int ret = -1; + int i, j; + int collection_index = -1; + struct hid_report *report; + struct hid_field *field; + struct hid_report_enum *report_enum; + struct hid_device *hdev = hsdev->hdev; + + /* Initialize with defaults */ + info->usage_id = usage_id; + info->attrib_id = attr_usage_id; + info->report_id = -1; + info->index = -1; + info->units = -1; + info->unit_expo = -1; + + for (i = 0; i < hdev->maxcollection; ++i) { + struct hid_collection *collection = &hdev->collection[i]; + if (usage_id == collection->usage) { + collection_index = i; + break; + } + } + if (collection_index == -1) + goto err_ret; + + report_enum = &hdev->report_enum[type]; + list_for_each_entry(report, &report_enum->report_list, list) { + for (i = 0; i < report->maxfield; ++i) { + field = report->field[i]; + if (field->physical == usage_id && + field->logical == attr_usage_id) { + sensor_hub_fill_attr_info(info, i, report->id, + field->unit, field->unit_exponent, + field->report_size); + ret = 0; + } else { + for (j = 0; j < field->maxusage; ++j) { + if (field->usage[j].hid == + attr_usage_id && + field->usage[j].collection_index == + collection_index) { + sensor_hub_fill_attr_info(info, + i, report->id, + field->unit, + field->unit_exponent, + field->report_size); + ret = 0; + break; + } + } + } + if (ret == 0) + break; + } + } + +err_ret: + return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info); + +#ifdef CONFIG_PM +static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message) +{ + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + struct hid_sensor_hub_callbacks_list *callback; + + hid_dbg(hdev, " sensor_hub_suspend\n"); + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) { + if (callback->usage_callback->suspend) + callback->usage_callback->suspend( + pdata->hsdev, callback->priv); + } + spin_unlock(&pdata->dyn_callback_lock); + + return 0; +} + +static int sensor_hub_resume(struct hid_device *hdev) +{ + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + struct hid_sensor_hub_callbacks_list *callback; + + hid_dbg(hdev, " sensor_hub_resume\n"); + spin_lock(&pdata->dyn_callback_lock); + list_for_each_entry(callback, &pdata->dyn_callback_list, list) { + if (callback->usage_callback->resume) + callback->usage_callback->resume( + pdata->hsdev, callback->priv); + } + spin_unlock(&pdata->dyn_callback_lock); + + return 0; +} + +static int sensor_hub_reset_resume(struct hid_device *hdev) +{ + return 0; +} +#endif +/* + * Handle raw report as sent by device + */ +static int sensor_hub_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *raw_data, int size) +{ + int i; + u8 *ptr; + int sz; + struct sensor_hub_data *pdata = hid_get_drvdata(hdev); + unsigned long flags; + struct hid_sensor_hub_callbacks *callback = NULL; + struct hid_collection *collection = NULL; + void *priv = NULL; + + hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n", + report->id, size, report->type); + hid_dbg(hdev, "maxfield:%d\n", report->maxfield); + if (report->type != HID_INPUT_REPORT) + return 1; + + ptr = raw_data; + ptr++; /*Skip report id*/ + + if (!report) + goto err_report; + + spin_lock_irqsave(&pdata->lock, flags); + + for (i = 0; i < report->maxfield; ++i) { + + hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n", + i, report->field[i]->usage->collection_index, + report->field[i]->usage->hid, + report->field[i]->report_size/8); + + sz = report->field[i]->report_size/8; + if (pdata->pending.status && pdata->pending.attr_usage_id == + report->field[i]->usage->hid) { + hid_dbg(hdev, "data was pending ...\n"); + pdata->pending.raw_data = kmalloc(sz, GFP_KERNEL); + if (pdata->pending.raw_data) { + memcpy(pdata->pending.raw_data, ptr, sz); + pdata->pending.raw_size = sz; + } else + pdata->pending.raw_size = 0; + complete(&pdata->pending.ready); + } + collection = &hdev->collection[ + report->field[i]->usage->collection_index]; + hid_dbg(hdev, "collection->usage %x\n", + collection->usage); + callback = sensor_hub_get_callback(pdata->hsdev->hdev, + report->field[i]->physical, + &priv); + if (callback && callback->capture_sample) { + if (report->field[i]->logical) + callback->capture_sample(pdata->hsdev, + report->field[i]->logical, sz, ptr, + callback->pdev); + else + callback->capture_sample(pdata->hsdev, + report->field[i]->usage->hid, sz, ptr, + callback->pdev); + } + ptr += sz; + } + if (callback && collection && callback->send_event) + callback->send_event(pdata->hsdev, collection->usage, + callback->pdev); + spin_unlock_irqrestore(&pdata->lock, flags); + +err_report: + return 1; +} + +static int sensor_hub_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + struct sensor_hub_data *sd; + int i; + char *name; + struct hid_report *report; + struct hid_report_enum *report_enum; + struct hid_field *field; + int dev_cnt; + + sd = kzalloc(sizeof(struct sensor_hub_data), GFP_KERNEL); + if (!sd) { + hid_err(hdev, "cannot allocate Sensor data\n"); + return -ENOMEM; + } + sd->hsdev = kzalloc(sizeof(struct hid_sensor_hub_device), GFP_KERNEL); + if (!sd->hsdev) { + hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); + ret = -ENOMEM; + goto err_free_hub; + } + hid_set_drvdata(hdev, sd); + sd->hsdev->hdev = hdev; + sd->hsdev->vendor_id = hdev->vendor; + sd->hsdev->product_id = hdev->product; + spin_lock_init(&sd->lock); + spin_lock_init(&sd->dyn_callback_lock); + mutex_init(&sd->mutex); + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err_free; + } + if (sensor_hub_check_for_sensor_page(hdev) < 0) { + hid_err(hdev, "sensor page not found\n"); + goto err_free; + } + INIT_LIST_HEAD(&hdev->inputs); + + hdev->claimed = HID_CLAIMED_INPUT; + ret = hid_hw_start(hdev, 0); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err_free; + } + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "failed to open input interrupt pipe\n"); + goto err_stop_hw; + } + + INIT_LIST_HEAD(&sd->dyn_callback_list); + sd->hid_sensor_client_cnt = 0; + report_enum = &hdev->report_enum[HID_INPUT_REPORT]; + + dev_cnt = sensor_hub_get_physical_device_count(report_enum); + if (dev_cnt > HID_MAX_PHY_DEVICES) { + hid_err(hdev, "Invalid Physical device count\n"); + ret = -EINVAL; + goto err_close; + } + sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt * + sizeof(struct mfd_cell), + GFP_KERNEL); + if (sd->hid_sensor_hub_client_devs == NULL) { + hid_err(hdev, + "Failed to allocate memory for mfd cells\n"); + ret = -ENOMEM; + goto err_close; + } + list_for_each_entry(report, &report_enum->report_list, list) { + hid_dbg(hdev, "Report id:%x\n", report->id); + field = report->field[0]; + if (report->maxfield && field && + field->physical) { + name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x", + field->physical); + if (name == NULL) { + hid_err(hdev, + "Failed MFD device name\n"); + ret = -ENOMEM; + goto err_free_cells; + } + sd->hid_sensor_hub_client_devs[ + sd->hid_sensor_client_cnt].name = name; + sd->hid_sensor_hub_client_devs[ + sd->hid_sensor_client_cnt].platform_data = + sd->hsdev; + sd->hid_sensor_hub_client_devs[ + sd->hid_sensor_client_cnt].pdata_size = + sizeof(*sd->hsdev); + hid_dbg(hdev, "Adding %s:%p\n", name, sd); + sd->hid_sensor_client_cnt++; + } + } + ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs, + sd->hid_sensor_client_cnt, NULL, 0); + if (ret < 0) + goto err_free_names; + + return ret; + +err_free_names: + for (i = 0; i < sd->hid_sensor_client_cnt ; ++i) + kfree(sd->hid_sensor_hub_client_devs[i].name); +err_free_cells: + kfree(sd->hid_sensor_hub_client_devs); +err_close: + hid_hw_stop(hdev); + hid_hw_close(hdev); +err_stop_hw: + hid_hw_stop(hdev); +err_free: + kfree(sd->hsdev); +err_free_hub: + kfree(sd); + + return ret; +} + +static void sensor_hub_remove(struct hid_device *hdev) +{ + struct sensor_hub_data *data = hid_get_drvdata(hdev); + unsigned long flags; + int i; + + hid_dbg(hdev, " hardware removed\n"); + hdev->claimed &= ~HID_CLAIMED_INPUT; + hid_hw_stop(hdev); + hid_hw_close(hdev); + spin_lock_irqsave(&data->lock, flags); + if (data->pending.status) + complete(&data->pending.ready); + spin_unlock_irqrestore(&data->lock, flags); + mfd_remove_devices(&hdev->dev); + for (i = 0; i < data->hid_sensor_client_cnt ; ++i) + kfree(data->hid_sensor_hub_client_devs[i].name); + kfree(data->hid_sensor_hub_client_devs); + hid_set_drvdata(hdev, NULL); + mutex_destroy(&data->mutex); + kfree(data->hsdev); + kfree(data); +} + +static const struct hid_device_id sensor_hub_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, + USB_DEVICE_ID_SENSOR_HUB_1020) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, + USB_DEVICE_ID_SENSOR_HUB_1020) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, + USB_DEVICE_ID_SENSOR_HUB_09FA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, + USB_DEVICE_ID_SENSOR_HUB_09FA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, + USB_DEVICE_ID_SENSOR_HUB_7014) }, + { } +}; +MODULE_DEVICE_TABLE(hid, sensor_hub_devices); + +static const struct hid_usage_id sensor_hub_grabbed_usages[] = { + { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 } +}; + +static struct hid_driver sensor_hub_driver = { + .name = "hid-sensor-hub", + .id_table = sensor_hub_devices, + .probe = sensor_hub_probe, + .remove = sensor_hub_remove, + .raw_event = sensor_hub_raw_event, +#ifdef CONFIG_PM + .suspend = sensor_hub_suspend, + .resume = sensor_hub_resume, + .reset_resume = sensor_hub_reset_resume, +#endif +}; + +static int __init sensor_hub_init(void) +{ + return hid_register_driver(&sensor_hub_driver); +} + +static void __exit sensor_hub_exit(void) +{ + hid_unregister_driver(&sensor_hub_driver); +} + +module_init(sensor_hub_init); +module_exit(sensor_hub_exit); + +MODULE_DESCRIPTION("HID Sensor Hub driver"); +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 56825e6f5725..6e3f143fc71d 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -54,10 +54,15 @@ config IIO_CONSUMERS_PER_TRIGGER This value controls the maximum number of consumers that a given trigger may handle. Default is 2. +source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/light/Kconfig" source "drivers/iio/frequency/Kconfig" source "drivers/iio/dac/Kconfig" +source "drivers/iio/common/Kconfig" +source "drivers/iio/gyro/Kconfig" +source "drivers/iio/light/Kconfig" +source "drivers/iio/magnetometer/Kconfig" endif # IIO diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 34309abb7979..f7fa3c0867b4 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -10,8 +10,13 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o +obj-y += accel/ obj-y += adc/ obj-y += amplifiers/ obj-y += light/ obj-y += frequency/ obj-y += dac/ +obj-y += common/ +obj-y += gyro/ +obj-y += light/ +obj-y += magnetometer/ diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig new file mode 100644 index 000000000000..b2510c4d9a5a --- /dev/null +++ b/drivers/iio/accel/Kconfig @@ -0,0 +1,16 @@ +# +# Accelerometer drivers +# +menu "Accelerometers" + +config HID_SENSOR_ACCEL_3D + depends on HID_SENSOR_HUB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select HID_SENSOR_IIO_COMMON + tristate "HID Acelerometers 3D" + help + Say yes here to build support for the HID SENSOR + accelerometers 3D. + +endmenu diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile new file mode 100644 index 000000000000..5bc6855a973e --- /dev/null +++ b/drivers/iio/accel/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for industrial I/O accelerometer drivers +# + +obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c new file mode 100644 index 000000000000..314a4057879e --- /dev/null +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -0,0 +1,418 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../common/hid-sensors/hid-sensor-attributes.h" +#include "../common/hid-sensors/hid-sensor-trigger.h" + +/*Format: HID-SENSOR-usage_id_in_hex*/ +/*Usage ID from spec for Accelerometer-3D: 0x200073*/ +#define DRIVER_NAME "HID-SENSOR-200073" + +enum accel_3d_channel { + CHANNEL_SCAN_INDEX_X, + CHANNEL_SCAN_INDEX_Y, + CHANNEL_SCAN_INDEX_Z, + ACCEL_3D_CHANNEL_MAX, +}; + +struct accel_3d_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_iio_common common_attributes; + struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX]; + u32 accel_val[ACCEL_3D_CHANNEL_MAX]; +}; + +static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = { + HID_USAGE_SENSOR_ACCEL_X_AXIS, + HID_USAGE_SENSOR_ACCEL_Y_AXIS, + HID_USAGE_SENSOR_ACCEL_Z_AXIS +}; + +/* Channel definitions */ +static const struct iio_chan_spec accel_3d_channels[] = { + { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_X, + }, { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_Y, + }, { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_Z, + } +}; + +/* Adjust channel real bits based on report descriptor */ +static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels, + int channel, int size) +{ + channels[channel].scan_type.sign = 's'; + /* Real storage bits will change based on the report desc. */ + channels[channel].scan_type.realbits = size * 8; + /* Maximum size of a sample to capture is u32 */ + channels[channel].scan_type.storagebits = sizeof(u32) * 8; +} + +/* Channel read_raw handler */ +static int accel_3d_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct accel_3d_state *accel_state = iio_priv(indio_dev); + int report_id = -1; + u32 address; + int ret; + int ret_type; + + *val = 0; + *val2 = 0; + switch (mask) { + case 0: + report_id = accel_state->accel[chan->scan_index].report_id; + address = accel_3d_addresses[chan->scan_index]; + if (report_id >= 0) + *val = sensor_hub_input_attr_get_raw_value( + accel_state->common_attributes.hsdev, + HID_USAGE_SENSOR_ACCEL_3D, address, + report_id); + else { + *val = 0; + return -EINVAL; + } + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = accel_state->accel[CHANNEL_SCAN_INDEX_X].units; + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_OFFSET: + *val = hid_sensor_convert_exponent( + accel_state->accel[CHANNEL_SCAN_INDEX_X].unit_expo); + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_read_samp_freq_value( + &accel_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_read_raw_hyst_value( + &accel_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret_type = -EINVAL; + break; + } + + return ret_type; +} + +/* Channel write_raw handler */ +static int accel_3d_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct accel_3d_state *accel_state = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_write_samp_freq_value( + &accel_state->common_attributes, val, val2); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_write_raw_hyst_value( + &accel_state->common_attributes, val, val2); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int accel_3d_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_MICRO; +} + +static const struct iio_info accel_3d_info = { + .driver_module = THIS_MODULE, + .read_raw = &accel_3d_read_raw, + .write_raw = &accel_3d_write_raw, + .write_raw_get_fmt = &accel_3d_write_raw_get_fmt, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +{ + struct iio_buffer *buffer = indio_dev->buffer; + int datum_sz; + + dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); + if (!buffer) { + dev_err(&indio_dev->dev, "Buffer == NULL\n"); + return; + } + datum_sz = buffer->access->get_bytes_per_datum(buffer); + if (len > datum_sz) { + dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, + datum_sz); + return; + } + iio_push_to_buffer(buffer, (u8 *)data); +} + +/* Callback handler to send event after all samples are received and captured */ +static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct accel_3d_state *accel_state = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "accel_3d_proc_event [%d]\n", + accel_state->common_attributes.data_ready); + if (accel_state->common_attributes.data_ready) + hid_sensor_push_data(indio_dev, + (u8 *)accel_state->accel_val, + sizeof(accel_state->accel_val)); + + return 0; +} + +/* Capture samples in local storage */ +static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + size_t raw_len, char *raw_data, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct accel_3d_state *accel_state = iio_priv(indio_dev); + int offset; + int ret = -EINVAL; + + switch (usage_id) { + case HID_USAGE_SENSOR_ACCEL_X_AXIS: + case HID_USAGE_SENSOR_ACCEL_Y_AXIS: + case HID_USAGE_SENSOR_ACCEL_Z_AXIS: + offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS; + accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] = + *(u32 *)raw_data; + ret = 0; + break; + default: + break; + } + + return ret; +} + +/* Parse report which is specific to an usage id*/ +static int accel_3d_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + unsigned usage_id, + struct accel_3d_state *st) +{ + int ret; + int i; + + for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) { + ret = sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_ACCEL_X_AXIS + i, + &st->accel[CHANNEL_SCAN_INDEX_X + i]); + if (ret < 0) + break; + accel_3d_adjust_channel_bit_mask(channels, + CHANNEL_SCAN_INDEX_X + i, + st->accel[CHANNEL_SCAN_INDEX_X + i].size); + } + dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n", + st->accel[0].index, + st->accel[0].report_id, + st->accel[1].index, st->accel[1].report_id, + st->accel[2].index, st->accel[2].report_id); + + return ret; +} + +/* Function to initialize the processing for usage id */ +static int __devinit hid_accel_3d_probe(struct platform_device *pdev) +{ + int ret = 0; + static const char *name = "accel_3d"; + struct iio_dev *indio_dev; + struct accel_3d_state *accel_state; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_chan_spec *channels; + + indio_dev = iio_device_alloc(sizeof(struct accel_3d_state)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, indio_dev); + + accel_state = iio_priv(indio_dev); + accel_state->common_attributes.hsdev = hsdev; + accel_state->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, + HID_USAGE_SENSOR_ACCEL_3D, + &accel_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "failed to setup common attributes\n"); + goto error_free_dev; + } + + channels = kmemdup(accel_3d_channels, + sizeof(accel_3d_channels), + GFP_KERNEL); + if (!channels) { + dev_err(&pdev->dev, "failed to duplicate channels\n"); + goto error_free_dev; + } + + ret = accel_3d_parse_report(pdev, hsdev, channels, + HID_USAGE_SENSOR_ACCEL_3D, accel_state); + if (ret) { + dev_err(&pdev->dev, "failed to setup attributes\n"); + goto error_free_dev_mem; + } + + indio_dev->channels = channels; + indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &accel_3d_info; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); + goto error_free_dev_mem; + } + accel_state->common_attributes.data_ready = false; + ret = hid_sensor_setup_trigger(indio_dev, name, + &accel_state->common_attributes); + if (ret < 0) { + dev_err(&pdev->dev, "trigger setup failed\n"); + goto error_unreg_buffer_funcs; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_trigger; + } + + accel_state->callbacks.send_event = accel_3d_proc_event; + accel_state->callbacks.capture_sample = accel_3d_capture_sample; + accel_state->callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D, + &accel_state->callbacks); + if (ret < 0) { + dev_err(&pdev->dev, "callback reg failed\n"); + goto error_iio_unreg; + } + + return ret; + +error_iio_unreg: + iio_device_unregister(indio_dev); +error_remove_trigger: + hid_sensor_remove_trigger(indio_dev); +error_unreg_buffer_funcs: + iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: + kfree(indio_dev->channels); +error_free_dev: + iio_device_free(indio_dev); +error_ret: + return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int __devinit hid_accel_3d_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D); + iio_device_unregister(indio_dev); + hid_sensor_remove_trigger(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + kfree(indio_dev->channels); + iio_device_free(indio_dev); + + return 0; +} + +static struct platform_driver hid_accel_3d_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = hid_accel_3d_probe, + .remove = hid_accel_3d_remove, +}; +module_platform_driver(hid_accel_3d_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Accel 3D"); +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index a2c50713b0b6..d0ae71ec2aa0 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -18,6 +18,18 @@ config AD7266 Say yes here to build support for Analog Devices AD7265 and AD7266 ADCs. +config AD7791 + tristate "Analog Devices AD7791 ADC driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7787, AD7788, AD7789, + AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say + N (but it is safe to say "Y"). + + To compile this driver as a module, choose M here: the module will be + called ad7791. + config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 5989356c5735..f187ff6c2a16 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD7266) += ad7266.o +obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AT91_ADC) += at91_adc.o diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 5c3f1ba5a06d..b11f214779a2 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -99,7 +99,7 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p) if (ret == 0) { if (indio_dev->scan_timestamp) ((s64 *)st->data)[1] = pf->timestamp; - iio_push_to_buffer(buffer, (u8 *)st->data, pf->timestamp); + iio_push_to_buffer(buffer, (u8 *)st->data); } iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c new file mode 100644 index 000000000000..e93740843b2b --- /dev/null +++ b/drivers/iio/adc/ad7791.c @@ -0,0 +1,460 @@ +/* + * AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver + * + * Copyright 2012 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD7791_REG_COMM 0x0 /* For writes */ +#define AD7791_REG_STATUS 0x0 /* For reads */ +#define AD7791_REG_MODE 0x1 +#define AD7791_REG_FILTER 0x2 +#define AD7791_REG_DATA 0x3 + +#define AD7791_MODE_CONTINUOUS 0x00 +#define AD7791_MODE_SINGLE 0x02 +#define AD7791_MODE_POWERDOWN 0x03 + +#define AD7791_CH_AIN1P_AIN1N 0x00 +#define AD7791_CH_AIN2 0x01 +#define AD7791_CH_AIN1N_AIN1N 0x02 +#define AD7791_CH_AVDD_MONITOR 0x03 + +#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4) +#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4) +#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4) +#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4) +#define AD7791_FILTER_CLK_MASK (0x3 << 4) +#define AD7791_FILTER_RATE_120 0x0 +#define AD7791_FILTER_RATE_100 0x1 +#define AD7791_FILTER_RATE_33_3 0x2 +#define AD7791_FILTER_RATE_20 0x3 +#define AD7791_FILTER_RATE_16_6 0x4 +#define AD7791_FILTER_RATE_16_7 0x5 +#define AD7791_FILTER_RATE_13_3 0x6 +#define AD7791_FILTER_RATE_9_5 0x7 +#define AD7791_FILTER_RATE_MASK 0x7 + +#define AD7791_MODE_BUFFER BIT(1) +#define AD7791_MODE_UNIPOLAR BIT(2) +#define AD7791_MODE_BURNOUT BIT(3) +#define AD7791_MODE_SEL_MASK (0x3 << 6) +#define AD7791_MODE_SEL(x) ((x) << 6) + +#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \ +const struct iio_chan_spec name[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + (bits), (storagebits), 0), \ + AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \ + AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \ + (bits), (storagebits), 0), \ + AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \ + (bits), (storagebits), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \ +const struct iio_chan_spec name[] = { \ + AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + (bits), (storagebits), 0), \ + AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \ + (bits), (storagebits), 0), \ + AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \ + (bits), (storagebits), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(3), \ +} + +static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32); +static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16); +static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32); + +enum { + AD7787, + AD7788, + AD7789, + AD7790, + AD7791, +}; + +enum ad7791_chip_info_flags { + AD7791_FLAG_HAS_FILTER = (1 << 0), + AD7791_FLAG_HAS_BUFFER = (1 << 1), + AD7791_FLAG_HAS_UNIPOLAR = (1 << 2), + AD7791_FLAG_HAS_BURNOUT = (1 << 3), +}; + +struct ad7791_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + enum ad7791_chip_info_flags flags; +}; + +static const struct ad7791_chip_info ad7791_chip_infos[] = { + [AD7787] = { + .channels = ad7787_channels, + .num_channels = ARRAY_SIZE(ad7787_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, + }, + [AD7788] = { + .channels = ad7790_channels, + .num_channels = ARRAY_SIZE(ad7790_channels), + .flags = AD7791_FLAG_HAS_UNIPOLAR, + }, + [AD7789] = { + .channels = ad7791_channels, + .num_channels = ARRAY_SIZE(ad7791_channels), + .flags = AD7791_FLAG_HAS_UNIPOLAR, + }, + [AD7790] = { + .channels = ad7790_channels, + .num_channels = ARRAY_SIZE(ad7790_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_BURNOUT, + }, + [AD7791] = { + .channels = ad7791_channels, + .num_channels = ARRAY_SIZE(ad7791_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, + }, +}; + +struct ad7791_state { + struct ad_sigma_delta sd; + uint8_t mode; + uint8_t filter; + + struct regulator *reg; + const struct ad7791_chip_info *info; +}; + +static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7791_state, sd); +} + +static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + ad_sd_set_comm(sd, channel); + + return 0; +} + +static int ad7791_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd); + + switch (mode) { + case AD_SD_MODE_CONTINUOUS: + mode = AD7791_MODE_CONTINUOUS; + break; + case AD_SD_MODE_SINGLE: + mode = AD7791_MODE_SINGLE; + break; + case AD_SD_MODE_IDLE: + case AD_SD_MODE_POWERDOWN: + mode = AD7791_MODE_POWERDOWN; + break; + } + + st->mode &= ~AD7791_MODE_SEL_MASK; + st->mode |= AD7791_MODE_SEL(mode); + + return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode); +} + +static const struct ad_sigma_delta_info ad7791_sigma_delta_info = { + .set_channel = ad7791_set_channel, + .set_mode = ad7791_set_mode, + .has_registers = true, + .addr_shift = 4, + .read_mask = BIT(3), +}; + +static int ad7791_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + struct ad7791_state *st = iio_priv(indio_dev); + bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR); + unsigned long long scale_pv; + + switch (info) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_OFFSET: + /** + * Unipolar: 0 to VREF + * Bipolar -VREF to VREF + **/ + if (unipolar) + *val = 0; + else + *val = -(1 << (chan->scan_type.realbits - 1)); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* The monitor channel uses an internal reference. */ + if (chan->address == AD7791_CH_AVDD_MONITOR) { + scale_pv = 5850000000000ULL; + } else { + int voltage_uv; + + voltage_uv = regulator_get_voltage(st->reg); + if (voltage_uv < 0) + return voltage_uv; + scale_pv = (unsigned long long)voltage_uv * 1000000; + } + if (unipolar) + scale_pv >>= chan->scan_type.realbits; + else + scale_pv >>= chan->scan_type.realbits - 1; + *val2 = do_div(scale_pv, 1000000000); + *val = scale_pv; + + return IIO_VAL_INT_PLUS_NANO; + } + + return -EINVAL; +} + +static const char * const ad7791_sample_freq_avail[] = { + [AD7791_FILTER_RATE_120] = "120", + [AD7791_FILTER_RATE_100] = "100", + [AD7791_FILTER_RATE_33_3] = "33.3", + [AD7791_FILTER_RATE_20] = "20", + [AD7791_FILTER_RATE_16_6] = "16.6", + [AD7791_FILTER_RATE_16_7] = "16.7", + [AD7791_FILTER_RATE_13_3] = "13.3", + [AD7791_FILTER_RATE_9_5] = "9.5", +}; + +static ssize_t ad7791_read_frequency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7791_state *st = iio_priv(indio_dev); + unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK; + + return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]); +} + +static ssize_t ad7791_write_frequency(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7791_state *st = iio_priv(indio_dev); + int i, ret; + + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + mutex_unlock(&indio_dev->mlock); + + ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) { + if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) { + + mutex_lock(&indio_dev->mlock); + st->filter &= ~AD7791_FILTER_RATE_MASK; + st->filter |= i; + ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, + sizeof(st->filter), st->filter); + mutex_unlock(&indio_dev->mlock); + ret = 0; + break; + } + } + + return ret ? ret : len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + ad7791_read_frequency, + ad7791_write_frequency); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5"); + +static struct attribute *ad7791_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7791_attribute_group = { + .attrs = ad7791_attributes, +}; + +static const struct iio_info ad7791_info = { + .read_raw = &ad7791_read_raw, + .attrs = &ad7791_attribute_group, + .validate_trigger = ad_sd_validate_trigger, + .driver_module = THIS_MODULE, +}; + +static const struct iio_info ad7791_no_filter_info = { + .read_raw = &ad7791_read_raw, + .validate_trigger = ad_sd_validate_trigger, + .driver_module = THIS_MODULE, +}; + +static int __devinit ad7791_setup(struct ad7791_state *st, + struct ad7791_platform_data *pdata) +{ + /* Set to poweron-reset default values */ + st->mode = AD7791_MODE_BUFFER; + st->filter = AD7791_FILTER_RATE_16_6; + + if (!pdata) + return 0; + + if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered) + st->mode &= ~AD7791_MODE_BUFFER; + + if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) && + pdata->burnout_current) + st->mode |= AD7791_MODE_BURNOUT; + + if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar) + st->mode |= AD7791_MODE_UNIPOLAR; + + return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode), + st->mode); +} + +static int __devinit ad7791_probe(struct spi_device *spi) +{ + struct ad7791_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad7791_state *st; + int ret; + + if (!spi->irq) { + dev_err(&spi->dev, "Missing IRQ.\n"); + return -ENXIO; + } + + indio_dev = iio_device_alloc(sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = regulator_get(&spi->dev, "refin"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto err_iio_free; + } + + ret = regulator_enable(st->reg); + if (ret) + goto error_put_reg; + + st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data]; + ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info); + + spi_set_drvdata(spi, indio_dev); + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + if (st->info->flags & AD7791_FLAG_HAS_FILTER) + indio_dev->info = &ad7791_info; + else + indio_dev->info = &ad7791_no_filter_info; + + ret = ad_sd_setup_buffer_and_trigger(indio_dev); + if (ret) + goto error_disable_reg; + + ret = ad7791_setup(st, pdata); + if (ret) + goto error_remove_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_remove_trigger; + + return 0; + +error_remove_trigger: + ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_disable_reg: + regulator_disable(st->reg); +error_put_reg: + regulator_put(st->reg); +err_iio_free: + iio_device_free(indio_dev); + + return ret; +} + +static int __devexit ad7791_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7791_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + ad_sd_cleanup_buffer_and_trigger(indio_dev); + + regulator_disable(st->reg); + regulator_put(st->reg); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct spi_device_id ad7791_spi_ids[] = { + { "ad7787", AD7787 }, + { "ad7788", AD7788 }, + { "ad7789", AD7789 }, + { "ad7790", AD7790 }, + { "ad7791", AD7791 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7791_spi_ids); + +static struct spi_driver ad7791_driver = { + .driver = { + .name = "ad7791", + .owner = THIS_MODULE, + }, + .probe = ad7791_probe, + .remove = __devexit_p(ad7791_remove), + .id_table = ad7791_spi_ids, +}; +module_spi_driver(ad7791_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index ae847c5a1361..67baa1363d7a 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -391,7 +391,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) break; } - iio_push_to_buffer(indio_dev->buffer, (uint8_t *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (uint8_t *)data); iio_trigger_notify_done(indio_dev->trig); sigma_delta->irq_dis = false; diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index c1e4690f188d..bc10091fe76c 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -82,7 +82,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp); + buffer->access->store_to(buffer, (u8 *)st->buffer); iio_trigger_notify_done(idev->trig); st->irq_enabled = true; diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig new file mode 100644 index 000000000000..ed45ee54500c --- /dev/null +++ b/drivers/iio/common/Kconfig @@ -0,0 +1,5 @@ +# +# IIO common modules +# + +source "drivers/iio/common/hid-sensors/Kconfig" diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile new file mode 100644 index 000000000000..81584009b21b --- /dev/null +++ b/drivers/iio/common/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the IIO common modules. +# Common modules contains modules, which can be shared among multiple +# IIO modules. For example if the trigger processing is common for +# multiple IIO modules then this can be moved to a common module +# instead of duplicating in each module. +# + +obj-y += hid-sensors/ diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig new file mode 100644 index 000000000000..8e63d81d652a --- /dev/null +++ b/drivers/iio/common/hid-sensors/Kconfig @@ -0,0 +1,26 @@ +# +# Hid Sensor common modules +# +menu "Hid Sensor IIO Common" + +config HID_SENSOR_IIO_COMMON + tristate "Common modules for all HID Sensor IIO drivers" + depends on HID_SENSOR_HUB + select IIO_TRIGGER if IIO_BUFFER + help + Say yes here to build support for HID sensor to use + HID sensor common processing for attributes and IIO triggers. + There are many attributes which can be shared among multiple + HID sensor drivers, this module contains processing for those + attributes. + +config HID_SENSOR_ENUM_BASE_QUIRKS + tristate "ENUM base quirks for HID Sensor IIO drivers" + depends on HID_SENSOR_IIO_COMMON + help + Say yes here to build support for sensor hub FW using + enumeration, which is using 1 as base instead of 0. + Since logical minimum is still set 0 instead of 1, + there is no easy way to differentiate. + +endmenu diff --git a/drivers/iio/common/hid-sensors/Makefile b/drivers/iio/common/hid-sensors/Makefile new file mode 100644 index 000000000000..1f463e00c242 --- /dev/null +++ b/drivers/iio/common/hid-sensors/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Hid sensor common modules. +# + +obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o +hid-sensor-iio-common-y := hid-sensor-attributes.o hid-sensor-trigger.o diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c new file mode 100644 index 000000000000..75374955caba --- /dev/null +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -0,0 +1,250 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hid-sensor-attributes.h" + +static int pow_10(unsigned power) +{ + int i; + int ret = 1; + for (i = 0; i < power; ++i) + ret = ret * 10; + + return ret; +} + +static void simple_div(int dividend, int divisor, int *whole, + int *micro_frac) +{ + int rem; + int exp = 0; + + *micro_frac = 0; + if (divisor == 0) { + *whole = 0; + return; + } + *whole = dividend/divisor; + rem = dividend % divisor; + if (rem) { + while (rem <= divisor) { + rem *= 10; + exp++; + } + *micro_frac = (rem / divisor) * pow_10(6-exp); + } +} + +static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2) +{ + *val1 = no/pow_10(exp); + *val2 = no%pow_10(exp) * pow_10(6-exp); +} + +/* +VTF format uses exponent and variable size format. +For example if the size is 2 bytes +0x0067 with VTF16E14 format -> +1.03 +To convert just change to 0x67 to decimal and use two decimal as E14 stands +for 10^-2. +Negative numbers are 2's complement +*/ +static void convert_from_vtf_format(u32 value, int size, int exp, + int *val1, int *val2) +{ + int sign = 1; + + if (value & BIT(size*8 - 1)) { + value = ((1LL << (size * 8)) - value); + sign = -1; + } + exp = hid_sensor_convert_exponent(exp); + if (exp >= 0) { + *val1 = sign * value * pow_10(exp); + *val2 = 0; + } else { + split_micro_fraction(value, -exp, val1, val2); + if (*val1) + *val1 = sign * (*val1); + else + *val2 = sign * (*val2); + } +} + +static u32 convert_to_vtf_format(int size, int exp, int val1, int val2) +{ + u32 value; + int sign = 1; + + if (val1 < 0 || val2 < 0) + sign = -1; + exp = hid_sensor_convert_exponent(exp); + if (exp < 0) { + value = abs(val1) * pow_10(-exp); + value += abs(val2) / pow_10(6+exp); + } else + value = abs(val1) / pow_10(exp); + if (sign < 0) + value = ((1LL << (size * 8)) - value); + + return value; +} + +int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st, + int *val1, int *val2) +{ + s32 value; + int ret; + + ret = sensor_hub_get_feature(st->hsdev, + st->poll.report_id, + st->poll.index, &value); + if (ret < 0 || value < 0) { + *val1 = *val2 = 0; + return -EINVAL; + } else { + if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND) + simple_div(1000, value, val1, val2); + else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND) + simple_div(1, value, val1, val2); + else { + *val1 = *val2 = 0; + return -EINVAL; + } + } + + return IIO_VAL_INT_PLUS_MICRO; +} +EXPORT_SYMBOL(hid_sensor_read_samp_freq_value); + +int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st, + int val1, int val2) +{ + s32 value; + int ret; + + if (val1 < 0 || val2 < 0) + ret = -EINVAL; + + value = val1 * pow_10(6) + val2; + if (value) { + if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND) + value = pow_10(9)/value; + else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND) + value = pow_10(6)/value; + else + value = 0; + } + ret = sensor_hub_set_feature(st->hsdev, + st->poll.report_id, + st->poll.index, value); + if (ret < 0 || value < 0) + ret = -EINVAL; + + return ret; +} +EXPORT_SYMBOL(hid_sensor_write_samp_freq_value); + +int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st, + int *val1, int *val2) +{ + s32 value; + int ret; + + ret = sensor_hub_get_feature(st->hsdev, + st->sensitivity.report_id, + st->sensitivity.index, &value); + if (ret < 0 || value < 0) { + *val1 = *val2 = 0; + return -EINVAL; + } else { + convert_from_vtf_format(value, st->sensitivity.size, + st->sensitivity.unit_expo, + val1, val2); + } + + return IIO_VAL_INT_PLUS_MICRO; +} +EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value); + +int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st, + int val1, int val2) +{ + s32 value; + int ret; + + value = convert_to_vtf_format(st->sensitivity.size, + st->sensitivity.unit_expo, + val1, val2); + ret = sensor_hub_set_feature(st->hsdev, + st->sensitivity.report_id, + st->sensitivity.index, value); + if (ret < 0 || value < 0) + ret = -EINVAL; + + return ret; +} +EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value); + +int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct hid_sensor_iio_common *st) +{ + + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_PROP_REPORT_INTERVAL, + &st->poll); + + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_PROP_REPORT_STATE, + &st->report_state); + + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_PROY_POWER_STATE, + &st->power_state); + + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, + &st->sensitivity); + + hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n", + st->poll.index, st->poll.report_id, + st->report_state.index, st->report_state.report_id, + st->power_state.index, st->power_state.report_id, + st->sensitivity.index, st->sensitivity.report_id); + + return 0; +} +EXPORT_SYMBOL(hid_sensor_parse_common_attributes); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_DESCRIPTION("HID Sensor common attribute processing"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.h b/drivers/iio/common/hid-sensors/hid-sensor-attributes.h new file mode 100644 index 000000000000..a4676a0c3de5 --- /dev/null +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.h @@ -0,0 +1,57 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef _HID_SENSORS_ATTRIBUTES_H +#define _HID_SENSORS_ATTRIBUTES_H + +/* Common hid sensor iio structure */ +struct hid_sensor_iio_common { + struct hid_sensor_hub_device *hsdev; + struct platform_device *pdev; + unsigned usage_id; + bool data_ready; + struct hid_sensor_hub_attribute_info poll; + struct hid_sensor_hub_attribute_info report_state; + struct hid_sensor_hub_attribute_info power_state; + struct hid_sensor_hub_attribute_info sensitivity; +}; + +/*Convert from hid unit expo to regular exponent*/ +static inline int hid_sensor_convert_exponent(int unit_expo) +{ + if (unit_expo < 0x08) + return unit_expo; + else if (unit_expo <= 0x0f) + return -(0x0f-unit_expo+1); + else + return 0; +} + +int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct hid_sensor_iio_common *st); +int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st, + int val1, int val2); +int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st, + int *val1, int *val2); +int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st, + int val1, int val2); +int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st, + int *val1, int *val2); + +#endif diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c new file mode 100644 index 000000000000..12277e8bbd80 --- /dev/null +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -0,0 +1,102 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hid-sensor-attributes.h" +#include "hid-sensor-trigger.h" + +static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct hid_sensor_iio_common *st = trig->private_data; + int state_val; + + state_val = state ? 1 : 0; +#if (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS) || \ + (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS_MODULE) + ++state_val; +#endif + st->data_ready = state; + sensor_hub_set_feature(st->hsdev, st->power_state.report_id, + st->power_state.index, + (s32)state_val); + + sensor_hub_set_feature(st->hsdev, st->report_state.report_id, + st->report_state.index, + (s32)state_val); + + return 0; +} + +void hid_sensor_remove_trigger(struct iio_dev *indio_dev) +{ + iio_trigger_unregister(indio_dev->trig); + iio_trigger_free(indio_dev->trig); +} +EXPORT_SYMBOL(hid_sensor_remove_trigger); + +static const struct iio_trigger_ops hid_sensor_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = &hid_sensor_data_rdy_trigger_set_state, +}; + +int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, + struct hid_sensor_iio_common *attrb) +{ + int ret; + struct iio_trigger *trig; + + trig = iio_trigger_alloc("%s-dev%d", name, indio_dev->id); + if (trig == NULL) { + dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); + ret = -ENOMEM; + goto error_ret; + } + + trig->dev.parent = indio_dev->dev.parent; + trig->private_data = attrb; + trig->ops = &hid_sensor_trigger_ops; + ret = iio_trigger_register(trig); + + if (ret) { + dev_err(&indio_dev->dev, "Trigger Register Failed\n"); + goto error_free_trig; + } + indio_dev->trig = trig; + + return ret; + +error_free_trig: + iio_trigger_free(trig); +error_ret: + return ret; +} +EXPORT_SYMBOL(hid_sensor_setup_trigger); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_DESCRIPTION("HID Sensor trigger processing"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h new file mode 100644 index 000000000000..fd982971b1b8 --- /dev/null +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h @@ -0,0 +1,26 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef _HID_SENSOR_TRIGGER_H +#define _HID_SENSOR_TRIGGER_H + +int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, + struct hid_sensor_iio_common *attrb); +void hid_sensor_remove_trigger(struct iio_dev *indio_dev); + +#endif diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 293b61dcc559..78452630f85d 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -59,10 +59,10 @@ config AD5446 tristate "Analog Devices AD5446 and similar single channel DACs driver" depends on (SPI_MASTER || I2C) help - Say yes here to build support for Analog Devices AD5602, AD5612, AD5622, - AD5444, AD5446, AD5450, AD5451, AD5452, AD5453, AD5512A, AD5541A, AD5542A, - AD5543, AD5553, AD5601, AD5611, AD5620, AD5621, AD5640, AD5660, AD5662 - DACs. + Say yes here to build support for Analog Devices AD5300, AD5301, AD5310, + AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453, + AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612, + AD5620, AD5621, AD5622, AD5640, AD5660, AD5662 DACs. To compile this driver as a module, choose M here: the module will be called ad5446. diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 7f11c1c89960..3310cbbd41e7 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -321,6 +321,9 @@ static int ad5660_write(struct ad5446_state *st, unsigned val) * parts are supported here. */ enum ad5446_supported_spi_device_ids { + ID_AD5300, + ID_AD5310, + ID_AD5320, ID_AD5444, ID_AD5446, ID_AD5450, @@ -341,6 +344,18 @@ enum ad5446_supported_spi_device_ids { }; static const struct ad5446_chip_info ad5446_spi_chip_info[] = { + [ID_AD5300] = { + .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4), + .write = ad5446_write, + }, + [ID_AD5310] = { + .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2), + .write = ad5446_write, + }, + [ID_AD5320] = { + .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0), + .write = ad5446_write, + }, [ID_AD5444] = { .channel = AD5446_CHANNEL(12, 16, 2), .write = ad5446_write, @@ -418,6 +433,9 @@ static const struct ad5446_chip_info ad5446_spi_chip_info[] = { }; static const struct spi_device_id ad5446_spi_ids[] = { + {"ad5300", ID_AD5300}, + {"ad5310", ID_AD5310}, + {"ad5320", ID_AD5320}, {"ad5444", ID_AD5444}, {"ad5446", ID_AD5446}, {"ad5450", ID_AD5450}, @@ -534,6 +552,9 @@ static int __devexit ad5446_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id ad5446_i2c_ids[] = { + {"ad5301", ID_AD5602}, + {"ad5311", ID_AD5612}, + {"ad5321", ID_AD5622}, {"ad5602", ID_AD5602}, {"ad5612", ID_AD5612}, {"ad5622", ID_AD5622}, diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig new file mode 100644 index 000000000000..21e27e2fc68c --- /dev/null +++ b/drivers/iio/gyro/Kconfig @@ -0,0 +1,16 @@ +# +# IIO Digital Gyroscope Sensor drivers configuration +# +menu "Digital gyroscope sensors" + +config HID_SENSOR_GYRO_3D + depends on HID_SENSOR_HUB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select HID_SENSOR_IIO_COMMON + tristate "HID Gyroscope 3D" + help + Say yes here to build support for the HID SENSOR + Gyroscope 3D. + +endmenu diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile new file mode 100644 index 000000000000..8a895d9fcbce --- /dev/null +++ b/drivers/iio/gyro/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for industrial I/O gyroscope sensor drivers +# + +obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c new file mode 100644 index 000000000000..4c56ada51c39 --- /dev/null +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -0,0 +1,418 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../common/hid-sensors/hid-sensor-attributes.h" +#include "../common/hid-sensors/hid-sensor-trigger.h" + +/*Format: HID-SENSOR-usage_id_in_hex*/ +/*Usage ID from spec for Gyro-3D: 0x200076*/ +#define DRIVER_NAME "HID-SENSOR-200076" + +enum gyro_3d_channel { + CHANNEL_SCAN_INDEX_X, + CHANNEL_SCAN_INDEX_Y, + CHANNEL_SCAN_INDEX_Z, + GYRO_3D_CHANNEL_MAX, +}; + +struct gyro_3d_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_iio_common common_attributes; + struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX]; + u32 gyro_val[GYRO_3D_CHANNEL_MAX]; +}; + +static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = { + HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS, + HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS, + HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS +}; + +/* Channel definitions */ +static const struct iio_chan_spec gyro_3d_channels[] = { + { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_X, + }, { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_Y, + }, { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_Z, + } +}; + +/* Adjust channel real bits based on report descriptor */ +static void gyro_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels, + int channel, int size) +{ + channels[channel].scan_type.sign = 's'; + /* Real storage bits will change based on the report desc. */ + channels[channel].scan_type.realbits = size * 8; + /* Maximum size of a sample to capture is u32 */ + channels[channel].scan_type.storagebits = sizeof(u32) * 8; +} + +/* Channel read_raw handler */ +static int gyro_3d_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct gyro_3d_state *gyro_state = iio_priv(indio_dev); + int report_id = -1; + u32 address; + int ret; + int ret_type; + + *val = 0; + *val2 = 0; + switch (mask) { + case 0: + report_id = gyro_state->gyro[chan->scan_index].report_id; + address = gyro_3d_addresses[chan->scan_index]; + if (report_id >= 0) + *val = sensor_hub_input_attr_get_raw_value( + gyro_state->common_attributes.hsdev, + HID_USAGE_SENSOR_GYRO_3D, address, + report_id); + else { + *val = 0; + return -EINVAL; + } + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = gyro_state->gyro[CHANNEL_SCAN_INDEX_X].units; + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_OFFSET: + *val = hid_sensor_convert_exponent( + gyro_state->gyro[CHANNEL_SCAN_INDEX_X].unit_expo); + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_read_samp_freq_value( + &gyro_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_read_raw_hyst_value( + &gyro_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret_type = -EINVAL; + break; + } + + return ret_type; +} + +/* Channel write_raw handler */ +static int gyro_3d_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct gyro_3d_state *gyro_state = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_write_samp_freq_value( + &gyro_state->common_attributes, val, val2); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_write_raw_hyst_value( + &gyro_state->common_attributes, val, val2); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int gyro_3d_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_MICRO; +} + +static const struct iio_info gyro_3d_info = { + .driver_module = THIS_MODULE, + .read_raw = &gyro_3d_read_raw, + .write_raw = &gyro_3d_write_raw, + .write_raw_get_fmt = &gyro_3d_write_raw_get_fmt, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +{ + struct iio_buffer *buffer = indio_dev->buffer; + int datum_sz; + + dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); + if (!buffer) { + dev_err(&indio_dev->dev, "Buffer == NULL\n"); + return; + } + datum_sz = buffer->access->get_bytes_per_datum(buffer); + if (len > datum_sz) { + dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, + datum_sz); + return; + } + iio_push_to_buffer(buffer, (u8 *)data); +} + +/* Callback handler to send event after all samples are received and captured */ +static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct gyro_3d_state *gyro_state = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "gyro_3d_proc_event [%d]\n", + gyro_state->common_attributes.data_ready); + if (gyro_state->common_attributes.data_ready) + hid_sensor_push_data(indio_dev, + (u8 *)gyro_state->gyro_val, + sizeof(gyro_state->gyro_val)); + + return 0; +} + +/* Capture samples in local storage */ +static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + size_t raw_len, char *raw_data, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct gyro_3d_state *gyro_state = iio_priv(indio_dev); + int offset; + int ret = -EINVAL; + + switch (usage_id) { + case HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS: + case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS: + case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS: + offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS; + gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] = + *(u32 *)raw_data; + ret = 0; + break; + default: + break; + } + + return ret; +} + +/* Parse report which is specific to an usage id*/ +static int gyro_3d_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + unsigned usage_id, + struct gyro_3d_state *st) +{ + int ret; + int i; + + for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) { + ret = sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + i, + &st->gyro[CHANNEL_SCAN_INDEX_X + i]); + if (ret < 0) + break; + gyro_3d_adjust_channel_bit_mask(channels, + CHANNEL_SCAN_INDEX_X + i, + st->gyro[CHANNEL_SCAN_INDEX_X + i].size); + } + dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n", + st->gyro[0].index, + st->gyro[0].report_id, + st->gyro[1].index, st->gyro[1].report_id, + st->gyro[2].index, st->gyro[2].report_id); + + return ret; +} + +/* Function to initialize the processing for usage id */ +static int __devinit hid_gyro_3d_probe(struct platform_device *pdev) +{ + int ret = 0; + static const char *name = "gyro_3d"; + struct iio_dev *indio_dev; + struct gyro_3d_state *gyro_state; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_chan_spec *channels; + + indio_dev = iio_device_alloc(sizeof(struct gyro_3d_state)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, indio_dev); + + gyro_state = iio_priv(indio_dev); + gyro_state->common_attributes.hsdev = hsdev; + gyro_state->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, + HID_USAGE_SENSOR_GYRO_3D, + &gyro_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "failed to setup common attributes\n"); + goto error_free_dev; + } + + channels = kmemdup(gyro_3d_channels, + sizeof(gyro_3d_channels), + GFP_KERNEL); + if (!channels) { + dev_err(&pdev->dev, "failed to duplicate channels\n"); + goto error_free_dev; + } + + ret = gyro_3d_parse_report(pdev, hsdev, channels, + HID_USAGE_SENSOR_GYRO_3D, gyro_state); + if (ret) { + dev_err(&pdev->dev, "failed to setup attributes\n"); + goto error_free_dev_mem; + } + + indio_dev->channels = channels; + indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &gyro_3d_info; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); + goto error_free_dev_mem; + } + gyro_state->common_attributes.data_ready = false; + ret = hid_sensor_setup_trigger(indio_dev, name, + &gyro_state->common_attributes); + if (ret < 0) { + dev_err(&pdev->dev, "trigger setup failed\n"); + goto error_unreg_buffer_funcs; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_trigger; + } + + gyro_state->callbacks.send_event = gyro_3d_proc_event; + gyro_state->callbacks.capture_sample = gyro_3d_capture_sample; + gyro_state->callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D, + &gyro_state->callbacks); + if (ret < 0) { + dev_err(&pdev->dev, "callback reg failed\n"); + goto error_iio_unreg; + } + + return ret; + +error_iio_unreg: + iio_device_unregister(indio_dev); +error_remove_trigger: + hid_sensor_remove_trigger(indio_dev); +error_unreg_buffer_funcs: + iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: + kfree(indio_dev->channels); +error_free_dev: + iio_device_free(indio_dev); +error_ret: + return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int __devinit hid_gyro_3d_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); + iio_device_unregister(indio_dev); + hid_sensor_remove_trigger(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + kfree(indio_dev->channels); + iio_device_free(indio_dev); + + return 0; +} + +static struct platform_driver hid_gyro_3d_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = hid_gyro_3d_probe, + .remove = hid_gyro_3d_remove, +}; +module_platform_driver(hid_gyro_3d_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Gyroscope 3D"); +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 774891cf8139..d4ad37455a67 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -682,12 +682,11 @@ static unsigned char *iio_demux(struct iio_buffer *buffer, return buffer->demux_bounce; } -int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data, - s64 timestamp) +int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data) { unsigned char *dataout = iio_demux(buffer, data); - return buffer->access->store_to(buffer, dataout, timestamp); + return buffer->access->store_to(buffer, dataout); } EXPORT_SYMBOL_GPL(iio_push_to_buffer); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index fa3b9f5e6c45..0499330d6e98 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -99,6 +99,7 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_FREQUENCY] = "frequency", [IIO_CHAN_INFO_PHASE] = "phase", [IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain", + [IIO_CHAN_INFO_HYSTERESIS] = "hysteresis", }; const struct iio_chan_spec diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c index 63da42498a9a..5bc5c860e9ca 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -95,8 +95,7 @@ static int iio_set_length_kfifo(struct iio_buffer *r, int length) } static int iio_store_to_kfifo(struct iio_buffer *r, - u8 *data, - s64 timestamp) + u8 *data) { int ret; struct iio_kfifo *kf = iio_to_kfifo(r); diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 91d15d2f694f..1763c9bcb98a 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -42,4 +42,14 @@ config VCNL4000 To compile this driver as a module, choose M here: the module will be called vcnl4000. +config HID_SENSOR_ALS + depends on HID_SENSOR_HUB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select HID_SENSOR_IIO_COMMON + tristate "HID ALS" + help + Say yes here to build support for the HID SENSOR + Ambient light sensor. + endmenu diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 13f8a782d292..21a8f0df1407 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o obj-$(CONFIG_VCNL4000) += vcnl4000.o +obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 9a99f43094f0..164b62b91a4b 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -187,7 +187,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64))) = time_ns; - iio_push_to_buffer(buffer, (u8 *)data->buffer, time_ns); + iio_push_to_buffer(buffer, (u8 *)data->buffer); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c new file mode 100644 index 000000000000..96e3691e42c4 --- /dev/null +++ b/drivers/iio/light/hid-sensor-als.c @@ -0,0 +1,385 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../common/hid-sensors/hid-sensor-attributes.h" +#include "../common/hid-sensors/hid-sensor-trigger.h" + +/*Format: HID-SENSOR-usage_id_in_hex*/ +/*Usage ID from spec for Accelerometer-3D: 0x200041*/ +#define DRIVER_NAME "HID-SENSOR-200041" + +#define CHANNEL_SCAN_INDEX_ILLUM 0 + +struct als_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_iio_common common_attributes; + struct hid_sensor_hub_attribute_info als_illum; + u32 illum; +}; + +/* Channel definitions */ +static const struct iio_chan_spec als_channels[] = { + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_BOTH, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_ILLUM, + } +}; + +/* Adjust channel real bits based on report descriptor */ +static void als_adjust_channel_bit_mask(struct iio_chan_spec *channels, + int channel, int size) +{ + channels[channel].scan_type.sign = 's'; + /* Real storage bits will change based on the report desc. */ + channels[channel].scan_type.realbits = size * 8; + /* Maximum size of a sample to capture is u32 */ + channels[channel].scan_type.storagebits = sizeof(u32) * 8; +} + +/* Channel read_raw handler */ +static int als_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct als_state *als_state = iio_priv(indio_dev); + int report_id = -1; + u32 address; + int ret; + int ret_type; + + *val = 0; + *val2 = 0; + switch (mask) { + case 0: + switch (chan->scan_index) { + case CHANNEL_SCAN_INDEX_ILLUM: + report_id = als_state->als_illum.report_id; + address = + HID_USAGE_SENSOR_LIGHT_ILLUM; + break; + default: + report_id = -1; + break; + } + if (report_id >= 0) + *val = sensor_hub_input_attr_get_raw_value( + als_state->common_attributes.hsdev, + HID_USAGE_SENSOR_ALS, address, + report_id); + else { + *val = 0; + return -EINVAL; + } + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = als_state->als_illum.units; + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_OFFSET: + *val = hid_sensor_convert_exponent( + als_state->als_illum.unit_expo); + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_read_samp_freq_value( + &als_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_read_raw_hyst_value( + &als_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret_type = -EINVAL; + break; + } + + return ret_type; +} + +/* Channel write_raw handler */ +static int als_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct als_state *als_state = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_write_samp_freq_value( + &als_state->common_attributes, val, val2); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_write_raw_hyst_value( + &als_state->common_attributes, val, val2); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int als_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_MICRO; +} + +static const struct iio_info als_info = { + .driver_module = THIS_MODULE, + .read_raw = &als_read_raw, + .write_raw = &als_write_raw, + .write_raw_get_fmt = &als_write_raw_get_fmt, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +{ + struct iio_buffer *buffer = indio_dev->buffer; + int datum_sz; + + dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); + if (!buffer) { + dev_err(&indio_dev->dev, "Buffer == NULL\n"); + return; + } + datum_sz = buffer->access->get_bytes_per_datum(buffer); + if (len > datum_sz) { + dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, + datum_sz); + return; + } + iio_push_to_buffer(buffer, (u8 *)data); +} + +/* Callback handler to send event after all samples are received and captured */ +static int als_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct als_state *als_state = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "als_proc_event [%d]\n", + als_state->common_attributes.data_ready); + if (als_state->common_attributes.data_ready) + hid_sensor_push_data(indio_dev, + (u8 *)&als_state->illum, + sizeof(als_state->illum)); + + return 0; +} + +/* Capture samples in local storage */ +static int als_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + size_t raw_len, char *raw_data, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct als_state *als_state = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (usage_id) { + case HID_USAGE_SENSOR_LIGHT_ILLUM: + als_state->illum = *(u32 *)raw_data; + ret = 0; + break; + default: + break; + } + + return ret; +} + +/* Parse report which is specific to an usage id*/ +static int als_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + unsigned usage_id, + struct als_state *st) +{ + int ret; + + ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_LIGHT_ILLUM, + &st->als_illum); + if (ret < 0) + return ret; + als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM, + st->als_illum.size); + + dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index, + st->als_illum.report_id); + + return ret; +} + +/* Function to initialize the processing for usage id */ +static int __devinit hid_als_probe(struct platform_device *pdev) +{ + int ret = 0; + static const char *name = "als"; + struct iio_dev *indio_dev; + struct als_state *als_state; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_chan_spec *channels; + + indio_dev = iio_device_alloc(sizeof(struct als_state)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, indio_dev); + + als_state = iio_priv(indio_dev); + als_state->common_attributes.hsdev = hsdev; + als_state->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS, + &als_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "failed to setup common attributes\n"); + goto error_free_dev; + } + + channels = kmemdup(als_channels, + sizeof(als_channels), + GFP_KERNEL); + if (!channels) { + dev_err(&pdev->dev, "failed to duplicate channels\n"); + goto error_free_dev; + } + + ret = als_parse_report(pdev, hsdev, channels, + HID_USAGE_SENSOR_ALS, als_state); + if (ret) { + dev_err(&pdev->dev, "failed to setup attributes\n"); + goto error_free_dev_mem; + } + + indio_dev->channels = channels; + indio_dev->num_channels = + ARRAY_SIZE(als_channels); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &als_info; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); + goto error_free_dev_mem; + } + als_state->common_attributes.data_ready = false; + ret = hid_sensor_setup_trigger(indio_dev, name, + &als_state->common_attributes); + if (ret < 0) { + dev_err(&pdev->dev, "trigger setup failed\n"); + goto error_unreg_buffer_funcs; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_trigger; + } + + als_state->callbacks.send_event = als_proc_event; + als_state->callbacks.capture_sample = als_capture_sample; + als_state->callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ALS, + &als_state->callbacks); + if (ret < 0) { + dev_err(&pdev->dev, "callback reg failed\n"); + goto error_iio_unreg; + } + + return ret; + +error_iio_unreg: + iio_device_unregister(indio_dev); +error_remove_trigger: + hid_sensor_remove_trigger(indio_dev); +error_unreg_buffer_funcs: + iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: + kfree(indio_dev->channels); +error_free_dev: + iio_device_free(indio_dev); +error_ret: + return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int __devinit hid_als_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS); + iio_device_unregister(indio_dev); + hid_sensor_remove_trigger(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + kfree(indio_dev->channels); + iio_device_free(indio_dev); + + return 0; +} + +static struct platform_driver hid_als_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = hid_als_probe, + .remove = hid_als_remove, +}; +module_platform_driver(hid_als_platform_driver); + +MODULE_DESCRIPTION("HID Sensor ALS"); +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig new file mode 100644 index 000000000000..c1f0cdd57037 --- /dev/null +++ b/drivers/iio/magnetometer/Kconfig @@ -0,0 +1,16 @@ +# +# Magnetometer sensors +# +menu "Magnetometer sensors" + +config HID_SENSOR_MAGNETOMETER_3D + depends on HID_SENSOR_HUB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select HID_SENSOR_IIO_COMMON + tristate "HID Magenetometer 3D" + help + Say yes here to build support for the HID SENSOR + Magnetometer 3D. + +endmenu diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile new file mode 100644 index 000000000000..60dc4f2b1963 --- /dev/null +++ b/drivers/iio/magnetometer/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for industrial I/O Magnetometer sensor drivers +# + +obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c new file mode 100644 index 000000000000..c4f0d274f577 --- /dev/null +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -0,0 +1,419 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../common/hid-sensors/hid-sensor-attributes.h" +#include "../common/hid-sensors/hid-sensor-trigger.h" + +/*Format: HID-SENSOR-usage_id_in_hex*/ +/*Usage ID from spec for Magnetometer-3D: 0x200083*/ +#define DRIVER_NAME "HID-SENSOR-200083" + +enum magn_3d_channel { + CHANNEL_SCAN_INDEX_X, + CHANNEL_SCAN_INDEX_Y, + CHANNEL_SCAN_INDEX_Z, + MAGN_3D_CHANNEL_MAX, +}; + +struct magn_3d_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_iio_common common_attributes; + struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX]; + u32 magn_val[MAGN_3D_CHANNEL_MAX]; +}; + +static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = { + HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS, + HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS, + HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS +}; + +/* Channel definitions */ +static const struct iio_chan_spec magn_3d_channels[] = { + { + .type = IIO_MAGN, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_X, + }, { + .type = IIO_MAGN, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_Y, + }, { + .type = IIO_MAGN, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT | + IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT | + IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT, + .scan_index = CHANNEL_SCAN_INDEX_Z, + } +}; + +/* Adjust channel real bits based on report descriptor */ +static void magn_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels, + int channel, int size) +{ + channels[channel].scan_type.sign = 's'; + /* Real storage bits will change based on the report desc. */ + channels[channel].scan_type.realbits = size * 8; + /* Maximum size of a sample to capture is u32 */ + channels[channel].scan_type.storagebits = sizeof(u32) * 8; +} + +/* Channel read_raw handler */ +static int magn_3d_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct magn_3d_state *magn_state = iio_priv(indio_dev); + int report_id = -1; + u32 address; + int ret; + int ret_type; + + *val = 0; + *val2 = 0; + switch (mask) { + case 0: + report_id = + magn_state->magn[chan->scan_index].report_id; + address = magn_3d_addresses[chan->scan_index]; + if (report_id >= 0) + *val = sensor_hub_input_attr_get_raw_value( + magn_state->common_attributes.hsdev, + HID_USAGE_SENSOR_COMPASS_3D, address, + report_id); + else { + *val = 0; + return -EINVAL; + } + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = magn_state->magn[CHANNEL_SCAN_INDEX_X].units; + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_OFFSET: + *val = hid_sensor_convert_exponent( + magn_state->magn[CHANNEL_SCAN_INDEX_X].unit_expo); + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_read_samp_freq_value( + &magn_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_read_raw_hyst_value( + &magn_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret_type = -EINVAL; + break; + } + + return ret_type; +} + +/* Channel write_raw handler */ +static int magn_3d_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct magn_3d_state *magn_state = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_write_samp_freq_value( + &magn_state->common_attributes, val, val2); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_write_raw_hyst_value( + &magn_state->common_attributes, val, val2); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int magn_3d_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_MICRO; +} + +static const struct iio_info magn_3d_info = { + .driver_module = THIS_MODULE, + .read_raw = &magn_3d_read_raw, + .write_raw = &magn_3d_write_raw, + .write_raw_get_fmt = &magn_3d_write_raw_get_fmt, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +{ + struct iio_buffer *buffer = indio_dev->buffer; + int datum_sz; + + dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); + if (!buffer) { + dev_err(&indio_dev->dev, "Buffer == NULL\n"); + return; + } + datum_sz = buffer->access->get_bytes_per_datum(buffer); + if (len > datum_sz) { + dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len, + datum_sz); + return; + } + iio_push_to_buffer(buffer, (u8 *)data); +} + +/* Callback handler to send event after all samples are received and captured */ +static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct magn_3d_state *magn_state = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "magn_3d_proc_event [%d]\n", + magn_state->common_attributes.data_ready); + if (magn_state->common_attributes.data_ready) + hid_sensor_push_data(indio_dev, + (u8 *)magn_state->magn_val, + sizeof(magn_state->magn_val)); + + return 0; +} + +/* Capture samples in local storage */ +static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + size_t raw_len, char *raw_data, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct magn_3d_state *magn_state = iio_priv(indio_dev); + int offset; + int ret = -EINVAL; + + switch (usage_id) { + case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS: + case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS: + case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS: + offset = usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS; + magn_state->magn_val[CHANNEL_SCAN_INDEX_X + offset] = + *(u32 *)raw_data; + ret = 0; + break; + default: + break; + } + + return ret; +} + +/* Parse report which is specific to an usage id*/ +static int magn_3d_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + unsigned usage_id, + struct magn_3d_state *st) +{ + int ret; + int i; + + for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) { + ret = sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS + i, + &st->magn[CHANNEL_SCAN_INDEX_X + i]); + if (ret < 0) + break; + magn_3d_adjust_channel_bit_mask(channels, + CHANNEL_SCAN_INDEX_X + i, + st->magn[CHANNEL_SCAN_INDEX_X + i].size); + } + dev_dbg(&pdev->dev, "magn_3d %x:%x, %x:%x, %x:%x\n", + st->magn[0].index, + st->magn[0].report_id, + st->magn[1].index, st->magn[1].report_id, + st->magn[2].index, st->magn[2].report_id); + + return ret; +} + +/* Function to initialize the processing for usage id */ +static int __devinit hid_magn_3d_probe(struct platform_device *pdev) +{ + int ret = 0; + static char *name = "magn_3d"; + struct iio_dev *indio_dev; + struct magn_3d_state *magn_state; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_chan_spec *channels; + + indio_dev = iio_device_alloc(sizeof(struct magn_3d_state)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, indio_dev); + + magn_state = iio_priv(indio_dev); + magn_state->common_attributes.hsdev = hsdev; + magn_state->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, + HID_USAGE_SENSOR_COMPASS_3D, + &magn_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "failed to setup common attributes\n"); + goto error_free_dev; + } + + channels = kmemdup(magn_3d_channels, + sizeof(magn_3d_channels), + GFP_KERNEL); + if (!channels) { + dev_err(&pdev->dev, "failed to duplicate channels\n"); + goto error_free_dev; + } + + ret = magn_3d_parse_report(pdev, hsdev, channels, + HID_USAGE_SENSOR_COMPASS_3D, magn_state); + if (ret) { + dev_err(&pdev->dev, "failed to setup attributes\n"); + goto error_free_dev_mem; + } + + indio_dev->channels = channels; + indio_dev->num_channels = ARRAY_SIZE(magn_3d_channels); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &magn_3d_info; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); + goto error_free_dev_mem; + } + magn_state->common_attributes.data_ready = false; + ret = hid_sensor_setup_trigger(indio_dev, name, + &magn_state->common_attributes); + if (ret < 0) { + dev_err(&pdev->dev, "trigger setup failed\n"); + goto error_unreg_buffer_funcs; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_trigger; + } + + magn_state->callbacks.send_event = magn_3d_proc_event; + magn_state->callbacks.capture_sample = magn_3d_capture_sample; + magn_state->callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D, + &magn_state->callbacks); + if (ret < 0) { + dev_err(&pdev->dev, "callback reg failed\n"); + goto error_iio_unreg; + } + + return ret; + +error_iio_unreg: + iio_device_unregister(indio_dev); +error_remove_trigger: + hid_sensor_remove_trigger(indio_dev); +error_unreg_buffer_funcs: + iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: + kfree(indio_dev->channels); +error_free_dev: + iio_device_free(indio_dev); +error_ret: + return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int __devinit hid_magn_3d_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D); + iio_device_unregister(indio_dev); + hid_sensor_remove_trigger(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + kfree(indio_dev->channels); + iio_device_free(indio_dev); + + return 0; +} + +static struct platform_driver hid_magn_3d_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = hid_magn_3d_probe, + .remove = hid_magn_3d_remove, +}; +module_platform_driver(hid_magn_3d_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Magnetometer 3D"); +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/accel/adis16201_ring.c b/drivers/staging/iio/accel/adis16201_ring.c index 03fcf6e319db..97c09f0c26ae 100644 --- a/drivers/staging/iio/accel/adis16201_ring.c +++ b/drivers/staging/iio/accel/adis16201_ring.c @@ -62,7 +62,6 @@ static irqreturn_t adis16201_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adis16201_state *st = iio_priv(indio_dev); - struct iio_buffer *ring = indio_dev->buffer; int i = 0; s16 *data; @@ -83,7 +82,7 @@ static irqreturn_t adis16201_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - ring->access->store_to(ring, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16203_ring.c b/drivers/staging/iio/accel/adis16203_ring.c index c16b2b7323ac..7507e1a04591 100644 --- a/drivers/staging/iio/accel/adis16203_ring.c +++ b/drivers/staging/iio/accel/adis16203_ring.c @@ -61,7 +61,6 @@ static irqreturn_t adis16203_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adis16203_state *st = iio_priv(indio_dev); - struct iio_buffer *ring = indio_dev->buffer; int i = 0; s16 *data; @@ -82,9 +81,7 @@ static irqreturn_t adis16203_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - ring->access->store_to(ring, - (u8 *)data, - pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16204_ring.c b/drivers/staging/iio/accel/adis16204_ring.c index 1d2b31cc849e..4c976bec986b 100644 --- a/drivers/staging/iio/accel/adis16204_ring.c +++ b/drivers/staging/iio/accel/adis16204_ring.c @@ -59,7 +59,6 @@ static irqreturn_t adis16204_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adis16204_state *st = iio_priv(indio_dev); - struct iio_buffer *ring = indio_dev->buffer; int i = 0; s16 *data; @@ -79,7 +78,7 @@ static irqreturn_t adis16204_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - ring->access->store_to(ring, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16209_ring.c b/drivers/staging/iio/accel/adis16209_ring.c index 1a4a55c27c7c..f939e29d6c82 100644 --- a/drivers/staging/iio/accel/adis16209_ring.c +++ b/drivers/staging/iio/accel/adis16209_ring.c @@ -59,7 +59,6 @@ static irqreturn_t adis16209_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adis16209_state *st = iio_priv(indio_dev); - struct iio_buffer *ring = indio_dev->buffer; int i = 0; s16 *data; @@ -79,7 +78,7 @@ static irqreturn_t adis16209_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - ring->access->store_to(ring, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/staging/iio/accel/adis16240_ring.c index 360dfed6d4d1..caff8e25e0a2 100644 --- a/drivers/staging/iio/accel/adis16240_ring.c +++ b/drivers/staging/iio/accel/adis16240_ring.c @@ -56,7 +56,6 @@ static irqreturn_t adis16240_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adis16240_state *st = iio_priv(indio_dev); - struct iio_buffer *ring = indio_dev->buffer; int i = 0; s16 *data; @@ -77,7 +76,7 @@ static irqreturn_t adis16240_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - ring->access->store_to(ring, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c index 7da2703885d6..247605ce5051 100644 --- a/drivers/staging/iio/accel/lis3l02dq_ring.c +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -135,7 +135,6 @@ static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - struct iio_buffer *buffer = indio_dev->buffer; int len = 0; char *data; @@ -153,7 +152,7 @@ static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = pf->timestamp; - buffer->access->store_to(buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/adc/ad7298_ring.c b/drivers/staging/iio/adc/ad7298_ring.c index 506016f01593..c2906a85fedb 100644 --- a/drivers/staging/iio/adc/ad7298_ring.c +++ b/drivers/staging/iio/adc/ad7298_ring.c @@ -75,7 +75,6 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct ad7298_state *st = iio_priv(indio_dev); - struct iio_buffer *ring = indio_dev->buffer; s64 time_ns = 0; __u16 buf[16]; int b_sent, i; @@ -94,7 +93,7 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p) indio_dev->masklength); i++) buf[i] = be16_to_cpu(st->rx_buf[i]); - indio_dev->buffer->access->store_to(ring, (u8 *)buf, time_ns); + iio_push_to_buffer(indio_dev->buffer, (u8 *)buf); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/adc/ad7476_ring.c b/drivers/staging/iio/adc/ad7476_ring.c index d087b21c51f6..940681f29b87 100644 --- a/drivers/staging/iio/adc/ad7476_ring.c +++ b/drivers/staging/iio/adc/ad7476_ring.c @@ -44,7 +44,7 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p) memcpy(rxbuf + indio_dev->scan_bytes - sizeof(s64), &time_ns, sizeof(time_ns)); - indio_dev->buffer->access->store_to(indio_dev->buffer, rxbuf, time_ns); + iio_push_to_buffer(indio_dev->buffer, rxbuf); done: iio_trigger_notify_done(indio_dev->trig); kfree(rxbuf); diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c index f15afe47c20d..ba04d0ffd4f4 100644 --- a/drivers/staging/iio/adc/ad7606_ring.c +++ b/drivers/staging/iio/adc/ad7606_ring.c @@ -46,7 +46,6 @@ static void ad7606_poll_bh_to_ring(struct work_struct *work_s) struct ad7606_state *st = container_of(work_s, struct ad7606_state, poll_work); struct iio_dev *indio_dev = iio_priv_to_dev(st); - struct iio_buffer *ring = indio_dev->buffer; s64 time_ns; __u8 *buf; int ret; @@ -84,7 +83,7 @@ static void ad7606_poll_bh_to_ring(struct work_struct *work_s) if (indio_dev->scan_timestamp) *((s64 *)(buf + indio_dev->scan_bytes - sizeof(s64))) = time_ns; - ring->access->store_to(indio_dev->buffer, buf, time_ns); + iio_push_to_buffer(indio_dev->buffer, buf); done: gpio_set_value(st->pdata->gpio_convst, 0); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/adc/ad7887_ring.c b/drivers/staging/iio/adc/ad7887_ring.c index c76fdb5081c2..b39923bbeedc 100644 --- a/drivers/staging/iio/adc/ad7887_ring.c +++ b/drivers/staging/iio/adc/ad7887_ring.c @@ -95,7 +95,7 @@ static irqreturn_t ad7887_trigger_handler(int irq, void *p) memcpy(buf + indio_dev->scan_bytes - sizeof(s64), &time_ns, sizeof(time_ns)); - indio_dev->buffer->access->store_to(indio_dev->buffer, buf, time_ns); + iio_push_to_buffer(indio_dev->buffer, buf); done: kfree(buf); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/adc/ad799x_ring.c b/drivers/staging/iio/adc/ad799x_ring.c index 858a685e3889..86026d9b20bc 100644 --- a/drivers/staging/iio/adc/ad799x_ring.c +++ b/drivers/staging/iio/adc/ad799x_ring.c @@ -35,7 +35,6 @@ static irqreturn_t ad799x_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct ad799x_state *st = iio_priv(indio_dev); - struct iio_buffer *ring = indio_dev->buffer; s64 time_ns; __u8 *rxbuf; int b_sent; @@ -78,7 +77,7 @@ static irqreturn_t ad799x_trigger_handler(int irq, void *p) memcpy(rxbuf + indio_dev->scan_bytes - sizeof(s64), &time_ns, sizeof(time_ns)); - ring->access->store_to(indio_dev->buffer, rxbuf, time_ns); + iio_push_to_buffer(indio_dev->buffer, rxbuf); done: kfree(rxbuf); out: diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c index 774ae1b63550..5f74f3b7671a 100644 --- a/drivers/staging/iio/adc/max1363_ring.c +++ b/drivers/staging/iio/adc/max1363_ring.c @@ -80,7 +80,7 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); - iio_push_to_buffer(indio_dev->buffer, rxbuf, time_ns); + iio_push_to_buffer(indio_dev->buffer, rxbuf); done_free: kfree(rxbuf); diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index ae549e572e5d..ca7c1fa88e71 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -256,7 +256,7 @@ static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p) *timestamp = pf->timestamp; } - iio_push_to_buffer(buffer, (u8 *)lradc->buffer, pf->timestamp); + iio_push_to_buffer(buffer, (u8 *)lradc->buffer); iio_trigger_notify_done(iio->trig); diff --git a/drivers/staging/iio/gyro/adis16260_ring.c b/drivers/staging/iio/gyro/adis16260_ring.c index eeee8e760e6c..e294cb49736d 100644 --- a/drivers/staging/iio/gyro/adis16260_ring.c +++ b/drivers/staging/iio/gyro/adis16260_ring.c @@ -62,7 +62,6 @@ static irqreturn_t adis16260_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adis16260_state *st = iio_priv(indio_dev); - struct iio_buffer *ring = indio_dev->buffer; int i = 0; s16 *data; @@ -82,7 +81,7 @@ static irqreturn_t adis16260_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - ring->access->store_to(ring, (u8 *)data, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)data); kfree(data); done: diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c index bd628de472a9..1fd38095b2ca 100644 --- a/drivers/staging/iio/iio_simple_dummy_buffer.c +++ b/drivers/staging/iio/iio_simple_dummy_buffer.c @@ -87,7 +87,7 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) if (indio_dev->scan_timestamp) *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = iio_get_time_ns(); - buffer->access->store_to(buffer, (u8 *)data, pf->timestamp); + iio_push_to_buffer(buffer, (u8 *)data); kfree(data); diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index e239ea91f407..de21d47f33e9 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -678,7 +678,7 @@ static void ad5933_work(struct work_struct *work) buf[0] = be16_to_cpu(buf[0]); } /* save datum to the ring */ - ring->access->store_to(ring, (u8 *)buf, iio_get_time_ns()); + iio_push_to_buffer(ring, (u8 *)buf); } else { /* no data available - try again later */ schedule_delayed_work(&st->work, st->poll_time_jiffies); diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c index beec650ddbdb..260bdd1a4681 100644 --- a/drivers/staging/iio/imu/adis16400_ring.c +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -150,7 +150,7 @@ static irqreturn_t adis16400_trigger_handler(int irq, void *p) /* Guaranteed to be aligned with 8 byte boundary */ if (ring->scan_timestamp) *((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp; - ring->access->store_to(indio_dev->buffer, (u8 *) data, pf->timestamp); + iio_push_to_buffer(ring, (u8 *) data); done: kfree(data); diff --git a/drivers/staging/iio/light/isl29018.c b/drivers/staging/iio/light/isl29018.c index 31d22f5591ca..6ee5567d9813 100644 --- a/drivers/staging/iio/light/isl29018.c +++ b/drivers/staging/iio/light/isl29018.c @@ -63,6 +63,7 @@ struct isl29018_chip { struct regmap *regmap; struct mutex lock; unsigned int lux_scale; + unsigned int lux_uscale; unsigned int range; unsigned int adc_bit; int prox_scheme; @@ -145,13 +146,22 @@ static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode) static int isl29018_read_lux(struct isl29018_chip *chip, int *lux) { int lux_data; + unsigned int data_x_range, lux_unshifted; lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE); if (lux_data < 0) return lux_data; - *lux = (lux_data * chip->range * chip->lux_scale) >> chip->adc_bit; + /* To support fractional scaling, separate the unshifted lux + * into two calculations: int scaling and micro-scaling. + * lux_uscale ranges from 0-999999, so about 20 bits. Split + * the /1,000,000 in two to reduce the risk of over/underflow. + */ + data_x_range = lux_data * chip->range; + lux_unshifted = data_x_range * chip->lux_scale; + lux_unshifted += data_x_range / 1000 * chip->lux_uscale / 1000; + *lux = lux_unshifted >> chip->adc_bit; return 0; } @@ -339,6 +349,8 @@ static int isl29018_write_raw(struct iio_dev *indio_dev, mutex_lock(&chip->lock); if (mask == IIO_CHAN_INFO_CALIBSCALE && chan->type == IIO_LIGHT) { chip->lux_scale = val; + /* With no write_raw_get_fmt(), val2 is a MICRO fraction. */ + chip->lux_uscale = val2; ret = 0; } mutex_unlock(&chip->lock); @@ -379,7 +391,8 @@ static int isl29018_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_CALIBSCALE: if (chan->type == IIO_LIGHT) { *val = chip->lux_scale; - ret = IIO_VAL_INT; + *val2 = chip->lux_uscale; + ret = IIO_VAL_INT_PLUS_MICRO; } break; default: diff --git a/drivers/staging/iio/meter/ade7758_ring.c b/drivers/staging/iio/meter/ade7758_ring.c index 1ce10b21f4d6..9e49baccf660 100644 --- a/drivers/staging/iio/meter/ade7758_ring.c +++ b/drivers/staging/iio/meter/ade7758_ring.c @@ -61,7 +61,6 @@ static irqreturn_t ade7758_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - struct iio_buffer *ring = indio_dev->buffer; struct ade7758_state *st = iio_priv(indio_dev); s64 dat64[2]; u32 *dat32 = (u32 *)dat64; @@ -74,7 +73,7 @@ static irqreturn_t ade7758_trigger_handler(int irq, void *p) if (indio_dev->scan_timestamp) dat64[1] = pf->timestamp; - ring->access->store_to(ring, (u8 *)dat64, pf->timestamp); + iio_push_to_buffer(indio_dev->buffer, (u8 *)dat64); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c index f61c8fdaab06..3a45f9a52de8 100644 --- a/drivers/staging/iio/ring_sw.c +++ b/drivers/staging/iio/ring_sw.c @@ -65,7 +65,7 @@ static inline void __iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring) /* Lock always held if their is a chance this may be called */ /* Only one of these per ring may run concurrently - enforced by drivers */ static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring, - unsigned char *data, s64 timestamp) + unsigned char *data) { int ret = 0; unsigned char *temp_ptr, *change_test_ptr; @@ -256,11 +256,10 @@ error_ret: } static int iio_store_to_sw_rb(struct iio_buffer *r, - u8 *data, - s64 timestamp) + u8 *data) { struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r); - return iio_store_to_sw_ring(ring, data, timestamp); + return iio_store_to_sw_ring(ring, data); } static int iio_request_update_sw_rb(struct iio_buffer *r) diff --git a/drivers/staging/iio/trigger/Kconfig b/drivers/staging/iio/trigger/Kconfig index b8abf5473ddc..7d3207559265 100644 --- a/drivers/staging/iio/trigger/Kconfig +++ b/drivers/staging/iio/trigger/Kconfig @@ -21,6 +21,8 @@ config IIO_GPIO_TRIGGER config IIO_SYSFS_TRIGGER tristate "SYSFS trigger" depends on SYSFS + depends on HAVE_IRQ_WORK + select IRQ_WORK help Provides support for using SYSFS entry as IIO triggers. If unsure, say N (but it's safe to say "Y"). diff --git a/drivers/staging/iio/trigger/iio-trig-sysfs.c b/drivers/staging/iio/trigger/iio-trig-sysfs.c index fee474648108..3bac97224bf4 100644 --- a/drivers/staging/iio/trigger/iio-trig-sysfs.c +++ b/drivers/staging/iio/trigger/iio-trig-sysfs.c @@ -10,12 +10,14 @@ #include #include #include +#include #include #include struct iio_sysfs_trig { struct iio_trigger *trig; + struct irq_work work; int id; struct list_head l; }; @@ -89,11 +91,21 @@ static struct device iio_sysfs_trig_dev = { .release = &iio_trigger_sysfs_release, }; +static void iio_sysfs_trigger_work(struct irq_work *work) +{ + struct iio_sysfs_trig *trig = container_of(work, struct iio_sysfs_trig, + work); + + iio_trigger_poll(trig->trig, 0); +} + static ssize_t iio_sysfs_trigger_poll(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct iio_trigger *trig = to_iio_trigger(dev); - iio_trigger_poll_chained(trig, 0); + struct iio_sysfs_trig *sysfs_trig = trig->private_data; + + irq_work_queue(&sysfs_trig->work); return count; } @@ -148,6 +160,9 @@ static int iio_sysfs_trigger_probe(int id) t->trig->dev.groups = iio_sysfs_trigger_attr_groups; t->trig->ops = &iio_sysfs_trigger_ops; t->trig->dev.parent = &iio_sysfs_trig_dev; + t->trig->private_data = t; + + init_irq_work(&t->work, iio_sysfs_trigger_work); ret = iio_trigger_register(t->trig); if (ret) diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h new file mode 100644 index 000000000000..0aa5f4c42ae6 --- /dev/null +++ b/include/linux/hid-sensor-hub.h @@ -0,0 +1,160 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef _HID_SENSORS_HUB_H +#define _HID_SENSORS_HUB_H + +#include +#include + +/** + * struct hid_sensor_hub_attribute_info - Attribute info + * @usage_id: Parent usage id of a physical device. + * @attrib_id: Attribute id for this attribute. + * @report_id: Report id in which this information resides. + * @index: Field index in the report. + * @units: Measurment unit for this attribute. + * @unit_expo: Exponent used in the data. + * @size: Size in bytes for data size. + */ +struct hid_sensor_hub_attribute_info { + u32 usage_id; + u32 attrib_id; + s32 report_id; + s32 index; + s32 units; + s32 unit_expo; + s32 size; +}; + +/** + * struct hid_sensor_hub_device - Stores the hub instance data + * @hdev: Stores the hid instance. + * @vendor_id: Vendor id of hub device. + * @product_id: Product id of hub device. + */ +struct hid_sensor_hub_device { + struct hid_device *hdev; + u32 vendor_id; + u32 product_id; +}; + +/** + * struct hid_sensor_hub_callbacks - Client callback functions + * @pdev: Platform device instance of the client driver. + * @suspend: Suspend callback. + * @resume: Resume callback. + * @capture_sample: Callback to get a sample. + * @send_event: Send notification to indicate all samples are + * captured, process and send event + */ +struct hid_sensor_hub_callbacks { + struct platform_device *pdev; + int (*suspend)(struct hid_sensor_hub_device *hsdev, void *priv); + int (*resume)(struct hid_sensor_hub_device *hsdev, void *priv); + int (*capture_sample)(struct hid_sensor_hub_device *hsdev, + u32 usage_id, size_t raw_len, char *raw_data, + void *priv); + int (*send_event)(struct hid_sensor_hub_device *hsdev, u32 usage_id, + void *priv); +}; + +/* Registration functions */ + +/** +* sensor_hub_register_callback() - Register client callbacks +* @hsdev: Hub device instance. +* @usage_id: Usage id of the client (E.g. 0x200076 for Gyro). +* @usage_callback: Callback function storage +* +* Used to register callbacks by client processing drivers. Sensor +* hub core driver will call these callbacks to offload processing +* of data streams and notifications. +*/ +int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct hid_sensor_hub_callbacks *usage_callback); + +/** +* sensor_hub_remove_callback() - Remove client callbacks +* @hsdev: Hub device instance. +* @usage_id: Usage id of the client (E.g. 0x200076 for Gyro). +* +* If there is a callback registred, this call will remove that +* callbacks, so that it will stop data and event notifications. +*/ +int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, + u32 usage_id); + + +/* Hid sensor hub core interfaces */ + +/** +* sensor_hub_input_get_attribute_info() - Get an attribute information +* @hsdev: Hub device instance. +* @type: Type of this attribute, input/output/feature +* @usage_id: Attribute usage id of parent physical device as per spec +* @attr_usage_id: Attribute usage id as per spec +* @info: return information about attribute after parsing report +* +* Parses report and returns the attribute information such as report id, +* field index, units and exponet etc. +*/ +int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, + u8 type, + u32 usage_id, u32 attr_usage_id, + struct hid_sensor_hub_attribute_info *info); + +/** +* sensor_hub_input_attr_get_raw_value() - Synchronous read request +* @usage_id: Attribute usage id of parent physical device as per spec +* @attr_usage_id: Attribute usage id as per spec +* @report_id: Report id to look for +* +* Issues a synchronous read request for an input attribute. Returns +* data upto 32 bits. Since client can get events, so this call should +* not be used for data paths, this will impact performance. +*/ + +int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + u32 attr_usage_id, u32 report_id); +/** +* sensor_hub_set_feature() - Feature set request +* @report_id: Report id to look for +* @field_index: Field index inside a report +* @value: Value to set +* +* Used to set a field in feature report. For example this can set polling +* interval, sensitivity, activate/deactivate state. +*/ +int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 value); + +/** +* sensor_hub_get_feature() - Feature get request +* @report_id: Report id to look for +* @field_index: Field index inside a report +* @value: Place holder for return value +* +* Used to get a field in feature report. For example this can get polling +* interval, sensitivity, activate/deactivate state. +*/ +int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, + u32 field_index, s32 *value); +#endif diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h new file mode 100644 index 000000000000..ca8d7e94eb3c --- /dev/null +++ b/include/linux/hid-sensor-ids.h @@ -0,0 +1,112 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef _HID_SENSORS_IDS_H +#define _HID_SENSORS_IDS_H + +#define HID_UP_SENSOR 0x00200000 +#define HID_MAX_PHY_DEVICES 0xFF + +/* Accel 3D (200073) */ +#define HID_USAGE_SENSOR_ACCEL_3D 0x200073 +#define HID_USAGE_SENSOR_ACCEL_X_AXIS 0x200453 +#define HID_USAGE_SENSOR_ACCEL_Y_AXIS 0x200454 +#define HID_USAGE_SENSOR_ACCEL_Z_AXIS 0x200455 + +/* ALS (200041) */ +#define HID_USAGE_SENSOR_ALS 0x200041 +#define HID_USAGE_SENSOR_LIGHT_ILLUM 0x2004d1 + +/* Gyro 3D: (200076) */ +#define HID_USAGE_SENSOR_GYRO_3D 0x200076 +#define HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS 0x200457 +#define HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS 0x200458 +#define HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS 0x200459 + +/*ORIENTATION: Compass 3D: (200083) */ +#define HID_USAGE_SENSOR_COMPASS_3D 0x200083 +#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING 0x200471 +#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_X 0x200472 +#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_Y 0x200473 +#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_Z 0x200474 + +#define HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH 0x200475 +#define HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH 0x200476 +#define HID_USAGE_SENSOR_ORIENT_MAGN_NORTH 0x200477 +#define HID_USAGE_SENSOR_ORIENT_TRUE_NORTH 0x200478 + +#define HID_USAGE_SENSOR_ORIENT_DISTANCE 0x200479 +#define HID_USAGE_SENSOR_ORIENT_DISTANCE_X 0x20047A +#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Y 0x20047B +#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Z 0x20047C +#define HID_USAGE_SENSOR_ORIENT_DISTANCE_OUT_OF_RANGE 0x20047D +#define HID_USAGE_SENSOR_ORIENT_TILT 0x20047E +#define HID_USAGE_SENSOR_ORIENT_TILT_X 0x20047F +#define HID_USAGE_SENSOR_ORIENT_TILT_Y 0x200480 +#define HID_USAGE_SENSOR_ORIENT_TILT_Z 0x200481 +#define HID_USAGE_SENSOR_ORIENT_ROTATION_MATRIX 0x200482 +#define HID_USAGE_SENSOR_ORIENT_QUATERNION 0x200483 +#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX 0x200484 + +#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS 0x200485 +#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS 0x200486 +#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS 0x200487 + +/* Units */ +#define HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED 0x00 +#define HID_USAGE_SENSOR_UNITS_LUX 0x01 +#define HID_USAGE_SENSOR_UNITS_KELVIN 0x01000100 +#define HID_USAGE_SENSOR_UNITS_FAHRENHEIT 0x03000100 +#define HID_USAGE_SENSOR_UNITS_PASCAL 0xF1E1 +#define HID_USAGE_SENSOR_UNITS_NEWTON 0x11E1 +#define HID_USAGE_SENSOR_UNITS_METERS_PER_SECOND 0x11F0 +#define HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD 0x11E0 +#define HID_USAGE_SENSOR_UNITS_FARAD 0xE14F2000 +#define HID_USAGE_SENSOR_UNITS_AMPERE 0x01001000 +#define HID_USAGE_SENSOR_UNITS_WATT 0x21d1 +#define HID_USAGE_SENSOR_UNITS_HENRY 0x21E1E000 +#define HID_USAGE_SENSOR_UNITS_OHM 0x21D1E000 +#define HID_USAGE_SENSOR_UNITS_VOLT 0x21D1F000 +#define HID_USAGE_SENSOR_UNITS_HERTZ 0x01F0 +#define HID_USAGE_SENSOR_UNITS_DEGREES_PER_SEC_SQRD 0x14E0 +#define HID_USAGE_SENSOR_UNITS_RADIANS 0x12 +#define HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND 0x12F0 +#define HID_USAGE_SENSOR_UNITS_RADIANS_PER_SEC_SQRD 0x12E0 +#define HID_USAGE_SENSOR_UNITS_SECOND 0x0110 +#define HID_USAGE_SENSOR_UNITS_GAUSS 0x01E1F000 +#define HID_USAGE_SENSOR_UNITS_GRAM 0x0101 +#define HID_USAGE_SENSOR_UNITS_CENTIMETER 0x11 +#define HID_USAGE_SENSOR_UNITS_G 0x1A +#define HID_USAGE_SENSOR_UNITS_MILLISECOND 0x19 +#define HID_USAGE_SENSOR_UNITS_PERCENT 0x17 +#define HID_USAGE_SENSOR_UNITS_DEGREES 0x14 +#define HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND 0x15 + +/* Common selectors */ +#define HID_USAGE_SENSOR_PROP_REPORT_INTERVAL 0x20030E +#define HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS 0x20030F +#define HID_USAGE_SENSOR_PROP_SENSITIVITY_RANGE_PCT 0x200310 +#define HID_USAGE_SENSOR_PROP_SENSITIVITY_REL_PCT 0x200311 +#define HID_USAGE_SENSOR_PROP_ACCURACY 0x200312 +#define HID_USAGE_SENSOR_PROP_RESOLUTION 0x200313 +#define HID_USAGE_SENSOR_PROP_RANGE_MAXIMUM 0x200314 +#define HID_USAGE_SENSOR_PROP_RANGE_MINIMUM 0x200315 +#define HID_USAGE_SENSOR_PROP_REPORT_STATE 0x200316 +#define HID_USAGE_SENSOR_PROY_POWER_STATE 0x200319 + +#endif diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 8ba516fc2ec6..c629b3a1d9a9 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -36,7 +36,7 @@ struct iio_buffer; * any of them not existing. **/ struct iio_buffer_access_funcs { - int (*store_to)(struct iio_buffer *buffer, u8 *data, s64 timestamp); + int (*store_to)(struct iio_buffer *buffer, u8 *data); int (*read_first_n)(struct iio_buffer *buffer, size_t n, char __user *buf); @@ -118,10 +118,8 @@ int iio_scan_mask_set(struct iio_dev *indio_dev, * iio_push_to_buffer() - push to a registered buffer. * @buffer: IIO buffer structure for device * @data: the data to push to the buffer - * @timestamp: timestamp to associate with the data */ -int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data, - s64 timestamp); +int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data); int iio_update_demux(struct iio_dev *indio_dev); diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 057d60382eac..30affa533a1f 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -35,6 +35,7 @@ enum iio_chan_info_enum { IIO_CHAN_INFO_FREQUENCY, IIO_CHAN_INFO_PHASE, IIO_CHAN_INFO_HARDWAREGAIN, + IIO_CHAN_INFO_HYSTERESIS, }; #define IIO_CHAN_INFO_SHARED_BIT(type) BIT(type*2) @@ -100,6 +101,10 @@ enum iio_chan_info_enum { IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_HARDWAREGAIN) #define IIO_CHAN_INFO_HARDWAREGAIN_SHARED_BIT \ IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_HARDWAREGAIN) +#define IIO_CHAN_INFO_HYSTERESIS_SEPARATE_BIT \ + IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_HYSTERESIS) +#define IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT \ + IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_HYSTERESIS) enum iio_endian { IIO_CPU, diff --git a/include/linux/platform_data/ad7791.h b/include/linux/platform_data/ad7791.h new file mode 100644 index 000000000000..f9e4db1b82ae --- /dev/null +++ b/include/linux/platform_data/ad7791.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_PLATFORM_DATA_AD7791__ +#define __LINUX_PLATFORM_DATA_AD7791__ + +/** + * struct ad7791_platform_data - AD7791 device platform data + * @buffered: If set to true configure the device for buffered input mode. + * @burnout_current: If set to true the 100mA burnout current is enabled. + * @unipolar: If set to true sample in unipolar mode, if set to false sample in + * bipolar mode. + */ +struct ad7791_platform_data { + bool buffered; + bool burnout_current; + bool unipolar; +}; + +#endif