blob: 2353e91fb0172554645f33ee0e3a6120a0046059 [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>
Duy Truong790f06d2013-02-13 16:38:12 -08004 Copyright (c) 2012 The Linux Foundation. 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{
Sumit Bajpai686ac732012-12-10 21:55:05 +0530104 bdaddr_t *dst = &session->bdaddr;
105 struct hci_dev *hdev;
106 struct device *dev = NULL;
107
108 hdev = hci_get_route(dst, BDADDR_ANY);
109 if (hdev) {
110 session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
111 if (session->conn)
112 dev = &session->conn->dev;
113
114 hci_dev_put(hdev);
115 }
116
117 if (dev)
Mallikarjuna GBf4117692012-06-19 18:22:44 +0530118 hci_conn_put_device(session->conn);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700119
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 list_del(&session->list);
121 module_put(THIS_MODULE);
122}
123
124static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
125{
Vasiliy Kulikovd31dbf62010-10-30 18:26:31 +0400126 memset(ci, 0, sizeof(*ci));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 bacpy(&ci->bdaddr, &session->bdaddr);
128
129 ci->flags = session->flags;
130 ci->state = session->state;
131
132 ci->vendor = 0x0000;
133 ci->product = 0x0000;
134 ci->version = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
136 if (session->input) {
137 ci->vendor = session->input->id.vendor;
138 ci->product = session->input->id.product;
139 ci->version = session->input->id.version;
140 if (session->input->name)
141 strncpy(ci->name, session->input->name, 128);
142 else
143 strncpy(ci->name, "HID Boot Device", 128);
144 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100145
146 if (session->hid) {
147 ci->vendor = session->hid->vendor;
148 ci->product = session->hid->product;
149 ci->version = session->hid->version;
150 strncpy(ci->name, session->hid->name, 128);
151 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152}
153
Andrew Morton91f5cca2008-02-05 03:07:58 -0800154static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev,
155 unsigned int type, unsigned int code, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 unsigned char newleds;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100158 struct sk_buff *skb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100160 BT_DBG("session %p type %d code %d value %d", session, type, code, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
162 if (type != EV_LED)
163 return -1;
164
165 newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
166 (!!test_bit(LED_COMPOSE, dev->led) << 3) |
167 (!!test_bit(LED_SCROLLL, dev->led) << 2) |
168 (!!test_bit(LED_CAPSL, dev->led) << 1) |
169 (!!test_bit(LED_NUML, dev->led));
170
171 if (session->leds == newleds)
172 return 0;
173
174 session->leds = newleds;
175
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200176 skb = alloc_skb(3, GFP_ATOMIC);
177 if (!skb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 BT_ERR("Can't allocate memory for new frame");
179 return -ENOMEM;
180 }
181
182 *skb_put(skb, 1) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
183 *skb_put(skb, 1) = 0x01;
184 *skb_put(skb, 1) = newleds;
185
186 skb_queue_tail(&session->intr_transmit, skb);
187
188 hidp_schedule(session);
189
190 return 0;
191}
192
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100193static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
194{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200195 struct hid_device *hid = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100196 struct hidp_session *session = hid->driver_data;
197
198 return hidp_queue_event(session, dev, type, code, value);
199}
200
201static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
202{
Marcel Holtmann5be39462007-05-09 09:15:30 +0200203 struct hidp_session *session = input_get_drvdata(dev);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100204
205 return hidp_queue_event(session, dev, type, code, value);
206}
207
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
209{
210 struct input_dev *dev = session->input;
211 unsigned char *keys = session->keys;
212 unsigned char *udata = skb->data + 1;
213 signed char *sdata = skb->data + 1;
214 int i, size = skb->len - 1;
215
216 switch (skb->data[0]) {
217 case 0x01: /* Keyboard report */
218 for (i = 0; i < 8; i++)
219 input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
220
221 /* If all the key codes have been set to 0x01, it means
222 * too many keys were pressed at the same time. */
223 if (!memcmp(udata + 2, hidp_mkeyspat, 6))
224 break;
225
226 for (i = 2; i < 8; i++) {
227 if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
228 if (hidp_keycode[keys[i]])
229 input_report_key(dev, hidp_keycode[keys[i]], 0);
230 else
231 BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
232 }
233
234 if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
235 if (hidp_keycode[udata[i]])
236 input_report_key(dev, hidp_keycode[udata[i]], 1);
237 else
238 BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
239 }
240 }
241
242 memcpy(keys, udata, 8);
243 break;
244
245 case 0x02: /* Mouse report */
246 input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
247 input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
248 input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
249 input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
250 input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
251
252 input_report_rel(dev, REL_X, sdata[1]);
253 input_report_rel(dev, REL_Y, sdata[2]);
254
255 if (size > 3)
256 input_report_rel(dev, REL_WHEEL, sdata[3]);
257 break;
258 }
259
260 input_sync(dev);
261}
262
Bastien Nocera6bf82682010-01-20 12:00:42 +0000263static int __hidp_send_ctrl_message(struct hidp_session *session,
264 unsigned char hdr, unsigned char *data, int size)
265{
266 struct sk_buff *skb;
267
268 BT_DBG("session %p data %p size %d", session, data, size);
269
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200270 skb = alloc_skb(size + 1, GFP_ATOMIC);
271 if (!skb) {
Bastien Nocera6bf82682010-01-20 12:00:42 +0000272 BT_ERR("Can't allocate memory for new frame");
273 return -ENOMEM;
274 }
275
276 *skb_put(skb, 1) = hdr;
277 if (data && size > 0)
278 memcpy(skb_put(skb, size), data, size);
279
280 skb_queue_tail(&session->ctrl_transmit, skb);
281
282 return 0;
283}
284
285static inline int hidp_send_ctrl_message(struct hidp_session *session,
286 unsigned char hdr, unsigned char *data, int size)
287{
288 int err;
289
290 err = __hidp_send_ctrl_message(session, hdr, data, size);
291
292 hidp_schedule(session);
293
294 return err;
295}
296
Andrew Morton91f5cca2008-02-05 03:07:58 -0800297static int hidp_queue_report(struct hidp_session *session,
298 unsigned char *data, int size)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100299{
300 struct sk_buff *skb;
301
Dave Young6792b5e2007-10-20 14:15:39 +0200302 BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100303
Andrei Emeltchenko5a08ecc2011-01-11 17:20:20 +0200304 skb = alloc_skb(size + 1, GFP_ATOMIC);
305 if (!skb) {
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100306 BT_ERR("Can't allocate memory for new frame");
307 return -ENOMEM;
308 }
309
310 *skb_put(skb, 1) = 0xa2;
311 if (size > 0)
312 memcpy(skb_put(skb, size), data, size);
313
314 skb_queue_tail(&session->intr_transmit, skb);
315
316 hidp_schedule(session);
317
318 return 0;
319}
320
321static int hidp_send_report(struct hidp_session *session, struct hid_report *report)
322{
323 unsigned char buf[32];
324 int rsize;
325
326 rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0);
327 if (rsize > sizeof(buf))
328 return -EIO;
329
330 hid_output_report(report, buf);
331
332 return hidp_queue_report(session, buf, rsize);
333}
334
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100335static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
336 unsigned char report_type)
Jiri Kosina2da31932009-11-26 16:20:56 +0100337{
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100338 switch (report_type) {
339 case HID_FEATURE_REPORT:
340 report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
341 break;
342 case HID_OUTPUT_REPORT:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343 report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
Jiri Kosinad4bfa032010-01-29 15:03:36 +0100344 break;
345 default:
346 return -EINVAL;
347 }
348
349 if (hidp_send_ctrl_message(hid->driver_data, report_type,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700350 data, count))
351 return -ENOMEM;
352 return count;
Jiri Kosina2da31932009-11-26 16:20:56 +0100353}
354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355static void hidp_idle_timeout(unsigned long arg)
356{
357 struct hidp_session *session = (struct hidp_session *) arg;
358
Peter Hurley7bb59df2011-06-30 13:53:53 -0400359 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361}
362
Andrew Morton91f5cca2008-02-05 03:07:58 -0800363static void hidp_set_timer(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
365 if (session->idle_to > 0)
366 mod_timer(&session->timer, jiffies + HZ * session->idle_to);
367}
368
369static inline void hidp_del_timer(struct hidp_session *session)
370{
371 if (session->idle_to > 0)
372 del_timer(&session->timer);
373}
374
Andrew Morton91f5cca2008-02-05 03:07:58 -0800375static void hidp_process_handshake(struct hidp_session *session,
376 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377{
378 BT_DBG("session %p param 0x%02x", session, param);
379
380 switch (param) {
381 case HIDP_HSHK_SUCCESSFUL:
382 /* FIXME: Call into SET_ GET_ handlers here */
383 break;
384
385 case HIDP_HSHK_NOT_READY:
386 case HIDP_HSHK_ERR_INVALID_REPORT_ID:
387 case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
388 case HIDP_HSHK_ERR_INVALID_PARAMETER:
389 /* FIXME: Call into SET_ GET_ handlers here */
390 break;
391
392 case HIDP_HSHK_ERR_UNKNOWN:
393 break;
394
395 case HIDP_HSHK_ERR_FATAL:
396 /* Device requests a reboot, as this is the only way this error
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900397 * can be recovered. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 __hidp_send_ctrl_message(session,
399 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0);
400 break;
401
402 default:
403 __hidp_send_ctrl_message(session,
404 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
405 break;
406 }
407}
408
Andrew Morton91f5cca2008-02-05 03:07:58 -0800409static void hidp_process_hid_control(struct hidp_session *session,
410 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
412 BT_DBG("session %p param 0x%02x", session, param);
413
Dave Youngeff001e2008-02-05 03:07:14 -0800414 if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 /* Flush the transmit queues */
416 skb_queue_purge(&session->ctrl_transmit);
417 skb_queue_purge(&session->intr_transmit);
418
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700419 /* Kill session thread */
Peter Hurley7bb59df2011-06-30 13:53:53 -0400420 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700421 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 }
423}
424
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425static void hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
Andrew Morton91f5cca2008-02-05 03:07:58 -0800426 unsigned char param)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427{
428 BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param);
429
430 switch (param) {
431 case HIDP_DATA_RTYPE_INPUT:
432 hidp_set_timer(session);
433
434 if (session->input)
435 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100436
437 if (session->hid)
438 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700439
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 break;
441
442 case HIDP_DATA_RTYPE_OTHER:
443 case HIDP_DATA_RTYPE_OUPUT:
444 case HIDP_DATA_RTYPE_FEATURE:
445 break;
446
447 default:
448 __hidp_send_ctrl_message(session,
449 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
450 }
451}
452
Andrew Morton91f5cca2008-02-05 03:07:58 -0800453static void hidp_recv_ctrl_frame(struct hidp_session *session,
454 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455{
456 unsigned char hdr, type, param;
457
458 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
459
460 hdr = skb->data[0];
461 skb_pull(skb, 1);
462
463 type = hdr & HIDP_HEADER_TRANS_MASK;
464 param = hdr & HIDP_HEADER_PARAM_MASK;
465
466 switch (type) {
467 case HIDP_TRANS_HANDSHAKE:
468 hidp_process_handshake(session, param);
469 break;
470
471 case HIDP_TRANS_HID_CONTROL:
472 hidp_process_hid_control(session, param);
473 break;
474
475 case HIDP_TRANS_DATA:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700476 hidp_process_data(session, skb, param);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 break;
478
479 default:
480 __hidp_send_ctrl_message(session,
481 HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0);
482 break;
483 }
484
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700485 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486}
487
Andrew Morton91f5cca2008-02-05 03:07:58 -0800488static void hidp_recv_intr_frame(struct hidp_session *session,
489 struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490{
491 unsigned char hdr;
492
493 BT_DBG("session %p skb %p len %d", session, skb, skb->len);
494
495 hdr = skb->data[0];
496 skb_pull(skb, 1);
497
498 if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
499 hidp_set_timer(session);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 if (session->input)
502 hidp_input_report(session, skb);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100503
504 if (session->hid) {
505 hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1);
506 BT_DBG("report len %d", skb->len);
507 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 } else {
509 BT_DBG("Unsupported protocol header 0x%02x", hdr);
510 }
511
512 kfree_skb(skb);
513}
514
515static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
516{
517 struct kvec iv = { data, len };
518 struct msghdr msg;
519
520 BT_DBG("sock %p data %p len %d", sock, data, len);
521
522 if (!len)
523 return 0;
524
525 memset(&msg, 0, sizeof(msg));
526
527 return kernel_sendmsg(sock, &msg, &iv, 1, len);
528}
529
David S. Millerb03efcf2005-07-08 14:57:23 -0700530static void hidp_process_transmit(struct hidp_session *session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531{
532 struct sk_buff *skb;
533
534 BT_DBG("session %p", session);
535
536 while ((skb = skb_dequeue(&session->ctrl_transmit))) {
537 if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
538 skb_queue_head(&session->ctrl_transmit, skb);
539 break;
540 }
541
542 hidp_set_timer(session);
543 kfree_skb(skb);
544 }
545
546 while ((skb = skb_dequeue(&session->intr_transmit))) {
547 if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
548 skb_queue_head(&session->intr_transmit, skb);
549 break;
550 }
551
552 hidp_set_timer(session);
553 kfree_skb(skb);
554 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555}
556
557static int hidp_session(void *arg)
558{
559 struct hidp_session *session = arg;
560 struct sock *ctrl_sk = session->ctrl_sock->sk;
561 struct sock *intr_sk = session->intr_sock->sk;
562 struct sk_buff *skb;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563 int vendor = 0x0000, product = 0x0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 wait_queue_t ctrl_wait, intr_wait;
565
566 BT_DBG("session %p", session);
567
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568 if (session->input) {
569 vendor = session->input->id.vendor;
570 product = session->input->id.product;
571 }
572
573 if (session->hid) {
574 vendor = session->hid->vendor;
575 product = session->hid->product;
576 }
577
578 daemonize("khidpd_%04x%04x", vendor, product);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 set_user_nice(current, -15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
581 init_waitqueue_entry(&ctrl_wait, current);
582 init_waitqueue_entry(&intr_wait, current);
Eric Dumazetaa395142010-04-20 13:03:51 +0000583 add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
584 add_wait_queue(sk_sleep(intr_sk), &intr_wait);
Peter Hurley7bb59df2011-06-30 13:53:53 -0400585 while (!atomic_read(&session->terminate)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700586 set_current_state(TASK_INTERRUPTIBLE);
587
Szymon Janc17f09a72011-03-21 14:20:01 +0100588 if (ctrl_sk->sk_state != BT_CONNECTED ||
589 intr_sk->sk_state != BT_CONNECTED)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 break;
591
Gustavo F. Padovandc0da5c2011-10-06 18:02:13 -0300592 while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
593 skb_orphan(skb);
594 if (!skb_linearize(skb))
595 hidp_recv_ctrl_frame(session, skb);
596 else
597 kfree_skb(skb);
598 }
599
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
601 skb_orphan(skb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700602 if (!skb_linearize(skb))
603 hidp_recv_intr_frame(session, skb);
604 else
605 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 }
607
608 hidp_process_transmit(session);
609
610 schedule();
611 }
612 set_current_state(TASK_RUNNING);
Eric Dumazetaa395142010-04-20 13:03:51 +0000613 remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
614 remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
616 down_write(&hidp_session_sem);
617
618 hidp_del_timer(session);
619
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 if (session->input) {
621 input_unregister_device(session->input);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500622 session->input = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 }
624
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100625 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200626 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700627 session->hid = NULL;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100628 }
629
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200630 /* Wakeup user-space polling for socket errors */
631 session->intr_sock->sk->sk_err = EUNATCH;
632 session->ctrl_sock->sk->sk_err = EUNATCH;
633
634 hidp_schedule(session);
635
David Woodhouse1c398582007-07-07 14:58:39 -0400636 fput(session->intr_sock->file);
637
Eric Dumazetaa395142010-04-20 13:03:51 +0000638 wait_event_timeout(*(sk_sleep(ctrl_sk)),
David Woodhouse1c398582007-07-07 14:58:39 -0400639 (ctrl_sk->sk_state == BT_CLOSED), msecs_to_jiffies(500));
640
641 fput(session->ctrl_sock->file);
642
643 __hidp_unlink_session(session);
644
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 up_write(&hidp_session_sem);
646
647 kfree(session);
648 return 0;
649}
650
Gustavo F. Padovanc7e4ebd2011-10-07 01:29:51 -0300651static struct hci_conn *hidp_get_connection(struct hidp_session *session)
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400652{
653 bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
654 bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
655 struct hci_conn *conn;
656 struct hci_dev *hdev;
657
658 hdev = hci_get_route(dst, src);
659 if (!hdev)
660 return NULL;
661
662 hci_dev_lock_bh(hdev);
663 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
664 if (conn)
665 hci_conn_hold_device(conn);
666 hci_dev_unlock_bh(hdev);
667
668 hci_dev_put(hdev);
669
670 return conn;
671}
672
Andrew Morton91f5cca2008-02-05 03:07:58 -0800673static int hidp_setup_input(struct hidp_session *session,
674 struct hidp_connadd_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675{
Jiri Slabyc500c972008-05-16 11:49:16 +0200676 struct input_dev *input;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700677 int err, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Jiri Slabyc500c972008-05-16 11:49:16 +0200679 input = input_allocate_device();
680 if (!input)
681 return -ENOMEM;
682
683 session->input = input;
684
Marcel Holtmann5be39462007-05-09 09:15:30 +0200685 input_set_drvdata(input, session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500687 input->name = "Bluetooth HID Boot Protocol Device";
688
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 input->id.bustype = BUS_BLUETOOTH;
690 input->id.vendor = req->vendor;
691 input->id.product = req->product;
692 input->id.version = req->version;
693
694 if (req->subclass & 0x40) {
695 set_bit(EV_KEY, input->evbit);
696 set_bit(EV_LED, input->evbit);
697 set_bit(EV_REP, input->evbit);
698
699 set_bit(LED_NUML, input->ledbit);
700 set_bit(LED_CAPSL, input->ledbit);
701 set_bit(LED_SCROLLL, input->ledbit);
702 set_bit(LED_COMPOSE, input->ledbit);
703 set_bit(LED_KANA, input->ledbit);
704
705 for (i = 0; i < sizeof(hidp_keycode); i++)
706 set_bit(hidp_keycode[i], input->keybit);
707 clear_bit(0, input->keybit);
708 }
709
710 if (req->subclass & 0x80) {
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700711 input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
712 input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
713 BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
714 input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
715 input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
716 BIT_MASK(BTN_EXTRA);
717 input->relbit[0] |= BIT_MASK(REL_WHEEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 }
719
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400720 input->dev.parent = &session->conn->dev;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200721
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 input->event = hidp_input_event;
723
Marcel Holtmannedad6382009-08-22 14:22:15 -0700724 err = input_register_device(input);
725 if (err < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700726 hci_conn_put_device(session->conn);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700727 return err;
728 }
729
730 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731}
732
Marcel Holtmannf5ffd462007-02-17 23:58:53 +0100733static int hidp_open(struct hid_device *hid)
734{
735 return 0;
736}
737
738static void hidp_close(struct hid_device *hid)
739{
740}
741
Jiri Slabyc500c972008-05-16 11:49:16 +0200742static int hidp_parse(struct hid_device *hid)
743{
744 struct hidp_session *session = hid->driver_data;
Jiri Slabyc500c972008-05-16 11:49:16 +0200745
Michael Poole15c697c2010-02-05 12:23:43 -0500746 return hid_parse_report(session->hid, session->rd_data,
747 session->rd_size);
Jiri Slabyc500c972008-05-16 11:49:16 +0200748}
749
750static int hidp_start(struct hid_device *hid)
751{
752 struct hidp_session *session = hid->driver_data;
753 struct hid_report *report;
754
755 list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
756 report_list, list)
757 hidp_send_report(session, report);
758
759 list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
760 report_list, list)
761 hidp_send_report(session, report);
762
Jiri Slabyc500c972008-05-16 11:49:16 +0200763 return 0;
764}
765
766static void hidp_stop(struct hid_device *hid)
767{
768 struct hidp_session *session = hid->driver_data;
769
770 skb_queue_purge(&session->ctrl_transmit);
771 skb_queue_purge(&session->intr_transmit);
772
Jiri Slabyc500c972008-05-16 11:49:16 +0200773 hid->claimed = 0;
774}
775
776static struct hid_ll_driver hidp_hid_driver = {
777 .parse = hidp_parse,
778 .start = hidp_start,
779 .stop = hidp_stop,
780 .open = hidp_open,
781 .close = hidp_close,
782 .hidinput_input_event = hidp_hidinput_event,
783};
784
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200785static int hidp_setup_hid(struct hidp_session *session,
Andrew Morton91f5cca2008-02-05 03:07:58 -0800786 struct hidp_connadd_req *req)
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100787{
Jiri Slabyc500c972008-05-16 11:49:16 +0200788 struct hid_device *hid;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700789 int err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100790
Michael Poole15c697c2010-02-05 12:23:43 -0500791 session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
792 if (!session->rd_data)
793 return -ENOMEM;
794
795 if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
796 err = -EFAULT;
797 goto fault;
798 }
799 session->rd_size = req->rd_size;
800
Jiri Slabyc500c972008-05-16 11:49:16 +0200801 hid = hid_allocate_device();
Michael Poole15c697c2010-02-05 12:23:43 -0500802 if (IS_ERR(hid)) {
803 err = PTR_ERR(hid);
804 goto fault;
805 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100806
Jiri Slabyc500c972008-05-16 11:49:16 +0200807 session->hid = hid;
Michael Poole15c697c2010-02-05 12:23:43 -0500808
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100809 hid->driver_data = session;
810
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100811 hid->bus = BUS_BLUETOOTH;
812 hid->vendor = req->vendor;
813 hid->product = req->product;
814 hid->version = req->version;
Jiri Slabyc500c972008-05-16 11:49:16 +0200815 hid->country = req->country;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100816
817 strncpy(hid->name, req->name, 128);
Gustavo F. Padovand6b2eb22010-09-03 18:29:46 -0300818 strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
819 strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100820
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400821 hid->dev.parent = &session->conn->dev;
Jiri Slabyc500c972008-05-16 11:49:16 +0200822 hid->ll_driver = &hidp_hid_driver;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200823
Jiri Kosina2da31932009-11-26 16:20:56 +0100824 hid->hid_output_raw_report = hidp_output_raw_report;
825
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700826 err = hid_add_device(hid);
827 if (err < 0)
828 goto failed;
829
Jiri Slabyc500c972008-05-16 11:49:16 +0200830 return 0;
Marcel Holtmannedad6382009-08-22 14:22:15 -0700831
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700832failed:
833 hid_destroy_device(hid);
834 session->hid = NULL;
835
Michael Poole15c697c2010-02-05 12:23:43 -0500836fault:
837 kfree(session->rd_data);
838 session->rd_data = NULL;
839
Marcel Holtmannedad6382009-08-22 14:22:15 -0700840 return err;
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100841}
842
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
844{
845 struct hidp_session *session, *s;
846 int err;
847
848 BT_DBG("");
849
850 if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) ||
851 bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
852 return -ENOTUNIQ;
853
Marcel Holtmann25ea6db2006-07-06 15:40:09 +0200854 session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
Dmitry Torokhov34abf912005-09-15 02:01:40 -0500855 if (!session)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100858 BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
859
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 down_write(&hidp_session_sem);
861
862 s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
863 if (s && s->state == BT_CONNECTED) {
864 err = -EEXIST;
865 goto failed;
866 }
867
868 bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
869
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700870 session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
871 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 -0700872
873 BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
874
875 session->ctrl_sock = ctrl_sock;
876 session->intr_sock = intr_sock;
877 session->state = BT_CONNECTED;
878
Gustavo F. Padovanbe96be72011-10-20 17:21:34 -0200879 session->conn = hidp_get_connection(session);
880 if (!session->conn) {
881 err = -ENOTCONN;
882 goto failed;
883 }
884
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -0800885 setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
887 skb_queue_head_init(&session->ctrl_transmit);
888 skb_queue_head_init(&session->intr_transmit);
889
890 session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
891 session->idle_to = req->idle_to;
892
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400893 __hidp_link_session(session);
894
Jiri Slabyc500c972008-05-16 11:49:16 +0200895 if (req->rd_size > 0) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200896 err = hidp_setup_hid(session, req);
Jiri Slabyd458a9d2008-05-16 11:49:20 +0200897 if (err && err != -ENODEV)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700898 goto purge;
Jiri Slabyc500c972008-05-16 11:49:16 +0200899 }
900
901 if (!session->hid) {
902 err = hidp_setup_input(session, req);
903 if (err < 0)
Marcel Holtmannedad6382009-08-22 14:22:15 -0700904 goto purge;
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200905 }
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100906
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 hidp_set_timer(session);
908
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700909 err = kernel_thread(hidp_session, session, CLONE_KERNEL);
910 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 goto unlink;
912
913 if (session->input) {
914 hidp_send_ctrl_message(session,
915 HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0);
916 session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
917
918 session->leds = 0xff;
919 hidp_input_event(session->input, EV_LED, 0, 0);
920 }
921
922 up_write(&hidp_session_sem);
923 return 0;
924
925unlink:
926 hidp_del_timer(session);
927
Marcel Holtmannedad6382009-08-22 14:22:15 -0700928 if (session->input) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 input_unregister_device(session->input);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700930 session->input = NULL;
931 }
932
933 if (session->hid) {
Jiri Slaby85cdaf52008-05-16 11:49:15 +0200934 hid_destroy_device(session->hid);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700935 session->hid = NULL;
936 }
937
Michael Poole15c697c2010-02-05 12:23:43 -0500938 kfree(session->rd_data);
939 session->rd_data = NULL;
940
Marcel Holtmannedad6382009-08-22 14:22:15 -0700941purge:
Peter Hurleye0a15ce2011-08-30 11:53:35 -0400942 __hidp_unlink_session(session);
943
Jiri Slabyc500c972008-05-16 11:49:16 +0200944 skb_queue_purge(&session->ctrl_transmit);
945 skb_queue_purge(&session->intr_transmit);
Marcel Holtmannedad6382009-08-22 14:22:15 -0700946
Jiri Slabyc500c972008-05-16 11:49:16 +0200947failed:
948 up_write(&hidp_session_sem);
Marcel Holtmanne1aaadd2007-02-17 23:58:49 +0100949
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700950 input_free_device(session->input);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 kfree(session);
952 return err;
953}
954
955int hidp_del_connection(struct hidp_conndel_req *req)
956{
957 struct hidp_session *session;
958 int err = 0;
959
960 BT_DBG("");
961
962 down_read(&hidp_session_sem);
963
964 session = __hidp_get_session(&req->bdaddr);
965 if (session) {
966 if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
967 hidp_send_ctrl_message(session,
968 HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0);
969 } else {
970 /* Flush the transmit queues */
971 skb_queue_purge(&session->ctrl_transmit);
972 skb_queue_purge(&session->intr_transmit);
973
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700974 /* Wakeup user-space polling for socket errors */
975 session->intr_sock->sk->sk_err = EUNATCH;
976 session->ctrl_sock->sk->sk_err = EUNATCH;
977
978 /* Kill session thread */
Peter Hurley7bb59df2011-06-30 13:53:53 -0400979 atomic_inc(&session->terminate);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980 hidp_schedule(session);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 }
982 } else
983 err = -ENOENT;
984
985 up_read(&hidp_session_sem);
986 return err;
987}
988
989int hidp_get_connlist(struct hidp_connlist_req *req)
990{
991 struct list_head *p;
992 int err = 0, n = 0;
993
994 BT_DBG("");
995
996 down_read(&hidp_session_sem);
997
998 list_for_each(p, &hidp_session_list) {
999 struct hidp_session *session;
1000 struct hidp_conninfo ci;
1001
1002 session = list_entry(p, struct hidp_session, list);
1003
1004 __hidp_copy_session(session, &ci);
1005
1006 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
1007 err = -EFAULT;
1008 break;
1009 }
1010
1011 if (++n >= req->cnum)
1012 break;
1013
1014 req->ci++;
1015 }
1016 req->cnum = n;
1017
1018 up_read(&hidp_session_sem);
1019 return err;
1020}
1021
1022int hidp_get_conninfo(struct hidp_conninfo *ci)
1023{
1024 struct hidp_session *session;
1025 int err = 0;
1026
1027 down_read(&hidp_session_sem);
1028
1029 session = __hidp_get_session(&ci->bdaddr);
1030 if (session)
1031 __hidp_copy_session(session, ci);
1032 else
1033 err = -ENOENT;
1034
1035 up_read(&hidp_session_sem);
1036 return err;
1037}
1038
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001039static const struct hid_device_id hidp_table[] = {
1040 { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
1041 { }
1042};
1043
1044static struct hid_driver hidp_driver = {
1045 .name = "generic-bluetooth",
1046 .id_table = hidp_table,
1047};
1048
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049static int __init hidp_init(void)
1050{
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001051 int ret;
1052
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
1054
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001055 ret = hid_register_driver(&hidp_driver);
1056 if (ret)
1057 goto err;
1058
1059 ret = hidp_init_sockets();
1060 if (ret)
1061 goto err_drv;
1062
1063 return 0;
1064err_drv:
1065 hid_unregister_driver(&hidp_driver);
1066err:
1067 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068}
1069
1070static void __exit hidp_exit(void)
1071{
1072 hidp_cleanup_sockets();
Jiri Slaby85cdaf52008-05-16 11:49:15 +02001073 hid_unregister_driver(&hidp_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074}
1075
1076module_init(hidp_init);
1077module_exit(hidp_exit);
1078
1079MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
1080MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
1081MODULE_VERSION(VERSION);
1082MODULE_LICENSE("GPL");
1083MODULE_ALIAS("bt-proto-6");