blob: 67cc4bc82c6878d888307b3466c530b3dc6428d9 [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
160 if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
161 BT_ERR("Can't allocate memory for new frame");
162 return -ENOMEM;
163 }
164
165 *skb_put(skb, 1) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
166 *skb_put(skb, 1) = 0x01;
167 *skb_put(skb, 1) = newleds;
168
169 skb_queue_tail(&session->intr_transmit, skb);
170
171 hidp_schedule(session);
172
173 return 0;
174}
175
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100176static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
177{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200178 struct hid_device *hid = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100179 struct hidp_session *session = hid->driver_data;
180
181 return hidp_queue_event(session, dev, type, code, value);
182}
183
184static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
185{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200186 struct hidp_session *session = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100187
188 return hidp_queue_event(session, dev, type, code, value);
189}
190
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
192{
193 struct input_dev *dev = session->input;
194 unsigned char *keys = session->keys;
195 unsigned char *udata = skb->data + 1;
196 signed char *sdata = skb->data + 1;
197 int i, size = skb->len - 1;
198
199 switch (skb->data[0]) {
200 case 0x01: /* Keyboard report */
201 for (i = 0; i < 8; i++)
202 input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
203
204 /* If all the key codes have been set to 0x01, it means
205 * too many keys were pressed at the same time. */
206 if (!memcmp(udata + 2, hidp_mkeyspat, 6))
207 break;
208
209 for (i = 2; i < 8; i++) {
210 if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
211 if (hidp_keycode[keys[i]])
212 input_report_key(dev, hidp_keycode[keys[i]], 0);
213 else
214 BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
215 }
216
217 if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
218 if (hidp_keycode[udata[i]])
219 input_report_key(dev, hidp_keycode[udata[i]], 1);
220 else
221 BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
222 }
223 }
224
225 memcpy(keys, udata, 8);
226 break;
227
228 case 0x02: /* Mouse report */
229 input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
230 input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
231 input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
232 input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
233 input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
234
235 input_report_rel(dev, REL_X, sdata[1]);
236 input_report_rel(dev, REL_Y, sdata[2]);
237
238 if (size > 3)
239 input_report_rel(dev, REL_WHEEL, sdata[3]);
240 break;
241 }
242
243 input_sync(dev);
244}
245
Bastien Nocera6bf82682010-01-20 12:00:42 +0000246static int __hidp_send_ctrl_message(struct hidp_session *session,
247 unsigned char hdr, unsigned char *data, int size)
248{
249 struct sk_buff *skb;
250
251 BT_DBG("session %p data %p size %d", session, data, size);
252
253 if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) {
254 BT_ERR("Can't allocate memory for new frame");
255 return -ENOMEM;
256 }
257
258 *skb_put(skb, 1) = hdr;
259 if (data && size > 0)
260 memcpy(skb_put(skb, size), data, size);
261
262 skb_queue_tail(&session->ctrl_transmit, skb);
263
264 return 0;
265}
266
267static inline int hidp_send_ctrl_message(struct hidp_session *session,
268 unsigned char hdr, unsigned char *data, int size)
269{
270 int err;
271
272 err = __hidp_send_ctrl_message(session, hdr, data, size);
273
274 hidp_schedule(session);
275
276 return err;
277}
278
Andrew Morton91f5cca2008-02-05 03:07:58 -0800279static int hidp_queue_report(struct hidp_session *session,
280 unsigned char *data, int size)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100281{
282 struct sk_buff *skb;
283
Dave Young6792b5e2007-10-20 14:15:39 +0200284 BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100285
286 if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) {
287 BT_ERR("Can't allocate memory for new frame");
288 return -ENOMEM;
289 }
290
291 *skb_put(skb, 1) = 0xa2;
292 if (size > 0)
293 memcpy(skb_put(skb, size), data, size);
294
295 skb_queue_tail(&session->intr_transmit, skb);
296
297 hidp_schedule(session);
298
299 return 0;
300}
301
302static int hidp_send_report(struct hidp_session *session, struct hid_report *report)
303{
304 unsigned char buf[32];
305 int rsize;
306
307 rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
308 if (rsize > sizeof(buf))
309 return -EIO;
310
311 hid_output_report(report, buf);
312
313 return hidp_queue_report(session, buf, rsize);
314}
315
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100316static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
317 unsigned char report_type)
Jiri Kosina2da31932009-11-26 16:20:56 +0100318{
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100319 switch (report_type) {
320 case HID_FEATURE_REPORT:
321 report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
322 break;
323 case HID_OUTPUT_REPORT:
324 report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
325 break;
326 default:
327 return -EINVAL;
328 }
329
330 if (hidp_send_ctrl_message(hid->driver_data, report_type,
Bastien Nocera6bf82682010-01-20 12:00:42 +0000331 data, count))
Jiri Kosina2da31932009-11-26 16:20:56 +0100332 return -ENOMEM;
333 return count;
334}
335
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336static void hidp_idle_timeout(unsigned long arg)
337{
338 struct hidp_session *session = (struct hidp_session *) arg;
339
340 atomic_inc(&session->terminate);
341 hidp_schedule(session);
342}
343
Andrew Morton91f5cca2008-02-05 03:07:58 -0800344static void hidp_set_timer(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345{
346 if (session->idle_to > 0)
347 mod_timer(&session->timer, jiffies + HZ * session->idle_to);
348}
349
350static inline void hidp_del_timer(struct hidp_session *session)
351{
352 if (session->idle_to > 0)
353 del_timer(&session->timer);
354}
355
Andrew Morton91f5cca2008-02-05 03:07:58 -0800356static void hidp_process_handshake(struct hidp_session *session,
357 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
359 BT_DBG("session %p param 0x%02x", session, param);
360
361 switch (param) {
362 case HIDP_HSHK_SUCCESSFUL:
363 /* FIXME: Call into SET_ GET_ handlers here */
364 break;
365
366 case HIDP_HSHK_NOT_READY:
367 case HIDP_HSHK_ERR_INVALID_REPORT_ID:
368 case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
369 case HIDP_HSHK_ERR_INVALID_PARAMETER:
370 /* FIXME: Call into SET_ GET_ handlers here */
371 break;
372
373 case HIDP_HSHK_ERR_UNKNOWN:
374 break;
375
376 case HIDP_HSHK_ERR_FATAL:
377 /* Device requests a reboot, as this is the only way this error
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900378 * can be recovered. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 __hidp_send_ctrl_message(session,
380 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0);
381 break;
382
383 default:
384 __hidp_send_ctrl_message(session,
385 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
386 break;
387 }
388}
389
Andrew Morton91f5cca2008-02-05 03:07:58 -0800390static void hidp_process_hid_control(struct hidp_session *session,
391 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392{
393 BT_DBG("session %p param 0x%02x", session, param);
394
Dave Youngeff001e2008-02-05 03:07:14 -0800395 if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 /* Flush the transmit queues */
397 skb_queue_purge(&session->ctrl_transmit);
398 skb_queue_purge(&session->intr_transmit);
399
400 /* Kill session thread */
401 atomic_inc(&session->terminate);
Vikram Kandukuri981b1412009-07-01 11:39:58 +0530402 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 }
404}
405
Andrew Morton91f5cca2008-02-05 03:07:58 -0800406static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
407 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
409 BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param);
410
411 switch (param) {
412 case HIDP_DATA_RTYPE_INPUT:
413 hidp_set_timer(session);
414
415 if (session->input)
416 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100417
418 if (session->hid)
419 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
420
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 break;
422
423 case HIDP_DATA_RTYPE_OTHER:
424 case HIDP_DATA_RTYPE_OUPUT:
425 case HIDP_DATA_RTYPE_FEATURE:
426 break;
427
428 default:
429 __hidp_send_ctrl_message(session,
430 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
431 }
432}
433
Andrew Morton91f5cca2008-02-05 03:07:58 -0800434static void hidp_recv_ctrl_frame(struct hidp_session *session,
435 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436{
437 unsigned char hdr, type, param;
438
439 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
440
441 hdr = skb->data[0];
442 skb_pull(skb, 1);
443
444 type = hdr & HIDP_HEADER_TRANS_MASK;
445 param = hdr & HIDP_HEADER_PARAM_MASK;
446
447 switch (type) {
448 case HIDP_TRANS_HANDSHAKE:
449 hidp_process_handshake(session, param);
450 break;
451
452 case HIDP_TRANS_HID_CONTROL:
453 hidp_process_hid_control(session, param);
454 break;
455
456 case HIDP_TRANS_DATA:
457 hidp_process_data(session, skb, param);
458 break;
459
460 default:
461 __hidp_send_ctrl_message(session,
462 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0);
463 break;
464 }
465
466 kfree_skb(skb);
467}
468
Andrew Morton91f5cca2008-02-05 03:07:58 -0800469static void hidp_recv_intr_frame(struct hidp_session *session,
470 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471{
472 unsigned char hdr;
473
474 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
475
476 hdr = skb->data[0];
477 skb_pull(skb, 1);
478
479 if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
480 hidp_set_timer(session);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 if (session->input)
483 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100484
485 if (session->hid) {
486 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1);
487 BT_DBG("report len %d", skb->len);
488 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 } else {
490 BT_DBG("Unsupported protocol header 0x%02x", hdr);
491 }
492
493 kfree_skb(skb);
494}
495
496static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
497{
498 struct kvec iv = { data, len };
499 struct msghdr msg;
500
501 BT_DBG("sock %p data %p len %d", sock, data, len);
502
503 if (!len)
504 return 0;
505
506 memset(&msg, 0, sizeof(msg));
507
508 return kernel_sendmsg(sock, &msg, &iv, 1, len);
509}
510
David S. Millerb03efcf2005-07-08 14:57:23 -0700511static void hidp_process_transmit(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512{
513 struct sk_buff *skb;
514
515 BT_DBG("session %p", session);
516
517 while ((skb = skb_dequeue(&session->ctrl_transmit))) {
518 if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
519 skb_queue_head(&session->ctrl_transmit, skb);
520 break;
521 }
522
523 hidp_set_timer(session);
524 kfree_skb(skb);
525 }
526
527 while ((skb = skb_dequeue(&session->intr_transmit))) {
528 if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
529 skb_queue_head(&session->intr_transmit, skb);
530 break;
531 }
532
533 hidp_set_timer(session);
534 kfree_skb(skb);
535 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536}
537
538static int hidp_session(void *arg)
539{
540 struct hidp_session *session = arg;
541 struct sock *ctrl_sk = session->ctrl_sock->sk;
542 struct sock *intr_sk = session->intr_sock->sk;
543 struct sk_buff *skb;
544 int vendor = 0x0000, product = 0x0000;
545 wait_queue_t ctrl_wait, intr_wait;
546
547 BT_DBG("session %p", session);
548
549 if (session->input) {
550 vendor = session->input->id.vendor;
551 product = session->input->id.product;
552 }
553
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100554 if (session->hid) {
555 vendor = session->hid->vendor;
556 product = session->hid->product;
557 }
558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 daemonize("khidpd_%04x%04x", vendor, product);
560 set_user_nice(current, -15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
562 init_waitqueue_entry(&ctrl_wait, current);
563 init_waitqueue_entry(&intr_wait, current);
Eric Dumazetaa395142010-04-20 13:03:51 +0000564 add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
565 add_wait_queue(sk_sleep(intr_sk), &intr_wait);
Alan Ott0f69dca2011-01-18 03:04:37 -0500566 session->waiting_for_startup = 0;
567 wake_up_interruptible(&session->startup_queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 while (!atomic_read(&session->terminate)) {
569 set_current_state(TASK_INTERRUPTIBLE);
570
571 if (ctrl_sk->sk_state != BT_CONNECTED || intr_sk->sk_state != BT_CONNECTED)
572 break;
573
574 while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
575 skb_orphan(skb);
576 hidp_recv_ctrl_frame(session, skb);
577 }
578
579 while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
580 skb_orphan(skb);
581 hidp_recv_intr_frame(session, skb);
582 }
583
584 hidp_process_transmit(session);
585
586 schedule();
587 }
588 set_current_state(TASK_RUNNING);
Eric Dumazetaa395142010-04-20 13:03:51 +0000589 remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
590 remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
592 down_write(&hidp_session_sem);
593
594 hidp_del_timer(session);
595
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 if (session->input) {
597 input_unregister_device(session->input);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500598 session->input = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 }
600
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100601 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200602 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700603 session->hid = NULL;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100604 }
605
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200606 /* Wakeup user-space polling for socket errors */
607 session->intr_sock->sk->sk_err = EUNATCH;
608 session->ctrl_sock->sk->sk_err = EUNATCH;
609
610 hidp_schedule(session);
611
David Woodhouse1c398582007-07-07 14:58:39 -0400612 fput(session->intr_sock->file);
613
Eric Dumazetaa395142010-04-20 13:03:51 +0000614 wait_event_timeout(*(sk_sleep(ctrl_sk)),
David Woodhouse1c398582007-07-07 14:58:39 -0400615 (ctrl_sk->sk_state == BT_CLOSED), msecs_to_jiffies(500));
616
617 fput(session->ctrl_sock->file);
618
619 __hidp_unlink_session(session);
620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 up_write(&hidp_session_sem);
622
623 kfree(session);
624 return 0;
625}
626
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200627static struct device *hidp_get_device(struct hidp_session *session)
628{
629 bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
630 bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700631 struct device *device = NULL;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200632 struct hci_dev *hdev;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200633
634 hdev = hci_get_route(dst, src);
635 if (!hdev)
636 return NULL;
637
Marcel Holtmannedad6382009-08-22 14:22:15 -0700638 session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
639 if (session->conn)
640 device = &session->conn->dev;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200641
642 hci_dev_put(hdev);
643
Marcel Holtmannedad6382009-08-22 14:22:15 -0700644 return device;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200645}
646
Andrew Morton91f5cca2008-02-05 03:07:58 -0800647static int hidp_setup_input(struct hidp_session *session,
648 struct hidp_connadd_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649{
Jiri Slabyc500c972008-05-16 11:49:16 +0200650 struct input_dev *input;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700651 int err, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Jiri Slabyc500c972008-05-16 11:49:16 +0200653 input = input_allocate_device();
654 if (!input)
655 return -ENOMEM;
656
657 session->input = input;
658
Marcel Holtmann5be39462007-05-09 09:15:30 +0200659 input_set_drvdata(input, session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500661 input->name = "Bluetooth HID Boot Protocol Device";
662
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 input->id.bustype = BUS_BLUETOOTH;
664 input->id.vendor = req->vendor;
665 input->id.product = req->product;
666 input->id.version = req->version;
667
668 if (req->subclass & 0x40) {
669 set_bit(EV_KEY, input->evbit);
670 set_bit(EV_LED, input->evbit);
671 set_bit(EV_REP, input->evbit);
672
673 set_bit(LED_NUML, input->ledbit);
674 set_bit(LED_CAPSL, input->ledbit);
675 set_bit(LED_SCROLLL, input->ledbit);
676 set_bit(LED_COMPOSE, input->ledbit);
677 set_bit(LED_KANA, input->ledbit);
678
679 for (i = 0; i < sizeof(hidp_keycode); i++)
680 set_bit(hidp_keycode[i], input->keybit);
681 clear_bit(0, input->keybit);
682 }
683
684 if (req->subclass & 0x80) {
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700685 input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
686 input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
687 BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
688 input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
689 input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
690 BIT_MASK(BTN_EXTRA);
691 input->relbit[0] |= BIT_MASK(REL_WHEEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 }
693
Marcel Holtmann5be39462007-05-09 09:15:30 +0200694 input->dev.parent = hidp_get_device(session);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 input->event = hidp_input_event;
697
Marcel Holtmannedad6382009-08-22 14:22:15 -0700698 err = input_register_device(input);
699 if (err < 0) {
700 hci_conn_put_device(session->conn);
701 return err;
702 }
703
704 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705}
706
Marcel Holtmannf5ffd462007-02-17 23:58:53 +0100707static int hidp_open(struct hid_device *hid)
708{
709 return 0;
710}
711
712static void hidp_close(struct hid_device *hid)
713{
714}
715
Jiri Slabyc500c972008-05-16 11:49:16 +0200716static int hidp_parse(struct hid_device *hid)
717{
718 struct hidp_session *session = hid->driver_data;
Jiri Slabyc500c972008-05-16 11:49:16 +0200719
Michael Poole15c697c2010-02-05 12:23:43 -0500720 return hid_parse_report(session->hid, session->rd_data,
721 session->rd_size);
Jiri Slabyc500c972008-05-16 11:49:16 +0200722}
723
724static int hidp_start(struct hid_device *hid)
725{
726 struct hidp_session *session = hid->driver_data;
727 struct hid_report *report;
728
729 list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
730 report_list, list)
731 hidp_send_report(session, report);
732
733 list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
734 report_list, list)
735 hidp_send_report(session, report);
736
Jiri Slabyc500c972008-05-16 11:49:16 +0200737 return 0;
738}
739
740static void hidp_stop(struct hid_device *hid)
741{
742 struct hidp_session *session = hid->driver_data;
743
744 skb_queue_purge(&session->ctrl_transmit);
745 skb_queue_purge(&session->intr_transmit);
746
Jiri Slabyc500c972008-05-16 11:49:16 +0200747 hid->claimed = 0;
748}
749
750static struct hid_ll_driver hidp_hid_driver = {
751 .parse = hidp_parse,
752 .start = hidp_start,
753 .stop = hidp_stop,
754 .open = hidp_open,
755 .close = hidp_close,
756 .hidinput_input_event = hidp_hidinput_event,
757};
758
Alan Ott0f69dca2011-01-18 03:04:37 -0500759/* This function sets up the hid device. It does not add it
760 to the HID system. That is done in hidp_add_connection(). */
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200761static int hidp_setup_hid(struct hidp_session *session,
Andrew Morton91f5cca2008-02-05 03:07:58 -0800762 struct hidp_connadd_req *req)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100763{
Jiri Slabyc500c972008-05-16 11:49:16 +0200764 struct hid_device *hid;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700765 int err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100766
Michael Poole15c697c2010-02-05 12:23:43 -0500767 session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
768 if (!session->rd_data)
769 return -ENOMEM;
770
771 if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
772 err = -EFAULT;
773 goto fault;
774 }
775 session->rd_size = req->rd_size;
776
Jiri Slabyc500c972008-05-16 11:49:16 +0200777 hid = hid_allocate_device();
Michael Poole15c697c2010-02-05 12:23:43 -0500778 if (IS_ERR(hid)) {
779 err = PTR_ERR(hid);
780 goto fault;
781 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100782
Jiri Slabyc500c972008-05-16 11:49:16 +0200783 session->hid = hid;
Michael Poole15c697c2010-02-05 12:23:43 -0500784
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100785 hid->driver_data = session;
786
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100787 hid->bus = BUS_BLUETOOTH;
788 hid->vendor = req->vendor;
789 hid->product = req->product;
790 hid->version = req->version;
Jiri Slabyc500c972008-05-16 11:49:16 +0200791 hid->country = req->country;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100792
793 strncpy(hid->name, req->name, 128);
Gustavo F. Padovand6b2eb22010-09-03 18:29:46 -0300794 strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
795 strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100796
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200797 hid->dev.parent = hidp_get_device(session);
Jiri Slabyc500c972008-05-16 11:49:16 +0200798 hid->ll_driver = &hidp_hid_driver;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200799
Jiri Kosina2da31932009-11-26 16:20:56 +0100800 hid->hid_output_raw_report = hidp_output_raw_report;
801
Jiri Slabyc500c972008-05-16 11:49:16 +0200802 return 0;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700803
Michael Poole15c697c2010-02-05 12:23:43 -0500804fault:
805 kfree(session->rd_data);
806 session->rd_data = NULL;
807
Marcel Holtmannedad6382009-08-22 14:22:15 -0700808 return err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100809}
810
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
812{
813 struct hidp_session *session, *s;
814 int err;
815
816 BT_DBG("");
817
818 if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) ||
819 bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
820 return -ENOTUNIQ;
821
Marcel Holtmann25ea6db2006-07-06 15:40:09 +0200822 session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500823 if (!session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100826 BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
827
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 down_write(&hidp_session_sem);
829
830 s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
831 if (s && s->state == BT_CONNECTED) {
832 err = -EEXIST;
833 goto failed;
834 }
835
836 bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
837
838 session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
839 session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
840
841 BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
842
843 session->ctrl_sock = ctrl_sock;
844 session->intr_sock = intr_sock;
845 session->state = BT_CONNECTED;
846
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800847 setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848
849 skb_queue_head_init(&session->ctrl_transmit);
850 skb_queue_head_init(&session->intr_transmit);
851
Alan Ott0f69dca2011-01-18 03:04:37 -0500852 init_waitqueue_head(&session->startup_queue);
853 session->waiting_for_startup = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
855 session->idle_to = req->idle_to;
856
Jiri Slabyc500c972008-05-16 11:49:16 +0200857 if (req->rd_size > 0) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200858 err = hidp_setup_hid(session, req);
Jiri Slabyd458a9d2008-05-16 11:49:20 +0200859 if (err && err != -ENODEV)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700860 goto purge;
Jiri Slabyc500c972008-05-16 11:49:16 +0200861 }
862
863 if (!session->hid) {
864 err = hidp_setup_input(session, req);
865 if (err < 0)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700866 goto purge;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200867 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100868
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 __hidp_link_session(session);
870
871 hidp_set_timer(session);
872
873 err = kernel_thread(hidp_session, session, CLONE_KERNEL);
874 if (err < 0)
875 goto unlink;
Alan Ott0f69dca2011-01-18 03:04:37 -0500876 while (session->waiting_for_startup) {
877 wait_event_interruptible(session->startup_queue,
878 !session->waiting_for_startup);
879 }
880
881 err = hid_add_device(session->hid);
882 if (err < 0)
883 goto err_add_device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
885 if (session->input) {
886 hidp_send_ctrl_message(session,
887 HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0);
888 session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
889
890 session->leds = 0xff;
891 hidp_input_event(session->input, EV_LED, 0, 0);
892 }
893
894 up_write(&hidp_session_sem);
895 return 0;
896
Alan Ott0f69dca2011-01-18 03:04:37 -0500897err_add_device:
898 hid_destroy_device(session->hid);
899 session->hid = NULL;
900 atomic_inc(&session->terminate);
901 hidp_schedule(session);
902
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903unlink:
904 hidp_del_timer(session);
905
906 __hidp_unlink_session(session);
907
Marcel Holtmannedad6382009-08-22 14:22:15 -0700908 if (session->input) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 input_unregister_device(session->input);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700910 session->input = NULL;
911 }
912
913 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200914 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700915 session->hid = NULL;
916 }
917
Michael Poole15c697c2010-02-05 12:23:43 -0500918 kfree(session->rd_data);
919 session->rd_data = NULL;
920
Marcel Holtmannedad6382009-08-22 14:22:15 -0700921purge:
Jiri Slabyc500c972008-05-16 11:49:16 +0200922 skb_queue_purge(&session->ctrl_transmit);
923 skb_queue_purge(&session->intr_transmit);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700924
Jiri Slabyc500c972008-05-16 11:49:16 +0200925failed:
926 up_write(&hidp_session_sem);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100927
Marcel Holtmann5be39462007-05-09 09:15:30 +0200928 input_free_device(session->input);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 kfree(session);
930 return err;
931}
932
933int hidp_del_connection(struct hidp_conndel_req *req)
934{
935 struct hidp_session *session;
936 int err = 0;
937
938 BT_DBG("");
939
940 down_read(&hidp_session_sem);
941
942 session = __hidp_get_session(&req->bdaddr);
943 if (session) {
944 if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
945 hidp_send_ctrl_message(session,
946 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0);
947 } else {
948 /* Flush the transmit queues */
949 skb_queue_purge(&session->ctrl_transmit);
950 skb_queue_purge(&session->intr_transmit);
951
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200952 /* Wakeup user-space polling for socket errors */
953 session->intr_sock->sk->sk_err = EUNATCH;
954 session->ctrl_sock->sk->sk_err = EUNATCH;
955
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 /* Kill session thread */
957 atomic_inc(&session->terminate);
958 hidp_schedule(session);
959 }
960 } else
961 err = -ENOENT;
962
963 up_read(&hidp_session_sem);
964 return err;
965}
966
967int hidp_get_connlist(struct hidp_connlist_req *req)
968{
969 struct list_head *p;
970 int err = 0, n = 0;
971
972 BT_DBG("");
973
974 down_read(&hidp_session_sem);
975
976 list_for_each(p, &hidp_session_list) {
977 struct hidp_session *session;
978 struct hidp_conninfo ci;
979
980 session = list_entry(p, struct hidp_session, list);
981
982 __hidp_copy_session(session, &ci);
983
984 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
985 err = -EFAULT;
986 break;
987 }
988
989 if (++n >= req->cnum)
990 break;
991
992 req->ci++;
993 }
994 req->cnum = n;
995
996 up_read(&hidp_session_sem);
997 return err;
998}
999
1000int hidp_get_conninfo(struct hidp_conninfo *ci)
1001{
1002 struct hidp_session *session;
1003 int err = 0;
1004
1005 down_read(&hidp_session_sem);
1006
1007 session = __hidp_get_session(&ci->bdaddr);
1008 if (session)
1009 __hidp_copy_session(session, ci);
1010 else
1011 err = -ENOENT;
1012
1013 up_read(&hidp_session_sem);
1014 return err;
1015}
1016
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001017static const struct hid_device_id hidp_table[] = {
1018 { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
1019 { }
1020};
1021
1022static struct hid_driver hidp_driver = {
1023 .name = "generic-bluetooth",
1024 .id_table = hidp_table,
1025};
1026
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027static int __init hidp_init(void)
1028{
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001029 int ret;
1030
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 l2cap_load();
1032
1033 BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
1034
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001035 ret = hid_register_driver(&hidp_driver);
1036 if (ret)
1037 goto err;
1038
1039 ret = hidp_init_sockets();
1040 if (ret)
1041 goto err_drv;
1042
1043 return 0;
1044err_drv:
1045 hid_unregister_driver(&hidp_driver);
1046err:
1047 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048}
1049
1050static void __exit hidp_exit(void)
1051{
1052 hidp_cleanup_sockets();
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001053 hid_unregister_driver(&hidp_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054}
1055
1056module_init(hidp_init);
1057module_exit(hidp_exit);
1058
1059MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
1060MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
1061MODULE_VERSION(VERSION);
1062MODULE_LICENSE("GPL");
1063MODULE_ALIAS("bt-proto-6");