aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>2014-04-28 19:51:00 -0400
committerJonathan Cameron <jic23@kernel.org>2014-04-29 17:11:53 -0400
commitfc18dddc0625cd1fdf6a823e85138ff05848a85f (patch)
treeebc67d8d86ed874316c8bb856b6ad77aa3f52450 /drivers/iio
parent5082f405b74ad1b69aa9595555ce55b75b59b2ec (diff)
iio: hid-sensors: Added device rotation support
Added usage id processing for device rotation. 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. Data is exported to user space in the form of quaternion rotation format. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/orientation/Kconfig12
-rw-r--r--drivers/iio/orientation/Makefile1
-rw-r--r--drivers/iio/orientation/hid-sensor-rotation.c348
3 files changed, 361 insertions, 0 deletions
diff --git a/drivers/iio/orientation/Kconfig b/drivers/iio/orientation/Kconfig
index 58c62c837e12..e3aa1e58d920 100644
--- a/drivers/iio/orientation/Kconfig
+++ b/drivers/iio/orientation/Kconfig
@@ -16,4 +16,16 @@ config HID_SENSOR_INCLINOMETER_3D
16 Say yes here to build support for the HID SENSOR 16 Say yes here to build support for the HID SENSOR
17 Inclinometer 3D. 17 Inclinometer 3D.
18 18
19config HID_SENSOR_DEVICE_ROTATION
20 depends on HID_SENSOR_HUB
21 select IIO_BUFFER
22 select IIO_TRIGGERED_BUFFER
23 select HID_SENSOR_IIO_COMMON
24 select HID_SENSOR_IIO_TRIGGER
25 tristate "HID Device Rotation"
26 help
27 Say yes here to build support for the HID SENSOR
28 device rotation. The output of a device rotation sensor
29 is presented using quaternion format.
30
19endmenu 31endmenu
diff --git a/drivers/iio/orientation/Makefile b/drivers/iio/orientation/Makefile
index 2c97572ee919..4734dabbde13 100644
--- a/drivers/iio/orientation/Makefile
+++ b/drivers/iio/orientation/Makefile
@@ -4,3 +4,4 @@
4 4
5# When adding new entries keep the list in alphabetical order 5# When adding new entries keep the list in alphabetical order
6obj-$(CONFIG_HID_SENSOR_INCLINOMETER_3D) += hid-sensor-incl-3d.o 6obj-$(CONFIG_HID_SENSOR_INCLINOMETER_3D) += hid-sensor-incl-3d.o
7obj-$(CONFIG_HID_SENSOR_DEVICE_ROTATION) += hid-sensor-rotation.o
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
new file mode 100644
index 000000000000..51387bbc1ce1
--- /dev/null
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -0,0 +1,348 @@
1/*
2 * HID Sensors Driver
3 * Copyright (c) 2014, 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
15#include <linux/device.h>
16#include <linux/platform_device.h>
17#include <linux/module.h>
18#include <linux/interrupt.h>
19#include <linux/irq.h>
20#include <linux/slab.h>
21#include <linux/hid-sensor-hub.h>
22#include <linux/iio/iio.h>
23#include <linux/iio/sysfs.h>
24#include <linux/iio/buffer.h>
25#include <linux/iio/trigger_consumer.h>
26#include <linux/iio/triggered_buffer.h>
27#include "../common/hid-sensors/hid-sensor-trigger.h"
28
29struct dev_rot_state {
30 struct hid_sensor_hub_callbacks callbacks;
31 struct hid_sensor_common common_attributes;
32 struct hid_sensor_hub_attribute_info quaternion;
33 u32 sampled_vals[4];
34};
35
36/* Channel definitions */
37static const struct iio_chan_spec dev_rot_channels[] = {
38 {
39 .type = IIO_ROT,
40 .modified = 1,
41 .channel2 = IIO_MOD_QUATERNION,
42 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
43 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
44 BIT(IIO_CHAN_INFO_HYSTERESIS)
45 }
46};
47
48/* Adjust channel real bits based on report descriptor */
49static void dev_rot_adjust_channel_bit_mask(struct iio_chan_spec *chan,
50 int size)
51{
52 chan->scan_type.sign = 's';
53 /* Real storage bits will change based on the report desc. */
54 chan->scan_type.realbits = size * 8;
55 /* Maximum size of a sample to capture is u32 */
56 chan->scan_type.storagebits = sizeof(u32) * 8;
57 chan->scan_type.repeat = 4;
58}
59
60/* Channel read_raw handler */
61static int dev_rot_read_raw(struct iio_dev *indio_dev,
62 struct iio_chan_spec const *chan,
63 int size, int *vals, int *val_len,
64 long mask)
65{
66 struct dev_rot_state *rot_state = iio_priv(indio_dev);
67 int ret_type;
68 int i;
69
70 vals[0] = 0;
71 vals[1] = 0;
72
73 switch (mask) {
74 case IIO_CHAN_INFO_RAW:
75 if (size >= 4) {
76 for (i = 0; i < 4; ++i)
77 vals[i] = rot_state->sampled_vals[i];
78 ret_type = IIO_VAL_INT_MULTIPLE;
79 *val_len = 4;
80 } else
81 ret_type = -EINVAL;
82 break;
83 case IIO_CHAN_INFO_SAMP_FREQ:
84 ret_type = hid_sensor_read_samp_freq_value(
85 &rot_state->common_attributes, &vals[0], &vals[1]);
86 break;
87 case IIO_CHAN_INFO_HYSTERESIS:
88 ret_type = hid_sensor_read_raw_hyst_value(
89 &rot_state->common_attributes, &vals[0], &vals[1]);
90 break;
91 default:
92 ret_type = -EINVAL;
93 break;
94 }
95
96 return ret_type;
97}
98
99/* Channel write_raw handler */
100static int dev_rot_write_raw(struct iio_dev *indio_dev,
101 struct iio_chan_spec const *chan,
102 int val,
103 int val2,
104 long mask)
105{
106 struct dev_rot_state *rot_state = iio_priv(indio_dev);
107 int ret;
108
109 switch (mask) {
110 case IIO_CHAN_INFO_SAMP_FREQ:
111 ret = hid_sensor_write_samp_freq_value(
112 &rot_state->common_attributes, val, val2);
113 break;
114 case IIO_CHAN_INFO_HYSTERESIS:
115 ret = hid_sensor_write_raw_hyst_value(
116 &rot_state->common_attributes, val, val2);
117 break;
118 default:
119 ret = -EINVAL;
120 }
121
122 return ret;
123}
124
125static const struct iio_info dev_rot_info = {
126 .driver_module = THIS_MODULE,
127 .read_raw_multi = &dev_rot_read_raw,
128 .write_raw = &dev_rot_write_raw,
129};
130
131/* Function to push data to buffer */
132static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
133{
134 dev_dbg(&indio_dev->dev, "hid_sensor_push_data >>\n");
135 iio_push_to_buffers(indio_dev, (u8 *)data);
136 dev_dbg(&indio_dev->dev, "hid_sensor_push_data <<\n");
137
138}
139
140/* Callback handler to send event after all samples are received and captured */
141static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
142 unsigned usage_id,
143 void *priv)
144{
145 struct iio_dev *indio_dev = platform_get_drvdata(priv);
146 struct dev_rot_state *rot_state = iio_priv(indio_dev);
147
148 dev_dbg(&indio_dev->dev, "dev_rot_proc_event [%d]\n",
149 rot_state->common_attributes.data_ready);
150
151 if (rot_state->common_attributes.data_ready)
152 hid_sensor_push_data(indio_dev,
153 (u8 *)rot_state->sampled_vals,
154 sizeof(rot_state->sampled_vals));
155
156 return 0;
157}
158
159/* Capture samples in local storage */
160static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
161 unsigned usage_id,
162 size_t raw_len, char *raw_data,
163 void *priv)
164{
165 struct iio_dev *indio_dev = platform_get_drvdata(priv);
166 struct dev_rot_state *rot_state = iio_priv(indio_dev);
167
168 if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) {
169 memcpy(rot_state->sampled_vals, raw_data,
170 sizeof(rot_state->sampled_vals));
171 dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len,
172 sizeof(rot_state->sampled_vals));
173 }
174
175 return 0;
176}
177
178/* Parse report which is specific to an usage id*/
179static int dev_rot_parse_report(struct platform_device *pdev,
180 struct hid_sensor_hub_device *hsdev,
181 struct iio_chan_spec *channels,
182 unsigned usage_id,
183 struct dev_rot_state *st)
184{
185 int ret;
186
187 ret = sensor_hub_input_get_attribute_info(hsdev,
188 HID_INPUT_REPORT,
189 usage_id,
190 HID_USAGE_SENSOR_ORIENT_QUATERNION,
191 &st->quaternion);
192 if (ret)
193 return ret;
194
195 dev_rot_adjust_channel_bit_mask(&channels[0],
196 st->quaternion.size / 4);
197
198 dev_dbg(&pdev->dev, "dev_rot %x:%x\n", st->quaternion.index,
199 st->quaternion.report_id);
200
201 dev_dbg(&pdev->dev, "dev_rot: attrib size %d\n",
202 st->quaternion.size);
203
204 /* Set Sensitivity field ids, when there is no individual modifier */
205 if (st->common_attributes.sensitivity.index < 0) {
206 sensor_hub_input_get_attribute_info(hsdev,
207 HID_FEATURE_REPORT, usage_id,
208 HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
209 HID_USAGE_SENSOR_DATA_ORIENTATION,
210 &st->common_attributes.sensitivity);
211 dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
212 st->common_attributes.sensitivity.index,
213 st->common_attributes.sensitivity.report_id);
214 }
215
216 return 0;
217}
218
219/* Function to initialize the processing for usage id */
220static int hid_dev_rot_probe(struct platform_device *pdev)
221{
222 int ret;
223 static char *name = "dev_rotation";
224 struct iio_dev *indio_dev;
225 struct dev_rot_state *rot_state;
226 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
227 struct iio_chan_spec *channels;
228
229 indio_dev = devm_iio_device_alloc(&pdev->dev,
230 sizeof(struct dev_rot_state));
231 if (indio_dev == NULL)
232 return -ENOMEM;
233
234 platform_set_drvdata(pdev, indio_dev);
235
236 rot_state = iio_priv(indio_dev);
237 rot_state->common_attributes.hsdev = hsdev;
238 rot_state->common_attributes.pdev = pdev;
239
240 ret = hid_sensor_parse_common_attributes(hsdev,
241 HID_USAGE_SENSOR_DEVICE_ORIENTATION,
242 &rot_state->common_attributes);
243 if (ret) {
244 dev_err(&pdev->dev, "failed to setup common attributes\n");
245 return ret;
246 }
247
248 channels = devm_kmemdup(&pdev->dev, dev_rot_channels,
249 sizeof(dev_rot_channels), GFP_KERNEL);
250 if (!channels) {
251 dev_err(&pdev->dev, "failed to duplicate channels\n");
252 return -ENOMEM;
253 }
254
255 ret = dev_rot_parse_report(pdev, hsdev, channels,
256 HID_USAGE_SENSOR_DEVICE_ORIENTATION, rot_state);
257 if (ret) {
258 dev_err(&pdev->dev, "failed to setup attributes\n");
259 return ret;
260 }
261
262 indio_dev->channels = channels;
263 indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels);
264 indio_dev->dev.parent = &pdev->dev;
265 indio_dev->info = &dev_rot_info;
266 indio_dev->name = name;
267 indio_dev->modes = INDIO_DIRECT_MODE;
268
269 ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
270 NULL, NULL);
271 if (ret) {
272 dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
273 return ret;
274 }
275 rot_state->common_attributes.data_ready = false;
276 ret = hid_sensor_setup_trigger(indio_dev, name,
277 &rot_state->common_attributes);
278 if (ret) {
279 dev_err(&pdev->dev, "trigger setup failed\n");
280 goto error_unreg_buffer_funcs;
281 }
282
283 ret = iio_device_register(indio_dev);
284 if (ret) {
285 dev_err(&pdev->dev, "device register failed\n");
286 goto error_remove_trigger;
287 }
288
289 rot_state->callbacks.send_event = dev_rot_proc_event;
290 rot_state->callbacks.capture_sample = dev_rot_capture_sample;
291 rot_state->callbacks.pdev = pdev;
292 ret = sensor_hub_register_callback(hsdev,
293 HID_USAGE_SENSOR_DEVICE_ORIENTATION,
294 &rot_state->callbacks);
295 if (ret) {
296 dev_err(&pdev->dev, "callback reg failed\n");
297 goto error_iio_unreg;
298 }
299
300 return 0;
301
302error_iio_unreg:
303 iio_device_unregister(indio_dev);
304error_remove_trigger:
305 hid_sensor_remove_trigger(&rot_state->common_attributes);
306error_unreg_buffer_funcs:
307 iio_triggered_buffer_cleanup(indio_dev);
308 return ret;
309}
310
311/* Function to deinitialize the processing for usage id */
312static int hid_dev_rot_remove(struct platform_device *pdev)
313{
314 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
315 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
316 struct dev_rot_state *rot_state = iio_priv(indio_dev);
317
318 sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_DEVICE_ORIENTATION);
319 iio_device_unregister(indio_dev);
320 hid_sensor_remove_trigger(&rot_state->common_attributes);
321 iio_triggered_buffer_cleanup(indio_dev);
322
323 return 0;
324}
325
326static struct platform_device_id hid_dev_rot_ids[] = {
327 {
328 /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
329 .name = "HID-SENSOR-20008a",
330 },
331 { /* sentinel */ }
332};
333MODULE_DEVICE_TABLE(platform, hid_dev_rot_ids);
334
335static struct platform_driver hid_dev_rot_platform_driver = {
336 .id_table = hid_dev_rot_ids,
337 .driver = {
338 .name = KBUILD_MODNAME,
339 .owner = THIS_MODULE,
340 },
341 .probe = hid_dev_rot_probe,
342 .remove = hid_dev_rot_remove,
343};
344module_platform_driver(hid_dev_rot_platform_driver);
345
346MODULE_DESCRIPTION("HID Sensor Device Rotation");
347MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
348MODULE_LICENSE("GPL");