blob: a91c97c4c021e84355d49d90aca1114fd96c06b0 [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>
Steve Mucklef132c6c2012-06-06 18:30:57 -070024#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
26#include <linux/types.h>
27#include <linux/errno.h>
28#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <linux/sched.h>
30#include <linux/slab.h>
31#include <linux/poll.h>
Rafael J. Wysocki83144182007-07-17 04:03:35 -070032#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/fcntl.h>
34#include <linux/skbuff.h>
35#include <linux/socket.h>
36#include <linux/ioctl.h>
37#include <linux/file.h>
38#include <linux/init.h>
39#include <linux/wait.h>
40#include <net/sock.h>
41
42#include <linux/input.h>
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +010043#include <linux/hid.h>
Marcel Holtmann364f6352009-08-22 14:15:53 -070044#include <linux/hidraw.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
46#include <net/bluetooth/bluetooth.h>
Marcel Holtmann0a85b962006-07-06 13:09:02 +020047#include <net/bluetooth/hci_core.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <net/bluetooth/l2cap.h>
49
50#include "hidp.h"
51
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +010052#define VERSION "1.2"
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54static DECLARE_RWSEM(hidp_session_sem);
55static LIST_HEAD(hidp_session_list);
56
57static unsigned char hidp_keycode[256] = {
Szymon Janc17f09a72011-03-21 14:20:01 +010058 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36,
59 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45,
60 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1,
61 14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52,
62 53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
63 99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69,
64 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73,
65 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190,
66 191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135,
67 136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94,
68 95, 0, 0, 0, 122, 123, 90, 91, 85, 0, 0, 0, 0, 0,
69 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,
71 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74 29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115,
75 114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140
Linus Torvalds1da177e2005-04-16 15:20:36 -070076};
77
78static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
79
80static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
81{
82 struct hidp_session *session;
83 struct list_head *p;
84
85 BT_DBG("");
86
87 list_for_each(p, &hidp_session_list) {
88 session = list_entry(p, struct hidp_session, list);
89 if (!bacmp(bdaddr, &session->bdaddr))
90 return session;
91 }
92 return NULL;
93}
94
95static void __hidp_link_session(struct hidp_session *session)
96{
97 __module_get(THIS_MODULE);
98 list_add(&session->list, &hidp_session_list);
Marcel Holtmannedad6382009-08-22 14:22:15 -070099
100 hci_conn_hold_device(session->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101}
102
103static void __hidp_unlink_session(struct hidp_session *session)
104{
Marcel Holtmannedad6382009-08-22 14:22:15 -0700105 hci_conn_put_device(session->conn);
106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 list_del(&session->list);
108 module_put(THIS_MODULE);
109}
110
111static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
112{
Vasiliy Kulikovd31dbf62010-10-30 18:26:31 +0400113 memset(ci, 0, sizeof(*ci));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 bacpy(&ci->bdaddr, &session->bdaddr);
115
116 ci->flags = session->flags;
117 ci->state = session->state;
118
119 ci->vendor = 0x0000;
120 ci->product = 0x0000;
121 ci->version = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122
123 if (session->input) {
124 ci->vendor = session->input->id.vendor;
125 ci->product = session->input->id.product;
126 ci->version = session->input->id.version;
127 if (session->input->name)
128 strncpy(ci->name, session->input->name, 128);
129 else
130 strncpy(ci->name, "HID Boot Device", 128);
131 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100132
133 if (session->hid) {
134 ci->vendor = session->hid->vendor;
135 ci->product = session->hid->product;
136 ci->version = session->hid->version;
137 strncpy(ci->name, session->hid->name, 128);
138 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139}
140
Andrew Morton91f5cca2008-02-05 03:07:58 -0800141static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev,
142 unsigned int type, unsigned int code, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 unsigned char newleds;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100145 struct sk_buff *skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100147 BT_DBG("session %p type %d code %d value %d", session, type, code, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
149 if (type != EV_LED)
150 return -1;
151
152 newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
153 (!!test_bit(LED_COMPOSE, dev->led) << 3) |
154 (!!test_bit(LED_SCROLLL, dev->led) << 2) |
155 (!!test_bit(LED_CAPSL, dev->led) << 1) |
156 (!!test_bit(LED_NUML, dev->led));
157
158 if (session->leds == newleds)
159 return 0;
160
161 session->leds = newleds;
162
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200163 skb = alloc_skb(3, GFP_ATOMIC);
164 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 BT_ERR("Can't allocate memory for new frame");
166 return -ENOMEM;
167 }
168
169 *skb_put(skb, 1) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
170 *skb_put(skb, 1) = 0x01;
171 *skb_put(skb, 1) = newleds;
172
173 skb_queue_tail(&session->intr_transmit, skb);
174
175 hidp_schedule(session);
176
177 return 0;
178}
179
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100180static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
181{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200182 struct hid_device *hid = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100183 struct hidp_session *session = hid->driver_data;
184
185 return hidp_queue_event(session, dev, type, code, value);
186}
187
188static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
189{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200190 struct hidp_session *session = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100191
192 return hidp_queue_event(session, dev, type, code, value);
193}
194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
196{
197 struct input_dev *dev = session->input;
198 unsigned char *keys = session->keys;
199 unsigned char *udata = skb->data + 1;
200 signed char *sdata = skb->data + 1;
201 int i, size = skb->len - 1;
202
203 switch (skb->data[0]) {
204 case 0x01: /* Keyboard report */
205 for (i = 0; i < 8; i++)
206 input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
207
208 /* If all the key codes have been set to 0x01, it means
209 * too many keys were pressed at the same time. */
210 if (!memcmp(udata + 2, hidp_mkeyspat, 6))
211 break;
212
213 for (i = 2; i < 8; i++) {
214 if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
215 if (hidp_keycode[keys[i]])
216 input_report_key(dev, hidp_keycode[keys[i]], 0);
217 else
218 BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
219 }
220
221 if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
222 if (hidp_keycode[udata[i]])
223 input_report_key(dev, hidp_keycode[udata[i]], 1);
224 else
225 BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
226 }
227 }
228
229 memcpy(keys, udata, 8);
230 break;
231
232 case 0x02: /* Mouse report */
233 input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
234 input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
235 input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
236 input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
237 input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
238
239 input_report_rel(dev, REL_X, sdata[1]);
240 input_report_rel(dev, REL_Y, sdata[2]);
241
242 if (size > 3)
243 input_report_rel(dev, REL_WHEEL, sdata[3]);
244 break;
245 }
246
247 input_sync(dev);
248}
249
Bastien Nocera6bf82682010-01-20 12:00:42 +0000250static int __hidp_send_ctrl_message(struct hidp_session *session,
251 unsigned char hdr, unsigned char *data, int size)
252{
253 struct sk_buff *skb;
254
255 BT_DBG("session %p data %p size %d", session, data, size);
256
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200257 skb = alloc_skb(size + 1, GFP_ATOMIC);
258 if (!skb) {
Bastien Nocera6bf82682010-01-20 12:00:42 +0000259 BT_ERR("Can't allocate memory for new frame");
260 return -ENOMEM;
261 }
262
263 *skb_put(skb, 1) = hdr;
264 if (data && size > 0)
265 memcpy(skb_put(skb, size), data, size);
266
267 skb_queue_tail(&session->ctrl_transmit, skb);
268
269 return 0;
270}
271
272static inline int hidp_send_ctrl_message(struct hidp_session *session,
273 unsigned char hdr, unsigned char *data, int size)
274{
275 int err;
276
277 err = __hidp_send_ctrl_message(session, hdr, data, size);
278
279 hidp_schedule(session);
280
281 return err;
282}
283
Andrew Morton91f5cca2008-02-05 03:07:58 -0800284static int hidp_queue_report(struct hidp_session *session,
285 unsigned char *data, int size)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100286{
287 struct sk_buff *skb;
288
Dave Young6792b5e2007-10-20 14:15:39 +0200289 BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100290
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200291 skb = alloc_skb(size + 1, GFP_ATOMIC);
292 if (!skb) {
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100293 BT_ERR("Can't allocate memory for new frame");
294 return -ENOMEM;
295 }
296
297 *skb_put(skb, 1) = 0xa2;
298 if (size > 0)
299 memcpy(skb_put(skb, size), data, size);
300
301 skb_queue_tail(&session->intr_transmit, skb);
302
303 hidp_schedule(session);
304
305 return 0;
306}
307
308static int hidp_send_report(struct hidp_session *session, struct hid_report *report)
309{
310 unsigned char buf[32];
311 int rsize;
312
313 rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
314 if (rsize > sizeof(buf))
315 return -EIO;
316
317 hid_output_report(report, buf);
318
319 return hidp_queue_report(session, buf, rsize);
320}
321
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100322static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
323 unsigned char report_type)
Jiri Kosina2da31932009-11-26 16:20:56 +0100324{
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100325 switch (report_type) {
326 case HID_FEATURE_REPORT:
327 report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
328 break;
329 case HID_OUTPUT_REPORT:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330 report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100331 break;
332 default:
333 return -EINVAL;
334 }
335
336 if (hidp_send_ctrl_message(hid->driver_data, report_type,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700337 data, count))
338 return -ENOMEM;
339 return count;
Jiri Kosina2da31932009-11-26 16:20:56 +0100340}
341
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342static void hidp_idle_timeout(unsigned long arg)
343{
344 struct hidp_session *session = (struct hidp_session *) arg;
345
Peter Hurley7bb59df2011-06-30 13:53:53 -0400346 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700347 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348}
349
Andrew Morton91f5cca2008-02-05 03:07:58 -0800350static void hidp_set_timer(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
352 if (session->idle_to > 0)
353 mod_timer(&session->timer, jiffies + HZ * session->idle_to);
354}
355
356static inline void hidp_del_timer(struct hidp_session *session)
357{
358 if (session->idle_to > 0)
359 del_timer(&session->timer);
360}
361
Andrew Morton91f5cca2008-02-05 03:07:58 -0800362static void hidp_process_handshake(struct hidp_session *session,
363 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
365 BT_DBG("session %p param 0x%02x", session, param);
366
367 switch (param) {
368 case HIDP_HSHK_SUCCESSFUL:
369 /* FIXME: Call into SET_ GET_ handlers here */
370 break;
371
372 case HIDP_HSHK_NOT_READY:
373 case HIDP_HSHK_ERR_INVALID_REPORT_ID:
374 case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
375 case HIDP_HSHK_ERR_INVALID_PARAMETER:
376 /* FIXME: Call into SET_ GET_ handlers here */
377 break;
378
379 case HIDP_HSHK_ERR_UNKNOWN:
380 break;
381
382 case HIDP_HSHK_ERR_FATAL:
383 /* Device requests a reboot, as this is the only way this error
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900384 * can be recovered. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 __hidp_send_ctrl_message(session,
386 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0);
387 break;
388
389 default:
390 __hidp_send_ctrl_message(session,
391 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
392 break;
393 }
394}
395
Andrew Morton91f5cca2008-02-05 03:07:58 -0800396static void hidp_process_hid_control(struct hidp_session *session,
397 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398{
399 BT_DBG("session %p param 0x%02x", session, param);
400
Dave Youngeff001e2008-02-05 03:07:14 -0800401 if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 /* Flush the transmit queues */
403 skb_queue_purge(&session->ctrl_transmit);
404 skb_queue_purge(&session->intr_transmit);
405
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 /* Kill session thread */
Peter Hurley7bb59df2011-06-30 13:53:53 -0400407 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700408 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 }
410}
411
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
Andrew Morton91f5cca2008-02-05 03:07:58 -0800413 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
415 BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param);
416
417 switch (param) {
418 case HIDP_DATA_RTYPE_INPUT:
419 hidp_set_timer(session);
420
421 if (session->input)
422 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100423
424 if (session->hid)
425 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 break;
428
429 case HIDP_DATA_RTYPE_OTHER:
430 case HIDP_DATA_RTYPE_OUPUT:
431 case HIDP_DATA_RTYPE_FEATURE:
432 break;
433
434 default:
435 __hidp_send_ctrl_message(session,
436 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
437 }
438}
439
Andrew Morton91f5cca2008-02-05 03:07:58 -0800440static void hidp_recv_ctrl_frame(struct hidp_session *session,
441 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442{
443 unsigned char hdr, type, param;
444
445 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
446
447 hdr = skb->data[0];
448 skb_pull(skb, 1);
449
450 type = hdr & HIDP_HEADER_TRANS_MASK;
451 param = hdr & HIDP_HEADER_PARAM_MASK;
452
453 switch (type) {
454 case HIDP_TRANS_HANDSHAKE:
455 hidp_process_handshake(session, param);
456 break;
457
458 case HIDP_TRANS_HID_CONTROL:
459 hidp_process_hid_control(session, param);
460 break;
461
462 case HIDP_TRANS_DATA:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463 hidp_process_data(session, skb, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 break;
465
466 default:
467 __hidp_send_ctrl_message(session,
468 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0);
469 break;
470 }
471
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473}
474
Andrew Morton91f5cca2008-02-05 03:07:58 -0800475static void hidp_recv_intr_frame(struct hidp_session *session,
476 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
478 unsigned char hdr;
479
480 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
481
482 hdr = skb->data[0];
483 skb_pull(skb, 1);
484
485 if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
486 hidp_set_timer(session);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100487
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 if (session->input)
489 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100490
491 if (session->hid) {
492 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1);
493 BT_DBG("report len %d", skb->len);
494 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 } else {
496 BT_DBG("Unsupported protocol header 0x%02x", hdr);
497 }
498
499 kfree_skb(skb);
500}
501
502static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
503{
504 struct kvec iv = { data, len };
505 struct msghdr msg;
506
507 BT_DBG("sock %p data %p len %d", sock, data, len);
508
509 if (!len)
510 return 0;
511
512 memset(&msg, 0, sizeof(msg));
513
514 return kernel_sendmsg(sock, &msg, &iv, 1, len);
515}
516
David S. Millerb03efcf2005-07-08 14:57:23 -0700517static void hidp_process_transmit(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518{
519 struct sk_buff *skb;
520
521 BT_DBG("session %p", session);
522
523 while ((skb = skb_dequeue(&session->ctrl_transmit))) {
524 if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
525 skb_queue_head(&session->ctrl_transmit, skb);
526 break;
527 }
528
529 hidp_set_timer(session);
530 kfree_skb(skb);
531 }
532
533 while ((skb = skb_dequeue(&session->intr_transmit))) {
534 if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
535 skb_queue_head(&session->intr_transmit, skb);
536 break;
537 }
538
539 hidp_set_timer(session);
540 kfree_skb(skb);
541 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542}
543
544static int hidp_session(void *arg)
545{
546 struct hidp_session *session = arg;
547 struct sock *ctrl_sk = session->ctrl_sock->sk;
548 struct sock *intr_sk = session->intr_sock->sk;
549 struct sk_buff *skb;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550 int vendor = 0x0000, product = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 wait_queue_t ctrl_wait, intr_wait;
552
553 BT_DBG("session %p", session);
554
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555 if (session->input) {
556 vendor = session->input->id.vendor;
557 product = session->input->id.product;
558 }
559
560 if (session->hid) {
561 vendor = session->hid->vendor;
562 product = session->hid->product;
563 }
564
565 daemonize("khidpd_%04x%04x", vendor, product);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 set_user_nice(current, -15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
568 init_waitqueue_entry(&ctrl_wait, current);
569 init_waitqueue_entry(&intr_wait, current);
Eric Dumazetaa395142010-04-20 13:03:51 +0000570 add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
571 add_wait_queue(sk_sleep(intr_sk), &intr_wait);
Peter Hurley7bb59df2011-06-30 13:53:53 -0400572 while (!atomic_read(&session->terminate)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700573 set_current_state(TASK_INTERRUPTIBLE);
574
Szymon Janc17f09a72011-03-21 14:20:01 +0100575 if (ctrl_sk->sk_state != BT_CONNECTED ||
576 intr_sk->sk_state != BT_CONNECTED)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 break;
578
Gustavo F. Padovandc0da5c2011-10-06 18:02:13 -0300579 while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
580 skb_orphan(skb);
581 if (!skb_linearize(skb))
582 hidp_recv_ctrl_frame(session, skb);
583 else
584 kfree_skb(skb);
585 }
586
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
588 skb_orphan(skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589 if (!skb_linearize(skb))
590 hidp_recv_intr_frame(session, skb);
591 else
592 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 }
594
595 hidp_process_transmit(session);
596
597 schedule();
598 }
599 set_current_state(TASK_RUNNING);
Eric Dumazetaa395142010-04-20 13:03:51 +0000600 remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
601 remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
603 down_write(&hidp_session_sem);
604
605 hidp_del_timer(session);
606
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 if (session->input) {
608 input_unregister_device(session->input);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500609 session->input = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 }
611
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100612 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200613 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700614 session->hid = NULL;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100615 }
616
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200617 /* Wakeup user-space polling for socket errors */
618 session->intr_sock->sk->sk_err = EUNATCH;
619 session->ctrl_sock->sk->sk_err = EUNATCH;
620
621 hidp_schedule(session);
622
David Woodhouse1c398582007-07-07 14:58:39 -0400623 fput(session->intr_sock->file);
624
Eric Dumazetaa395142010-04-20 13:03:51 +0000625 wait_event_timeout(*(sk_sleep(ctrl_sk)),
David Woodhouse1c398582007-07-07 14:58:39 -0400626 (ctrl_sk->sk_state == BT_CLOSED), msecs_to_jiffies(500));
627
628 fput(session->ctrl_sock->file);
629
630 __hidp_unlink_session(session);
631
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 up_write(&hidp_session_sem);
633
634 kfree(session);
635 return 0;
636}
637
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200638static struct device *hidp_get_device(struct hidp_session *session)
639{
640 bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
641 bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700642 struct device *device = NULL;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200643 struct hci_dev *hdev;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200644
645 hdev = hci_get_route(dst, src);
646 if (!hdev)
647 return NULL;
648
Marcel Holtmannedad6382009-08-22 14:22:15 -0700649 session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
650 if (session->conn)
651 device = &session->conn->dev;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200652
653 hci_dev_put(hdev);
654
Marcel Holtmannedad6382009-08-22 14:22:15 -0700655 return device;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200656}
657
Andrew Morton91f5cca2008-02-05 03:07:58 -0800658static int hidp_setup_input(struct hidp_session *session,
659 struct hidp_connadd_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
Jiri Slabyc500c972008-05-16 11:49:16 +0200661 struct input_dev *input;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700662 int err, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Jiri Slabyc500c972008-05-16 11:49:16 +0200664 input = input_allocate_device();
665 if (!input)
666 return -ENOMEM;
667
668 session->input = input;
669
Marcel Holtmann5be39462007-05-09 09:15:30 +0200670 input_set_drvdata(input, session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500672 input->name = "Bluetooth HID Boot Protocol Device";
673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 input->id.bustype = BUS_BLUETOOTH;
675 input->id.vendor = req->vendor;
676 input->id.product = req->product;
677 input->id.version = req->version;
678
679 if (req->subclass & 0x40) {
680 set_bit(EV_KEY, input->evbit);
681 set_bit(EV_LED, input->evbit);
682 set_bit(EV_REP, input->evbit);
683
684 set_bit(LED_NUML, input->ledbit);
685 set_bit(LED_CAPSL, input->ledbit);
686 set_bit(LED_SCROLLL, input->ledbit);
687 set_bit(LED_COMPOSE, input->ledbit);
688 set_bit(LED_KANA, input->ledbit);
689
690 for (i = 0; i < sizeof(hidp_keycode); i++)
691 set_bit(hidp_keycode[i], input->keybit);
692 clear_bit(0, input->keybit);
693 }
694
695 if (req->subclass & 0x80) {
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700696 input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
697 input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
698 BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
699 input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
700 input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
701 BIT_MASK(BTN_EXTRA);
702 input->relbit[0] |= BIT_MASK(REL_WHEEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 }
704
Marcel Holtmann5be39462007-05-09 09:15:30 +0200705 input->dev.parent = hidp_get_device(session);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200706
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 input->event = hidp_input_event;
708
Marcel Holtmannedad6382009-08-22 14:22:15 -0700709 err = input_register_device(input);
710 if (err < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700711 hci_conn_put_device(session->conn);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700712 return err;
713 }
714
715 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716}
717
Marcel Holtmannf5ffd462007-02-17 23:58:53 +0100718static int hidp_open(struct hid_device *hid)
719{
720 return 0;
721}
722
723static void hidp_close(struct hid_device *hid)
724{
725}
726
Jiri Slabyc500c972008-05-16 11:49:16 +0200727static int hidp_parse(struct hid_device *hid)
728{
729 struct hidp_session *session = hid->driver_data;
Jiri Slabyc500c972008-05-16 11:49:16 +0200730
Michael Poole15c697c2010-02-05 12:23:43 -0500731 return hid_parse_report(session->hid, session->rd_data,
732 session->rd_size);
Jiri Slabyc500c972008-05-16 11:49:16 +0200733}
734
735static int hidp_start(struct hid_device *hid)
736{
737 struct hidp_session *session = hid->driver_data;
738 struct hid_report *report;
739
740 list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
741 report_list, list)
742 hidp_send_report(session, report);
743
744 list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
745 report_list, list)
746 hidp_send_report(session, report);
747
Jiri Slabyc500c972008-05-16 11:49:16 +0200748 return 0;
749}
750
751static void hidp_stop(struct hid_device *hid)
752{
753 struct hidp_session *session = hid->driver_data;
754
755 skb_queue_purge(&session->ctrl_transmit);
756 skb_queue_purge(&session->intr_transmit);
757
Jiri Slabyc500c972008-05-16 11:49:16 +0200758 hid->claimed = 0;
759}
760
761static struct hid_ll_driver hidp_hid_driver = {
762 .parse = hidp_parse,
763 .start = hidp_start,
764 .stop = hidp_stop,
765 .open = hidp_open,
766 .close = hidp_close,
767 .hidinput_input_event = hidp_hidinput_event,
768};
769
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200770static int hidp_setup_hid(struct hidp_session *session,
Andrew Morton91f5cca2008-02-05 03:07:58 -0800771 struct hidp_connadd_req *req)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100772{
Jiri Slabyc500c972008-05-16 11:49:16 +0200773 struct hid_device *hid;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700774 int err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100775
Michael Poole15c697c2010-02-05 12:23:43 -0500776 session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
777 if (!session->rd_data)
778 return -ENOMEM;
779
780 if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
781 err = -EFAULT;
782 goto fault;
783 }
784 session->rd_size = req->rd_size;
785
Jiri Slabyc500c972008-05-16 11:49:16 +0200786 hid = hid_allocate_device();
Michael Poole15c697c2010-02-05 12:23:43 -0500787 if (IS_ERR(hid)) {
788 err = PTR_ERR(hid);
789 goto fault;
790 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100791
Jiri Slabyc500c972008-05-16 11:49:16 +0200792 session->hid = hid;
Michael Poole15c697c2010-02-05 12:23:43 -0500793
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100794 hid->driver_data = session;
795
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100796 hid->bus = BUS_BLUETOOTH;
797 hid->vendor = req->vendor;
798 hid->product = req->product;
799 hid->version = req->version;
Jiri Slabyc500c972008-05-16 11:49:16 +0200800 hid->country = req->country;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100801
802 strncpy(hid->name, req->name, 128);
Gustavo F. Padovand6b2eb22010-09-03 18:29:46 -0300803 strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
804 strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100805
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200806 hid->dev.parent = hidp_get_device(session);
Jiri Slabyc500c972008-05-16 11:49:16 +0200807 hid->ll_driver = &hidp_hid_driver;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200808
Jiri Kosina2da31932009-11-26 16:20:56 +0100809 hid->hid_output_raw_report = hidp_output_raw_report;
810
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700811 err = hid_add_device(hid);
812 if (err < 0)
813 goto failed;
814
Jiri Slabyc500c972008-05-16 11:49:16 +0200815 return 0;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700816
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700817failed:
818 hid_destroy_device(hid);
819 session->hid = NULL;
820
Michael Poole15c697c2010-02-05 12:23:43 -0500821fault:
822 kfree(session->rd_data);
823 session->rd_data = NULL;
824
Marcel Holtmannedad6382009-08-22 14:22:15 -0700825 return err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100826}
827
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
829{
830 struct hidp_session *session, *s;
831 int err;
832
833 BT_DBG("");
834
835 if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) ||
836 bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
837 return -ENOTUNIQ;
838
Marcel Holtmann25ea6db2006-07-06 15:40:09 +0200839 session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500840 if (!session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100843 BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
844
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 down_write(&hidp_session_sem);
846
847 s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
848 if (s && s->state == BT_CONNECTED) {
849 err = -EEXIST;
850 goto failed;
851 }
852
853 bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
854
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700855 session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
856 session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
858 BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
859
860 session->ctrl_sock = ctrl_sock;
861 session->intr_sock = intr_sock;
862 session->state = BT_CONNECTED;
863
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800864 setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865
866 skb_queue_head_init(&session->ctrl_transmit);
867 skb_queue_head_init(&session->intr_transmit);
868
869 session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
870 session->idle_to = req->idle_to;
871
Jiri Slabyc500c972008-05-16 11:49:16 +0200872 if (req->rd_size > 0) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200873 err = hidp_setup_hid(session, req);
Jiri Slabyd458a9d2008-05-16 11:49:20 +0200874 if (err && err != -ENODEV)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700875 goto purge;
Jiri Slabyc500c972008-05-16 11:49:16 +0200876 }
877
878 if (!session->hid) {
879 err = hidp_setup_input(session, req);
880 if (err < 0)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700881 goto purge;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200882 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100883
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 __hidp_link_session(session);
885
886 hidp_set_timer(session);
887
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700888 err = kernel_thread(hidp_session, session, CLONE_KERNEL);
889 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 goto unlink;
891
892 if (session->input) {
893 hidp_send_ctrl_message(session,
894 HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0);
895 session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
896
897 session->leds = 0xff;
898 hidp_input_event(session->input, EV_LED, 0, 0);
899 }
900
901 up_write(&hidp_session_sem);
902 return 0;
903
904unlink:
905 hidp_del_timer(session);
906
907 __hidp_unlink_session(session);
908
Marcel Holtmannedad6382009-08-22 14:22:15 -0700909 if (session->input) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 input_unregister_device(session->input);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700911 session->input = NULL;
912 }
913
914 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200915 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700916 session->hid = NULL;
917 }
918
Michael Poole15c697c2010-02-05 12:23:43 -0500919 kfree(session->rd_data);
920 session->rd_data = NULL;
921
Marcel Holtmannedad6382009-08-22 14:22:15 -0700922purge:
Jiri Slabyc500c972008-05-16 11:49:16 +0200923 skb_queue_purge(&session->ctrl_transmit);
924 skb_queue_purge(&session->intr_transmit);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700925
Jiri Slabyc500c972008-05-16 11:49:16 +0200926failed:
927 up_write(&hidp_session_sem);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100928
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700929 input_free_device(session->input);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 kfree(session);
931 return err;
932}
933
934int hidp_del_connection(struct hidp_conndel_req *req)
935{
936 struct hidp_session *session;
937 int err = 0;
938
939 BT_DBG("");
940
941 down_read(&hidp_session_sem);
942
943 session = __hidp_get_session(&req->bdaddr);
944 if (session) {
945 if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
946 hidp_send_ctrl_message(session,
947 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0);
948 } else {
949 /* Flush the transmit queues */
950 skb_queue_purge(&session->ctrl_transmit);
951 skb_queue_purge(&session->intr_transmit);
952
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700953 /* Wakeup user-space polling for socket errors */
954 session->intr_sock->sk->sk_err = EUNATCH;
955 session->ctrl_sock->sk->sk_err = EUNATCH;
956
957 /* Kill session thread */
Peter Hurley7bb59df2011-06-30 13:53:53 -0400958 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700959 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 }
961 } else
962 err = -ENOENT;
963
964 up_read(&hidp_session_sem);
965 return err;
966}
967
968int hidp_get_connlist(struct hidp_connlist_req *req)
969{
970 struct list_head *p;
971 int err = 0, n = 0;
972
973 BT_DBG("");
974
975 down_read(&hidp_session_sem);
976
977 list_for_each(p, &hidp_session_list) {
978 struct hidp_session *session;
979 struct hidp_conninfo ci;
980
981 session = list_entry(p, struct hidp_session, list);
982
983 __hidp_copy_session(session, &ci);
984
985 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
986 err = -EFAULT;
987 break;
988 }
989
990 if (++n >= req->cnum)
991 break;
992
993 req->ci++;
994 }
995 req->cnum = n;
996
997 up_read(&hidp_session_sem);
998 return err;
999}
1000
1001int hidp_get_conninfo(struct hidp_conninfo *ci)
1002{
1003 struct hidp_session *session;
1004 int err = 0;
1005
1006 down_read(&hidp_session_sem);
1007
1008 session = __hidp_get_session(&ci->bdaddr);
1009 if (session)
1010 __hidp_copy_session(session, ci);
1011 else
1012 err = -ENOENT;
1013
1014 up_read(&hidp_session_sem);
1015 return err;
1016}
1017
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001018static const struct hid_device_id hidp_table[] = {
1019 { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
1020 { }
1021};
1022
1023static struct hid_driver hidp_driver = {
1024 .name = "generic-bluetooth",
1025 .id_table = hidp_table,
1026};
1027
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028static int __init hidp_init(void)
1029{
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001030 int ret;
1031
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
1033
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001034 ret = hid_register_driver(&hidp_driver);
1035 if (ret)
1036 goto err;
1037
1038 ret = hidp_init_sockets();
1039 if (ret)
1040 goto err_drv;
1041
1042 return 0;
1043err_drv:
1044 hid_unregister_driver(&hidp_driver);
1045err:
1046 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047}
1048
1049static void __exit hidp_exit(void)
1050{
1051 hidp_cleanup_sockets();
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001052 hid_unregister_driver(&hidp_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053}
1054
1055module_init(hidp_init);
1056module_exit(hidp_exit);
1057
1058MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
1059MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
1060MODULE_VERSION(VERSION);
1061MODULE_LICENSE("GPL");
1062MODULE_ALIAS("bt-proto-6");