aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio/magnetometer/hid-sensor-magn-3d.c
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:22:32 -0400
commitbc1d57ba0669877819822c05861961bb1f348840 (patch)
treeb28deef90e4d1c25d3a23adebb2e4f5d8c695dde /drivers/iio/magnetometer/hid-sensor-magn-3d.c
parentc5bdbef704ba4c71a4fa2edc94d1930afad3b4c6 (diff)
iio: hid-sensors: Added Compass/Magnetometer 3D
Added usage id processing for Compass 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/magnetometer/hid-sensor-magn-3d.c')
-rw-r--r--drivers/iio/magnetometer/hid-sensor-magn-3d.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
new file mode 100644
index 000000000000..07591f443554
--- /dev/null
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -0,0 +1,420 @@
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 Magnetometer-3D: 0x200083*/
36#define DRIVER_NAME "HID-SENSOR-200083"
37
38enum magn_3d_channel {
39 CHANNEL_SCAN_INDEX_X,
40 CHANNEL_SCAN_INDEX_Y,
41 CHANNEL_SCAN_INDEX_Z,
42 MAGN_3D_CHANNEL_MAX,
43};
44
45struct magn_3d_state {
46 struct hid_sensor_hub_callbacks callbacks;
47 struct hid_sensor_iio_common common_attributes;
48 struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
49 u32 magn_val[MAGN_3D_CHANNEL_MAX];
50};
51
52static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
53 HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS,
54 HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS,
55 HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS
56};
57
58/* Channel definitions */
59static const struct iio_chan_spec magn_3d_channels[] = {
60 {
61 .type = IIO_MAGN,
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_MAGN,
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_MAGN,
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 magn_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 magn_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 magn_3d_state *magn_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 =
118 magn_state->magn[chan->scan_index].report_id;
119 address = magn_3d_addresses[chan->scan_index];
120 if (report_id >= 0)
121 *val = sensor_hub_input_attr_get_raw_value(
122 magn_state->common_attributes.hsdev,
123 HID_USAGE_SENSOR_COMPASS_3D, address,
124 report_id);
125 else {
126 *val = 0;
127 return -EINVAL;
128 }
129 ret_type = IIO_VAL_INT;
130 break;
131 case IIO_CHAN_INFO_SCALE:
132 *val = magn_state->magn[CHANNEL_SCAN_INDEX_X].units;
133 ret_type = IIO_VAL_INT;
134 break;
135 case IIO_CHAN_INFO_OFFSET:
136 *val = hid_sensor_convert_exponent(
137 magn_state->magn[CHANNEL_SCAN_INDEX_X].unit_expo);
138 ret_type = IIO_VAL_INT;
139 break;
140 case IIO_CHAN_INFO_SAMP_FREQ:
141 ret = hid_sensor_read_samp_freq_value(
142 &magn_state->common_attributes, val, val2);
143 ret_type = IIO_VAL_INT_PLUS_MICRO;
144 break;
145 case IIO_CHAN_INFO_HYSTERESIS:
146 ret = hid_sensor_read_raw_hyst_value(
147 &magn_state->common_attributes, val, val2);
148 ret_type = IIO_VAL_INT_PLUS_MICRO;
149 break;
150 default:
151 ret_type = -EINVAL;
152 break;
153 }
154
155 return ret_type;
156}
157
158/* Channel write_raw handler */
159static int magn_3d_write_raw(struct iio_dev *indio_dev,
160 struct iio_chan_spec const *chan,
161 int val,
162 int val2,
163 long mask)
164{
165 struct magn_3d_state *magn_state = iio_priv(indio_dev);
166 int ret = 0;
167
168 switch (mask) {
169 case IIO_CHAN_INFO_SAMP_FREQ:
170 ret = hid_sensor_write_samp_freq_value(
171 &magn_state->common_attributes, val, val2);
172 break;
173 case IIO_CHAN_INFO_HYSTERESIS:
174 ret = hid_sensor_write_raw_hyst_value(
175 &magn_state->common_attributes, val, val2);
176 break;
177 default:
178 ret = -EINVAL;
179 }
180
181 return ret;
182}
183
184static int magn_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
185 struct iio_chan_spec const *chan,
186 long mask)
187{
188 return IIO_VAL_INT_PLUS_MICRO;
189}
190
191static const struct iio_info magn_3d_info = {
192 .driver_module = THIS_MODULE,
193 .read_raw = &magn_3d_read_raw,
194 .write_raw = &magn_3d_write_raw,
195 .write_raw_get_fmt = &magn_3d_write_raw_get_fmt,
196};
197
198/* Function to push data to buffer */
199static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
200{
201 struct iio_buffer *buffer = indio_dev->buffer;
202 s64 timestamp = iio_get_time_ns();
203 int datum_sz;
204
205 dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
206 if (!buffer) {
207 dev_err(&indio_dev->dev, "Buffer == NULL\n");
208 return;
209 }
210 datum_sz = buffer->access->get_bytes_per_datum(buffer);
211 if (len > datum_sz) {
212 dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
213 datum_sz);
214 return;
215 }
216 buffer->access->store_to(buffer, (u8 *)data, timestamp);
217}
218
219/* Callback handler to send event after all samples are received and captured */
220static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
221 unsigned usage_id,
222 void *priv)
223{
224 struct iio_dev *indio_dev = platform_get_drvdata(priv);
225 struct magn_3d_state *magn_state = iio_priv(indio_dev);
226
227 dev_dbg(&indio_dev->dev, "magn_3d_proc_event [%d]\n",
228 magn_state->common_attributes.data_ready);
229 if (magn_state->common_attributes.data_ready)
230 hid_sensor_push_data(indio_dev,
231 (u8 *)magn_state->magn_val,
232 sizeof(magn_state->magn_val));
233
234 return 0;
235}
236
237/* Capture samples in local storage */
238static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
239 unsigned usage_id,
240 size_t raw_len, char *raw_data,
241 void *priv)
242{
243 struct iio_dev *indio_dev = platform_get_drvdata(priv);
244 struct magn_3d_state *magn_state = iio_priv(indio_dev);
245 int offset;
246 int ret = -EINVAL;
247
248 switch (usage_id) {
249 case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS:
250 case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS:
251 case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS:
252 offset = usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS;
253 magn_state->magn_val[CHANNEL_SCAN_INDEX_X + offset] =
254 *(u32 *)raw_data;
255 ret = 0;
256 break;
257 default:
258 break;
259 }
260
261 return ret;
262}
263
264/* Parse report which is specific to an usage id*/
265static int magn_3d_parse_report(struct platform_device *pdev,
266 struct hid_sensor_hub_device *hsdev,
267 struct iio_chan_spec *channels,
268 unsigned usage_id,
269 struct magn_3d_state *st)
270{
271 int ret;
272 int i;
273
274 for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
275 ret = sensor_hub_input_get_attribute_info(hsdev,
276 HID_INPUT_REPORT,
277 usage_id,
278 HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS + i,
279 &st->magn[CHANNEL_SCAN_INDEX_X + i]);
280 if (ret < 0)
281 break;
282 magn_3d_adjust_channel_bit_mask(channels,
283 CHANNEL_SCAN_INDEX_X + i,
284 st->magn[CHANNEL_SCAN_INDEX_X + i].size);
285 }
286 dev_dbg(&pdev->dev, "magn_3d %x:%x, %x:%x, %x:%x\n",
287 st->magn[0].index,
288 st->magn[0].report_id,
289 st->magn[1].index, st->magn[1].report_id,
290 st->magn[2].index, st->magn[2].report_id);
291
292 return ret;
293}
294
295/* Function to initialize the processing for usage id */
296static int __devinit hid_magn_3d_probe(struct platform_device *pdev)
297{
298 int ret = 0;
299 static char *name = "magn_3d";
300 struct iio_dev *indio_dev;
301 struct magn_3d_state *magn_state;
302 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
303 struct iio_chan_spec *channels;
304
305 indio_dev = iio_device_alloc(sizeof(struct magn_3d_state));
306 if (indio_dev == NULL) {
307 ret = -ENOMEM;
308 goto error_ret;
309 }
310 platform_set_drvdata(pdev, indio_dev);
311
312 magn_state = iio_priv(indio_dev);
313 magn_state->common_attributes.hsdev = hsdev;
314 magn_state->common_attributes.pdev = pdev;
315
316 ret = hid_sensor_parse_common_attributes(hsdev,
317 HID_USAGE_SENSOR_COMPASS_3D,
318 &magn_state->common_attributes);
319 if (ret) {
320 dev_err(&pdev->dev, "failed to setup common attributes\n");
321 goto error_free_dev;
322 }
323
324 channels = kmemdup(magn_3d_channels,
325 sizeof(magn_3d_channels),
326 GFP_KERNEL);
327 if (!channels) {
328 dev_err(&pdev->dev, "failed to duplicate channels\n");
329 goto error_free_dev;
330 }
331
332 ret = magn_3d_parse_report(pdev, hsdev, channels,
333 HID_USAGE_SENSOR_COMPASS_3D, magn_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(magn_3d_channels);
341 indio_dev->dev.parent = &pdev->dev;
342 indio_dev->info = &magn_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 magn_state->common_attributes.data_ready = false;
353 ret = hid_sensor_setup_trigger(indio_dev, name,
354 &magn_state->common_attributes);
355 if (ret < 0) {
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 magn_state->callbacks.send_event = magn_3d_proc_event;
367 magn_state->callbacks.capture_sample = magn_3d_capture_sample;
368 magn_state->callbacks.pdev = pdev;
369 ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,
370 &magn_state->callbacks);
371 if (ret < 0) {
372 dev_err(&pdev->dev, "callback reg failed\n");
373 goto error_iio_unreg;
374 }
375
376 return ret;
377
378error_iio_unreg:
379 iio_device_unregister(indio_dev);
380error_remove_trigger:
381 hid_sensor_remove_trigger(indio_dev);
382error_unreg_buffer_funcs:
383 iio_triggered_buffer_cleanup(indio_dev);
384error_free_dev_mem:
385 kfree(indio_dev->channels);
386error_free_dev:
387 iio_device_free(indio_dev);
388error_ret:
389 return ret;
390}
391
392/* Function to deinitialize the processing for usage id */
393static int __devinit hid_magn_3d_remove(struct platform_device *pdev)
394{
395 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
396 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
397
398 sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
399 iio_device_unregister(indio_dev);
400 hid_sensor_remove_trigger(indio_dev);
401 iio_triggered_buffer_cleanup(indio_dev);
402 kfree(indio_dev->channels);
403 iio_device_free(indio_dev);
404
405 return 0;
406}
407
408static struct platform_driver hid_magn_3d_platform_driver = {
409 .driver = {
410 .name = DRIVER_NAME,
411 .owner = THIS_MODULE,
412 },
413 .probe = hid_magn_3d_probe,
414 .remove = hid_magn_3d_remove,
415};
416module_platform_driver(hid_magn_3d_platform_driver);
417
418MODULE_DESCRIPTION("HID Sensor Magnetometer 3D");
419MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
420MODULE_LICENSE("GPL");