diff options
Diffstat (limited to 'net/bluetooth/hidp')
-rw-r--r-- | net/bluetooth/hidp/Kconfig | 12 | ||||
-rw-r--r-- | net/bluetooth/hidp/Makefile | 7 | ||||
-rw-r--r-- | net/bluetooth/hidp/core.c | 772 | ||||
-rw-r--r-- | net/bluetooth/hidp/hidp.h | 167 | ||||
-rw-r--r-- | net/bluetooth/hidp/sock.c | 232 |
5 files changed, 1190 insertions, 0 deletions
diff --git a/net/bluetooth/hidp/Kconfig b/net/bluetooth/hidp/Kconfig new file mode 100644 index 000000000000..4e958f7d9418 --- /dev/null +++ b/net/bluetooth/hidp/Kconfig | |||
@@ -0,0 +1,12 @@ | |||
1 | config BT_HIDP | ||
2 | tristate "HIDP protocol support" | ||
3 | depends on BT && BT_L2CAP | ||
4 | select INPUT | ||
5 | help | ||
6 | HIDP (Human Interface Device Protocol) is a transport layer | ||
7 | for HID reports. HIDP is required for the Bluetooth Human | ||
8 | Interface Device Profile. | ||
9 | |||
10 | Say Y here to compile HIDP support into the kernel or say M to | ||
11 | compile it as module (hidp). | ||
12 | |||
diff --git a/net/bluetooth/hidp/Makefile b/net/bluetooth/hidp/Makefile new file mode 100644 index 000000000000..a9ee115696ae --- /dev/null +++ b/net/bluetooth/hidp/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # | ||
2 | # Makefile for the Linux Bluetooth HIDP layer | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_BT_HIDP) += hidp.o | ||
6 | |||
7 | hidp-objs := core.o sock.o | ||
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c new file mode 100644 index 000000000000..2cf98ceabcc7 --- /dev/null +++ b/net/bluetooth/hidp/core.c | |||
@@ -0,0 +1,772 @@ | |||
1 | /* | ||
2 | HIDP implementation for Linux Bluetooth stack (BlueZ). | ||
3 | Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License version 2 as | ||
7 | published by the Free Software Foundation; | ||
8 | |||
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
10 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
12 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
13 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
17 | |||
18 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
19 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
20 | SOFTWARE IS DISCLAIMED. | ||
21 | */ | ||
22 | |||
23 | #include <linux/config.h> | ||
24 | #include <linux/module.h> | ||
25 | |||
26 | #include <linux/types.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/major.h> | ||
30 | #include <linux/sched.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/poll.h> | ||
33 | #include <linux/fcntl.h> | ||
34 | #include <linux/skbuff.h> | ||
35 | #include <linux/socket.h> | ||
36 | #include <linux/ioctl.h> | ||
37 | #include <linux/file.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/wait.h> | ||
40 | #include <net/sock.h> | ||
41 | |||
42 | #include <linux/input.h> | ||
43 | |||
44 | #include <net/bluetooth/bluetooth.h> | ||
45 | #include <net/bluetooth/l2cap.h> | ||
46 | |||
47 | #include "hidp.h" | ||
48 | |||
49 | #ifndef CONFIG_BT_HIDP_DEBUG | ||
50 | #undef BT_DBG | ||
51 | #define BT_DBG(D...) | ||
52 | #endif | ||
53 | |||
54 | #define VERSION "1.1" | ||
55 | |||
56 | static DECLARE_RWSEM(hidp_session_sem); | ||
57 | static LIST_HEAD(hidp_session_list); | ||
58 | |||
59 | static unsigned char hidp_keycode[256] = { | ||
60 | 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, | ||
61 | 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, | ||
62 | 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, | ||
63 | 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, | ||
64 | 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, | ||
65 | 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, | ||
66 | 72, 73, 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,136,113, | ||
68 | 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0, | ||
69 | 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
70 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
71 | 0, 0, 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, 0, 0, | ||
73 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
74 | 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, | ||
75 | 150,158,159,128,136,177,178,176,142,152,173,140 | ||
76 | }; | ||
77 | |||
78 | static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; | ||
79 | |||
80 | static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) | ||
81 | { | ||
82 | struct hidp_session *session; | ||
83 | struct list_head *p; | ||
84 | |||
85 | BT_DBG(""); | ||
86 | |||
87 | list_for_each(p, &hidp_session_list) { | ||
88 | session = list_entry(p, struct hidp_session, list); | ||
89 | if (!bacmp(bdaddr, &session->bdaddr)) | ||
90 | return session; | ||
91 | } | ||
92 | return NULL; | ||
93 | } | ||
94 | |||
95 | static void __hidp_link_session(struct hidp_session *session) | ||
96 | { | ||
97 | __module_get(THIS_MODULE); | ||
98 | list_add(&session->list, &hidp_session_list); | ||
99 | } | ||
100 | |||
101 | static void __hidp_unlink_session(struct hidp_session *session) | ||
102 | { | ||
103 | list_del(&session->list); | ||
104 | module_put(THIS_MODULE); | ||
105 | } | ||
106 | |||
107 | static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci) | ||
108 | { | ||
109 | bacpy(&ci->bdaddr, &session->bdaddr); | ||
110 | |||
111 | ci->flags = session->flags; | ||
112 | ci->state = session->state; | ||
113 | |||
114 | ci->vendor = 0x0000; | ||
115 | ci->product = 0x0000; | ||
116 | ci->version = 0x0000; | ||
117 | memset(ci->name, 0, 128); | ||
118 | |||
119 | if (session->input) { | ||
120 | ci->vendor = session->input->id.vendor; | ||
121 | ci->product = session->input->id.product; | ||
122 | ci->version = session->input->id.version; | ||
123 | if (session->input->name) | ||
124 | strncpy(ci->name, session->input->name, 128); | ||
125 | else | ||
126 | strncpy(ci->name, "HID Boot Device", 128); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | ||
131 | { | ||
132 | struct hidp_session *session = dev->private; | ||
133 | struct sk_buff *skb; | ||
134 | unsigned char newleds; | ||
135 | |||
136 | BT_DBG("input %p type %d code %d value %d", dev, type, code, value); | ||
137 | |||
138 | if (type != EV_LED) | ||
139 | return -1; | ||
140 | |||
141 | newleds = (!!test_bit(LED_KANA, dev->led) << 3) | | ||
142 | (!!test_bit(LED_COMPOSE, dev->led) << 3) | | ||
143 | (!!test_bit(LED_SCROLLL, dev->led) << 2) | | ||
144 | (!!test_bit(LED_CAPSL, dev->led) << 1) | | ||
145 | (!!test_bit(LED_NUML, dev->led)); | ||
146 | |||
147 | if (session->leds == newleds) | ||
148 | return 0; | ||
149 | |||
150 | session->leds = newleds; | ||
151 | |||
152 | if (!(skb = alloc_skb(3, GFP_ATOMIC))) { | ||
153 | BT_ERR("Can't allocate memory for new frame"); | ||
154 | return -ENOMEM; | ||
155 | } | ||
156 | |||
157 | *skb_put(skb, 1) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; | ||
158 | *skb_put(skb, 1) = 0x01; | ||
159 | *skb_put(skb, 1) = newleds; | ||
160 | |||
161 | skb_queue_tail(&session->intr_transmit, skb); | ||
162 | |||
163 | hidp_schedule(session); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) | ||
169 | { | ||
170 | struct input_dev *dev = session->input; | ||
171 | unsigned char *keys = session->keys; | ||
172 | unsigned char *udata = skb->data + 1; | ||
173 | signed char *sdata = skb->data + 1; | ||
174 | int i, size = skb->len - 1; | ||
175 | |||
176 | switch (skb->data[0]) { | ||
177 | case 0x01: /* Keyboard report */ | ||
178 | for (i = 0; i < 8; i++) | ||
179 | input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1); | ||
180 | |||
181 | /* If all the key codes have been set to 0x01, it means | ||
182 | * too many keys were pressed at the same time. */ | ||
183 | if (!memcmp(udata + 2, hidp_mkeyspat, 6)) | ||
184 | break; | ||
185 | |||
186 | for (i = 2; i < 8; i++) { | ||
187 | if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) { | ||
188 | if (hidp_keycode[keys[i]]) | ||
189 | input_report_key(dev, hidp_keycode[keys[i]], 0); | ||
190 | else | ||
191 | BT_ERR("Unknown key (scancode %#x) released.", keys[i]); | ||
192 | } | ||
193 | |||
194 | if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) { | ||
195 | if (hidp_keycode[udata[i]]) | ||
196 | input_report_key(dev, hidp_keycode[udata[i]], 1); | ||
197 | else | ||
198 | BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | memcpy(keys, udata, 8); | ||
203 | break; | ||
204 | |||
205 | case 0x02: /* Mouse report */ | ||
206 | input_report_key(dev, BTN_LEFT, sdata[0] & 0x01); | ||
207 | input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02); | ||
208 | input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04); | ||
209 | input_report_key(dev, BTN_SIDE, sdata[0] & 0x08); | ||
210 | input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10); | ||
211 | |||
212 | input_report_rel(dev, REL_X, sdata[1]); | ||
213 | input_report_rel(dev, REL_Y, sdata[2]); | ||
214 | |||
215 | if (size > 3) | ||
216 | input_report_rel(dev, REL_WHEEL, sdata[3]); | ||
217 | break; | ||
218 | } | ||
219 | |||
220 | input_sync(dev); | ||
221 | } | ||
222 | |||
223 | static void hidp_idle_timeout(unsigned long arg) | ||
224 | { | ||
225 | struct hidp_session *session = (struct hidp_session *) arg; | ||
226 | |||
227 | atomic_inc(&session->terminate); | ||
228 | hidp_schedule(session); | ||
229 | } | ||
230 | |||
231 | static inline void hidp_set_timer(struct hidp_session *session) | ||
232 | { | ||
233 | if (session->idle_to > 0) | ||
234 | mod_timer(&session->timer, jiffies + HZ * session->idle_to); | ||
235 | } | ||
236 | |||
237 | static inline void hidp_del_timer(struct hidp_session *session) | ||
238 | { | ||
239 | if (session->idle_to > 0) | ||
240 | del_timer(&session->timer); | ||
241 | } | ||
242 | |||
243 | static int __hidp_send_ctrl_message(struct hidp_session *session, | ||
244 | unsigned char hdr, unsigned char *data, int size) | ||
245 | { | ||
246 | struct sk_buff *skb; | ||
247 | |||
248 | BT_DBG("session %p data %p size %d", session, data, size); | ||
249 | |||
250 | if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) { | ||
251 | BT_ERR("Can't allocate memory for new frame"); | ||
252 | return -ENOMEM; | ||
253 | } | ||
254 | |||
255 | *skb_put(skb, 1) = hdr; | ||
256 | if (data && size > 0) | ||
257 | memcpy(skb_put(skb, size), data, size); | ||
258 | |||
259 | skb_queue_tail(&session->ctrl_transmit, skb); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int inline hidp_send_ctrl_message(struct hidp_session *session, | ||
265 | unsigned char hdr, unsigned char *data, int size) | ||
266 | { | ||
267 | int err; | ||
268 | |||
269 | err = __hidp_send_ctrl_message(session, hdr, data, size); | ||
270 | |||
271 | hidp_schedule(session); | ||
272 | |||
273 | return err; | ||
274 | } | ||
275 | |||
276 | static inline void hidp_process_handshake(struct hidp_session *session, unsigned char param) | ||
277 | { | ||
278 | BT_DBG("session %p param 0x%02x", session, param); | ||
279 | |||
280 | switch (param) { | ||
281 | case HIDP_HSHK_SUCCESSFUL: | ||
282 | /* FIXME: Call into SET_ GET_ handlers here */ | ||
283 | break; | ||
284 | |||
285 | case HIDP_HSHK_NOT_READY: | ||
286 | case HIDP_HSHK_ERR_INVALID_REPORT_ID: | ||
287 | case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: | ||
288 | case HIDP_HSHK_ERR_INVALID_PARAMETER: | ||
289 | /* FIXME: Call into SET_ GET_ handlers here */ | ||
290 | break; | ||
291 | |||
292 | case HIDP_HSHK_ERR_UNKNOWN: | ||
293 | break; | ||
294 | |||
295 | case HIDP_HSHK_ERR_FATAL: | ||
296 | /* Device requests a reboot, as this is the only way this error | ||
297 | * can be recovered. */ | ||
298 | __hidp_send_ctrl_message(session, | ||
299 | HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0); | ||
300 | break; | ||
301 | |||
302 | default: | ||
303 | __hidp_send_ctrl_message(session, | ||
304 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | ||
305 | break; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | static inline void hidp_process_hid_control(struct hidp_session *session, unsigned char param) | ||
310 | { | ||
311 | BT_DBG("session %p param 0x%02x", session, param); | ||
312 | |||
313 | switch (param) { | ||
314 | case HIDP_CTRL_NOP: | ||
315 | break; | ||
316 | |||
317 | case HIDP_CTRL_VIRTUAL_CABLE_UNPLUG: | ||
318 | /* Flush the transmit queues */ | ||
319 | skb_queue_purge(&session->ctrl_transmit); | ||
320 | skb_queue_purge(&session->intr_transmit); | ||
321 | |||
322 | /* Kill session thread */ | ||
323 | atomic_inc(&session->terminate); | ||
324 | break; | ||
325 | |||
326 | case HIDP_CTRL_HARD_RESET: | ||
327 | case HIDP_CTRL_SOFT_RESET: | ||
328 | case HIDP_CTRL_SUSPEND: | ||
329 | case HIDP_CTRL_EXIT_SUSPEND: | ||
330 | /* FIXME: We have to parse these and return no error */ | ||
331 | break; | ||
332 | |||
333 | default: | ||
334 | __hidp_send_ctrl_message(session, | ||
335 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | ||
336 | break; | ||
337 | } | ||
338 | } | ||
339 | |||
340 | static inline void hidp_process_data(struct hidp_session *session, struct sk_buff *skb, unsigned char param) | ||
341 | { | ||
342 | BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); | ||
343 | |||
344 | switch (param) { | ||
345 | case HIDP_DATA_RTYPE_INPUT: | ||
346 | hidp_set_timer(session); | ||
347 | |||
348 | if (session->input) | ||
349 | hidp_input_report(session, skb); | ||
350 | break; | ||
351 | |||
352 | case HIDP_DATA_RTYPE_OTHER: | ||
353 | case HIDP_DATA_RTYPE_OUPUT: | ||
354 | case HIDP_DATA_RTYPE_FEATURE: | ||
355 | break; | ||
356 | |||
357 | default: | ||
358 | __hidp_send_ctrl_message(session, | ||
359 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | static inline void hidp_recv_ctrl_frame(struct hidp_session *session, struct sk_buff *skb) | ||
364 | { | ||
365 | unsigned char hdr, type, param; | ||
366 | |||
367 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); | ||
368 | |||
369 | hdr = skb->data[0]; | ||
370 | skb_pull(skb, 1); | ||
371 | |||
372 | type = hdr & HIDP_HEADER_TRANS_MASK; | ||
373 | param = hdr & HIDP_HEADER_PARAM_MASK; | ||
374 | |||
375 | switch (type) { | ||
376 | case HIDP_TRANS_HANDSHAKE: | ||
377 | hidp_process_handshake(session, param); | ||
378 | break; | ||
379 | |||
380 | case HIDP_TRANS_HID_CONTROL: | ||
381 | hidp_process_hid_control(session, param); | ||
382 | break; | ||
383 | |||
384 | case HIDP_TRANS_DATA: | ||
385 | hidp_process_data(session, skb, param); | ||
386 | break; | ||
387 | |||
388 | default: | ||
389 | __hidp_send_ctrl_message(session, | ||
390 | HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0); | ||
391 | break; | ||
392 | } | ||
393 | |||
394 | kfree_skb(skb); | ||
395 | } | ||
396 | |||
397 | static inline void hidp_recv_intr_frame(struct hidp_session *session, struct sk_buff *skb) | ||
398 | { | ||
399 | unsigned char hdr; | ||
400 | |||
401 | BT_DBG("session %p skb %p len %d", session, skb, skb->len); | ||
402 | |||
403 | hdr = skb->data[0]; | ||
404 | skb_pull(skb, 1); | ||
405 | |||
406 | if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { | ||
407 | hidp_set_timer(session); | ||
408 | if (session->input) | ||
409 | hidp_input_report(session, skb); | ||
410 | } else { | ||
411 | BT_DBG("Unsupported protocol header 0x%02x", hdr); | ||
412 | } | ||
413 | |||
414 | kfree_skb(skb); | ||
415 | } | ||
416 | |||
417 | static int hidp_send_frame(struct socket *sock, unsigned char *data, int len) | ||
418 | { | ||
419 | struct kvec iv = { data, len }; | ||
420 | struct msghdr msg; | ||
421 | |||
422 | BT_DBG("sock %p data %p len %d", sock, data, len); | ||
423 | |||
424 | if (!len) | ||
425 | return 0; | ||
426 | |||
427 | memset(&msg, 0, sizeof(msg)); | ||
428 | |||
429 | return kernel_sendmsg(sock, &msg, &iv, 1, len); | ||
430 | } | ||
431 | |||
432 | static int hidp_process_transmit(struct hidp_session *session) | ||
433 | { | ||
434 | struct sk_buff *skb; | ||
435 | |||
436 | BT_DBG("session %p", session); | ||
437 | |||
438 | while ((skb = skb_dequeue(&session->ctrl_transmit))) { | ||
439 | if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) { | ||
440 | skb_queue_head(&session->ctrl_transmit, skb); | ||
441 | break; | ||
442 | } | ||
443 | |||
444 | hidp_set_timer(session); | ||
445 | kfree_skb(skb); | ||
446 | } | ||
447 | |||
448 | while ((skb = skb_dequeue(&session->intr_transmit))) { | ||
449 | if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) { | ||
450 | skb_queue_head(&session->intr_transmit, skb); | ||
451 | break; | ||
452 | } | ||
453 | |||
454 | hidp_set_timer(session); | ||
455 | kfree_skb(skb); | ||
456 | } | ||
457 | |||
458 | return skb_queue_len(&session->ctrl_transmit) + | ||
459 | skb_queue_len(&session->intr_transmit); | ||
460 | } | ||
461 | |||
462 | static int hidp_session(void *arg) | ||
463 | { | ||
464 | struct hidp_session *session = arg; | ||
465 | struct sock *ctrl_sk = session->ctrl_sock->sk; | ||
466 | struct sock *intr_sk = session->intr_sock->sk; | ||
467 | struct sk_buff *skb; | ||
468 | int vendor = 0x0000, product = 0x0000; | ||
469 | wait_queue_t ctrl_wait, intr_wait; | ||
470 | |||
471 | BT_DBG("session %p", session); | ||
472 | |||
473 | if (session->input) { | ||
474 | vendor = session->input->id.vendor; | ||
475 | product = session->input->id.product; | ||
476 | } | ||
477 | |||
478 | daemonize("khidpd_%04x%04x", vendor, product); | ||
479 | set_user_nice(current, -15); | ||
480 | current->flags |= PF_NOFREEZE; | ||
481 | |||
482 | init_waitqueue_entry(&ctrl_wait, current); | ||
483 | init_waitqueue_entry(&intr_wait, current); | ||
484 | add_wait_queue(ctrl_sk->sk_sleep, &ctrl_wait); | ||
485 | add_wait_queue(intr_sk->sk_sleep, &intr_wait); | ||
486 | while (!atomic_read(&session->terminate)) { | ||
487 | set_current_state(TASK_INTERRUPTIBLE); | ||
488 | |||
489 | if (ctrl_sk->sk_state != BT_CONNECTED || intr_sk->sk_state != BT_CONNECTED) | ||
490 | break; | ||
491 | |||
492 | while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { | ||
493 | skb_orphan(skb); | ||
494 | hidp_recv_ctrl_frame(session, skb); | ||
495 | } | ||
496 | |||
497 | while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { | ||
498 | skb_orphan(skb); | ||
499 | hidp_recv_intr_frame(session, skb); | ||
500 | } | ||
501 | |||
502 | hidp_process_transmit(session); | ||
503 | |||
504 | schedule(); | ||
505 | } | ||
506 | set_current_state(TASK_RUNNING); | ||
507 | remove_wait_queue(intr_sk->sk_sleep, &intr_wait); | ||
508 | remove_wait_queue(ctrl_sk->sk_sleep, &ctrl_wait); | ||
509 | |||
510 | down_write(&hidp_session_sem); | ||
511 | |||
512 | hidp_del_timer(session); | ||
513 | |||
514 | if (intr_sk->sk_state != BT_CONNECTED) | ||
515 | wait_event_timeout(*(ctrl_sk->sk_sleep), (ctrl_sk->sk_state == BT_CLOSED), HZ); | ||
516 | |||
517 | fput(session->ctrl_sock->file); | ||
518 | |||
519 | wait_event_timeout(*(intr_sk->sk_sleep), (intr_sk->sk_state == BT_CLOSED), HZ); | ||
520 | |||
521 | fput(session->intr_sock->file); | ||
522 | |||
523 | __hidp_unlink_session(session); | ||
524 | |||
525 | if (session->input) { | ||
526 | input_unregister_device(session->input); | ||
527 | kfree(session->input); | ||
528 | } | ||
529 | |||
530 | up_write(&hidp_session_sem); | ||
531 | |||
532 | kfree(session); | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static inline void hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req) | ||
537 | { | ||
538 | struct input_dev *input = session->input; | ||
539 | int i; | ||
540 | |||
541 | input->private = session; | ||
542 | |||
543 | input->id.bustype = BUS_BLUETOOTH; | ||
544 | input->id.vendor = req->vendor; | ||
545 | input->id.product = req->product; | ||
546 | input->id.version = req->version; | ||
547 | |||
548 | if (req->subclass & 0x40) { | ||
549 | set_bit(EV_KEY, input->evbit); | ||
550 | set_bit(EV_LED, input->evbit); | ||
551 | set_bit(EV_REP, input->evbit); | ||
552 | |||
553 | set_bit(LED_NUML, input->ledbit); | ||
554 | set_bit(LED_CAPSL, input->ledbit); | ||
555 | set_bit(LED_SCROLLL, input->ledbit); | ||
556 | set_bit(LED_COMPOSE, input->ledbit); | ||
557 | set_bit(LED_KANA, input->ledbit); | ||
558 | |||
559 | for (i = 0; i < sizeof(hidp_keycode); i++) | ||
560 | set_bit(hidp_keycode[i], input->keybit); | ||
561 | clear_bit(0, input->keybit); | ||
562 | } | ||
563 | |||
564 | if (req->subclass & 0x80) { | ||
565 | input->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | ||
566 | input->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); | ||
567 | input->relbit[0] = BIT(REL_X) | BIT(REL_Y); | ||
568 | input->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); | ||
569 | input->relbit[0] |= BIT(REL_WHEEL); | ||
570 | } | ||
571 | |||
572 | input->event = hidp_input_event; | ||
573 | |||
574 | input_register_device(input); | ||
575 | } | ||
576 | |||
577 | int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) | ||
578 | { | ||
579 | struct hidp_session *session, *s; | ||
580 | int err; | ||
581 | |||
582 | BT_DBG(""); | ||
583 | |||
584 | if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) || | ||
585 | bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst)) | ||
586 | return -ENOTUNIQ; | ||
587 | |||
588 | session = kmalloc(sizeof(struct hidp_session), GFP_KERNEL); | ||
589 | if (!session) | ||
590 | return -ENOMEM; | ||
591 | memset(session, 0, sizeof(struct hidp_session)); | ||
592 | |||
593 | session->input = kmalloc(sizeof(struct input_dev), GFP_KERNEL); | ||
594 | if (!session->input) { | ||
595 | kfree(session); | ||
596 | return -ENOMEM; | ||
597 | } | ||
598 | memset(session->input, 0, sizeof(struct input_dev)); | ||
599 | |||
600 | down_write(&hidp_session_sem); | ||
601 | |||
602 | s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); | ||
603 | if (s && s->state == BT_CONNECTED) { | ||
604 | err = -EEXIST; | ||
605 | goto failed; | ||
606 | } | ||
607 | |||
608 | bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); | ||
609 | |||
610 | session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu); | ||
611 | session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu); | ||
612 | |||
613 | BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu); | ||
614 | |||
615 | session->ctrl_sock = ctrl_sock; | ||
616 | session->intr_sock = intr_sock; | ||
617 | session->state = BT_CONNECTED; | ||
618 | |||
619 | init_timer(&session->timer); | ||
620 | |||
621 | session->timer.function = hidp_idle_timeout; | ||
622 | session->timer.data = (unsigned long) session; | ||
623 | |||
624 | skb_queue_head_init(&session->ctrl_transmit); | ||
625 | skb_queue_head_init(&session->intr_transmit); | ||
626 | |||
627 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); | ||
628 | session->idle_to = req->idle_to; | ||
629 | |||
630 | if (session->input) | ||
631 | hidp_setup_input(session, req); | ||
632 | |||
633 | __hidp_link_session(session); | ||
634 | |||
635 | hidp_set_timer(session); | ||
636 | |||
637 | err = kernel_thread(hidp_session, session, CLONE_KERNEL); | ||
638 | if (err < 0) | ||
639 | goto unlink; | ||
640 | |||
641 | if (session->input) { | ||
642 | hidp_send_ctrl_message(session, | ||
643 | HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0); | ||
644 | session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); | ||
645 | |||
646 | session->leds = 0xff; | ||
647 | hidp_input_event(session->input, EV_LED, 0, 0); | ||
648 | } | ||
649 | |||
650 | up_write(&hidp_session_sem); | ||
651 | return 0; | ||
652 | |||
653 | unlink: | ||
654 | hidp_del_timer(session); | ||
655 | |||
656 | __hidp_unlink_session(session); | ||
657 | |||
658 | if (session->input) | ||
659 | input_unregister_device(session->input); | ||
660 | |||
661 | failed: | ||
662 | up_write(&hidp_session_sem); | ||
663 | |||
664 | if (session->input) | ||
665 | kfree(session->input); | ||
666 | |||
667 | kfree(session); | ||
668 | return err; | ||
669 | } | ||
670 | |||
671 | int hidp_del_connection(struct hidp_conndel_req *req) | ||
672 | { | ||
673 | struct hidp_session *session; | ||
674 | int err = 0; | ||
675 | |||
676 | BT_DBG(""); | ||
677 | |||
678 | down_read(&hidp_session_sem); | ||
679 | |||
680 | session = __hidp_get_session(&req->bdaddr); | ||
681 | if (session) { | ||
682 | if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) { | ||
683 | hidp_send_ctrl_message(session, | ||
684 | HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0); | ||
685 | } else { | ||
686 | /* Flush the transmit queues */ | ||
687 | skb_queue_purge(&session->ctrl_transmit); | ||
688 | skb_queue_purge(&session->intr_transmit); | ||
689 | |||
690 | /* Kill session thread */ | ||
691 | atomic_inc(&session->terminate); | ||
692 | hidp_schedule(session); | ||
693 | } | ||
694 | } else | ||
695 | err = -ENOENT; | ||
696 | |||
697 | up_read(&hidp_session_sem); | ||
698 | return err; | ||
699 | } | ||
700 | |||
701 | int hidp_get_connlist(struct hidp_connlist_req *req) | ||
702 | { | ||
703 | struct list_head *p; | ||
704 | int err = 0, n = 0; | ||
705 | |||
706 | BT_DBG(""); | ||
707 | |||
708 | down_read(&hidp_session_sem); | ||
709 | |||
710 | list_for_each(p, &hidp_session_list) { | ||
711 | struct hidp_session *session; | ||
712 | struct hidp_conninfo ci; | ||
713 | |||
714 | session = list_entry(p, struct hidp_session, list); | ||
715 | |||
716 | __hidp_copy_session(session, &ci); | ||
717 | |||
718 | if (copy_to_user(req->ci, &ci, sizeof(ci))) { | ||
719 | err = -EFAULT; | ||
720 | break; | ||
721 | } | ||
722 | |||
723 | if (++n >= req->cnum) | ||
724 | break; | ||
725 | |||
726 | req->ci++; | ||
727 | } | ||
728 | req->cnum = n; | ||
729 | |||
730 | up_read(&hidp_session_sem); | ||
731 | return err; | ||
732 | } | ||
733 | |||
734 | int hidp_get_conninfo(struct hidp_conninfo *ci) | ||
735 | { | ||
736 | struct hidp_session *session; | ||
737 | int err = 0; | ||
738 | |||
739 | down_read(&hidp_session_sem); | ||
740 | |||
741 | session = __hidp_get_session(&ci->bdaddr); | ||
742 | if (session) | ||
743 | __hidp_copy_session(session, ci); | ||
744 | else | ||
745 | err = -ENOENT; | ||
746 | |||
747 | up_read(&hidp_session_sem); | ||
748 | return err; | ||
749 | } | ||
750 | |||
751 | static int __init hidp_init(void) | ||
752 | { | ||
753 | l2cap_load(); | ||
754 | |||
755 | BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); | ||
756 | |||
757 | return hidp_init_sockets(); | ||
758 | } | ||
759 | |||
760 | static void __exit hidp_exit(void) | ||
761 | { | ||
762 | hidp_cleanup_sockets(); | ||
763 | } | ||
764 | |||
765 | module_init(hidp_init); | ||
766 | module_exit(hidp_exit); | ||
767 | |||
768 | MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); | ||
769 | MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION); | ||
770 | MODULE_VERSION(VERSION); | ||
771 | MODULE_LICENSE("GPL"); | ||
772 | MODULE_ALIAS("bt-proto-6"); | ||
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h new file mode 100644 index 000000000000..c2775f587d2e --- /dev/null +++ b/net/bluetooth/hidp/hidp.h | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | HIDP implementation for Linux Bluetooth stack (BlueZ). | ||
3 | Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License version 2 as | ||
7 | published by the Free Software Foundation; | ||
8 | |||
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
10 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
12 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
13 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
17 | |||
18 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
19 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
20 | SOFTWARE IS DISCLAIMED. | ||
21 | */ | ||
22 | |||
23 | #ifndef __HIDP_H | ||
24 | #define __HIDP_H | ||
25 | |||
26 | #include <linux/types.h> | ||
27 | #include <net/bluetooth/bluetooth.h> | ||
28 | |||
29 | /* HIDP header masks */ | ||
30 | #define HIDP_HEADER_TRANS_MASK 0xf0 | ||
31 | #define HIDP_HEADER_PARAM_MASK 0x0f | ||
32 | |||
33 | /* HIDP transaction types */ | ||
34 | #define HIDP_TRANS_HANDSHAKE 0x00 | ||
35 | #define HIDP_TRANS_HID_CONTROL 0x10 | ||
36 | #define HIDP_TRANS_GET_REPORT 0x40 | ||
37 | #define HIDP_TRANS_SET_REPORT 0x50 | ||
38 | #define HIDP_TRANS_GET_PROTOCOL 0x60 | ||
39 | #define HIDP_TRANS_SET_PROTOCOL 0x70 | ||
40 | #define HIDP_TRANS_GET_IDLE 0x80 | ||
41 | #define HIDP_TRANS_SET_IDLE 0x90 | ||
42 | #define HIDP_TRANS_DATA 0xa0 | ||
43 | #define HIDP_TRANS_DATC 0xb0 | ||
44 | |||
45 | /* HIDP handshake results */ | ||
46 | #define HIDP_HSHK_SUCCESSFUL 0x00 | ||
47 | #define HIDP_HSHK_NOT_READY 0x01 | ||
48 | #define HIDP_HSHK_ERR_INVALID_REPORT_ID 0x02 | ||
49 | #define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST 0x03 | ||
50 | #define HIDP_HSHK_ERR_INVALID_PARAMETER 0x04 | ||
51 | #define HIDP_HSHK_ERR_UNKNOWN 0x0e | ||
52 | #define HIDP_HSHK_ERR_FATAL 0x0f | ||
53 | |||
54 | /* HIDP control operation parameters */ | ||
55 | #define HIDP_CTRL_NOP 0x00 | ||
56 | #define HIDP_CTRL_HARD_RESET 0x01 | ||
57 | #define HIDP_CTRL_SOFT_RESET 0x02 | ||
58 | #define HIDP_CTRL_SUSPEND 0x03 | ||
59 | #define HIDP_CTRL_EXIT_SUSPEND 0x04 | ||
60 | #define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG 0x05 | ||
61 | |||
62 | /* HIDP data transaction headers */ | ||
63 | #define HIDP_DATA_RTYPE_MASK 0x03 | ||
64 | #define HIDP_DATA_RSRVD_MASK 0x0c | ||
65 | #define HIDP_DATA_RTYPE_OTHER 0x00 | ||
66 | #define HIDP_DATA_RTYPE_INPUT 0x01 | ||
67 | #define HIDP_DATA_RTYPE_OUPUT 0x02 | ||
68 | #define HIDP_DATA_RTYPE_FEATURE 0x03 | ||
69 | |||
70 | /* HIDP protocol header parameters */ | ||
71 | #define HIDP_PROTO_BOOT 0x00 | ||
72 | #define HIDP_PROTO_REPORT 0x01 | ||
73 | |||
74 | /* HIDP ioctl defines */ | ||
75 | #define HIDPCONNADD _IOW('H', 200, int) | ||
76 | #define HIDPCONNDEL _IOW('H', 201, int) | ||
77 | #define HIDPGETCONNLIST _IOR('H', 210, int) | ||
78 | #define HIDPGETCONNINFO _IOR('H', 211, int) | ||
79 | |||
80 | #define HIDP_VIRTUAL_CABLE_UNPLUG 0 | ||
81 | #define HIDP_BOOT_PROTOCOL_MODE 1 | ||
82 | #define HIDP_BLUETOOTH_VENDOR_ID 9 | ||
83 | |||
84 | struct hidp_connadd_req { | ||
85 | int ctrl_sock; // Connected control socket | ||
86 | int intr_sock; // Connteted interrupt socket | ||
87 | __u16 parser; | ||
88 | __u16 rd_size; | ||
89 | __u8 *rd_data; | ||
90 | __u8 country; | ||
91 | __u8 subclass; | ||
92 | __u16 vendor; | ||
93 | __u16 product; | ||
94 | __u16 version; | ||
95 | __u32 flags; | ||
96 | __u32 idle_to; | ||
97 | char name[128]; | ||
98 | }; | ||
99 | |||
100 | struct hidp_conndel_req { | ||
101 | bdaddr_t bdaddr; | ||
102 | __u32 flags; | ||
103 | }; | ||
104 | |||
105 | struct hidp_conninfo { | ||
106 | bdaddr_t bdaddr; | ||
107 | __u32 flags; | ||
108 | __u16 state; | ||
109 | __u16 vendor; | ||
110 | __u16 product; | ||
111 | __u16 version; | ||
112 | char name[128]; | ||
113 | }; | ||
114 | |||
115 | struct hidp_connlist_req { | ||
116 | __u32 cnum; | ||
117 | struct hidp_conninfo __user *ci; | ||
118 | }; | ||
119 | |||
120 | int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock); | ||
121 | int hidp_del_connection(struct hidp_conndel_req *req); | ||
122 | int hidp_get_connlist(struct hidp_connlist_req *req); | ||
123 | int hidp_get_conninfo(struct hidp_conninfo *ci); | ||
124 | |||
125 | /* HIDP session defines */ | ||
126 | struct hidp_session { | ||
127 | struct list_head list; | ||
128 | |||
129 | struct socket *ctrl_sock; | ||
130 | struct socket *intr_sock; | ||
131 | |||
132 | bdaddr_t bdaddr; | ||
133 | |||
134 | unsigned long state; | ||
135 | unsigned long flags; | ||
136 | unsigned long idle_to; | ||
137 | |||
138 | uint ctrl_mtu; | ||
139 | uint intr_mtu; | ||
140 | |||
141 | atomic_t terminate; | ||
142 | |||
143 | unsigned char keys[8]; | ||
144 | unsigned char leds; | ||
145 | |||
146 | struct input_dev *input; | ||
147 | |||
148 | struct timer_list timer; | ||
149 | |||
150 | struct sk_buff_head ctrl_transmit; | ||
151 | struct sk_buff_head intr_transmit; | ||
152 | }; | ||
153 | |||
154 | static inline void hidp_schedule(struct hidp_session *session) | ||
155 | { | ||
156 | struct sock *ctrl_sk = session->ctrl_sock->sk; | ||
157 | struct sock *intr_sk = session->intr_sock->sk; | ||
158 | |||
159 | wake_up_interruptible(ctrl_sk->sk_sleep); | ||
160 | wake_up_interruptible(intr_sk->sk_sleep); | ||
161 | } | ||
162 | |||
163 | /* HIDP init defines */ | ||
164 | extern int __init hidp_init_sockets(void); | ||
165 | extern void __exit hidp_cleanup_sockets(void); | ||
166 | |||
167 | #endif /* __HIDP_H */ | ||
diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c new file mode 100644 index 000000000000..fabb36d4666b --- /dev/null +++ b/net/bluetooth/hidp/sock.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | HIDP implementation for Linux Bluetooth stack (BlueZ). | ||
3 | Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License version 2 as | ||
7 | published by the Free Software Foundation; | ||
8 | |||
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
10 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
12 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
13 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
17 | |||
18 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
19 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
20 | SOFTWARE IS DISCLAIMED. | ||
21 | */ | ||
22 | |||
23 | #include <linux/config.h> | ||
24 | #include <linux/module.h> | ||
25 | |||
26 | #include <linux/types.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/major.h> | ||
30 | #include <linux/sched.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/poll.h> | ||
33 | #include <linux/fcntl.h> | ||
34 | #include <linux/skbuff.h> | ||
35 | #include <linux/socket.h> | ||
36 | #include <linux/ioctl.h> | ||
37 | #include <linux/file.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <net/sock.h> | ||
40 | |||
41 | #include "hidp.h" | ||
42 | |||
43 | #ifndef CONFIG_BT_HIDP_DEBUG | ||
44 | #undef BT_DBG | ||
45 | #define BT_DBG(D...) | ||
46 | #endif | ||
47 | |||
48 | static int hidp_sock_release(struct socket *sock) | ||
49 | { | ||
50 | struct sock *sk = sock->sk; | ||
51 | |||
52 | BT_DBG("sock %p sk %p", sock, sk); | ||
53 | |||
54 | if (!sk) | ||
55 | return 0; | ||
56 | |||
57 | sock_orphan(sk); | ||
58 | sock_put(sk); | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) | ||
64 | { | ||
65 | void __user *argp = (void __user *) arg; | ||
66 | struct hidp_connadd_req ca; | ||
67 | struct hidp_conndel_req cd; | ||
68 | struct hidp_connlist_req cl; | ||
69 | struct hidp_conninfo ci; | ||
70 | struct socket *csock; | ||
71 | struct socket *isock; | ||
72 | int err; | ||
73 | |||
74 | BT_DBG("cmd %x arg %lx", cmd, arg); | ||
75 | |||
76 | switch (cmd) { | ||
77 | case HIDPCONNADD: | ||
78 | if (!capable(CAP_NET_ADMIN)) | ||
79 | return -EACCES; | ||
80 | |||
81 | if (copy_from_user(&ca, argp, sizeof(ca))) | ||
82 | return -EFAULT; | ||
83 | |||
84 | csock = sockfd_lookup(ca.ctrl_sock, &err); | ||
85 | if (!csock) | ||
86 | return err; | ||
87 | |||
88 | isock = sockfd_lookup(ca.intr_sock, &err); | ||
89 | if (!isock) { | ||
90 | fput(csock->file); | ||
91 | return err; | ||
92 | } | ||
93 | |||
94 | if (csock->sk->sk_state != BT_CONNECTED || isock->sk->sk_state != BT_CONNECTED) { | ||
95 | fput(csock->file); | ||
96 | fput(isock->file); | ||
97 | return -EBADFD; | ||
98 | } | ||
99 | |||
100 | err = hidp_add_connection(&ca, csock, isock); | ||
101 | if (!err) { | ||
102 | if (copy_to_user(argp, &ca, sizeof(ca))) | ||
103 | err = -EFAULT; | ||
104 | } else { | ||
105 | fput(csock->file); | ||
106 | fput(isock->file); | ||
107 | } | ||
108 | |||
109 | return err; | ||
110 | |||
111 | case HIDPCONNDEL: | ||
112 | if (!capable(CAP_NET_ADMIN)) | ||
113 | return -EACCES; | ||
114 | |||
115 | if (copy_from_user(&cd, argp, sizeof(cd))) | ||
116 | return -EFAULT; | ||
117 | |||
118 | return hidp_del_connection(&cd); | ||
119 | |||
120 | case HIDPGETCONNLIST: | ||
121 | if (copy_from_user(&cl, argp, sizeof(cl))) | ||
122 | return -EFAULT; | ||
123 | |||
124 | if (cl.cnum <= 0) | ||
125 | return -EINVAL; | ||
126 | |||
127 | err = hidp_get_connlist(&cl); | ||
128 | if (!err && copy_to_user(argp, &cl, sizeof(cl))) | ||
129 | return -EFAULT; | ||
130 | |||
131 | return err; | ||
132 | |||
133 | case HIDPGETCONNINFO: | ||
134 | if (copy_from_user(&ci, argp, sizeof(ci))) | ||
135 | return -EFAULT; | ||
136 | |||
137 | err = hidp_get_conninfo(&ci); | ||
138 | if (!err && copy_to_user(argp, &ci, sizeof(ci))) | ||
139 | return -EFAULT; | ||
140 | |||
141 | return err; | ||
142 | } | ||
143 | |||
144 | return -EINVAL; | ||
145 | } | ||
146 | |||
147 | static struct proto_ops hidp_sock_ops = { | ||
148 | .family = PF_BLUETOOTH, | ||
149 | .owner = THIS_MODULE, | ||
150 | .release = hidp_sock_release, | ||
151 | .ioctl = hidp_sock_ioctl, | ||
152 | .bind = sock_no_bind, | ||
153 | .getname = sock_no_getname, | ||
154 | .sendmsg = sock_no_sendmsg, | ||
155 | .recvmsg = sock_no_recvmsg, | ||
156 | .poll = sock_no_poll, | ||
157 | .listen = sock_no_listen, | ||
158 | .shutdown = sock_no_shutdown, | ||
159 | .setsockopt = sock_no_setsockopt, | ||
160 | .getsockopt = sock_no_getsockopt, | ||
161 | .connect = sock_no_connect, | ||
162 | .socketpair = sock_no_socketpair, | ||
163 | .accept = sock_no_accept, | ||
164 | .mmap = sock_no_mmap | ||
165 | }; | ||
166 | |||
167 | static struct proto hidp_proto = { | ||
168 | .name = "HIDP", | ||
169 | .owner = THIS_MODULE, | ||
170 | .obj_size = sizeof(struct bt_sock) | ||
171 | }; | ||
172 | |||
173 | static int hidp_sock_create(struct socket *sock, int protocol) | ||
174 | { | ||
175 | struct sock *sk; | ||
176 | |||
177 | BT_DBG("sock %p", sock); | ||
178 | |||
179 | if (sock->type != SOCK_RAW) | ||
180 | return -ESOCKTNOSUPPORT; | ||
181 | |||
182 | sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, &hidp_proto, 1); | ||
183 | if (!sk) | ||
184 | return -ENOMEM; | ||
185 | |||
186 | sock_init_data(sock, sk); | ||
187 | |||
188 | sock->ops = &hidp_sock_ops; | ||
189 | |||
190 | sock->state = SS_UNCONNECTED; | ||
191 | |||
192 | sock_reset_flag(sk, SOCK_ZAPPED); | ||
193 | |||
194 | sk->sk_protocol = protocol; | ||
195 | sk->sk_state = BT_OPEN; | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static struct net_proto_family hidp_sock_family_ops = { | ||
201 | .family = PF_BLUETOOTH, | ||
202 | .owner = THIS_MODULE, | ||
203 | .create = hidp_sock_create | ||
204 | }; | ||
205 | |||
206 | int __init hidp_init_sockets(void) | ||
207 | { | ||
208 | int err; | ||
209 | |||
210 | err = proto_register(&hidp_proto, 0); | ||
211 | if (err < 0) | ||
212 | return err; | ||
213 | |||
214 | err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops); | ||
215 | if (err < 0) | ||
216 | goto error; | ||
217 | |||
218 | return 0; | ||
219 | |||
220 | error: | ||
221 | BT_ERR("Can't register HIDP socket"); | ||
222 | proto_unregister(&hidp_proto); | ||
223 | return err; | ||
224 | } | ||
225 | |||
226 | void __exit hidp_cleanup_sockets(void) | ||
227 | { | ||
228 | if (bt_sock_unregister(BTPROTO_HIDP) < 0) | ||
229 | BT_ERR("Can't unregister HIDP socket"); | ||
230 | |||
231 | proto_unregister(&hidp_proto); | ||
232 | } | ||