aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>2013-11-05 19:11:00 -0500
committerJonathan Cameron <jic23@kernel.org>2013-12-03 15:31:59 -0500
commit098d3beccfb6d678a926c4cc4f5e70c3b752fd5e (patch)
treef74c36dcb651c9114ce5b19b1c2db41e2cbcef80
parent091a121b04547fab2045951aff6219318ede0560 (diff)
iio: hid-sensors: Added Inclinometer 3D
Added usage id processing for Inclinometer 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@linux.intel.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/orientation/Kconfig19
-rw-r--r--drivers/iio/orientation/Makefile6
-rw-r--r--drivers/iio/orientation/hid-sensor-incl-3d.c428
-rw-r--r--include/linux/hid-sensor-ids.h4
6 files changed, 459 insertions, 0 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index a5ed88226276..5dd0e120a504 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -69,6 +69,7 @@ source "drivers/iio/humidity/Kconfig"
69source "drivers/iio/imu/Kconfig" 69source "drivers/iio/imu/Kconfig"
70source "drivers/iio/light/Kconfig" 70source "drivers/iio/light/Kconfig"
71source "drivers/iio/magnetometer/Kconfig" 71source "drivers/iio/magnetometer/Kconfig"
72source "drivers/iio/orientation/Kconfig"
72if IIO_TRIGGER 73if IIO_TRIGGER
73 source "drivers/iio/trigger/Kconfig" 74 source "drivers/iio/trigger/Kconfig"
74endif #IIO_TRIGGER 75endif #IIO_TRIGGER
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index b3b50968b46b..887d39090d75 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -22,6 +22,7 @@ obj-y += humidity/
22obj-y += imu/ 22obj-y += imu/
23obj-y += light/ 23obj-y += light/
24obj-y += magnetometer/ 24obj-y += magnetometer/
25obj-y += orientation/
25obj-y += pressure/ 26obj-y += pressure/
26obj-y += temperature/ 27obj-y += temperature/
27obj-y += trigger/ 28obj-y += trigger/
diff --git a/drivers/iio/orientation/Kconfig b/drivers/iio/orientation/Kconfig
new file mode 100644
index 000000000000..58c62c837e12
--- /dev/null
+++ b/drivers/iio/orientation/Kconfig
@@ -0,0 +1,19 @@
1#
2# Inclinometer sensors
3#
4# When adding new entries keep the list in alphabetical order
5
6menu "Inclinometer sensors"
7
8config HID_SENSOR_INCLINOMETER_3D
9 depends on HID_SENSOR_HUB
10 select IIO_BUFFER
11 select IIO_TRIGGERED_BUFFER
12 select HID_SENSOR_IIO_COMMON
13 select HID_SENSOR_IIO_TRIGGER
14 tristate "HID Inclinometer 3D"
15 help
16 Say yes here to build support for the HID SENSOR
17 Inclinometer 3D.
18
19endmenu
diff --git a/drivers/iio/orientation/Makefile b/drivers/iio/orientation/Makefile
new file mode 100644
index 000000000000..2c97572ee919
--- /dev/null
+++ b/drivers/iio/orientation/Makefile
@@ -0,0 +1,6 @@
1#
2# Makefile for industrial I/O Inclinometer sensor drivers
3#
4
5# When adding new entries keep the list in alphabetical order
6obj-$(CONFIG_HID_SENSOR_INCLINOMETER_3D) += hid-sensor-incl-3d.o
diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c
new file mode 100644
index 000000000000..070feab08faa
--- /dev/null
+++ b/drivers/iio/orientation/hid-sensor-incl-3d.c
@@ -0,0 +1,428 @@
1/*
2 * HID Sensors Driver
3 * Copyright (c) 2013, 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 *
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-trigger.h"
32
33enum incl_3d_channel {
34 CHANNEL_SCAN_INDEX_X,
35 CHANNEL_SCAN_INDEX_Y,
36 CHANNEL_SCAN_INDEX_Z,
37 INCLI_3D_CHANNEL_MAX,
38};
39
40struct incl_3d_state {
41 struct hid_sensor_hub_callbacks callbacks;
42 struct hid_sensor_common common_attributes;
43 struct hid_sensor_hub_attribute_info incl[INCLI_3D_CHANNEL_MAX];
44 u32 incl_val[INCLI_3D_CHANNEL_MAX];
45};
46
47static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = {
48 HID_USAGE_SENSOR_ORIENT_TILT_X,
49 HID_USAGE_SENSOR_ORIENT_TILT_Y,
50 HID_USAGE_SENSOR_ORIENT_TILT_Z
51};
52
53/* Channel definitions */
54static const struct iio_chan_spec incl_3d_channels[] = {
55 {
56 .type = IIO_INCLI,
57 .modified = 1,
58 .channel2 = IIO_MOD_X,
59 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
60 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
61 BIT(IIO_CHAN_INFO_SCALE) |
62 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
63 BIT(IIO_CHAN_INFO_HYSTERESIS),
64 .scan_index = CHANNEL_SCAN_INDEX_X,
65 }, {
66 .type = IIO_INCLI,
67 .modified = 1,
68 .channel2 = IIO_MOD_Y,
69 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
70 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
71 BIT(IIO_CHAN_INFO_SCALE) |
72 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
73 BIT(IIO_CHAN_INFO_HYSTERESIS),
74 .scan_index = CHANNEL_SCAN_INDEX_Y,
75 }, {
76 .type = IIO_INCLI,
77 .modified = 1,
78 .channel2 = IIO_MOD_Z,
79 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
80 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
81 BIT(IIO_CHAN_INFO_SCALE) |
82 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
83 BIT(IIO_CHAN_INFO_HYSTERESIS),
84 .scan_index = CHANNEL_SCAN_INDEX_Z,
85 }
86};
87
88/* Adjust channel real bits based on report descriptor */
89static void incl_3d_adjust_channel_bit_mask(struct iio_chan_spec *chan,
90 int size)
91{
92 chan->scan_type.sign = 's';
93 /* Real storage bits will change based on the report desc. */
94 chan->scan_type.realbits = size * 8;
95 /* Maximum size of a sample to capture is u32 */
96 chan->scan_type.storagebits = sizeof(u32) * 8;
97}
98
99/* Channel read_raw handler */
100static int incl_3d_read_raw(struct iio_dev *indio_dev,
101 struct iio_chan_spec const *chan,
102 int *val, int *val2,
103 long mask)
104{
105 struct incl_3d_state *incl_state = iio_priv(indio_dev);
106 int report_id = -1;
107 u32 address;
108 int ret_type;
109
110 *val = 0;
111 *val2 = 0;
112 switch (mask) {
113 case IIO_CHAN_INFO_RAW:
114 report_id =
115 incl_state->incl[chan->scan_index].report_id;
116 address = incl_3d_addresses[chan->scan_index];
117 if (report_id >= 0)
118 *val = sensor_hub_input_attr_get_raw_value(
119 incl_state->common_attributes.hsdev,
120 HID_USAGE_SENSOR_INCLINOMETER_3D, address,
121 report_id);
122 else {
123 return -EINVAL;
124 }
125 ret_type = IIO_VAL_INT;
126 break;
127 case IIO_CHAN_INFO_SCALE:
128 *val = incl_state->incl[CHANNEL_SCAN_INDEX_X].units;
129 ret_type = IIO_VAL_INT;
130 break;
131 case IIO_CHAN_INFO_OFFSET:
132 *val = hid_sensor_convert_exponent(
133 incl_state->incl[CHANNEL_SCAN_INDEX_X].unit_expo);
134 ret_type = IIO_VAL_INT;
135 break;
136 case IIO_CHAN_INFO_SAMP_FREQ:
137 ret_type = hid_sensor_read_samp_freq_value(
138 &incl_state->common_attributes, val, val2);
139 break;
140 case IIO_CHAN_INFO_HYSTERESIS:
141 ret_type = hid_sensor_read_raw_hyst_value(
142 &incl_state->common_attributes, val, val2);
143 break;
144 default:
145 ret_type = -EINVAL;
146 break;
147 }
148
149 return ret_type;
150}
151
152/* Channel write_raw handler */
153static int incl_3d_write_raw(struct iio_dev *indio_dev,
154 struct iio_chan_spec const *chan,
155 int val,
156 int val2,
157 long mask)
158{
159 struct incl_3d_state *incl_state = iio_priv(indio_dev);
160 int ret;
161
162 switch (mask) {
163 case IIO_CHAN_INFO_SAMP_FREQ:
164 ret = hid_sensor_write_samp_freq_value(
165 &incl_state->common_attributes, val, val2);
166 break;
167 case IIO_CHAN_INFO_HYSTERESIS:
168 ret = hid_sensor_write_raw_hyst_value(
169 &incl_state->common_attributes, val, val2);
170 break;
171 default:
172 ret = -EINVAL;
173 }
174
175 return ret;
176}
177
178static const struct iio_info incl_3d_info = {
179 .driver_module = THIS_MODULE,
180 .read_raw = &incl_3d_read_raw,
181 .write_raw = &incl_3d_write_raw,
182};
183
184/* Function to push data to buffer */
185static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
186{
187 dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
188 iio_push_to_buffers(indio_dev, (u8 *)data);
189}
190
191/* Callback handler to send event after all samples are received and captured */
192static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev,
193 unsigned usage_id,
194 void *priv)
195{
196 struct iio_dev *indio_dev = platform_get_drvdata(priv);
197 struct incl_3d_state *incl_state = iio_priv(indio_dev);
198
199 dev_dbg(&indio_dev->dev, "incl_3d_proc_event [%d]\n",
200 incl_state->common_attributes.data_ready);
201 if (incl_state->common_attributes.data_ready)
202 hid_sensor_push_data(indio_dev,
203 (u8 *)incl_state->incl_val,
204 sizeof(incl_state->incl_val));
205
206 return 0;
207}
208
209/* Capture samples in local storage */
210static int incl_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
211 unsigned usage_id,
212 size_t raw_len, char *raw_data,
213 void *priv)
214{
215 struct iio_dev *indio_dev = platform_get_drvdata(priv);
216 struct incl_3d_state *incl_state = iio_priv(indio_dev);
217 int ret = 0;
218
219 switch (usage_id) {
220 case HID_USAGE_SENSOR_ORIENT_TILT_X:
221 incl_state->incl_val[CHANNEL_SCAN_INDEX_X] = *(u32 *)raw_data;
222 break;
223 case HID_USAGE_SENSOR_ORIENT_TILT_Y:
224 incl_state->incl_val[CHANNEL_SCAN_INDEX_Y] = *(u32 *)raw_data;
225 break;
226 case HID_USAGE_SENSOR_ORIENT_TILT_Z:
227 incl_state->incl_val[CHANNEL_SCAN_INDEX_Z] = *(u32 *)raw_data;
228 break;
229 default:
230 ret = -EINVAL;
231 break;
232 }
233
234 return ret;
235}
236
237/* Parse report which is specific to an usage id*/
238static int incl_3d_parse_report(struct platform_device *pdev,
239 struct hid_sensor_hub_device *hsdev,
240 struct iio_chan_spec *channels,
241 unsigned usage_id,
242 struct incl_3d_state *st)
243{
244 int ret;
245
246 ret = sensor_hub_input_get_attribute_info(hsdev,
247 HID_INPUT_REPORT,
248 usage_id,
249 HID_USAGE_SENSOR_ORIENT_TILT_X,
250 &st->incl[CHANNEL_SCAN_INDEX_X]);
251 if (ret)
252 return ret;
253 incl_3d_adjust_channel_bit_mask(&channels[CHANNEL_SCAN_INDEX_X],
254 st->incl[CHANNEL_SCAN_INDEX_X].size);
255
256 ret = sensor_hub_input_get_attribute_info(hsdev,
257 HID_INPUT_REPORT,
258 usage_id,
259 HID_USAGE_SENSOR_ORIENT_TILT_Y,
260 &st->incl[CHANNEL_SCAN_INDEX_Y]);
261 if (ret)
262 return ret;
263 incl_3d_adjust_channel_bit_mask(&channels[CHANNEL_SCAN_INDEX_Y],
264 st->incl[CHANNEL_SCAN_INDEX_Y].size);
265
266 ret = sensor_hub_input_get_attribute_info(hsdev,
267 HID_INPUT_REPORT,
268 usage_id,
269 HID_USAGE_SENSOR_ORIENT_TILT_Z,
270 &st->incl[CHANNEL_SCAN_INDEX_Z]);
271 if (ret)
272 return ret;
273 incl_3d_adjust_channel_bit_mask(&channels[CHANNEL_SCAN_INDEX_Z],
274 st->incl[CHANNEL_SCAN_INDEX_Z].size);
275
276 dev_dbg(&pdev->dev, "incl_3d %x:%x, %x:%x, %x:%x\n",
277 st->incl[0].index,
278 st->incl[0].report_id,
279 st->incl[1].index, st->incl[1].report_id,
280 st->incl[2].index, st->incl[2].report_id);
281
282 /* Set Sensitivity field ids, when there is no individual modifier */
283 if (st->common_attributes.sensitivity.index < 0) {
284 sensor_hub_input_get_attribute_info(hsdev,
285 HID_FEATURE_REPORT, usage_id,
286 HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
287 HID_USAGE_SENSOR_DATA_ORIENTATION,
288 &st->common_attributes.sensitivity);
289 dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
290 st->common_attributes.sensitivity.index,
291 st->common_attributes.sensitivity.report_id);
292 }
293 return ret;
294}
295
296/* Function to initialize the processing for usage id */
297static int hid_incl_3d_probe(struct platform_device *pdev)
298{
299 int ret;
300 static char *name = "incli_3d";
301 struct iio_dev *indio_dev;
302 struct incl_3d_state *incl_state;
303 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
304 struct iio_chan_spec *channels;
305
306 indio_dev = devm_iio_device_alloc(&pdev->dev,
307 sizeof(struct incl_3d_state));
308 if (indio_dev == NULL)
309 return -ENOMEM;
310
311 platform_set_drvdata(pdev, indio_dev);
312
313 incl_state = iio_priv(indio_dev);
314 incl_state->common_attributes.hsdev = hsdev;
315 incl_state->common_attributes.pdev = pdev;
316
317 ret = hid_sensor_parse_common_attributes(hsdev,
318 HID_USAGE_SENSOR_INCLINOMETER_3D,
319 &incl_state->common_attributes);
320 if (ret) {
321 dev_err(&pdev->dev, "failed to setup common attributes\n");
322 return ret;
323 }
324
325 channels = kmemdup(incl_3d_channels, sizeof(incl_3d_channels),
326 GFP_KERNEL);
327 if (!channels) {
328 dev_err(&pdev->dev, "failed to duplicate channels\n");
329 return -ENOMEM;
330 }
331
332 ret = incl_3d_parse_report(pdev, hsdev, channels,
333 HID_USAGE_SENSOR_INCLINOMETER_3D, incl_state);
334 if (ret) {
335 dev_err(&pdev->dev, "failed to setup attributes\n");
336 goto error_free_dev_mem;
337 }
338
339 indio_dev->channels = channels;
340 indio_dev->num_channels = ARRAY_SIZE(incl_3d_channels);
341 indio_dev->dev.parent = &pdev->dev;
342 indio_dev->info = &incl_3d_info;
343 indio_dev->name = name;
344 indio_dev->modes = INDIO_DIRECT_MODE;
345
346 ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
347 NULL, NULL);
348 if (ret) {
349 dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
350 goto error_free_dev_mem;
351 }
352 incl_state->common_attributes.data_ready = false;
353 ret = hid_sensor_setup_trigger(indio_dev, name,
354 &incl_state->common_attributes);
355 if (ret) {
356 dev_err(&pdev->dev, "trigger setup failed\n");
357 goto error_unreg_buffer_funcs;
358 }
359
360 ret = iio_device_register(indio_dev);
361 if (ret) {
362 dev_err(&pdev->dev, "device register failed\n");
363 goto error_remove_trigger;
364 }
365
366 incl_state->callbacks.send_event = incl_3d_proc_event;
367 incl_state->callbacks.capture_sample = incl_3d_capture_sample;
368 incl_state->callbacks.pdev = pdev;
369 ret = sensor_hub_register_callback(hsdev,
370 HID_USAGE_SENSOR_INCLINOMETER_3D,
371 &incl_state->callbacks);
372 if (ret) {
373 dev_err(&pdev->dev, "callback reg failed\n");
374 goto error_iio_unreg;
375 }
376
377 return 0;
378
379error_iio_unreg:
380 iio_device_unregister(indio_dev);
381error_remove_trigger:
382 hid_sensor_remove_trigger(&incl_state->common_attributes);
383error_unreg_buffer_funcs:
384 iio_triggered_buffer_cleanup(indio_dev);
385error_free_dev_mem:
386 kfree(indio_dev->channels);
387 return ret;
388}
389
390/* Function to deinitialize the processing for usage id */
391static int hid_incl_3d_remove(struct platform_device *pdev)
392{
393 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
394 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
395 struct incl_3d_state *incl_state = iio_priv(indio_dev);
396
397 sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D);
398 iio_device_unregister(indio_dev);
399 hid_sensor_remove_trigger(&incl_state->common_attributes);
400 iio_triggered_buffer_cleanup(indio_dev);
401 kfree(indio_dev->channels);
402
403 return 0;
404}
405
406static struct platform_device_id hid_incl_3d_ids[] = {
407 {
408 /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
409 .name = "HID-SENSOR-200086",
410 },
411 { /* sentinel */ }
412};
413MODULE_DEVICE_TABLE(platform, hid_incl_3d_ids);
414
415static struct platform_driver hid_incl_3d_platform_driver = {
416 .id_table = hid_incl_3d_ids,
417 .driver = {
418 .name = KBUILD_MODNAME,
419 .owner = THIS_MODULE,
420 },
421 .probe = hid_incl_3d_probe,
422 .remove = hid_incl_3d_remove,
423};
424module_platform_driver(hid_incl_3d_platform_driver);
425
426MODULE_DESCRIPTION("HID Sensor Inclinometer 3D");
427MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
428MODULE_LICENSE("GPL");
diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h
index 4cc165887b09..558b7d43de7d 100644
--- a/include/linux/hid-sensor-ids.h
+++ b/include/linux/hid-sensor-ids.h
@@ -58,10 +58,14 @@
58#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Y 0x20047B 58#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Y 0x20047B
59#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Z 0x20047C 59#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Z 0x20047C
60#define HID_USAGE_SENSOR_ORIENT_DISTANCE_OUT_OF_RANGE 0x20047D 60#define HID_USAGE_SENSOR_ORIENT_DISTANCE_OUT_OF_RANGE 0x20047D
61
62/* ORIENTATION: Inclinometer 3D: (200086) */
63#define HID_USAGE_SENSOR_INCLINOMETER_3D 0x200086
61#define HID_USAGE_SENSOR_ORIENT_TILT 0x20047E 64#define HID_USAGE_SENSOR_ORIENT_TILT 0x20047E
62#define HID_USAGE_SENSOR_ORIENT_TILT_X 0x20047F 65#define HID_USAGE_SENSOR_ORIENT_TILT_X 0x20047F
63#define HID_USAGE_SENSOR_ORIENT_TILT_Y 0x200480 66#define HID_USAGE_SENSOR_ORIENT_TILT_Y 0x200480
64#define HID_USAGE_SENSOR_ORIENT_TILT_Z 0x200481 67#define HID_USAGE_SENSOR_ORIENT_TILT_Z 0x200481
68
65#define HID_USAGE_SENSOR_ORIENT_ROTATION_MATRIX 0x200482 69#define HID_USAGE_SENSOR_ORIENT_ROTATION_MATRIX 0x200482
66#define HID_USAGE_SENSOR_ORIENT_QUATERNION 0x200483 70#define HID_USAGE_SENSOR_ORIENT_QUATERNION 0x200483
67#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX 0x200484 71#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX 0x200484