Third set of IIO rework and new drivers for the 3.7 cycle.
This set includes: 1) HID sensor drivers. This includes a core elements in the HID subsystem merged through the IIO tree because we have some ABI changes outstanding (some in this set) which will effect them. The HID sensors specification covers an extremely wide range of sensors so we will probably be seeing lots more elements of this as the hardware hits the market. 2) Some general abi cleanups to use the utility function iio_push_to_buffer and to drop the used timestamp parameter from the same call. For a long time timestamps have taken the same path as all other channel types into the buffers so it is good to clean this out. 3) More ADC driver support for Analog Devices parts in the form of one new driver and some additional supported parts via current drivers. 4) An increase to the accuracy of the calibration scale for the isl29018 driver. So a mixed bag, but all good additions to IIO. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iQIcBAABAgAGBQJQS6DYAAoJEFSFNJnE9BaInFEQAI5zrmwzKrQ8pYXRib1VIS1O eQ3GjWKi5ZSczpDLcKpV0rgT5rCXg/hU7x1qo9ZlyMjsRcRrYPfh1coCWHl3wTaN tAITNsAS/gSgneClycTkR9sc6FeDKimzCLrC+4IbEwGZAjHtezL4vko8B4aAyveo cz9LVeMHxzCQw+EThJj61pi/lZBjQqe3O5kspATzX4K9maCTWyW0a5X1TVymVXDe AShuxWP8/wY9DU5bPiPhbRGlz8u1VnUpm3NBcBoMwru9ok4WGaEHYMe7MAfj2u8y NOT6OZd+qBJ+OGaeSrzegWqEMBbJ9f//2AylkAsLPz79nEH95ZN7YxO/sdQLedf7 RfdtOt7cey+njfiHd+cNmXWz34EW2RWmtQ/qoBB15ol5i+qgcnpuOt7hMzMV8I4f SAaAPwwm8UYC7HjhVDo1LSXfdZfZ7wid9ReqElz6/kp9PU+MHbAQHkOhB/N4bi3p PStP8OiT0cSLq7/0mXOtEGZLL6J7+3Tn2RMAMDmZ3Hz3aS5iLSma/hZc80is+Hpn zLMacfzc/nbmnQUHIsaVYUYnzjBWE4k1XiACxhy3vh6tukKopYubLm/GyXbMfRAp hwNeYYRz/lfQfE7fmqhs3bSrcoEXeE01aUp/6x3p9M8U1BxyFxuhY4xLd2TJVHdC 7zBQwYAq8XYS5e9QgR4L =8GgV -----END PGP SIGNATURE----- Merge tag 'iio-for-3.7c' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into work-next Third set of IIO rework and new drivers for the 3.7 cycle. This set includes: 1) HID sensor drivers. This includes a core elements in the HID subsystem merged through the IIO tree because we have some ABI changes outstanding (some in this set) which will effect them. The HID sensors specification covers an extremely wide range of sensors so we will probably be seeing lots more elements of this as the hardware hits the market. 2) Some general abi cleanups to use the utility function iio_push_to_buffer and to drop the used timestamp parameter from the same call. For a long time timestamps have taken the same path as all other channel types into the buffers so it is good to clean this out. 3) More ADC driver support for Analog Devices parts in the form of one new driver and some additional supported parts via current drivers. 4) An increase to the accuracy of the calibration scale for the isl29018 driver. So a mixed bag, but all good additions to IIO.
This commit is contained in:
commit
1004689cd9
|
@ -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.
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/usb.h>
|
||||
#include "usbhid/usbhid.h"
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/hid-sensor-ids.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#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 <srinivas.pandruvada@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for industrial I/O accelerometer drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
|
@ -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 <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#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 <srinivas.pandruvada@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/adc/ad_sigma_delta.h>
|
||||
|
||||
#include <linux/platform_data/ad7791.h>
|
||||
|
||||
#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 <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# IIO common modules
|
||||
#
|
||||
|
||||
source "drivers/iio/common/hid-sensors/Kconfig"
|
|
@ -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/
|
|
@ -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
|
|
@ -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
|
|
@ -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 <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#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 <srinivas.pandruvada@intel.com>");
|
||||
MODULE_DESCRIPTION("HID Sensor common attribute processing");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -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
|
|
@ -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 <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#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 <srinivas.pandruvada@intel.com>");
|
||||
MODULE_DESCRIPTION("HID Sensor trigger processing");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for industrial I/O gyroscope sensor drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
|
|
@ -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 <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#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 <srinivas.pandruvada@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#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 <srinivas.pandruvada@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for industrial I/O Magnetometer sensor drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
|
|
@ -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 <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#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 <srinivas.pandruvada@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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").
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/irq_work.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
|
||||
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)
|
||||
|
|
|
@ -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 <linux/hid.h>
|
||||
#include <linux/hid-sensor-ids.h>
|
||||
|
||||
/**
|
||||
* 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
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue