diff options
author | srinivas pandruvada <srinivas.pandruvada@intel.com> | 2012-09-05 08:56:00 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2012-09-06 14:13:13 -0400 |
commit | 401ca24fb34aee0cedf9c4fef361e533224f15a1 (patch) | |
tree | 6841fc321a073643ebe8a4f9fd1a8c32fac13c3a /drivers/hid | |
parent | c8147d9ea19bfe7d8e569351bc7239e118dd6997 (diff) |
HID: sensors: introduce sensor framework
Adding processing for HID Sensor usage table as defined by
HID 1.12, Request #: HUTRR39, dated 05 May, 2011.
This driver uses HID driver framework to register, send and
receive events.
This uses MFD framework, so that actual processing for a
specific usage id can be done in a different driver. For
example an accelerometer driver can be a separate driver and
use the interface provided by this driver to register for
events.
Signed-off-by: srinivas pandruvada <srinivas.pandruvada@intel.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 14 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/hid-sensor-hub.c | 695 |
3 files changed, 710 insertions, 0 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fbf49503508d..f8d314060853 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -690,6 +690,20 @@ config HID_ZYDACRON | |||
690 | ---help--- | 690 | ---help--- |
691 | Support for Zydacron remote control. | 691 | Support for Zydacron remote control. |
692 | 692 | ||
693 | config HID_SENSOR_HUB | ||
694 | tristate "HID Sensors framework support" | ||
695 | depends on USB_HID | ||
696 | select MFD_CORE | ||
697 | default n | ||
698 | -- help--- | ||
699 | Support for HID Sensor framework. This creates a MFD instance | ||
700 | for a sensor hub and identifies all the sensors connected to it. | ||
701 | Each sensor is registered as a MFD cell, so that sensor specific | ||
702 | processing can be done in a separate driver. Each sensor | ||
703 | drivers can use the service provided by this driver to register | ||
704 | for events and handle data streams. Each sensor driver can format | ||
705 | data and present to user mode using input or IIO interface. | ||
706 | |||
693 | endmenu | 707 | endmenu |
694 | 708 | ||
695 | endif # HID | 709 | endif # HID |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index f975485f88b2..1173d7af20e8 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -91,6 +91,7 @@ obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o | |||
91 | obj-$(CONFIG_HID_WACOM) += hid-wacom.o | 91 | obj-$(CONFIG_HID_WACOM) += hid-wacom.o |
92 | obj-$(CONFIG_HID_WALTOP) += hid-waltop.o | 92 | obj-$(CONFIG_HID_WALTOP) += hid-waltop.o |
93 | obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o | 93 | obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o |
94 | obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o | ||
94 | 95 | ||
95 | obj-$(CONFIG_USB_HID) += usbhid/ | 96 | obj-$(CONFIG_USB_HID) += usbhid/ |
96 | obj-$(CONFIG_USB_MOUSE) += usbhid/ | 97 | obj-$(CONFIG_USB_MOUSE) += usbhid/ |
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c new file mode 100644 index 000000000000..34a35ba95fc1 --- /dev/null +++ b/drivers/hid/hid-sensor-hub.c | |||
@@ -0,0 +1,695 @@ | |||
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/hid.h> | ||
21 | #include <linux/usb.h> | ||
22 | #include "usbhid/usbhid.h" | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/mfd/core.h> | ||
26 | #include <linux/list.h> | ||
27 | #include <linux/hid-sensor-ids.h> | ||
28 | #include <linux/hid-sensor-hub.h> | ||
29 | #include "hid-ids.h" | ||
30 | |||
31 | /** | ||
32 | * struct sensor_hub_pending - Synchronous read pending information | ||
33 | * @status: Pending status true/false. | ||
34 | * @ready: Completion synchronization data. | ||
35 | * @usage_id: Usage id for physical device, E.g. Gyro usage id. | ||
36 | * @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro. | ||
37 | * @raw_size: Response size for a read request. | ||
38 | * @raw_data: Place holder for received response. | ||
39 | */ | ||
40 | struct sensor_hub_pending { | ||
41 | bool status; | ||
42 | struct completion ready; | ||
43 | u32 usage_id; | ||
44 | u32 attr_usage_id; | ||
45 | int raw_size; | ||
46 | u8 *raw_data; | ||
47 | }; | ||
48 | |||
49 | /** | ||
50 | * struct sensor_hub_data - Hold a instance data for a HID hub device | ||
51 | * @hsdev: Stored hid instance for current hub device. | ||
52 | * @mutex: Mutex to serialize synchronous request. | ||
53 | * @lock: Spin lock to protect pending request structure. | ||
54 | * @pending: Holds information of pending sync read request. | ||
55 | * @dyn_callback_list: Holds callback function | ||
56 | * @dyn_callback_lock: spin lock to protect callback list | ||
57 | * @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance. | ||
58 | * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached). | ||
59 | */ | ||
60 | struct sensor_hub_data { | ||
61 | struct hid_sensor_hub_device *hsdev; | ||
62 | struct mutex mutex; | ||
63 | spinlock_t lock; | ||
64 | struct sensor_hub_pending pending; | ||
65 | struct list_head dyn_callback_list; | ||
66 | spinlock_t dyn_callback_lock; | ||
67 | struct mfd_cell *hid_sensor_hub_client_devs; | ||
68 | int hid_sensor_client_cnt; | ||
69 | }; | ||
70 | |||
71 | /** | ||
72 | * struct hid_sensor_hub_callbacks_list - Stores callback list | ||
73 | * @list: list head. | ||
74 | * @usage_id: usage id for a physical device. | ||
75 | * @usage_callback: Stores registered callback functions. | ||
76 | * @priv: Private data for a physical device. | ||
77 | */ | ||
78 | struct hid_sensor_hub_callbacks_list { | ||
79 | struct list_head list; | ||
80 | u32 usage_id; | ||
81 | struct hid_sensor_hub_callbacks *usage_callback; | ||
82 | void *priv; | ||
83 | }; | ||
84 | |||
85 | static int sensor_hub_check_for_sensor_page(struct hid_device *hdev) | ||
86 | { | ||
87 | int i; | ||
88 | int ret = -EINVAL; | ||
89 | |||
90 | for (i = 0; i < hdev->maxcollection; i++) { | ||
91 | struct hid_collection *col = &hdev->collection[i]; | ||
92 | if (col->type == HID_COLLECTION_PHYSICAL && | ||
93 | (col->usage & HID_USAGE_PAGE) == HID_UP_SENSOR) { | ||
94 | ret = 0; | ||
95 | break; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev, | ||
103 | int dir) | ||
104 | { | ||
105 | struct hid_report *report; | ||
106 | |||
107 | list_for_each_entry(report, &hdev->report_enum[dir].report_list, list) { | ||
108 | if (report->id == id) | ||
109 | return report; | ||
110 | } | ||
111 | hid_warn(hdev, "No report with id 0x%x found\n", id); | ||
112 | |||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | static int sensor_hub_get_physical_device_count( | ||
117 | struct hid_report_enum *report_enum) | ||
118 | { | ||
119 | struct hid_report *report; | ||
120 | struct hid_field *field; | ||
121 | int cnt = 0; | ||
122 | |||
123 | list_for_each_entry(report, &report_enum->report_list, list) { | ||
124 | field = report->field[0]; | ||
125 | if (report->maxfield && field && | ||
126 | field->physical) | ||
127 | cnt++; | ||
128 | } | ||
129 | |||
130 | return cnt; | ||
131 | } | ||
132 | |||
133 | static void sensor_hub_fill_attr_info( | ||
134 | struct hid_sensor_hub_attribute_info *info, | ||
135 | s32 index, s32 report_id, s32 units, s32 unit_expo, s32 size) | ||
136 | { | ||
137 | info->index = index; | ||
138 | info->report_id = report_id; | ||
139 | info->units = units; | ||
140 | info->unit_expo = unit_expo; | ||
141 | info->size = size/8; | ||
142 | } | ||
143 | |||
144 | static struct hid_sensor_hub_callbacks *sensor_hub_get_callback( | ||
145 | struct hid_device *hdev, | ||
146 | u32 usage_id, void **priv) | ||
147 | { | ||
148 | struct hid_sensor_hub_callbacks_list *callback; | ||
149 | struct sensor_hub_data *pdata = hid_get_drvdata(hdev); | ||
150 | |||
151 | spin_lock(&pdata->dyn_callback_lock); | ||
152 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) | ||
153 | if (callback->usage_id == usage_id) { | ||
154 | *priv = callback->priv; | ||
155 | spin_unlock(&pdata->dyn_callback_lock); | ||
156 | return callback->usage_callback; | ||
157 | } | ||
158 | spin_unlock(&pdata->dyn_callback_lock); | ||
159 | |||
160 | return NULL; | ||
161 | } | ||
162 | |||
163 | int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, | ||
164 | u32 usage_id, | ||
165 | struct hid_sensor_hub_callbacks *usage_callback) | ||
166 | { | ||
167 | struct hid_sensor_hub_callbacks_list *callback; | ||
168 | struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); | ||
169 | |||
170 | spin_lock(&pdata->dyn_callback_lock); | ||
171 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) | ||
172 | if (callback->usage_id == usage_id) { | ||
173 | spin_unlock(&pdata->dyn_callback_lock); | ||
174 | return -EINVAL; | ||
175 | } | ||
176 | callback = kzalloc(sizeof(*callback), GFP_KERNEL); | ||
177 | if (!callback) { | ||
178 | spin_unlock(&pdata->dyn_callback_lock); | ||
179 | return -ENOMEM; | ||
180 | } | ||
181 | callback->usage_callback = usage_callback; | ||
182 | callback->usage_id = usage_id; | ||
183 | callback->priv = NULL; | ||
184 | list_add_tail(&callback->list, &pdata->dyn_callback_list); | ||
185 | spin_unlock(&pdata->dyn_callback_lock); | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | EXPORT_SYMBOL_GPL(sensor_hub_register_callback); | ||
190 | |||
191 | int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, | ||
192 | u32 usage_id) | ||
193 | { | ||
194 | struct hid_sensor_hub_callbacks_list *callback; | ||
195 | struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); | ||
196 | |||
197 | spin_lock(&pdata->dyn_callback_lock); | ||
198 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) | ||
199 | if (callback->usage_id == usage_id) { | ||
200 | list_del(&callback->list); | ||
201 | kfree(callback); | ||
202 | break; | ||
203 | } | ||
204 | spin_unlock(&pdata->dyn_callback_lock); | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | EXPORT_SYMBOL_GPL(sensor_hub_remove_callback); | ||
209 | |||
210 | int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, | ||
211 | u32 field_index, s32 value) | ||
212 | { | ||
213 | struct hid_report *report; | ||
214 | struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); | ||
215 | int ret = 0; | ||
216 | |||
217 | if (report_id < 0) | ||
218 | return -EINVAL; | ||
219 | |||
220 | mutex_lock(&data->mutex); | ||
221 | report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); | ||
222 | if (!report || (field_index >= report->maxfield)) { | ||
223 | ret = -EINVAL; | ||
224 | goto done_proc; | ||
225 | } | ||
226 | hid_set_field(report->field[field_index], 0, value); | ||
227 | usbhid_submit_report(hsdev->hdev, report, USB_DIR_OUT); | ||
228 | usbhid_wait_io(hsdev->hdev); | ||
229 | |||
230 | done_proc: | ||
231 | mutex_unlock(&data->mutex); | ||
232 | |||
233 | return ret; | ||
234 | } | ||
235 | EXPORT_SYMBOL_GPL(sensor_hub_set_feature); | ||
236 | |||
237 | int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, | ||
238 | u32 field_index, s32 *value) | ||
239 | { | ||
240 | struct hid_report *report; | ||
241 | struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); | ||
242 | int ret = 0; | ||
243 | |||
244 | if (report_id < 0) | ||
245 | return -EINVAL; | ||
246 | |||
247 | mutex_lock(&data->mutex); | ||
248 | report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); | ||
249 | if (!report || (field_index >= report->maxfield)) { | ||
250 | ret = -EINVAL; | ||
251 | goto done_proc; | ||
252 | } | ||
253 | usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); | ||
254 | usbhid_wait_io(hsdev->hdev); | ||
255 | *value = report->field[field_index]->value[0]; | ||
256 | |||
257 | done_proc: | ||
258 | mutex_unlock(&data->mutex); | ||
259 | |||
260 | return ret; | ||
261 | } | ||
262 | EXPORT_SYMBOL_GPL(sensor_hub_get_feature); | ||
263 | |||
264 | |||
265 | int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, | ||
266 | u32 usage_id, | ||
267 | u32 attr_usage_id, u32 report_id) | ||
268 | { | ||
269 | struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); | ||
270 | unsigned long flags; | ||
271 | struct hid_report *report; | ||
272 | int ret_val = 0; | ||
273 | |||
274 | if (report_id < 0) | ||
275 | return -EINVAL; | ||
276 | |||
277 | mutex_lock(&data->mutex); | ||
278 | memset(&data->pending, 0, sizeof(data->pending)); | ||
279 | init_completion(&data->pending.ready); | ||
280 | data->pending.usage_id = usage_id; | ||
281 | data->pending.attr_usage_id = attr_usage_id; | ||
282 | data->pending.raw_size = 0; | ||
283 | |||
284 | spin_lock_irqsave(&data->lock, flags); | ||
285 | data->pending.status = true; | ||
286 | report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); | ||
287 | if (!report) { | ||
288 | spin_unlock_irqrestore(&data->lock, flags); | ||
289 | goto err_free; | ||
290 | } | ||
291 | usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN); | ||
292 | spin_unlock_irqrestore(&data->lock, flags); | ||
293 | wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5); | ||
294 | switch (data->pending.raw_size) { | ||
295 | case 1: | ||
296 | ret_val = *(u8 *)data->pending.raw_data; | ||
297 | break; | ||
298 | case 2: | ||
299 | ret_val = *(u16 *)data->pending.raw_data; | ||
300 | break; | ||
301 | case 4: | ||
302 | ret_val = *(u32 *)data->pending.raw_data; | ||
303 | break; | ||
304 | default: | ||
305 | ret_val = 0; | ||
306 | } | ||
307 | kfree(data->pending.raw_data); | ||
308 | |||
309 | err_free: | ||
310 | data->pending.status = false; | ||
311 | mutex_unlock(&data->mutex); | ||
312 | |||
313 | return ret_val; | ||
314 | } | ||
315 | EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value); | ||
316 | |||
317 | int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, | ||
318 | u8 type, | ||
319 | u32 usage_id, | ||
320 | u32 attr_usage_id, | ||
321 | struct hid_sensor_hub_attribute_info *info) | ||
322 | { | ||
323 | int ret = -1; | ||
324 | int i, j; | ||
325 | int collection_index = -1; | ||
326 | struct hid_report *report; | ||
327 | struct hid_field *field; | ||
328 | struct hid_report_enum *report_enum; | ||
329 | struct hid_device *hdev = hsdev->hdev; | ||
330 | |||
331 | /* Initialize with defaults */ | ||
332 | info->usage_id = usage_id; | ||
333 | info->attrib_id = attr_usage_id; | ||
334 | info->report_id = -1; | ||
335 | info->index = -1; | ||
336 | info->units = -1; | ||
337 | info->unit_expo = -1; | ||
338 | |||
339 | for (i = 0; i < hdev->maxcollection; ++i) { | ||
340 | struct hid_collection *collection = &hdev->collection[i]; | ||
341 | if (usage_id == collection->usage) { | ||
342 | collection_index = i; | ||
343 | break; | ||
344 | } | ||
345 | } | ||
346 | if (collection_index == -1) | ||
347 | goto err_ret; | ||
348 | |||
349 | report_enum = &hdev->report_enum[type]; | ||
350 | list_for_each_entry(report, &report_enum->report_list, list) { | ||
351 | for (i = 0; i < report->maxfield; ++i) { | ||
352 | field = report->field[i]; | ||
353 | if (field->physical == usage_id && | ||
354 | field->logical == attr_usage_id) { | ||
355 | sensor_hub_fill_attr_info(info, i, report->id, | ||
356 | field->unit, field->unit_exponent, | ||
357 | field->report_size); | ||
358 | ret = 0; | ||
359 | } else { | ||
360 | for (j = 0; j < field->maxusage; ++j) { | ||
361 | if (field->usage[j].hid == | ||
362 | attr_usage_id && | ||
363 | field->usage[j].collection_index == | ||
364 | collection_index) { | ||
365 | sensor_hub_fill_attr_info(info, | ||
366 | i, report->id, | ||
367 | field->unit, | ||
368 | field->unit_exponent, | ||
369 | field->report_size); | ||
370 | ret = 0; | ||
371 | break; | ||
372 | } | ||
373 | } | ||
374 | } | ||
375 | if (ret == 0) | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | err_ret: | ||
381 | return ret; | ||
382 | } | ||
383 | EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info); | ||
384 | |||
385 | #ifdef CONFIG_PM | ||
386 | static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message) | ||
387 | { | ||
388 | struct sensor_hub_data *pdata = hid_get_drvdata(hdev); | ||
389 | struct hid_sensor_hub_callbacks_list *callback; | ||
390 | |||
391 | hid_dbg(hdev, " sensor_hub_suspend\n"); | ||
392 | spin_lock(&pdata->dyn_callback_lock); | ||
393 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) { | ||
394 | if (callback->usage_callback->suspend) | ||
395 | callback->usage_callback->suspend( | ||
396 | pdata->hsdev, callback->priv); | ||
397 | } | ||
398 | spin_unlock(&pdata->dyn_callback_lock); | ||
399 | |||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static int sensor_hub_resume(struct hid_device *hdev) | ||
404 | { | ||
405 | struct sensor_hub_data *pdata = hid_get_drvdata(hdev); | ||
406 | struct hid_sensor_hub_callbacks_list *callback; | ||
407 | |||
408 | hid_dbg(hdev, " sensor_hub_resume\n"); | ||
409 | spin_lock(&pdata->dyn_callback_lock); | ||
410 | list_for_each_entry(callback, &pdata->dyn_callback_list, list) { | ||
411 | if (callback->usage_callback->resume) | ||
412 | callback->usage_callback->resume( | ||
413 | pdata->hsdev, callback->priv); | ||
414 | } | ||
415 | spin_unlock(&pdata->dyn_callback_lock); | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static int sensor_hub_reset_resume(struct hid_device *hdev) | ||
421 | { | ||
422 | return 0; | ||
423 | } | ||
424 | #endif | ||
425 | /* | ||
426 | * Handle raw report as sent by device | ||
427 | */ | ||
428 | static int sensor_hub_raw_event(struct hid_device *hdev, | ||
429 | struct hid_report *report, u8 *raw_data, int size) | ||
430 | { | ||
431 | int i; | ||
432 | u8 *ptr; | ||
433 | int sz; | ||
434 | struct sensor_hub_data *pdata = hid_get_drvdata(hdev); | ||
435 | unsigned long flags; | ||
436 | struct hid_sensor_hub_callbacks *callback = NULL; | ||
437 | struct hid_collection *collection = NULL; | ||
438 | void *priv = NULL; | ||
439 | |||
440 | hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n", | ||
441 | report->id, size, report->type); | ||
442 | hid_dbg(hdev, "maxfield:%d\n", report->maxfield); | ||
443 | if (report->type != HID_INPUT_REPORT) | ||
444 | return 1; | ||
445 | |||
446 | ptr = raw_data; | ||
447 | ptr++; /*Skip report id*/ | ||
448 | |||
449 | if (!report) | ||
450 | goto err_report; | ||
451 | |||
452 | spin_lock_irqsave(&pdata->lock, flags); | ||
453 | |||
454 | for (i = 0; i < report->maxfield; ++i) { | ||
455 | |||
456 | hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n", | ||
457 | i, report->field[i]->usage->collection_index, | ||
458 | report->field[i]->usage->hid, | ||
459 | report->field[i]->report_size/8); | ||
460 | |||
461 | sz = report->field[i]->report_size/8; | ||
462 | if (pdata->pending.status && pdata->pending.attr_usage_id == | ||
463 | report->field[i]->usage->hid) { | ||
464 | hid_dbg(hdev, "data was pending ...\n"); | ||
465 | pdata->pending.raw_data = kmalloc(sz, GFP_KERNEL); | ||
466 | if (pdata->pending.raw_data) { | ||
467 | memcpy(pdata->pending.raw_data, ptr, sz); | ||
468 | pdata->pending.raw_size = sz; | ||
469 | } else | ||
470 | pdata->pending.raw_size = 0; | ||
471 | complete(&pdata->pending.ready); | ||
472 | } | ||
473 | collection = &hdev->collection[ | ||
474 | report->field[i]->usage->collection_index]; | ||
475 | hid_dbg(hdev, "collection->usage %x\n", | ||
476 | collection->usage); | ||
477 | callback = sensor_hub_get_callback(pdata->hsdev->hdev, | ||
478 | report->field[i]->physical, | ||
479 | &priv); | ||
480 | if (callback && callback->capture_sample) { | ||
481 | if (report->field[i]->logical) | ||
482 | callback->capture_sample(pdata->hsdev, | ||
483 | report->field[i]->logical, sz, ptr, | ||
484 | callback->pdev); | ||
485 | else | ||
486 | callback->capture_sample(pdata->hsdev, | ||
487 | report->field[i]->usage->hid, sz, ptr, | ||
488 | callback->pdev); | ||
489 | } | ||
490 | ptr += sz; | ||
491 | } | ||
492 | if (callback && collection && callback->send_event) | ||
493 | callback->send_event(pdata->hsdev, collection->usage, | ||
494 | callback->pdev); | ||
495 | spin_unlock_irqrestore(&pdata->lock, flags); | ||
496 | |||
497 | err_report: | ||
498 | return 1; | ||
499 | } | ||
500 | |||
501 | static int sensor_hub_probe(struct hid_device *hdev, | ||
502 | const struct hid_device_id *id) | ||
503 | { | ||
504 | int ret; | ||
505 | struct sensor_hub_data *sd; | ||
506 | int i; | ||
507 | char *name; | ||
508 | struct hid_report *report; | ||
509 | struct hid_report_enum *report_enum; | ||
510 | struct hid_field *field; | ||
511 | int dev_cnt; | ||
512 | |||
513 | sd = kzalloc(sizeof(struct sensor_hub_data), GFP_KERNEL); | ||
514 | if (!sd) { | ||
515 | hid_err(hdev, "cannot allocate Sensor data\n"); | ||
516 | return -ENOMEM; | ||
517 | } | ||
518 | sd->hsdev = kzalloc(sizeof(struct hid_sensor_hub_device), GFP_KERNEL); | ||
519 | if (!sd->hsdev) { | ||
520 | hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); | ||
521 | ret = -ENOMEM; | ||
522 | goto err_free_hub; | ||
523 | } | ||
524 | hid_set_drvdata(hdev, sd); | ||
525 | sd->hsdev->hdev = hdev; | ||
526 | sd->hsdev->vendor_id = hdev->vendor; | ||
527 | sd->hsdev->product_id = hdev->product; | ||
528 | spin_lock_init(&sd->lock); | ||
529 | spin_lock_init(&sd->dyn_callback_lock); | ||
530 | mutex_init(&sd->mutex); | ||
531 | ret = hid_parse(hdev); | ||
532 | if (ret) { | ||
533 | hid_err(hdev, "parse failed\n"); | ||
534 | goto err_free; | ||
535 | } | ||
536 | if (sensor_hub_check_for_sensor_page(hdev) < 0) { | ||
537 | hid_err(hdev, "sensor page not found\n"); | ||
538 | goto err_free; | ||
539 | } | ||
540 | INIT_LIST_HEAD(&hdev->inputs); | ||
541 | |||
542 | hdev->claimed = HID_CLAIMED_INPUT; | ||
543 | ret = hid_hw_start(hdev, 0); | ||
544 | if (ret) { | ||
545 | hid_err(hdev, "hw start failed\n"); | ||
546 | goto err_free; | ||
547 | } | ||
548 | ret = hid_hw_open(hdev); | ||
549 | if (ret) { | ||
550 | hid_err(hdev, "failed to open input interrupt pipe\n"); | ||
551 | goto err_stop_hw; | ||
552 | } | ||
553 | |||
554 | INIT_LIST_HEAD(&sd->dyn_callback_list); | ||
555 | sd->hid_sensor_client_cnt = 0; | ||
556 | report_enum = &hdev->report_enum[HID_INPUT_REPORT]; | ||
557 | |||
558 | dev_cnt = sensor_hub_get_physical_device_count(report_enum); | ||
559 | if (dev_cnt > HID_MAX_PHY_DEVICES) { | ||
560 | hid_err(hdev, "Invalid Physical device count\n"); | ||
561 | ret = -EINVAL; | ||
562 | goto err_close; | ||
563 | } | ||
564 | sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt * | ||
565 | sizeof(struct mfd_cell), | ||
566 | GFP_KERNEL); | ||
567 | if (sd->hid_sensor_hub_client_devs == NULL) { | ||
568 | hid_err(hdev, | ||
569 | "Failed to allocate memory for mfd cells\n"); | ||
570 | ret = -ENOMEM; | ||
571 | goto err_close; | ||
572 | } | ||
573 | list_for_each_entry(report, &report_enum->report_list, list) { | ||
574 | hid_dbg(hdev, "Report id:%x\n", report->id); | ||
575 | field = report->field[0]; | ||
576 | if (report->maxfield && field && | ||
577 | field->physical) { | ||
578 | name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x", | ||
579 | field->physical); | ||
580 | if (name == NULL) { | ||
581 | hid_err(hdev, | ||
582 | "Failed MFD device name\n"); | ||
583 | ret = -ENOMEM; | ||
584 | goto err_free_cells; | ||
585 | } | ||
586 | sd->hid_sensor_hub_client_devs[ | ||
587 | sd->hid_sensor_client_cnt].name = name; | ||
588 | sd->hid_sensor_hub_client_devs[ | ||
589 | sd->hid_sensor_client_cnt].platform_data = | ||
590 | sd->hsdev; | ||
591 | sd->hid_sensor_hub_client_devs[ | ||
592 | sd->hid_sensor_client_cnt].pdata_size = | ||
593 | sizeof(*sd->hsdev); | ||
594 | hid_dbg(hdev, "Adding %s:%p\n", name, sd); | ||
595 | sd->hid_sensor_client_cnt++; | ||
596 | } | ||
597 | } | ||
598 | ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs, | ||
599 | sd->hid_sensor_client_cnt, NULL, 0); | ||
600 | if (ret < 0) | ||
601 | goto err_free_names; | ||
602 | |||
603 | return ret; | ||
604 | |||
605 | err_free_names: | ||
606 | for (i = 0; i < sd->hid_sensor_client_cnt ; ++i) | ||
607 | kfree(sd->hid_sensor_hub_client_devs[i].name); | ||
608 | err_free_cells: | ||
609 | kfree(sd->hid_sensor_hub_client_devs); | ||
610 | err_close: | ||
611 | hid_hw_stop(hdev); | ||
612 | hid_hw_close(hdev); | ||
613 | err_stop_hw: | ||
614 | hid_hw_stop(hdev); | ||
615 | err_free: | ||
616 | kfree(sd->hsdev); | ||
617 | err_free_hub: | ||
618 | kfree(sd); | ||
619 | |||
620 | return ret; | ||
621 | } | ||
622 | |||
623 | static void sensor_hub_remove(struct hid_device *hdev) | ||
624 | { | ||
625 | struct sensor_hub_data *data = hid_get_drvdata(hdev); | ||
626 | unsigned long flags; | ||
627 | int i; | ||
628 | |||
629 | hid_dbg(hdev, " hardware removed\n"); | ||
630 | hdev->claimed &= ~HID_CLAIMED_INPUT; | ||
631 | hid_hw_stop(hdev); | ||
632 | hid_hw_close(hdev); | ||
633 | spin_lock_irqsave(&data->lock, flags); | ||
634 | if (data->pending.status) | ||
635 | complete(&data->pending.ready); | ||
636 | spin_unlock_irqrestore(&data->lock, flags); | ||
637 | mfd_remove_devices(&hdev->dev); | ||
638 | for (i = 0; i < data->hid_sensor_client_cnt ; ++i) | ||
639 | kfree(data->hid_sensor_hub_client_devs[i].name); | ||
640 | kfree(data->hid_sensor_hub_client_devs); | ||
641 | hid_set_drvdata(hdev, NULL); | ||
642 | mutex_destroy(&data->mutex); | ||
643 | kfree(data->hsdev); | ||
644 | kfree(data); | ||
645 | } | ||
646 | |||
647 | static const struct hid_device_id sensor_hub_devices[] = { | ||
648 | { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, | ||
649 | USB_DEVICE_ID_SENSOR_HUB_1020) }, | ||
650 | { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, | ||
651 | USB_DEVICE_ID_SENSOR_HUB_1020) }, | ||
652 | { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, | ||
653 | USB_DEVICE_ID_SENSOR_HUB_09FA) }, | ||
654 | { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, | ||
655 | USB_DEVICE_ID_SENSOR_HUB_09FA) }, | ||
656 | { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, | ||
657 | USB_DEVICE_ID_SENSOR_HUB_7014) }, | ||
658 | { } | ||
659 | }; | ||
660 | MODULE_DEVICE_TABLE(hid, sensor_hub_devices); | ||
661 | |||
662 | static const struct hid_usage_id sensor_hub_grabbed_usages[] = { | ||
663 | { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, | ||
664 | { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 } | ||
665 | }; | ||
666 | |||
667 | static struct hid_driver sensor_hub_driver = { | ||
668 | .name = "hid-sensor-hub", | ||
669 | .id_table = sensor_hub_devices, | ||
670 | .probe = sensor_hub_probe, | ||
671 | .remove = sensor_hub_remove, | ||
672 | .raw_event = sensor_hub_raw_event, | ||
673 | #ifdef CONFIG_PM | ||
674 | .suspend = sensor_hub_suspend, | ||
675 | .resume = sensor_hub_resume, | ||
676 | .reset_resume = sensor_hub_reset_resume, | ||
677 | #endif | ||
678 | }; | ||
679 | |||
680 | static int __init sensor_hub_init(void) | ||
681 | { | ||
682 | return hid_register_driver(&sensor_hub_driver); | ||
683 | } | ||
684 | |||
685 | static void __exit sensor_hub_exit(void) | ||
686 | { | ||
687 | hid_unregister_driver(&sensor_hub_driver); | ||
688 | } | ||
689 | |||
690 | module_init(sensor_hub_init); | ||
691 | module_exit(sensor_hub_exit); | ||
692 | |||
693 | MODULE_DESCRIPTION("HID Sensor Hub driver"); | ||
694 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); | ||
695 | MODULE_LICENSE("GPL"); | ||