blob: e0de92952f32589ef21c08dffc5e5a6d7a1f27e4 [file] [log] [blame]
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +09001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 HIDP implementation for Linux Bluetooth stack (BlueZ).
3 Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation;
8
9 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
12 IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090013 CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090018 ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
19 COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 SOFTWARE IS DISCLAIMED.
21*/
22
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/module.h>
24
25#include <linux/types.h>
26#include <linux/errno.h>
27#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/sched.h>
29#include <linux/slab.h>
30#include <linux/poll.h>
Rafael J. Wysocki83144182007-07-17 04:03:35 -070031#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/fcntl.h>
33#include <linux/skbuff.h>
34#include <linux/socket.h>
35#include <linux/ioctl.h>
36#include <linux/file.h>
37#include <linux/init.h>
38#include <linux/wait.h>
39#include <net/sock.h>
40
41#include <linux/input.h>
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +010042#include <linux/hid.h>
Marcel Holtmann364f6352009-08-22 14:15:53 -070043#include <linux/hidraw.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45#include <net/bluetooth/bluetooth.h>
Marcel Holtmann0a85b962006-07-06 13:09:02 +020046#include <net/bluetooth/hci_core.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#include <net/bluetooth/l2cap.h>
48
49#include "hidp.h"
50
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +010051#define VERSION "1.2"
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53static DECLARE_RWSEM(hidp_session_sem);
54static LIST_HEAD(hidp_session_list);
55
56static unsigned char hidp_keycode[256] = {
57 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
58 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
59 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
60 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
61 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
62 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
63 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
64 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
65 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
66 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
68 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
69 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
71 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
72 150,158,159,128,136,177,178,176,142,152,173,140
73};
74
75static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
76
77static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
78{
79 struct hidp_session *session;
80 struct list_head *p;
81
82 BT_DBG("");
83
84 list_for_each(p, &hidp_session_list) {
85 session = list_entry(p, struct hidp_session, list);
86 if (!bacmp(bdaddr, &session->bdaddr))
87 return session;
88 }
89 return NULL;
90}
91
92static void __hidp_link_session(struct hidp_session *session)
93{
94 __module_get(THIS_MODULE);
95 list_add(&session->list, &hidp_session_list);
Marcel Holtmannedad6382009-08-22 14:22:15 -070096
97 hci_conn_hold_device(session->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098}
99
100static void __hidp_unlink_session(struct hidp_session *session)
101{
Marcel Holtmannedad6382009-08-22 14:22:15 -0700102 hci_conn_put_device(session->conn);
103
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 list_del(&session->list);
105 module_put(THIS_MODULE);
106}
107
108static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
109{
Vasiliy Kulikovd31dbf62010-10-30 18:26:31 +0400110 memset(ci, 0, sizeof(*ci));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 bacpy(&ci->bdaddr, &session->bdaddr);
112
113 ci->flags = session->flags;
114 ci->state = session->state;
115
116 ci->vendor = 0x0000;
117 ci->product = 0x0000;
118 ci->version = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
120 if (session->input) {
121 ci->vendor = session->input->id.vendor;
122 ci->product = session->input->id.product;
123 ci->version = session->input->id.version;
124 if (session->input->name)
125 strncpy(ci->name, session->input->name, 128);
126 else
127 strncpy(ci->name, "HID Boot Device", 128);
128 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100129
130 if (session->hid) {
131 ci->vendor = session->hid->vendor;
132 ci->product = session->hid->product;
133 ci->version = session->hid->version;
134 strncpy(ci->name, session->hid->name, 128);
135 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
Andrew Morton91f5cca2008-02-05 03:07:58 -0800138static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev,
139 unsigned int type, unsigned int code, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 unsigned char newleds;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100142 struct sk_buff *skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100144 BT_DBG("session %p type %d code %d value %d", session, type, code, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
146 if (type != EV_LED)
147 return -1;
148
149 newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
150 (!!test_bit(LED_COMPOSE, dev->led) << 3) |
151 (!!test_bit(LED_SCROLLL, dev->led) << 2) |
152 (!!test_bit(LED_CAPSL, dev->led) << 1) |
153 (!!test_bit(LED_NUML, dev->led));
154
155 if (session->leds == newleds)
156 return 0;
157
158 session->leds = newleds;
159
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200160 skb = alloc_skb(3, GFP_ATOMIC);
161 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 BT_ERR("Can't allocate memory for new frame");
163 return -ENOMEM;
164 }
165
166 *skb_put(skb, 1) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
167 *skb_put(skb, 1) = 0x01;
168 *skb_put(skb, 1) = newleds;
169
170 skb_queue_tail(&session->intr_transmit, skb);
171
172 hidp_schedule(session);
173
174 return 0;
175}
176
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100177static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
178{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200179 struct hid_device *hid = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100180 struct hidp_session *session = hid->driver_data;
181
182 return hidp_queue_event(session, dev, type, code, value);
183}
184
185static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
186{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200187 struct hidp_session *session = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100188
189 return hidp_queue_event(session, dev, type, code, value);
190}
191
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
193{
194 struct input_dev *dev = session->input;
195 unsigned char *keys = session->keys;
196 unsigned char *udata = skb->data + 1;
197 signed char *sdata = skb->data + 1;
198 int i, size = skb->len - 1;
199
200 switch (skb->data[0]) {
201 case 0x01: /* Keyboard report */
202 for (i = 0; i < 8; i++)
203 input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
204
205 /* If all the key codes have been set to 0x01, it means
206 * too many keys were pressed at the same time. */
207 if (!memcmp(udata + 2, hidp_mkeyspat, 6))
208 break;
209
210 for (i = 2; i < 8; i++) {
211 if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
212 if (hidp_keycode[keys[i]])
213 input_report_key(dev, hidp_keycode[keys[i]], 0);
214 else
215 BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
216 }
217
218 if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
219 if (hidp_keycode[udata[i]])
220 input_report_key(dev, hidp_keycode[udata[i]], 1);
221 else
222 BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
223 }
224 }
225
226 memcpy(keys, udata, 8);
227 break;
228
229 case 0x02: /* Mouse report */
230 input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
231 input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
232 input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
233 input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
234 input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
235
236 input_report_rel(dev, REL_X, sdata[1]);
237 input_report_rel(dev, REL_Y, sdata[2]);
238
239 if (size > 3)
240 input_report_rel(dev, REL_WHEEL, sdata[3]);
241 break;
242 }
243
244 input_sync(dev);
245}
246
Bastien Nocera6bf82682010-01-20 12:00:42 +0000247static int __hidp_send_ctrl_message(struct hidp_session *session,
248 unsigned char hdr, unsigned char *data, int size)
249{
250 struct sk_buff *skb;
251
252 BT_DBG("session %p data %p size %d", session, data, size);
253
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200254 skb = alloc_skb(size + 1, GFP_ATOMIC);
255 if (!skb) {
Bastien Nocera6bf82682010-01-20 12:00:42 +0000256 BT_ERR("Can't allocate memory for new frame");
257 return -ENOMEM;
258 }
259
260 *skb_put(skb, 1) = hdr;
261 if (data && size > 0)
262 memcpy(skb_put(skb, size), data, size);
263
264 skb_queue_tail(&session->ctrl_transmit, skb);
265
266 return 0;
267}
268
269static inline int hidp_send_ctrl_message(struct hidp_session *session,
270 unsigned char hdr, unsigned char *data, int size)
271{
272 int err;
273
274 err = __hidp_send_ctrl_message(session, hdr, data, size);
275
276 hidp_schedule(session);
277
278 return err;
279}
280
Andrew Morton91f5cca2008-02-05 03:07:58 -0800281static int hidp_queue_report(struct hidp_session *session,
282 unsigned char *data, int size)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100283{
284 struct sk_buff *skb;
285
Dave Young6792b5e2007-10-20 14:15:39 +0200286 BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100287
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200288 skb = alloc_skb(size + 1, GFP_ATOMIC);
289 if (!skb) {
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100290 BT_ERR("Can't allocate memory for new frame");
291 return -ENOMEM;
292 }
293
294 *skb_put(skb, 1) = 0xa2;
295 if (size > 0)
296 memcpy(skb_put(skb, size), data, size);
297
298 skb_queue_tail(&session->intr_transmit, skb);
299
300 hidp_schedule(session);
301
302 return 0;
303}
304
305static int hidp_send_report(struct hidp_session *session, struct hid_report *report)
306{
307 unsigned char buf[32];
308 int rsize;
309
310 rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
311 if (rsize > sizeof(buf))
312 return -EIO;
313
314 hid_output_report(report, buf);
315
316 return hidp_queue_report(session, buf, rsize);
317}
318
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100319static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
320 unsigned char report_type)
Jiri Kosina2da31932009-11-26 16:20:56 +0100321{
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100322 switch (report_type) {
323 case HID_FEATURE_REPORT:
324 report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
325 break;
326 case HID_OUTPUT_REPORT:
327 report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
328 break;
329 default:
330 return -EINVAL;
331 }
332
333 if (hidp_send_ctrl_message(hid->driver_data, report_type,
Bastien Nocera6bf82682010-01-20 12:00:42 +0000334 data, count))
Jiri Kosina2da31932009-11-26 16:20:56 +0100335 return -ENOMEM;
336 return count;
337}
338
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339static void hidp_idle_timeout(unsigned long arg)
340{
341 struct hidp_session *session = (struct hidp_session *) arg;
342
343 atomic_inc(&session->terminate);
344 hidp_schedule(session);
345}
346
Andrew Morton91f5cca2008-02-05 03:07:58 -0800347static void hidp_set_timer(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348{
349 if (session->idle_to > 0)
350 mod_timer(&session->timer, jiffies + HZ * session->idle_to);
351}
352
353static inline void hidp_del_timer(struct hidp_session *session)
354{
355 if (session->idle_to > 0)
356 del_timer(&session->timer);
357}
358
Andrew Morton91f5cca2008-02-05 03:07:58 -0800359static void hidp_process_handshake(struct hidp_session *session,
360 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361{
362 BT_DBG("session %p param 0x%02x", session, param);
363
364 switch (param) {
365 case HIDP_HSHK_SUCCESSFUL:
366 /* FIXME: Call into SET_ GET_ handlers here */
367 break;
368
369 case HIDP_HSHK_NOT_READY:
370 case HIDP_HSHK_ERR_INVALID_REPORT_ID:
371 case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
372 case HIDP_HSHK_ERR_INVALID_PARAMETER:
373 /* FIXME: Call into SET_ GET_ handlers here */
374 break;
375
376 case HIDP_HSHK_ERR_UNKNOWN:
377 break;
378
379 case HIDP_HSHK_ERR_FATAL:
380 /* Device requests a reboot, as this is the only way this error
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900381 * can be recovered. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 __hidp_send_ctrl_message(session,
383 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0);
384 break;
385
386 default:
387 __hidp_send_ctrl_message(session,
388 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
389 break;
390 }
391}
392
Andrew Morton91f5cca2008-02-05 03:07:58 -0800393static void hidp_process_hid_control(struct hidp_session *session,
394 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395{
396 BT_DBG("session %p param 0x%02x", session, param);
397
Dave Youngeff001e2008-02-05 03:07:14 -0800398 if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 /* Flush the transmit queues */
400 skb_queue_purge(&session->ctrl_transmit);
401 skb_queue_purge(&session->intr_transmit);
402
403 /* Kill session thread */
404 atomic_inc(&session->terminate);
Vikram Kandukuri981b1412009-07-01 11:39:58 +0530405 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 }
407}
408
Andrew Morton91f5cca2008-02-05 03:07:58 -0800409static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
410 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
412 BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param);
413
414 switch (param) {
415 case HIDP_DATA_RTYPE_INPUT:
416 hidp_set_timer(session);
417
418 if (session->input)
419 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100420
421 if (session->hid)
422 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
423
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 break;
425
426 case HIDP_DATA_RTYPE_OTHER:
427 case HIDP_DATA_RTYPE_OUPUT:
428 case HIDP_DATA_RTYPE_FEATURE:
429 break;
430
431 default:
432 __hidp_send_ctrl_message(session,
433 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
434 }
435}
436
Andrew Morton91f5cca2008-02-05 03:07:58 -0800437static void hidp_recv_ctrl_frame(struct hidp_session *session,
438 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439{
440 unsigned char hdr, type, param;
441
442 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
443
444 hdr = skb->data[0];
445 skb_pull(skb, 1);
446
447 type = hdr & HIDP_HEADER_TRANS_MASK;
448 param = hdr & HIDP_HEADER_PARAM_MASK;
449
450 switch (type) {
451 case HIDP_TRANS_HANDSHAKE:
452 hidp_process_handshake(session, param);
453 break;
454
455 case HIDP_TRANS_HID_CONTROL:
456 hidp_process_hid_control(session, param);
457 break;
458
459 case HIDP_TRANS_DATA:
460 hidp_process_data(session, skb, param);
461 break;
462
463 default:
464 __hidp_send_ctrl_message(session,
465 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0);
466 break;
467 }
468
469 kfree_skb(skb);
470}
471
Andrew Morton91f5cca2008-02-05 03:07:58 -0800472static void hidp_recv_intr_frame(struct hidp_session *session,
473 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474{
475 unsigned char hdr;
476
477 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
478
479 hdr = skb->data[0];
480 skb_pull(skb, 1);
481
482 if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
483 hidp_set_timer(session);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100484
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 if (session->input)
486 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100487
488 if (session->hid) {
489 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1);
490 BT_DBG("report len %d", skb->len);
491 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 } else {
493 BT_DBG("Unsupported protocol header 0x%02x", hdr);
494 }
495
496 kfree_skb(skb);
497}
498
499static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
500{
501 struct kvec iv = { data, len };
502 struct msghdr msg;
503
504 BT_DBG("sock %p data %p len %d", sock, data, len);
505
506 if (!len)
507 return 0;
508
509 memset(&msg, 0, sizeof(msg));
510
511 return kernel_sendmsg(sock, &msg, &iv, 1, len);
512}
513
David S. Millerb03efcf2005-07-08 14:57:23 -0700514static void hidp_process_transmit(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515{
516 struct sk_buff *skb;
517
518 BT_DBG("session %p", session);
519
520 while ((skb = skb_dequeue(&session->ctrl_transmit))) {
521 if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
522 skb_queue_head(&session->ctrl_transmit, skb);
523 break;
524 }
525
526 hidp_set_timer(session);
527 kfree_skb(skb);
528 }
529
530 while ((skb = skb_dequeue(&session->intr_transmit))) {
531 if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
532 skb_queue_head(&session->intr_transmit, skb);
533 break;
534 }
535
536 hidp_set_timer(session);
537 kfree_skb(skb);
538 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539}
540
541static int hidp_session(void *arg)
542{
543 struct hidp_session *session = arg;
544 struct sock *ctrl_sk = session->ctrl_sock->sk;
545 struct sock *intr_sk = session->intr_sock->sk;
546 struct sk_buff *skb;
547 int vendor = 0x0000, product = 0x0000;
548 wait_queue_t ctrl_wait, intr_wait;
549
550 BT_DBG("session %p", session);
551
552 if (session->input) {
553 vendor = session->input->id.vendor;
554 product = session->input->id.product;
555 }
556
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100557 if (session->hid) {
558 vendor = session->hid->vendor;
559 product = session->hid->product;
560 }
561
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 daemonize("khidpd_%04x%04x", vendor, product);
563 set_user_nice(current, -15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
565 init_waitqueue_entry(&ctrl_wait, current);
566 init_waitqueue_entry(&intr_wait, current);
Eric Dumazetaa395142010-04-20 13:03:51 +0000567 add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
568 add_wait_queue(sk_sleep(intr_sk), &intr_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 while (!atomic_read(&session->terminate)) {
570 set_current_state(TASK_INTERRUPTIBLE);
571
572 if (ctrl_sk->sk_state != BT_CONNECTED || intr_sk->sk_state != BT_CONNECTED)
573 break;
574
575 while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
576 skb_orphan(skb);
577 hidp_recv_ctrl_frame(session, skb);
578 }
579
580 while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
581 skb_orphan(skb);
582 hidp_recv_intr_frame(session, skb);
583 }
584
585 hidp_process_transmit(session);
586
587 schedule();
588 }
589 set_current_state(TASK_RUNNING);
Eric Dumazetaa395142010-04-20 13:03:51 +0000590 remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
591 remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
593 down_write(&hidp_session_sem);
594
595 hidp_del_timer(session);
596
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 if (session->input) {
598 input_unregister_device(session->input);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500599 session->input = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 }
601
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100602 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200603 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700604 session->hid = NULL;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100605 }
606
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200607 /* Wakeup user-space polling for socket errors */
608 session->intr_sock->sk->sk_err = EUNATCH;
609 session->ctrl_sock->sk->sk_err = EUNATCH;
610
611 hidp_schedule(session);
612
David Woodhouse1c398582007-07-07 14:58:39 -0400613 fput(session->intr_sock->file);
614
Eric Dumazetaa395142010-04-20 13:03:51 +0000615 wait_event_timeout(*(sk_sleep(ctrl_sk)),
David Woodhouse1c398582007-07-07 14:58:39 -0400616 (ctrl_sk->sk_state == BT_CLOSED), msecs_to_jiffies(500));
617
618 fput(session->ctrl_sock->file);
619
620 __hidp_unlink_session(session);
621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 up_write(&hidp_session_sem);
623
624 kfree(session);
625 return 0;
626}
627
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200628static struct device *hidp_get_device(struct hidp_session *session)
629{
630 bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
631 bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700632 struct device *device = NULL;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200633 struct hci_dev *hdev;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200634
635 hdev = hci_get_route(dst, src);
636 if (!hdev)
637 return NULL;
638
Marcel Holtmannedad6382009-08-22 14:22:15 -0700639 session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
640 if (session->conn)
641 device = &session->conn->dev;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200642
643 hci_dev_put(hdev);
644
Marcel Holtmannedad6382009-08-22 14:22:15 -0700645 return device;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200646}
647
Andrew Morton91f5cca2008-02-05 03:07:58 -0800648static int hidp_setup_input(struct hidp_session *session,
649 struct hidp_connadd_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650{
Jiri Slabyc500c972008-05-16 11:49:16 +0200651 struct input_dev *input;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700652 int err, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
Jiri Slabyc500c972008-05-16 11:49:16 +0200654 input = input_allocate_device();
655 if (!input)
656 return -ENOMEM;
657
658 session->input = input;
659
Marcel Holtmann5be39462007-05-09 09:15:30 +0200660 input_set_drvdata(input, session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500662 input->name = "Bluetooth HID Boot Protocol Device";
663
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 input->id.bustype = BUS_BLUETOOTH;
665 input->id.vendor = req->vendor;
666 input->id.product = req->product;
667 input->id.version = req->version;
668
669 if (req->subclass & 0x40) {
670 set_bit(EV_KEY, input->evbit);
671 set_bit(EV_LED, input->evbit);
672 set_bit(EV_REP, input->evbit);
673
674 set_bit(LED_NUML, input->ledbit);
675 set_bit(LED_CAPSL, input->ledbit);
676 set_bit(LED_SCROLLL, input->ledbit);
677 set_bit(LED_COMPOSE, input->ledbit);
678 set_bit(LED_KANA, input->ledbit);
679
680 for (i = 0; i < sizeof(hidp_keycode); i++)
681 set_bit(hidp_keycode[i], input->keybit);
682 clear_bit(0, input->keybit);
683 }
684
685 if (req->subclass & 0x80) {
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700686 input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
687 input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
688 BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
689 input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
690 input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
691 BIT_MASK(BTN_EXTRA);
692 input->relbit[0] |= BIT_MASK(REL_WHEEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 }
694
Marcel Holtmann5be39462007-05-09 09:15:30 +0200695 input->dev.parent = hidp_get_device(session);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200696
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 input->event = hidp_input_event;
698
Marcel Holtmannedad6382009-08-22 14:22:15 -0700699 err = input_register_device(input);
700 if (err < 0) {
701 hci_conn_put_device(session->conn);
702 return err;
703 }
704
705 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706}
707
Marcel Holtmannf5ffd462007-02-17 23:58:53 +0100708static int hidp_open(struct hid_device *hid)
709{
710 return 0;
711}
712
713static void hidp_close(struct hid_device *hid)
714{
715}
716
Jiri Slabyc500c972008-05-16 11:49:16 +0200717static int hidp_parse(struct hid_device *hid)
718{
719 struct hidp_session *session = hid->driver_data;
Jiri Slabyc500c972008-05-16 11:49:16 +0200720
Michael Poole15c697c2010-02-05 12:23:43 -0500721 return hid_parse_report(session->hid, session->rd_data,
722 session->rd_size);
Jiri Slabyc500c972008-05-16 11:49:16 +0200723}
724
725static int hidp_start(struct hid_device *hid)
726{
727 struct hidp_session *session = hid->driver_data;
728 struct hid_report *report;
729
730 list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
731 report_list, list)
732 hidp_send_report(session, report);
733
734 list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
735 report_list, list)
736 hidp_send_report(session, report);
737
Jiri Slabyc500c972008-05-16 11:49:16 +0200738 return 0;
739}
740
741static void hidp_stop(struct hid_device *hid)
742{
743 struct hidp_session *session = hid->driver_data;
744
745 skb_queue_purge(&session->ctrl_transmit);
746 skb_queue_purge(&session->intr_transmit);
747
Jiri Slabyc500c972008-05-16 11:49:16 +0200748 hid->claimed = 0;
749}
750
751static struct hid_ll_driver hidp_hid_driver = {
752 .parse = hidp_parse,
753 .start = hidp_start,
754 .stop = hidp_stop,
755 .open = hidp_open,
756 .close = hidp_close,
757 .hidinput_input_event = hidp_hidinput_event,
758};
759
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200760static int hidp_setup_hid(struct hidp_session *session,
Andrew Morton91f5cca2008-02-05 03:07:58 -0800761 struct hidp_connadd_req *req)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100762{
Jiri Slabyc500c972008-05-16 11:49:16 +0200763 struct hid_device *hid;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700764 int err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100765
Michael Poole15c697c2010-02-05 12:23:43 -0500766 session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
767 if (!session->rd_data)
768 return -ENOMEM;
769
770 if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
771 err = -EFAULT;
772 goto fault;
773 }
774 session->rd_size = req->rd_size;
775
Jiri Slabyc500c972008-05-16 11:49:16 +0200776 hid = hid_allocate_device();
Michael Poole15c697c2010-02-05 12:23:43 -0500777 if (IS_ERR(hid)) {
778 err = PTR_ERR(hid);
779 goto fault;
780 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100781
Jiri Slabyc500c972008-05-16 11:49:16 +0200782 session->hid = hid;
Michael Poole15c697c2010-02-05 12:23:43 -0500783
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100784 hid->driver_data = session;
785
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100786 hid->bus = BUS_BLUETOOTH;
787 hid->vendor = req->vendor;
788 hid->product = req->product;
789 hid->version = req->version;
Jiri Slabyc500c972008-05-16 11:49:16 +0200790 hid->country = req->country;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100791
792 strncpy(hid->name, req->name, 128);
Gustavo F. Padovand6b2eb22010-09-03 18:29:46 -0300793 strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
794 strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100795
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200796 hid->dev.parent = hidp_get_device(session);
Jiri Slabyc500c972008-05-16 11:49:16 +0200797 hid->ll_driver = &hidp_hid_driver;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200798
Jiri Kosina2da31932009-11-26 16:20:56 +0100799 hid->hid_output_raw_report = hidp_output_raw_report;
800
Marcel Holtmannedad6382009-08-22 14:22:15 -0700801 err = hid_add_device(hid);
802 if (err < 0)
803 goto failed;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200804
Jiri Slabyc500c972008-05-16 11:49:16 +0200805 return 0;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700806
807failed:
Jiri Slabyc500c972008-05-16 11:49:16 +0200808 hid_destroy_device(hid);
809 session->hid = NULL;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700810
Michael Poole15c697c2010-02-05 12:23:43 -0500811fault:
812 kfree(session->rd_data);
813 session->rd_data = NULL;
814
Marcel Holtmannedad6382009-08-22 14:22:15 -0700815 return err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100816}
817
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
819{
820 struct hidp_session *session, *s;
821 int err;
822
823 BT_DBG("");
824
825 if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) ||
826 bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
827 return -ENOTUNIQ;
828
Marcel Holtmann25ea6db2006-07-06 15:40:09 +0200829 session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500830 if (!session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100833 BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
834
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 down_write(&hidp_session_sem);
836
837 s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
838 if (s && s->state == BT_CONNECTED) {
839 err = -EEXIST;
840 goto failed;
841 }
842
843 bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
844
845 session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
846 session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
847
848 BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
849
850 session->ctrl_sock = ctrl_sock;
851 session->intr_sock = intr_sock;
852 session->state = BT_CONNECTED;
853
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800854 setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
856 skb_queue_head_init(&session->ctrl_transmit);
857 skb_queue_head_init(&session->intr_transmit);
858
859 session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
860 session->idle_to = req->idle_to;
861
Jiri Slabyc500c972008-05-16 11:49:16 +0200862 if (req->rd_size > 0) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200863 err = hidp_setup_hid(session, req);
Jiri Slabyd458a9d2008-05-16 11:49:20 +0200864 if (err && err != -ENODEV)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700865 goto purge;
Jiri Slabyc500c972008-05-16 11:49:16 +0200866 }
867
868 if (!session->hid) {
869 err = hidp_setup_input(session, req);
870 if (err < 0)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700871 goto purge;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200872 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100873
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 __hidp_link_session(session);
875
876 hidp_set_timer(session);
877
878 err = kernel_thread(hidp_session, session, CLONE_KERNEL);
879 if (err < 0)
880 goto unlink;
881
882 if (session->input) {
883 hidp_send_ctrl_message(session,
884 HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0);
885 session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
886
887 session->leds = 0xff;
888 hidp_input_event(session->input, EV_LED, 0, 0);
889 }
890
891 up_write(&hidp_session_sem);
892 return 0;
893
894unlink:
895 hidp_del_timer(session);
896
897 __hidp_unlink_session(session);
898
Marcel Holtmannedad6382009-08-22 14:22:15 -0700899 if (session->input) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 input_unregister_device(session->input);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700901 session->input = NULL;
902 }
903
904 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200905 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700906 session->hid = NULL;
907 }
908
Michael Poole15c697c2010-02-05 12:23:43 -0500909 kfree(session->rd_data);
910 session->rd_data = NULL;
911
Marcel Holtmannedad6382009-08-22 14:22:15 -0700912purge:
Jiri Slabyc500c972008-05-16 11:49:16 +0200913 skb_queue_purge(&session->ctrl_transmit);
914 skb_queue_purge(&session->intr_transmit);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700915
Jiri Slabyc500c972008-05-16 11:49:16 +0200916failed:
917 up_write(&hidp_session_sem);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100918
Marcel Holtmann5be39462007-05-09 09:15:30 +0200919 input_free_device(session->input);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 kfree(session);
921 return err;
922}
923
924int hidp_del_connection(struct hidp_conndel_req *req)
925{
926 struct hidp_session *session;
927 int err = 0;
928
929 BT_DBG("");
930
931 down_read(&hidp_session_sem);
932
933 session = __hidp_get_session(&req->bdaddr);
934 if (session) {
935 if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
936 hidp_send_ctrl_message(session,
937 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0);
938 } else {
939 /* Flush the transmit queues */
940 skb_queue_purge(&session->ctrl_transmit);
941 skb_queue_purge(&session->intr_transmit);
942
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200943 /* Wakeup user-space polling for socket errors */
944 session->intr_sock->sk->sk_err = EUNATCH;
945 session->ctrl_sock->sk->sk_err = EUNATCH;
946
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 /* Kill session thread */
948 atomic_inc(&session->terminate);
949 hidp_schedule(session);
950 }
951 } else
952 err = -ENOENT;
953
954 up_read(&hidp_session_sem);
955 return err;
956}
957
958int hidp_get_connlist(struct hidp_connlist_req *req)
959{
960 struct list_head *p;
961 int err = 0, n = 0;
962
963 BT_DBG("");
964
965 down_read(&hidp_session_sem);
966
967 list_for_each(p, &hidp_session_list) {
968 struct hidp_session *session;
969 struct hidp_conninfo ci;
970
971 session = list_entry(p, struct hidp_session, list);
972
973 __hidp_copy_session(session, &ci);
974
975 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
976 err = -EFAULT;
977 break;
978 }
979
980 if (++n >= req->cnum)
981 break;
982
983 req->ci++;
984 }
985 req->cnum = n;
986
987 up_read(&hidp_session_sem);
988 return err;
989}
990
991int hidp_get_conninfo(struct hidp_conninfo *ci)
992{
993 struct hidp_session *session;
994 int err = 0;
995
996 down_read(&hidp_session_sem);
997
998 session = __hidp_get_session(&ci->bdaddr);
999 if (session)
1000 __hidp_copy_session(session, ci);
1001 else
1002 err = -ENOENT;
1003
1004 up_read(&hidp_session_sem);
1005 return err;
1006}
1007
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001008static const struct hid_device_id hidp_table[] = {
1009 { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
1010 { }
1011};
1012
1013static struct hid_driver hidp_driver = {
1014 .name = "generic-bluetooth",
1015 .id_table = hidp_table,
1016};
1017
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018static int __init hidp_init(void)
1019{
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001020 int ret;
1021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 l2cap_load();
1023
1024 BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
1025
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001026 ret = hid_register_driver(&hidp_driver);
1027 if (ret)
1028 goto err;
1029
1030 ret = hidp_init_sockets();
1031 if (ret)
1032 goto err_drv;
1033
1034 return 0;
1035err_drv:
1036 hid_unregister_driver(&hidp_driver);
1037err:
1038 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039}
1040
1041static void __exit hidp_exit(void)
1042{
1043 hidp_cleanup_sockets();
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001044 hid_unregister_driver(&hidp_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045}
1046
1047module_init(hidp_init);
1048module_exit(hidp_exit);
1049
1050MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
1051MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
1052MODULE_VERSION(VERSION);
1053MODULE_LICENSE("GPL");
1054MODULE_ALIAS("bt-proto-6");