aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorsrinivas pandruvada <srinivas.pandruvada@intel.com>2012-09-05 08:56:00 -0400
committerJonathan Cameron <jic23@kernel.org>2012-09-06 14:21:40 -0400
commitc5bdbef704ba4c71a4fa2edc94d1930afad3b4c6 (patch)
tree2f81b7e6e7d7e209149aba41db45c7301ba15d82 /drivers/iio
parent45fe6f7d002c4ce11ae966bce74c6714816390d7 (diff)
iio: hid-sensors: Added Gyroscope 3D
Added usage id processing for Gyroscope 3D. This uses IIO interfaces for triggered buffer to present data to user mode.This uses HID sensor framework for registering callback events from the sensor hub. Signed-off-by: srinivas pandruvada <srinivas.pandruvada@intel.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/gyro/Kconfig16
-rw-r--r--drivers/iio/gyro/Makefile5
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c419
5 files changed, 442 insertions, 0 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 0e05b90091b9..417ade39d5d0 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -61,5 +61,6 @@ source "drivers/iio/light/Kconfig"
61source "drivers/iio/frequency/Kconfig" 61source "drivers/iio/frequency/Kconfig"
62source "drivers/iio/dac/Kconfig" 62source "drivers/iio/dac/Kconfig"
63source "drivers/iio/common/Kconfig" 63source "drivers/iio/common/Kconfig"
64source "drivers/iio/gyro/Kconfig"
64 65
65endif # IIO 66endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index b21215c0f62d..a6406f7a153c 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -17,3 +17,4 @@ obj-y += light/
17obj-y += frequency/ 17obj-y += frequency/
18obj-y += dac/ 18obj-y += dac/
19obj-y += common/ 19obj-y += common/
20obj-y += gyro/
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
new file mode 100644
index 000000000000..21e27e2fc68c
--- /dev/null
+++ b/drivers/iio/gyro/Kconfig
@@ -0,0 +1,16 @@
1#
2# IIO Digital Gyroscope Sensor drivers configuration
3#
4menu "Digital gyroscope sensors"
5
6config HID_SENSOR_GYRO_3D
7 depends on HID_SENSOR_HUB
8 select IIO_BUFFER
9 select IIO_TRIGGERED_BUFFER
10 select HID_SENSOR_IIO_COMMON
11 tristate "HID Gyroscope 3D"
12 help
13 Say yes here to build support for the HID SENSOR
14 Gyroscope 3D.
15
16endmenu
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
new file mode 100644
index 000000000000..8a895d9fcbce
--- /dev/null
+++ b/drivers/iio/gyro/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for industrial I/O gyroscope sensor drivers
3#
4
5obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
new file mode 100644
index 000000000000..50ec09b0b368
--- /dev/null
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -0,0 +1,419 @@
1/*
2 * HID Sensors Driver
3 * Copyright (c) 2012, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 */
19#include <linux/device.h>
20#include <linux/platform_device.h>
21#include <linux/module.h>
22#include <linux/interrupt.h>
23#include <linux/irq.h>
24#include <linux/slab.h>
25#include <linux/hid-sensor-hub.h>
26#include <linux/iio/iio.h>
27#include <linux/iio/sysfs.h>
28#include <linux/iio/buffer.h>
29#include <linux/iio/trigger_consumer.h>
30#include <linux/iio/triggered_buffer.h>
31#include "../common/hid-sensors/hid-sensor-attributes.h"
32#include "../common/hid-sensors/hid-sensor-trigger.h"
33
34/*Format: HID-SENSOR-usage_id_in_hex*/
35/*Usage ID from spec for Gyro-3D: 0x200076*/
36#define DRIVER_NAME "HID-SENSOR-200076"
37
38enum gyro_3d_channel {
39 CHANNEL_SCAN_INDEX_X,
40 CHANNEL_SCAN_INDEX_Y,
41 CHANNEL_SCAN_INDEX_Z,
42 GYRO_3D_CHANNEL_MAX,
43};
44
45struct gyro_3d_state {
46 struct hid_sensor_hub_callbacks callbacks;
47 struct hid_sensor_iio_common common_attributes;
48 struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
49 u32 gyro_val[GYRO_3D_CHANNEL_MAX];
50};
51
52static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = {
53 HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS,
54 HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS,
55 HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS
56};
57
58/* Channel definitions */
59static const struct iio_chan_spec gyro_3d_channels[] = {
60 {
61 .type = IIO_ANGL_VEL,
62 .modified = 1,
63 .channel2 = IIO_MOD_X,
64 .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
65 IIO_CHAN_INFO_SCALE_SHARED_BIT |
66 IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
67 IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
68 .scan_index = CHANNEL_SCAN_INDEX_X,
69 }, {
70 .type = IIO_ANGL_VEL,
71 .modified = 1,
72 .channel2 = IIO_MOD_Y,
73 .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
74 IIO_CHAN_INFO_SCALE_SHARED_BIT |
75 IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
76 IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
77 .scan_index = CHANNEL_SCAN_INDEX_Y,
78 }, {
79 .type = IIO_ANGL_VEL,
80 .modified = 1,
81 .channel2 = IIO_MOD_Z,
82 .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
83 IIO_CHAN_INFO_SCALE_SHARED_BIT |
84 IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
85 IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
86 .scan_index = CHANNEL_SCAN_INDEX_Z,
87 }
88};
89
90/* Adjust channel real bits based on report descriptor */
91static void gyro_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
92 int channel, int size)
93{
94 channels[channel].scan_type.sign = 's';
95 /* Real storage bits will change based on the report desc. */
96 channels[channel].scan_type.realbits = size * 8;
97 /* Maximum size of a sample to capture is u32 */
98 channels[channel].scan_type.storagebits = sizeof(u32) * 8;
99}
100
101/* Channel read_raw handler */
102static int gyro_3d_read_raw(struct iio_dev *indio_dev,
103 struct iio_chan_spec const *chan,
104 int *val, int *val2,
105 long mask)
106{
107 struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
108 int report_id = -1;
109 u32 address;
110 int ret;
111 int ret_type;
112
113 *val = 0;
114 *val2 = 0;
115 switch (mask) {
116 case 0:
117 report_id = gyro_state->gyro[chan->scan_index].report_id;
118 address = gyro_3d_addresses[chan->scan_index];
119 if (report_id >= 0)
120 *val = sensor_hub_input_attr_get_raw_value(
121 gyro_state->common_attributes.hsdev,
122 HID_USAGE_SENSOR_GYRO_3D, address,
123 report_id);
124 else {
125 *val = 0;
126 return -EINVAL;
127 }
128 ret_type = IIO_VAL_INT;
129 break;
130 case IIO_CHAN_INFO_SCALE:
131 *val = gyro_state->gyro[CHANNEL_SCAN_INDEX_X].units;
132 ret_type = IIO_VAL_INT;
133 break;
134 case IIO_CHAN_INFO_OFFSET:
135 *val = hid_sensor_convert_exponent(
136 gyro_state->gyro[CHANNEL_SCAN_INDEX_X].unit_expo);
137 ret_type = IIO_VAL_INT;
138 break;
139 case IIO_CHAN_INFO_SAMP_FREQ:
140 ret = hid_sensor_read_samp_freq_value(
141 &gyro_state->common_attributes, val, val2);
142 ret_type = IIO_VAL_INT_PLUS_MICRO;
143 break;
144 case IIO_CHAN_INFO_HYSTERESIS:
145 ret = hid_sensor_read_raw_hyst_value(
146 &gyro_state->common_attributes, val, val2);
147 ret_type = IIO_VAL_INT_PLUS_MICRO;
148 break;
149 default:
150 ret_type = -EINVAL;
151 break;
152 }
153
154 return ret_type;
155}
156
157/* Channel write_raw handler */
158static int gyro_3d_write_raw(struct iio_dev *indio_dev,
159 struct iio_chan_spec const *chan,
160 int val,
161 int val2,
162 long mask)
163{
164 struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
165 int ret = 0;
166
167 switch (mask) {
168 case IIO_CHAN_INFO_SAMP_FREQ:
169 ret = hid_sensor_write_samp_freq_value(
170 &gyro_state->common_attributes, val, val2);
171 break;
172 case IIO_CHAN_INFO_HYSTERESIS:
173 ret = hid_sensor_write_raw_hyst_value(
174 &gyro_state->common_attributes, val, val2);
175 break;
176 default:
177 ret = -EINVAL;
178 }
179
180 return ret;
181}
182
183static int gyro_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
184 struct iio_chan_spec const *chan,
185 long mask)
186{
187 return IIO_VAL_INT_PLUS_MICRO;
188}
189
190static const struct iio_info gyro_3d_info = {
191 .driver_module = THIS_MODULE,
192 .read_raw = &gyro_3d_read_raw,
193 .write_raw = &gyro_3d_write_raw,
194 .write_raw_get_fmt = &gyro_3d_write_raw_get_fmt,
195};
196
197/* Function to push data to buffer */
198static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
199{
200 struct iio_buffer *buffer = indio_dev->buffer;
201 s64 timestamp = iio_get_time_ns();
202 int datum_sz;
203
204 dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
205 if (!buffer) {
206 dev_err(&indio_dev->dev, "Buffer == NULL\n");
207 return;
208 }
209 datum_sz = buffer->access->get_bytes_per_datum(buffer);
210 if (len > datum_sz) {
211 dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
212 datum_sz);
213 return;
214 }
215 buffer->access->store_to(buffer, (u8 *)data, timestamp);
216}
217
218/* Callback handler to send event after all samples are received and captured */
219static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
220 unsigned usage_id,
221 void *priv)
222{
223 struct iio_dev *indio_dev = platform_get_drvdata(priv);
224 struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
225
226 dev_dbg(&indio_dev->dev, "gyro_3d_proc_event [%d]\n",
227 gyro_state->common_attributes.data_ready);
228 if (gyro_state->common_attributes.data_ready)
229 hid_sensor_push_data(indio_dev,
230 (u8 *)gyro_state->gyro_val,
231 sizeof(gyro_state->gyro_val));
232
233 return 0;
234}
235
236/* Capture samples in local storage */
237static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
238 unsigned usage_id,
239 size_t raw_len, char *raw_data,
240 void *priv)
241{
242 struct iio_dev *indio_dev = platform_get_drvdata(priv);
243 struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
244 int offset;
245 int ret = -EINVAL;
246
247 switch (usage_id) {
248 case HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS:
249 case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS:
250 case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS:
251 offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
252 gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
253 *(u32 *)raw_data;
254 ret = 0;
255 break;
256 default:
257 break;
258 }
259
260 return ret;
261}
262
263/* Parse report which is specific to an usage id*/
264static int gyro_3d_parse_report(struct platform_device *pdev,
265 struct hid_sensor_hub_device *hsdev,
266 struct iio_chan_spec *channels,
267 unsigned usage_id,
268 struct gyro_3d_state *st)
269{
270 int ret;
271 int i;
272
273 for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
274 ret = sensor_hub_input_get_attribute_info(hsdev,
275 HID_INPUT_REPORT,
276 usage_id,
277 HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + i,
278 &st->gyro[CHANNEL_SCAN_INDEX_X + i]);
279 if (ret < 0)
280 break;
281 gyro_3d_adjust_channel_bit_mask(channels,
282 CHANNEL_SCAN_INDEX_X + i,
283 st->gyro[CHANNEL_SCAN_INDEX_X + i].size);
284 }
285 dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n",
286 st->gyro[0].index,
287 st->gyro[0].report_id,
288 st->gyro[1].index, st->gyro[1].report_id,
289 st->gyro[2].index, st->gyro[2].report_id);
290
291 return ret;
292}
293
294/* Function to initialize the processing for usage id */
295static int __devinit hid_gyro_3d_probe(struct platform_device *pdev)
296{
297 int ret = 0;
298 static const char *name = "gyro_3d";
299 struct iio_dev *indio_dev;
300 struct gyro_3d_state *gyro_state;
301 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
302 struct iio_chan_spec *channels;
303
304 indio_dev = iio_device_alloc(sizeof(struct gyro_3d_state));
305 if (indio_dev == NULL) {
306 ret = -ENOMEM;
307 goto error_ret;
308 }
309 platform_set_drvdata(pdev, indio_dev);
310
311 gyro_state = iio_priv(indio_dev);
312 gyro_state->common_attributes.hsdev = hsdev;
313 gyro_state->common_attributes.pdev = pdev;
314
315 ret = hid_sensor_parse_common_attributes(hsdev,
316 HID_USAGE_SENSOR_GYRO_3D,
317 &gyro_state->common_attributes);
318 if (ret) {
319 dev_err(&pdev->dev, "failed to setup common attributes\n");
320 goto error_free_dev;
321 }
322
323 channels = kmemdup(gyro_3d_channels,
324 sizeof(gyro_3d_channels),
325 GFP_KERNEL);
326 if (!channels) {
327 dev_err(&pdev->dev, "failed to duplicate channels\n");
328 goto error_free_dev;
329 }
330
331 ret = gyro_3d_parse_report(pdev, hsdev, channels,
332 HID_USAGE_SENSOR_GYRO_3D, gyro_state);
333 if (ret) {
334 dev_err(&pdev->dev, "failed to setup attributes\n");
335 goto error_free_dev_mem;
336 }
337
338 indio_dev->channels = channels;
339 indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels);
340 indio_dev->dev.parent = &pdev->dev;
341 indio_dev->info = &gyro_3d_info;
342 indio_dev->name = name;
343 indio_dev->modes = INDIO_DIRECT_MODE;
344
345 ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
346 NULL, NULL);
347 if (ret) {
348 dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
349 goto error_free_dev_mem;
350 }
351 gyro_state->common_attributes.data_ready = false;
352 ret = hid_sensor_setup_trigger(indio_dev, name,
353 &gyro_state->common_attributes);
354 if (ret < 0) {
355 dev_err(&pdev->dev, "trigger setup failed\n");
356 goto error_unreg_buffer_funcs;
357 }
358
359 ret = iio_device_register(indio_dev);
360 if (ret) {
361 dev_err(&pdev->dev, "device register failed\n");
362 goto error_remove_trigger;
363 }
364
365 gyro_state->callbacks.send_event = gyro_3d_proc_event;
366 gyro_state->callbacks.capture_sample = gyro_3d_capture_sample;
367 gyro_state->callbacks.pdev = pdev;
368 ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D,
369 &gyro_state->callbacks);
370 if (ret < 0) {
371 dev_err(&pdev->dev, "callback reg failed\n");
372 goto error_iio_unreg;
373 }
374
375 return ret;
376
377error_iio_unreg:
378 iio_device_unregister(indio_dev);
379error_remove_trigger:
380 hid_sensor_remove_trigger(indio_dev);
381error_unreg_buffer_funcs:
382 iio_triggered_buffer_cleanup(indio_dev);
383error_free_dev_mem:
384 kfree(indio_dev->channels);
385error_free_dev:
386 iio_device_free(indio_dev);
387error_ret:
388 return ret;
389}
390
391/* Function to deinitialize the processing for usage id */
392static int __devinit hid_gyro_3d_remove(struct platform_device *pdev)
393{
394 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
395 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
396
397 sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
398 iio_device_unregister(indio_dev);
399 hid_sensor_remove_trigger(indio_dev);
400 iio_triggered_buffer_cleanup(indio_dev);
401 kfree(indio_dev->channels);
402 iio_device_free(indio_dev);
403
404 return 0;
405}
406
407static struct platform_driver hid_gyro_3d_platform_driver = {
408 .driver = {
409 .name = DRIVER_NAME,
410 .owner = THIS_MODULE,
411 },
412 .probe = hid_gyro_3d_probe,
413 .remove = hid_gyro_3d_remove,
414};
415module_platform_driver(hid_gyro_3d_platform_driver);
416
417MODULE_DESCRIPTION("HID Sensor Gyroscope 3D");
418MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
419MODULE_LICENSE("GPL");