blob: 2c4ab786ae508e20e1bce9de58f6bd78d4b5a622 [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>
Mallikarjuna GBf4117692012-06-19 18:22:44 +05304 Copyright (c) 2012 Code Aurora Forum. All rights reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -07005
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation;
9
10 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
11 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
13 IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090014 CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
15 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Linus Torvalds1da177e2005-04-16 15:20:36 -070017 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090019 ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
20 COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 SOFTWARE IS DISCLAIMED.
22*/
23
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/module.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070025#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
27#include <linux/types.h>
28#include <linux/errno.h>
29#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/sched.h>
31#include <linux/slab.h>
32#include <linux/poll.h>
Rafael J. Wysocki83144182007-07-17 04:03:35 -070033#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <linux/fcntl.h>
35#include <linux/skbuff.h>
36#include <linux/socket.h>
37#include <linux/ioctl.h>
38#include <linux/file.h>
39#include <linux/init.h>
40#include <linux/wait.h>
41#include <net/sock.h>
42
43#include <linux/input.h>
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +010044#include <linux/hid.h>
Marcel Holtmann364f6352009-08-22 14:15:53 -070045#include <linux/hidraw.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
47#include <net/bluetooth/bluetooth.h>
Marcel Holtmann0a85b962006-07-06 13:09:02 +020048#include <net/bluetooth/hci_core.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <net/bluetooth/l2cap.h>
50
51#include "hidp.h"
52
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +010053#define VERSION "1.2"
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
55static DECLARE_RWSEM(hidp_session_sem);
56static LIST_HEAD(hidp_session_list);
57
58static unsigned char hidp_keycode[256] = {
Szymon Janc17f09a72011-03-21 14:20:01 +010059 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36,
60 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45,
61 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1,
62 14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52,
63 53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
64 99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69,
65 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73,
66 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190,
67 191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135,
68 136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94,
69 95, 0, 0, 0, 122, 123, 90, 91, 85, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115,
76 114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140
Linus Torvalds1da177e2005-04-16 15:20:36 -070077};
78
79static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
80
81static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
82{
83 struct hidp_session *session;
84 struct list_head *p;
85
86 BT_DBG("");
87
88 list_for_each(p, &hidp_session_list) {
89 session = list_entry(p, struct hidp_session, list);
90 if (!bacmp(bdaddr, &session->bdaddr))
91 return session;
92 }
93 return NULL;
94}
95
96static void __hidp_link_session(struct hidp_session *session)
97{
98 __module_get(THIS_MODULE);
99 list_add(&session->list, &hidp_session_list);
100}
101
102static void __hidp_unlink_session(struct hidp_session *session)
103{
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400104 if (session->conn)
Mallikarjuna GBf4117692012-06-19 18:22:44 +0530105 hci_conn_put_device(session->conn);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700106
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
Gustavo F. Padovanc7e4ebd2011-10-07 01:29:51 -0300638static struct hci_conn *hidp_get_connection(struct hidp_session *session)
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400639{
640 bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
641 bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
642 struct hci_conn *conn;
643 struct hci_dev *hdev;
644
645 hdev = hci_get_route(dst, src);
646 if (!hdev)
647 return NULL;
648
649 hci_dev_lock_bh(hdev);
650 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
651 if (conn)
652 hci_conn_hold_device(conn);
653 hci_dev_unlock_bh(hdev);
654
655 hci_dev_put(hdev);
656
657 return conn;
658}
659
Andrew Morton91f5cca2008-02-05 03:07:58 -0800660static int hidp_setup_input(struct hidp_session *session,
661 struct hidp_connadd_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662{
Jiri Slabyc500c972008-05-16 11:49:16 +0200663 struct input_dev *input;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700664 int err, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
Jiri Slabyc500c972008-05-16 11:49:16 +0200666 input = input_allocate_device();
667 if (!input)
668 return -ENOMEM;
669
670 session->input = input;
671
Marcel Holtmann5be39462007-05-09 09:15:30 +0200672 input_set_drvdata(input, session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500674 input->name = "Bluetooth HID Boot Protocol Device";
675
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 input->id.bustype = BUS_BLUETOOTH;
677 input->id.vendor = req->vendor;
678 input->id.product = req->product;
679 input->id.version = req->version;
680
681 if (req->subclass & 0x40) {
682 set_bit(EV_KEY, input->evbit);
683 set_bit(EV_LED, input->evbit);
684 set_bit(EV_REP, input->evbit);
685
686 set_bit(LED_NUML, input->ledbit);
687 set_bit(LED_CAPSL, input->ledbit);
688 set_bit(LED_SCROLLL, input->ledbit);
689 set_bit(LED_COMPOSE, input->ledbit);
690 set_bit(LED_KANA, input->ledbit);
691
692 for (i = 0; i < sizeof(hidp_keycode); i++)
693 set_bit(hidp_keycode[i], input->keybit);
694 clear_bit(0, input->keybit);
695 }
696
697 if (req->subclass & 0x80) {
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700698 input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
699 input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
700 BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
701 input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
702 input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
703 BIT_MASK(BTN_EXTRA);
704 input->relbit[0] |= BIT_MASK(REL_WHEEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 }
706
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400707 input->dev.parent = &session->conn->dev;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200708
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 input->event = hidp_input_event;
710
Marcel Holtmannedad6382009-08-22 14:22:15 -0700711 err = input_register_device(input);
712 if (err < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700713 hci_conn_put_device(session->conn);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700714 return err;
715 }
716
717 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718}
719
Marcel Holtmannf5ffd462007-02-17 23:58:53 +0100720static int hidp_open(struct hid_device *hid)
721{
722 return 0;
723}
724
725static void hidp_close(struct hid_device *hid)
726{
727}
728
Jiri Slabyc500c972008-05-16 11:49:16 +0200729static int hidp_parse(struct hid_device *hid)
730{
731 struct hidp_session *session = hid->driver_data;
Jiri Slabyc500c972008-05-16 11:49:16 +0200732
Michael Poole15c697c2010-02-05 12:23:43 -0500733 return hid_parse_report(session->hid, session->rd_data,
734 session->rd_size);
Jiri Slabyc500c972008-05-16 11:49:16 +0200735}
736
737static int hidp_start(struct hid_device *hid)
738{
739 struct hidp_session *session = hid->driver_data;
740 struct hid_report *report;
741
742 list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
743 report_list, list)
744 hidp_send_report(session, report);
745
746 list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
747 report_list, list)
748 hidp_send_report(session, report);
749
Jiri Slabyc500c972008-05-16 11:49:16 +0200750 return 0;
751}
752
753static void hidp_stop(struct hid_device *hid)
754{
755 struct hidp_session *session = hid->driver_data;
756
757 skb_queue_purge(&session->ctrl_transmit);
758 skb_queue_purge(&session->intr_transmit);
759
Jiri Slabyc500c972008-05-16 11:49:16 +0200760 hid->claimed = 0;
761}
762
763static struct hid_ll_driver hidp_hid_driver = {
764 .parse = hidp_parse,
765 .start = hidp_start,
766 .stop = hidp_stop,
767 .open = hidp_open,
768 .close = hidp_close,
769 .hidinput_input_event = hidp_hidinput_event,
770};
771
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200772static int hidp_setup_hid(struct hidp_session *session,
Andrew Morton91f5cca2008-02-05 03:07:58 -0800773 struct hidp_connadd_req *req)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100774{
Jiri Slabyc500c972008-05-16 11:49:16 +0200775 struct hid_device *hid;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700776 int err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100777
Michael Poole15c697c2010-02-05 12:23:43 -0500778 session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
779 if (!session->rd_data)
780 return -ENOMEM;
781
782 if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
783 err = -EFAULT;
784 goto fault;
785 }
786 session->rd_size = req->rd_size;
787
Jiri Slabyc500c972008-05-16 11:49:16 +0200788 hid = hid_allocate_device();
Michael Poole15c697c2010-02-05 12:23:43 -0500789 if (IS_ERR(hid)) {
790 err = PTR_ERR(hid);
791 goto fault;
792 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100793
Jiri Slabyc500c972008-05-16 11:49:16 +0200794 session->hid = hid;
Michael Poole15c697c2010-02-05 12:23:43 -0500795
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100796 hid->driver_data = session;
797
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100798 hid->bus = BUS_BLUETOOTH;
799 hid->vendor = req->vendor;
800 hid->product = req->product;
801 hid->version = req->version;
Jiri Slabyc500c972008-05-16 11:49:16 +0200802 hid->country = req->country;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100803
804 strncpy(hid->name, req->name, 128);
Gustavo F. Padovand6b2eb22010-09-03 18:29:46 -0300805 strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
806 strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100807
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400808 hid->dev.parent = &session->conn->dev;
Jiri Slabyc500c972008-05-16 11:49:16 +0200809 hid->ll_driver = &hidp_hid_driver;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200810
Jiri Kosina2da31932009-11-26 16:20:56 +0100811 hid->hid_output_raw_report = hidp_output_raw_report;
812
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700813 err = hid_add_device(hid);
814 if (err < 0)
815 goto failed;
816
Jiri Slabyc500c972008-05-16 11:49:16 +0200817 return 0;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700818
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700819failed:
820 hid_destroy_device(hid);
821 session->hid = NULL;
822
Michael Poole15c697c2010-02-05 12:23:43 -0500823fault:
824 kfree(session->rd_data);
825 session->rd_data = NULL;
826
Marcel Holtmannedad6382009-08-22 14:22:15 -0700827 return err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100828}
829
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
831{
832 struct hidp_session *session, *s;
833 int err;
834
835 BT_DBG("");
836
837 if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) ||
838 bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
839 return -ENOTUNIQ;
840
Marcel Holtmann25ea6db2006-07-06 15:40:09 +0200841 session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500842 if (!session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100845 BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
846
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 down_write(&hidp_session_sem);
848
849 s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
850 if (s && s->state == BT_CONNECTED) {
851 err = -EEXIST;
852 goto failed;
853 }
854
855 bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
856
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700857 session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
858 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 -0700859
860 BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
861
862 session->ctrl_sock = ctrl_sock;
863 session->intr_sock = intr_sock;
864 session->state = BT_CONNECTED;
865
Gustavo F. Padovanbe96be72011-10-20 17:21:34 -0200866 session->conn = hidp_get_connection(session);
867 if (!session->conn) {
868 err = -ENOTCONN;
869 goto failed;
870 }
871
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800872 setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873
874 skb_queue_head_init(&session->ctrl_transmit);
875 skb_queue_head_init(&session->intr_transmit);
876
877 session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
878 session->idle_to = req->idle_to;
879
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400880 __hidp_link_session(session);
881
Jiri Slabyc500c972008-05-16 11:49:16 +0200882 if (req->rd_size > 0) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200883 err = hidp_setup_hid(session, req);
Jiri Slabyd458a9d2008-05-16 11:49:20 +0200884 if (err && err != -ENODEV)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700885 goto purge;
Jiri Slabyc500c972008-05-16 11:49:16 +0200886 }
887
888 if (!session->hid) {
889 err = hidp_setup_input(session, req);
890 if (err < 0)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700891 goto purge;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200892 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100893
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 hidp_set_timer(session);
895
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700896 err = kernel_thread(hidp_session, session, CLONE_KERNEL);
897 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 goto unlink;
899
900 if (session->input) {
901 hidp_send_ctrl_message(session,
902 HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0);
903 session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
904
905 session->leds = 0xff;
906 hidp_input_event(session->input, EV_LED, 0, 0);
907 }
908
909 up_write(&hidp_session_sem);
910 return 0;
911
912unlink:
913 hidp_del_timer(session);
914
Marcel Holtmannedad6382009-08-22 14:22:15 -0700915 if (session->input) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 input_unregister_device(session->input);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700917 session->input = NULL;
918 }
919
920 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200921 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700922 session->hid = NULL;
923 }
924
Michael Poole15c697c2010-02-05 12:23:43 -0500925 kfree(session->rd_data);
926 session->rd_data = NULL;
927
Marcel Holtmannedad6382009-08-22 14:22:15 -0700928purge:
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400929 __hidp_unlink_session(session);
930
Jiri Slabyc500c972008-05-16 11:49:16 +0200931 skb_queue_purge(&session->ctrl_transmit);
932 skb_queue_purge(&session->intr_transmit);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700933
Jiri Slabyc500c972008-05-16 11:49:16 +0200934failed:
935 up_write(&hidp_session_sem);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100936
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700937 input_free_device(session->input);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 kfree(session);
939 return err;
940}
941
942int hidp_del_connection(struct hidp_conndel_req *req)
943{
944 struct hidp_session *session;
945 int err = 0;
946
947 BT_DBG("");
948
949 down_read(&hidp_session_sem);
950
951 session = __hidp_get_session(&req->bdaddr);
952 if (session) {
953 if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
954 hidp_send_ctrl_message(session,
955 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0);
956 } else {
957 /* Flush the transmit queues */
958 skb_queue_purge(&session->ctrl_transmit);
959 skb_queue_purge(&session->intr_transmit);
960
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700961 /* Wakeup user-space polling for socket errors */
962 session->intr_sock->sk->sk_err = EUNATCH;
963 session->ctrl_sock->sk->sk_err = EUNATCH;
964
965 /* Kill session thread */
Peter Hurley7bb59df2011-06-30 13:53:53 -0400966 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700967 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 }
969 } else
970 err = -ENOENT;
971
972 up_read(&hidp_session_sem);
973 return err;
974}
975
976int hidp_get_connlist(struct hidp_connlist_req *req)
977{
978 struct list_head *p;
979 int err = 0, n = 0;
980
981 BT_DBG("");
982
983 down_read(&hidp_session_sem);
984
985 list_for_each(p, &hidp_session_list) {
986 struct hidp_session *session;
987 struct hidp_conninfo ci;
988
989 session = list_entry(p, struct hidp_session, list);
990
991 __hidp_copy_session(session, &ci);
992
993 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
994 err = -EFAULT;
995 break;
996 }
997
998 if (++n >= req->cnum)
999 break;
1000
1001 req->ci++;
1002 }
1003 req->cnum = n;
1004
1005 up_read(&hidp_session_sem);
1006 return err;
1007}
1008
1009int hidp_get_conninfo(struct hidp_conninfo *ci)
1010{
1011 struct hidp_session *session;
1012 int err = 0;
1013
1014 down_read(&hidp_session_sem);
1015
1016 session = __hidp_get_session(&ci->bdaddr);
1017 if (session)
1018 __hidp_copy_session(session, ci);
1019 else
1020 err = -ENOENT;
1021
1022 up_read(&hidp_session_sem);
1023 return err;
1024}
1025
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001026static const struct hid_device_id hidp_table[] = {
1027 { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
1028 { }
1029};
1030
1031static struct hid_driver hidp_driver = {
1032 .name = "generic-bluetooth",
1033 .id_table = hidp_table,
1034};
1035
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036static int __init hidp_init(void)
1037{
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001038 int ret;
1039
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
1041
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001042 ret = hid_register_driver(&hidp_driver);
1043 if (ret)
1044 goto err;
1045
1046 ret = hidp_init_sockets();
1047 if (ret)
1048 goto err_drv;
1049
1050 return 0;
1051err_drv:
1052 hid_unregister_driver(&hidp_driver);
1053err:
1054 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055}
1056
1057static void __exit hidp_exit(void)
1058{
1059 hidp_cleanup_sockets();
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001060 hid_unregister_driver(&hidp_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061}
1062
1063module_init(hidp_init);
1064module_exit(hidp_exit);
1065
1066MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
1067MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
1068MODULE_VERSION(VERSION);
1069MODULE_LICENSE("GPL");
1070MODULE_ALIAS("bt-proto-6");