blob: 0a89c114438bcb0e49885b9835012874bfcf58a6 [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
Mallikarjuna GBf4117692012-06-19 18:22:44 +053096static struct device *hidp_get_device(struct hidp_session *session)
97{
98 bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
99 bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
100 struct device *device = NULL;
101 struct hci_dev *hdev;
102
103 hdev = hci_get_route(dst, src);
104 if (!hdev)
105 return NULL;
106
107 session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
108 if (session->conn)
109 device = &session->conn->dev;
110
111 hci_dev_put(hdev);
112
113 return device;
114}
115
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116static void __hidp_link_session(struct hidp_session *session)
117{
118 __module_get(THIS_MODULE);
119 list_add(&session->list, &hidp_session_list);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700120
121 hci_conn_hold_device(session->conn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122}
123
124static void __hidp_unlink_session(struct hidp_session *session)
125{
Mallikarjuna GBf4117692012-06-19 18:22:44 +0530126 struct device *dev;
127
128 dev = hidp_get_device(session);
129 if (dev)
130 hci_conn_put_device(session->conn);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700131
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 list_del(&session->list);
133 module_put(THIS_MODULE);
134}
135
136static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
137{
Vasiliy Kulikovd31dbf62010-10-30 18:26:31 +0400138 memset(ci, 0, sizeof(*ci));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 bacpy(&ci->bdaddr, &session->bdaddr);
140
141 ci->flags = session->flags;
142 ci->state = session->state;
143
144 ci->vendor = 0x0000;
145 ci->product = 0x0000;
146 ci->version = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
148 if (session->input) {
149 ci->vendor = session->input->id.vendor;
150 ci->product = session->input->id.product;
151 ci->version = session->input->id.version;
152 if (session->input->name)
153 strncpy(ci->name, session->input->name, 128);
154 else
155 strncpy(ci->name, "HID Boot Device", 128);
156 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100157
158 if (session->hid) {
159 ci->vendor = session->hid->vendor;
160 ci->product = session->hid->product;
161 ci->version = session->hid->version;
162 strncpy(ci->name, session->hid->name, 128);
163 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164}
165
Andrew Morton91f5cca2008-02-05 03:07:58 -0800166static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev,
167 unsigned int type, unsigned int code, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 unsigned char newleds;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100170 struct sk_buff *skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100172 BT_DBG("session %p type %d code %d value %d", session, type, code, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
174 if (type != EV_LED)
175 return -1;
176
177 newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
178 (!!test_bit(LED_COMPOSE, dev->led) << 3) |
179 (!!test_bit(LED_SCROLLL, dev->led) << 2) |
180 (!!test_bit(LED_CAPSL, dev->led) << 1) |
181 (!!test_bit(LED_NUML, dev->led));
182
183 if (session->leds == newleds)
184 return 0;
185
186 session->leds = newleds;
187
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200188 skb = alloc_skb(3, GFP_ATOMIC);
189 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 BT_ERR("Can't allocate memory for new frame");
191 return -ENOMEM;
192 }
193
194 *skb_put(skb, 1) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
195 *skb_put(skb, 1) = 0x01;
196 *skb_put(skb, 1) = newleds;
197
198 skb_queue_tail(&session->intr_transmit, skb);
199
200 hidp_schedule(session);
201
202 return 0;
203}
204
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100205static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
206{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200207 struct hid_device *hid = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100208 struct hidp_session *session = hid->driver_data;
209
210 return hidp_queue_event(session, dev, type, code, value);
211}
212
213static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
214{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200215 struct hidp_session *session = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100216
217 return hidp_queue_event(session, dev, type, code, value);
218}
219
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
221{
222 struct input_dev *dev = session->input;
223 unsigned char *keys = session->keys;
224 unsigned char *udata = skb->data + 1;
225 signed char *sdata = skb->data + 1;
226 int i, size = skb->len - 1;
227
228 switch (skb->data[0]) {
229 case 0x01: /* Keyboard report */
230 for (i = 0; i < 8; i++)
231 input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
232
233 /* If all the key codes have been set to 0x01, it means
234 * too many keys were pressed at the same time. */
235 if (!memcmp(udata + 2, hidp_mkeyspat, 6))
236 break;
237
238 for (i = 2; i < 8; i++) {
239 if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
240 if (hidp_keycode[keys[i]])
241 input_report_key(dev, hidp_keycode[keys[i]], 0);
242 else
243 BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
244 }
245
246 if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
247 if (hidp_keycode[udata[i]])
248 input_report_key(dev, hidp_keycode[udata[i]], 1);
249 else
250 BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
251 }
252 }
253
254 memcpy(keys, udata, 8);
255 break;
256
257 case 0x02: /* Mouse report */
258 input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
259 input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
260 input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
261 input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
262 input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
263
264 input_report_rel(dev, REL_X, sdata[1]);
265 input_report_rel(dev, REL_Y, sdata[2]);
266
267 if (size > 3)
268 input_report_rel(dev, REL_WHEEL, sdata[3]);
269 break;
270 }
271
272 input_sync(dev);
273}
274
Bastien Nocera6bf82682010-01-20 12:00:42 +0000275static int __hidp_send_ctrl_message(struct hidp_session *session,
276 unsigned char hdr, unsigned char *data, int size)
277{
278 struct sk_buff *skb;
279
280 BT_DBG("session %p data %p size %d", session, data, size);
281
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200282 skb = alloc_skb(size + 1, GFP_ATOMIC);
283 if (!skb) {
Bastien Nocera6bf82682010-01-20 12:00:42 +0000284 BT_ERR("Can't allocate memory for new frame");
285 return -ENOMEM;
286 }
287
288 *skb_put(skb, 1) = hdr;
289 if (data && size > 0)
290 memcpy(skb_put(skb, size), data, size);
291
292 skb_queue_tail(&session->ctrl_transmit, skb);
293
294 return 0;
295}
296
297static inline int hidp_send_ctrl_message(struct hidp_session *session,
298 unsigned char hdr, unsigned char *data, int size)
299{
300 int err;
301
302 err = __hidp_send_ctrl_message(session, hdr, data, size);
303
304 hidp_schedule(session);
305
306 return err;
307}
308
Andrew Morton91f5cca2008-02-05 03:07:58 -0800309static int hidp_queue_report(struct hidp_session *session,
310 unsigned char *data, int size)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100311{
312 struct sk_buff *skb;
313
Dave Young6792b5e2007-10-20 14:15:39 +0200314 BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100315
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200316 skb = alloc_skb(size + 1, GFP_ATOMIC);
317 if (!skb) {
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100318 BT_ERR("Can't allocate memory for new frame");
319 return -ENOMEM;
320 }
321
322 *skb_put(skb, 1) = 0xa2;
323 if (size > 0)
324 memcpy(skb_put(skb, size), data, size);
325
326 skb_queue_tail(&session->intr_transmit, skb);
327
328 hidp_schedule(session);
329
330 return 0;
331}
332
333static int hidp_send_report(struct hidp_session *session, struct hid_report *report)
334{
335 unsigned char buf[32];
336 int rsize;
337
338 rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
339 if (rsize > sizeof(buf))
340 return -EIO;
341
342 hid_output_report(report, buf);
343
344 return hidp_queue_report(session, buf, rsize);
345}
346
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100347static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
348 unsigned char report_type)
Jiri Kosina2da31932009-11-26 16:20:56 +0100349{
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100350 switch (report_type) {
351 case HID_FEATURE_REPORT:
352 report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
353 break;
354 case HID_OUTPUT_REPORT:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700355 report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100356 break;
357 default:
358 return -EINVAL;
359 }
360
361 if (hidp_send_ctrl_message(hid->driver_data, report_type,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362 data, count))
363 return -ENOMEM;
364 return count;
Jiri Kosina2da31932009-11-26 16:20:56 +0100365}
366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367static void hidp_idle_timeout(unsigned long arg)
368{
369 struct hidp_session *session = (struct hidp_session *) arg;
370
Peter Hurley7bb59df2011-06-30 13:53:53 -0400371 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373}
374
Andrew Morton91f5cca2008-02-05 03:07:58 -0800375static void hidp_set_timer(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376{
377 if (session->idle_to > 0)
378 mod_timer(&session->timer, jiffies + HZ * session->idle_to);
379}
380
381static inline void hidp_del_timer(struct hidp_session *session)
382{
383 if (session->idle_to > 0)
384 del_timer(&session->timer);
385}
386
Andrew Morton91f5cca2008-02-05 03:07:58 -0800387static void hidp_process_handshake(struct hidp_session *session,
388 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389{
390 BT_DBG("session %p param 0x%02x", session, param);
391
392 switch (param) {
393 case HIDP_HSHK_SUCCESSFUL:
394 /* FIXME: Call into SET_ GET_ handlers here */
395 break;
396
397 case HIDP_HSHK_NOT_READY:
398 case HIDP_HSHK_ERR_INVALID_REPORT_ID:
399 case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
400 case HIDP_HSHK_ERR_INVALID_PARAMETER:
401 /* FIXME: Call into SET_ GET_ handlers here */
402 break;
403
404 case HIDP_HSHK_ERR_UNKNOWN:
405 break;
406
407 case HIDP_HSHK_ERR_FATAL:
408 /* Device requests a reboot, as this is the only way this error
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900409 * can be recovered. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 __hidp_send_ctrl_message(session,
411 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0);
412 break;
413
414 default:
415 __hidp_send_ctrl_message(session,
416 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
417 break;
418 }
419}
420
Andrew Morton91f5cca2008-02-05 03:07:58 -0800421static void hidp_process_hid_control(struct hidp_session *session,
422 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423{
424 BT_DBG("session %p param 0x%02x", session, param);
425
Dave Youngeff001e2008-02-05 03:07:14 -0800426 if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 /* Flush the transmit queues */
428 skb_queue_purge(&session->ctrl_transmit);
429 skb_queue_purge(&session->intr_transmit);
430
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431 /* Kill session thread */
Peter Hurley7bb59df2011-06-30 13:53:53 -0400432 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700433 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 }
435}
436
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
Andrew Morton91f5cca2008-02-05 03:07:58 -0800438 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439{
440 BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param);
441
442 switch (param) {
443 case HIDP_DATA_RTYPE_INPUT:
444 hidp_set_timer(session);
445
446 if (session->input)
447 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100448
449 if (session->hid)
450 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 break;
453
454 case HIDP_DATA_RTYPE_OTHER:
455 case HIDP_DATA_RTYPE_OUPUT:
456 case HIDP_DATA_RTYPE_FEATURE:
457 break;
458
459 default:
460 __hidp_send_ctrl_message(session,
461 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
462 }
463}
464
Andrew Morton91f5cca2008-02-05 03:07:58 -0800465static void hidp_recv_ctrl_frame(struct hidp_session *session,
466 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467{
468 unsigned char hdr, type, param;
469
470 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
471
472 hdr = skb->data[0];
473 skb_pull(skb, 1);
474
475 type = hdr & HIDP_HEADER_TRANS_MASK;
476 param = hdr & HIDP_HEADER_PARAM_MASK;
477
478 switch (type) {
479 case HIDP_TRANS_HANDSHAKE:
480 hidp_process_handshake(session, param);
481 break;
482
483 case HIDP_TRANS_HID_CONTROL:
484 hidp_process_hid_control(session, param);
485 break;
486
487 case HIDP_TRANS_DATA:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700488 hidp_process_data(session, skb, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 break;
490
491 default:
492 __hidp_send_ctrl_message(session,
493 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0);
494 break;
495 }
496
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700497 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
499
Andrew Morton91f5cca2008-02-05 03:07:58 -0800500static void hidp_recv_intr_frame(struct hidp_session *session,
501 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502{
503 unsigned char hdr;
504
505 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
506
507 hdr = skb->data[0];
508 skb_pull(skb, 1);
509
510 if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
511 hidp_set_timer(session);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 if (session->input)
514 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100515
516 if (session->hid) {
517 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1);
518 BT_DBG("report len %d", skb->len);
519 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 } else {
521 BT_DBG("Unsupported protocol header 0x%02x", hdr);
522 }
523
524 kfree_skb(skb);
525}
526
527static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
528{
529 struct kvec iv = { data, len };
530 struct msghdr msg;
531
532 BT_DBG("sock %p data %p len %d", sock, data, len);
533
534 if (!len)
535 return 0;
536
537 memset(&msg, 0, sizeof(msg));
538
539 return kernel_sendmsg(sock, &msg, &iv, 1, len);
540}
541
David S. Millerb03efcf2005-07-08 14:57:23 -0700542static void hidp_process_transmit(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
544 struct sk_buff *skb;
545
546 BT_DBG("session %p", session);
547
548 while ((skb = skb_dequeue(&session->ctrl_transmit))) {
549 if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
550 skb_queue_head(&session->ctrl_transmit, skb);
551 break;
552 }
553
554 hidp_set_timer(session);
555 kfree_skb(skb);
556 }
557
558 while ((skb = skb_dequeue(&session->intr_transmit))) {
559 if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
560 skb_queue_head(&session->intr_transmit, skb);
561 break;
562 }
563
564 hidp_set_timer(session);
565 kfree_skb(skb);
566 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567}
568
569static int hidp_session(void *arg)
570{
571 struct hidp_session *session = arg;
572 struct sock *ctrl_sk = session->ctrl_sock->sk;
573 struct sock *intr_sk = session->intr_sock->sk;
574 struct sk_buff *skb;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575 int vendor = 0x0000, product = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 wait_queue_t ctrl_wait, intr_wait;
577
578 BT_DBG("session %p", session);
579
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580 if (session->input) {
581 vendor = session->input->id.vendor;
582 product = session->input->id.product;
583 }
584
585 if (session->hid) {
586 vendor = session->hid->vendor;
587 product = session->hid->product;
588 }
589
590 daemonize("khidpd_%04x%04x", vendor, product);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 set_user_nice(current, -15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
593 init_waitqueue_entry(&ctrl_wait, current);
594 init_waitqueue_entry(&intr_wait, current);
Eric Dumazetaa395142010-04-20 13:03:51 +0000595 add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
596 add_wait_queue(sk_sleep(intr_sk), &intr_wait);
Peter Hurley7bb59df2011-06-30 13:53:53 -0400597 while (!atomic_read(&session->terminate)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700598 set_current_state(TASK_INTERRUPTIBLE);
599
Szymon Janc17f09a72011-03-21 14:20:01 +0100600 if (ctrl_sk->sk_state != BT_CONNECTED ||
601 intr_sk->sk_state != BT_CONNECTED)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 break;
603
Gustavo F. Padovandc0da5c2011-10-06 18:02:13 -0300604 while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
605 skb_orphan(skb);
606 if (!skb_linearize(skb))
607 hidp_recv_ctrl_frame(session, skb);
608 else
609 kfree_skb(skb);
610 }
611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
613 skb_orphan(skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700614 if (!skb_linearize(skb))
615 hidp_recv_intr_frame(session, skb);
616 else
617 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 }
619
620 hidp_process_transmit(session);
621
622 schedule();
623 }
624 set_current_state(TASK_RUNNING);
Eric Dumazetaa395142010-04-20 13:03:51 +0000625 remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
626 remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
628 down_write(&hidp_session_sem);
629
630 hidp_del_timer(session);
631
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 if (session->input) {
633 input_unregister_device(session->input);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500634 session->input = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 }
636
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100637 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200638 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700639 session->hid = NULL;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100640 }
641
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200642 /* Wakeup user-space polling for socket errors */
643 session->intr_sock->sk->sk_err = EUNATCH;
644 session->ctrl_sock->sk->sk_err = EUNATCH;
645
646 hidp_schedule(session);
647
David Woodhouse1c398582007-07-07 14:58:39 -0400648 fput(session->intr_sock->file);
649
Eric Dumazetaa395142010-04-20 13:03:51 +0000650 wait_event_timeout(*(sk_sleep(ctrl_sk)),
David Woodhouse1c398582007-07-07 14:58:39 -0400651 (ctrl_sk->sk_state == BT_CLOSED), msecs_to_jiffies(500));
652
653 fput(session->ctrl_sock->file);
654
655 __hidp_unlink_session(session);
656
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 up_write(&hidp_session_sem);
658
659 kfree(session);
660 return 0;
661}
662
Andrew Morton91f5cca2008-02-05 03:07:58 -0800663static int hidp_setup_input(struct hidp_session *session,
664 struct hidp_connadd_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665{
Jiri Slabyc500c972008-05-16 11:49:16 +0200666 struct input_dev *input;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700667 int err, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Jiri Slabyc500c972008-05-16 11:49:16 +0200669 input = input_allocate_device();
670 if (!input)
671 return -ENOMEM;
672
673 session->input = input;
674
Marcel Holtmann5be39462007-05-09 09:15:30 +0200675 input_set_drvdata(input, session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500677 input->name = "Bluetooth HID Boot Protocol Device";
678
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 input->id.bustype = BUS_BLUETOOTH;
680 input->id.vendor = req->vendor;
681 input->id.product = req->product;
682 input->id.version = req->version;
683
684 if (req->subclass & 0x40) {
685 set_bit(EV_KEY, input->evbit);
686 set_bit(EV_LED, input->evbit);
687 set_bit(EV_REP, input->evbit);
688
689 set_bit(LED_NUML, input->ledbit);
690 set_bit(LED_CAPSL, input->ledbit);
691 set_bit(LED_SCROLLL, input->ledbit);
692 set_bit(LED_COMPOSE, input->ledbit);
693 set_bit(LED_KANA, input->ledbit);
694
695 for (i = 0; i < sizeof(hidp_keycode); i++)
696 set_bit(hidp_keycode[i], input->keybit);
697 clear_bit(0, input->keybit);
698 }
699
700 if (req->subclass & 0x80) {
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700701 input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
702 input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
703 BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
704 input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
705 input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
706 BIT_MASK(BTN_EXTRA);
707 input->relbit[0] |= BIT_MASK(REL_WHEEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
709
Marcel Holtmann5be39462007-05-09 09:15:30 +0200710 input->dev.parent = hidp_get_device(session);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200711
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 input->event = hidp_input_event;
713
Marcel Holtmannedad6382009-08-22 14:22:15 -0700714 err = input_register_device(input);
715 if (err < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700716 hci_conn_put_device(session->conn);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700717 return err;
718 }
719
720 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721}
722
Marcel Holtmannf5ffd462007-02-17 23:58:53 +0100723static int hidp_open(struct hid_device *hid)
724{
725 return 0;
726}
727
728static void hidp_close(struct hid_device *hid)
729{
730}
731
Jiri Slabyc500c972008-05-16 11:49:16 +0200732static int hidp_parse(struct hid_device *hid)
733{
734 struct hidp_session *session = hid->driver_data;
Jiri Slabyc500c972008-05-16 11:49:16 +0200735
Michael Poole15c697c2010-02-05 12:23:43 -0500736 return hid_parse_report(session->hid, session->rd_data,
737 session->rd_size);
Jiri Slabyc500c972008-05-16 11:49:16 +0200738}
739
740static int hidp_start(struct hid_device *hid)
741{
742 struct hidp_session *session = hid->driver_data;
743 struct hid_report *report;
744
745 list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
746 report_list, list)
747 hidp_send_report(session, report);
748
749 list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
750 report_list, list)
751 hidp_send_report(session, report);
752
Jiri Slabyc500c972008-05-16 11:49:16 +0200753 return 0;
754}
755
756static void hidp_stop(struct hid_device *hid)
757{
758 struct hidp_session *session = hid->driver_data;
759
760 skb_queue_purge(&session->ctrl_transmit);
761 skb_queue_purge(&session->intr_transmit);
762
Jiri Slabyc500c972008-05-16 11:49:16 +0200763 hid->claimed = 0;
764}
765
766static struct hid_ll_driver hidp_hid_driver = {
767 .parse = hidp_parse,
768 .start = hidp_start,
769 .stop = hidp_stop,
770 .open = hidp_open,
771 .close = hidp_close,
772 .hidinput_input_event = hidp_hidinput_event,
773};
774
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200775static int hidp_setup_hid(struct hidp_session *session,
Andrew Morton91f5cca2008-02-05 03:07:58 -0800776 struct hidp_connadd_req *req)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100777{
Jiri Slabyc500c972008-05-16 11:49:16 +0200778 struct hid_device *hid;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700779 int err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100780
Michael Poole15c697c2010-02-05 12:23:43 -0500781 session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
782 if (!session->rd_data)
783 return -ENOMEM;
784
785 if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
786 err = -EFAULT;
787 goto fault;
788 }
789 session->rd_size = req->rd_size;
790
Jiri Slabyc500c972008-05-16 11:49:16 +0200791 hid = hid_allocate_device();
Michael Poole15c697c2010-02-05 12:23:43 -0500792 if (IS_ERR(hid)) {
793 err = PTR_ERR(hid);
794 goto fault;
795 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100796
Jiri Slabyc500c972008-05-16 11:49:16 +0200797 session->hid = hid;
Michael Poole15c697c2010-02-05 12:23:43 -0500798
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100799 hid->driver_data = session;
800
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100801 hid->bus = BUS_BLUETOOTH;
802 hid->vendor = req->vendor;
803 hid->product = req->product;
804 hid->version = req->version;
Jiri Slabyc500c972008-05-16 11:49:16 +0200805 hid->country = req->country;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100806
807 strncpy(hid->name, req->name, 128);
Gustavo F. Padovand6b2eb22010-09-03 18:29:46 -0300808 strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
809 strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100810
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200811 hid->dev.parent = hidp_get_device(session);
Jiri Slabyc500c972008-05-16 11:49:16 +0200812 hid->ll_driver = &hidp_hid_driver;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200813
Jiri Kosina2da31932009-11-26 16:20:56 +0100814 hid->hid_output_raw_report = hidp_output_raw_report;
815
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700816 err = hid_add_device(hid);
817 if (err < 0)
818 goto failed;
819
Jiri Slabyc500c972008-05-16 11:49:16 +0200820 return 0;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700821
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700822failed:
823 hid_destroy_device(hid);
824 session->hid = NULL;
825
Michael Poole15c697c2010-02-05 12:23:43 -0500826fault:
827 kfree(session->rd_data);
828 session->rd_data = NULL;
829
Marcel Holtmannedad6382009-08-22 14:22:15 -0700830 return err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100831}
832
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
834{
835 struct hidp_session *session, *s;
836 int err;
837
838 BT_DBG("");
839
840 if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) ||
841 bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
842 return -ENOTUNIQ;
843
Marcel Holtmann25ea6db2006-07-06 15:40:09 +0200844 session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500845 if (!session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100848 BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
849
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 down_write(&hidp_session_sem);
851
852 s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
853 if (s && s->state == BT_CONNECTED) {
854 err = -EEXIST;
855 goto failed;
856 }
857
858 bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
859
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700860 session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
861 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 -0700862
863 BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
864
865 session->ctrl_sock = ctrl_sock;
866 session->intr_sock = intr_sock;
867 session->state = BT_CONNECTED;
868
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800869 setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870
871 skb_queue_head_init(&session->ctrl_transmit);
872 skb_queue_head_init(&session->intr_transmit);
873
874 session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
875 session->idle_to = req->idle_to;
876
Jiri Slabyc500c972008-05-16 11:49:16 +0200877 if (req->rd_size > 0) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200878 err = hidp_setup_hid(session, req);
Jiri Slabyd458a9d2008-05-16 11:49:20 +0200879 if (err && err != -ENODEV)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700880 goto purge;
Jiri Slabyc500c972008-05-16 11:49:16 +0200881 }
882
883 if (!session->hid) {
884 err = hidp_setup_input(session, req);
885 if (err < 0)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700886 goto purge;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200887 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100888
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 __hidp_link_session(session);
890
891 hidp_set_timer(session);
892
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700893 err = kernel_thread(hidp_session, session, CLONE_KERNEL);
894 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 goto unlink;
896
897 if (session->input) {
898 hidp_send_ctrl_message(session,
899 HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0);
900 session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
901
902 session->leds = 0xff;
903 hidp_input_event(session->input, EV_LED, 0, 0);
904 }
905
906 up_write(&hidp_session_sem);
907 return 0;
908
909unlink:
910 hidp_del_timer(session);
911
912 __hidp_unlink_session(session);
913
Marcel Holtmannedad6382009-08-22 14:22:15 -0700914 if (session->input) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 input_unregister_device(session->input);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700916 session->input = NULL;
917 }
918
919 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200920 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700921 session->hid = NULL;
922 }
923
Michael Poole15c697c2010-02-05 12:23:43 -0500924 kfree(session->rd_data);
925 session->rd_data = NULL;
926
Marcel Holtmannedad6382009-08-22 14:22:15 -0700927purge:
Jiri Slabyc500c972008-05-16 11:49:16 +0200928 skb_queue_purge(&session->ctrl_transmit);
929 skb_queue_purge(&session->intr_transmit);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700930
Jiri Slabyc500c972008-05-16 11:49:16 +0200931failed:
932 up_write(&hidp_session_sem);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100933
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700934 input_free_device(session->input);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 kfree(session);
936 return err;
937}
938
939int hidp_del_connection(struct hidp_conndel_req *req)
940{
941 struct hidp_session *session;
942 int err = 0;
943
944 BT_DBG("");
945
946 down_read(&hidp_session_sem);
947
948 session = __hidp_get_session(&req->bdaddr);
949 if (session) {
950 if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
951 hidp_send_ctrl_message(session,
952 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0);
953 } else {
954 /* Flush the transmit queues */
955 skb_queue_purge(&session->ctrl_transmit);
956 skb_queue_purge(&session->intr_transmit);
957
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700958 /* Wakeup user-space polling for socket errors */
959 session->intr_sock->sk->sk_err = EUNATCH;
960 session->ctrl_sock->sk->sk_err = EUNATCH;
961
962 /* Kill session thread */
Peter Hurley7bb59df2011-06-30 13:53:53 -0400963 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700964 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 }
966 } else
967 err = -ENOENT;
968
969 up_read(&hidp_session_sem);
970 return err;
971}
972
973int hidp_get_connlist(struct hidp_connlist_req *req)
974{
975 struct list_head *p;
976 int err = 0, n = 0;
977
978 BT_DBG("");
979
980 down_read(&hidp_session_sem);
981
982 list_for_each(p, &hidp_session_list) {
983 struct hidp_session *session;
984 struct hidp_conninfo ci;
985
986 session = list_entry(p, struct hidp_session, list);
987
988 __hidp_copy_session(session, &ci);
989
990 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
991 err = -EFAULT;
992 break;
993 }
994
995 if (++n >= req->cnum)
996 break;
997
998 req->ci++;
999 }
1000 req->cnum = n;
1001
1002 up_read(&hidp_session_sem);
1003 return err;
1004}
1005
1006int hidp_get_conninfo(struct hidp_conninfo *ci)
1007{
1008 struct hidp_session *session;
1009 int err = 0;
1010
1011 down_read(&hidp_session_sem);
1012
1013 session = __hidp_get_session(&ci->bdaddr);
1014 if (session)
1015 __hidp_copy_session(session, ci);
1016 else
1017 err = -ENOENT;
1018
1019 up_read(&hidp_session_sem);
1020 return err;
1021}
1022
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001023static const struct hid_device_id hidp_table[] = {
1024 { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
1025 { }
1026};
1027
1028static struct hid_driver hidp_driver = {
1029 .name = "generic-bluetooth",
1030 .id_table = hidp_table,
1031};
1032
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033static int __init hidp_init(void)
1034{
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001035 int ret;
1036
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
1038
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001039 ret = hid_register_driver(&hidp_driver);
1040 if (ret)
1041 goto err;
1042
1043 ret = hidp_init_sockets();
1044 if (ret)
1045 goto err_drv;
1046
1047 return 0;
1048err_drv:
1049 hid_unregister_driver(&hidp_driver);
1050err:
1051 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052}
1053
1054static void __exit hidp_exit(void)
1055{
1056 hidp_cleanup_sockets();
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001057 hid_unregister_driver(&hidp_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058}
1059
1060module_init(hidp_init);
1061module_exit(hidp_exit);
1062
1063MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
1064MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
1065MODULE_VERSION(VERSION);
1066MODULE_LICENSE("GPL");
1067MODULE_ALIAS("bt-proto-6");