1 | /* $NetBSD: bthidev.c,v 1.29 2014/08/05 07:55:31 rtr Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2006 Itronix Inc. |
5 | * All rights reserved. |
6 | * |
7 | * Written by Iain Hibbert for Itronix Inc. |
8 | * |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions |
11 | * are met: |
12 | * 1. Redistributions of source code must retain the above copyright |
13 | * notice, this list of conditions and the following disclaimer. |
14 | * 2. Redistributions in binary form must reproduce the above copyright |
15 | * notice, this list of conditions and the following disclaimer in the |
16 | * documentation and/or other materials provided with the distribution. |
17 | * 3. The name of Itronix Inc. may not be used to endorse |
18 | * or promote products derived from this software without specific |
19 | * prior written permission. |
20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY |
25 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
28 | * ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 | * POSSIBILITY OF SUCH DAMAGE. |
32 | */ |
33 | |
34 | #include <sys/cdefs.h> |
35 | __KERNEL_RCSID(0, "$NetBSD: bthidev.c,v 1.29 2014/08/05 07:55:31 rtr Exp $" ); |
36 | |
37 | #include <sys/param.h> |
38 | #include <sys/condvar.h> |
39 | #include <sys/conf.h> |
40 | #include <sys/device.h> |
41 | #include <sys/fcntl.h> |
42 | #include <sys/kernel.h> |
43 | #include <sys/kthread.h> |
44 | #include <sys/queue.h> |
45 | #include <sys/malloc.h> |
46 | #include <sys/mbuf.h> |
47 | #include <sys/mutex.h> |
48 | #include <sys/proc.h> |
49 | #include <sys/socketvar.h> |
50 | #include <sys/systm.h> |
51 | |
52 | #include <prop/proplib.h> |
53 | |
54 | #include <netbt/bluetooth.h> |
55 | #include <netbt/l2cap.h> |
56 | |
57 | #include <dev/usb/hid.h> |
58 | #include <dev/bluetooth/btdev.h> |
59 | #include <dev/bluetooth/bthid.h> |
60 | #include <dev/bluetooth/bthidev.h> |
61 | |
62 | #include "locators.h" |
63 | |
64 | /***************************************************************************** |
65 | * |
66 | * Bluetooth HID device |
67 | */ |
68 | |
69 | #define MAX_DESCRIPTOR_LEN 1024 /* sanity check */ |
70 | |
71 | /* bthidev softc */ |
72 | struct bthidev_softc { |
73 | uint16_t sc_state; |
74 | uint16_t sc_flags; |
75 | device_t sc_dev; |
76 | |
77 | bdaddr_t sc_laddr; /* local address */ |
78 | bdaddr_t sc_raddr; /* remote address */ |
79 | struct sockopt sc_mode; /* link mode sockopt */ |
80 | |
81 | uint16_t sc_ctlpsm; /* control PSM */ |
82 | struct l2cap_channel *sc_ctl; /* control channel */ |
83 | struct l2cap_channel *sc_ctl_l; /* control listen */ |
84 | |
85 | uint16_t sc_intpsm; /* interrupt PSM */ |
86 | struct l2cap_channel *sc_int; /* interrupt channel */ |
87 | struct l2cap_channel *sc_int_l; /* interrupt listen */ |
88 | |
89 | MBUFQ_HEAD() sc_inq; /* input queue */ |
90 | kmutex_t sc_lock; /* input queue lock */ |
91 | kcondvar_t sc_cv; /* input queue trigger */ |
92 | lwp_t *sc_lwp; /* input queue processor */ |
93 | int sc_detach; |
94 | |
95 | LIST_HEAD(,bthidev) sc_list; /* child list */ |
96 | |
97 | callout_t sc_reconnect; |
98 | int sc_attempts; /* connection attempts */ |
99 | }; |
100 | |
101 | /* sc_flags */ |
102 | #define BTHID_RECONNECT (1 << 0) /* reconnect on link loss */ |
103 | #define BTHID_CONNECTING (1 << 1) /* we are connecting */ |
104 | |
105 | /* device state */ |
106 | #define BTHID_CLOSED 0 |
107 | #define BTHID_WAIT_CTL 1 |
108 | #define BTHID_WAIT_INT 2 |
109 | #define BTHID_OPEN 3 |
110 | |
111 | #define BTHID_RETRY_INTERVAL 5 /* seconds between connection attempts */ |
112 | |
113 | /* bthidev internals */ |
114 | static void bthidev_timeout(void *); |
115 | static int bthidev_listen(struct bthidev_softc *); |
116 | static int bthidev_connect(struct bthidev_softc *); |
117 | static int bthidev_output(struct bthidev *, uint8_t *, int); |
118 | static void bthidev_null(struct bthidev *, uint8_t *, int); |
119 | static void bthidev_process(void *); |
120 | static void bthidev_process_one(struct bthidev_softc *, struct mbuf *); |
121 | |
122 | /* autoconf(9) glue */ |
123 | static int bthidev_match(device_t, cfdata_t, void *); |
124 | static void bthidev_attach(device_t, device_t, void *); |
125 | static int bthidev_detach(device_t, int); |
126 | static int bthidev_print(void *, const char *); |
127 | |
128 | CFATTACH_DECL_NEW(bthidev, sizeof(struct bthidev_softc), |
129 | bthidev_match, bthidev_attach, bthidev_detach, NULL); |
130 | |
131 | /* bluetooth(9) protocol methods for L2CAP */ |
132 | static void bthidev_connecting(void *); |
133 | static void bthidev_ctl_connected(void *); |
134 | static void bthidev_int_connected(void *); |
135 | static void bthidev_ctl_disconnected(void *, int); |
136 | static void bthidev_int_disconnected(void *, int); |
137 | static void *bthidev_ctl_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); |
138 | static void *bthidev_int_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); |
139 | static void bthidev_complete(void *, int); |
140 | static void bthidev_linkmode(void *, int); |
141 | static void bthidev_input(void *, struct mbuf *); |
142 | |
143 | static const struct btproto bthidev_ctl_proto = { |
144 | bthidev_connecting, |
145 | bthidev_ctl_connected, |
146 | bthidev_ctl_disconnected, |
147 | bthidev_ctl_newconn, |
148 | bthidev_complete, |
149 | bthidev_linkmode, |
150 | bthidev_input, |
151 | }; |
152 | |
153 | static const struct btproto bthidev_int_proto = { |
154 | bthidev_connecting, |
155 | bthidev_int_connected, |
156 | bthidev_int_disconnected, |
157 | bthidev_int_newconn, |
158 | bthidev_complete, |
159 | bthidev_linkmode, |
160 | bthidev_input, |
161 | }; |
162 | |
163 | /***************************************************************************** |
164 | * |
165 | * bthidev autoconf(9) routines |
166 | */ |
167 | |
168 | static int |
169 | bthidev_match(device_t self, cfdata_t cfdata, void *aux) |
170 | { |
171 | prop_dictionary_t dict = aux; |
172 | prop_object_t obj; |
173 | |
174 | obj = prop_dictionary_get(dict, BTDEVservice); |
175 | if (prop_string_equals_cstring(obj, "HID" )) |
176 | return 1; |
177 | |
178 | return 0; |
179 | } |
180 | |
181 | static void |
182 | bthidev_attach(device_t parent, device_t self, void *aux) |
183 | { |
184 | struct bthidev_softc *sc = device_private(self); |
185 | prop_dictionary_t dict = aux; |
186 | prop_object_t obj; |
187 | device_t dev; |
188 | struct bthidev_attach_args bha; |
189 | struct bthidev *hidev; |
190 | struct hid_data *d; |
191 | struct hid_item h; |
192 | const void *desc; |
193 | int locs[BTHIDBUSCF_NLOCS]; |
194 | int maxid, rep, dlen; |
195 | int vendor, product; |
196 | int err; |
197 | |
198 | /* |
199 | * Init softc |
200 | */ |
201 | sc->sc_dev = self; |
202 | LIST_INIT(&sc->sc_list); |
203 | MBUFQ_INIT(&sc->sc_inq); |
204 | callout_init(&sc->sc_reconnect, 0); |
205 | callout_setfunc(&sc->sc_reconnect, bthidev_timeout, sc); |
206 | sc->sc_state = BTHID_CLOSED; |
207 | sc->sc_flags = BTHID_CONNECTING; |
208 | sc->sc_ctlpsm = L2CAP_PSM_HID_CNTL; |
209 | sc->sc_intpsm = L2CAP_PSM_HID_INTR; |
210 | |
211 | sockopt_init(&sc->sc_mode, BTPROTO_L2CAP, SO_L2CAP_LM, 0); |
212 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); |
213 | cv_init(&sc->sc_cv, device_xname(self)); |
214 | |
215 | /* |
216 | * extract config from proplist |
217 | */ |
218 | obj = prop_dictionary_get(dict, BTDEVladdr); |
219 | bdaddr_copy(&sc->sc_laddr, prop_data_data_nocopy(obj)); |
220 | |
221 | obj = prop_dictionary_get(dict, BTDEVraddr); |
222 | bdaddr_copy(&sc->sc_raddr, prop_data_data_nocopy(obj)); |
223 | |
224 | obj = prop_dictionary_get(dict, BTDEVvendor); |
225 | vendor = (int)prop_number_integer_value(obj); |
226 | |
227 | obj = prop_dictionary_get(dict, BTDEVproduct); |
228 | product = (int)prop_number_integer_value(obj); |
229 | |
230 | obj = prop_dictionary_get(dict, BTDEVmode); |
231 | if (prop_object_type(obj) == PROP_TYPE_STRING) { |
232 | if (prop_string_equals_cstring(obj, BTDEVauth)) |
233 | sockopt_setint(&sc->sc_mode, L2CAP_LM_AUTH); |
234 | else if (prop_string_equals_cstring(obj, BTDEVencrypt)) |
235 | sockopt_setint(&sc->sc_mode, L2CAP_LM_ENCRYPT); |
236 | else if (prop_string_equals_cstring(obj, BTDEVsecure)) |
237 | sockopt_setint(&sc->sc_mode, L2CAP_LM_SECURE); |
238 | else { |
239 | aprint_error(" unknown %s\n" , BTDEVmode); |
240 | return; |
241 | } |
242 | |
243 | aprint_verbose(" %s %s" , BTDEVmode, |
244 | prop_string_cstring_nocopy(obj)); |
245 | } else |
246 | sockopt_setint(&sc->sc_mode, 0); |
247 | |
248 | obj = prop_dictionary_get(dict, BTHIDEVcontrolpsm); |
249 | if (prop_object_type(obj) == PROP_TYPE_NUMBER) { |
250 | sc->sc_ctlpsm = prop_number_integer_value(obj); |
251 | if (L2CAP_PSM_INVALID(sc->sc_ctlpsm)) { |
252 | aprint_error(" invalid %s\n" , BTHIDEVcontrolpsm); |
253 | return; |
254 | } |
255 | } |
256 | |
257 | obj = prop_dictionary_get(dict, BTHIDEVinterruptpsm); |
258 | if (prop_object_type(obj) == PROP_TYPE_NUMBER) { |
259 | sc->sc_intpsm = prop_number_integer_value(obj); |
260 | if (L2CAP_PSM_INVALID(sc->sc_intpsm)) { |
261 | aprint_error(" invalid %s\n" , BTHIDEVinterruptpsm); |
262 | return; |
263 | } |
264 | } |
265 | |
266 | obj = prop_dictionary_get(dict, BTHIDEVdescriptor); |
267 | if (prop_object_type(obj) == PROP_TYPE_DATA) { |
268 | dlen = prop_data_size(obj); |
269 | desc = prop_data_data_nocopy(obj); |
270 | } else { |
271 | aprint_error(" no %s\n" , BTHIDEVdescriptor); |
272 | return; |
273 | } |
274 | |
275 | obj = prop_dictionary_get(dict, BTHIDEVreconnect); |
276 | if (prop_object_type(obj) == PROP_TYPE_BOOL |
277 | && !prop_bool_true(obj)) |
278 | sc->sc_flags |= BTHID_RECONNECT; |
279 | |
280 | /* |
281 | * Parse the descriptor and attach child devices, one per report. |
282 | */ |
283 | maxid = -1; |
284 | h.report_ID = 0; |
285 | d = hid_start_parse(desc, dlen, hid_none); |
286 | while (hid_get_item(d, &h)) { |
287 | if (h.report_ID > maxid) |
288 | maxid = h.report_ID; |
289 | } |
290 | hid_end_parse(d); |
291 | |
292 | if (maxid < 0) { |
293 | aprint_error(" no reports found\n" ); |
294 | return; |
295 | } |
296 | |
297 | aprint_normal("\n" ); |
298 | |
299 | if (kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL, bthidev_process, |
300 | sc, &sc->sc_lwp, "%s" , device_xname(self)) != 0) { |
301 | aprint_error_dev(self, "failed to create input thread\n" ); |
302 | return; |
303 | } |
304 | |
305 | for (rep = 0 ; rep <= maxid ; rep++) { |
306 | if (hid_report_size(desc, dlen, hid_feature, rep) == 0 |
307 | && hid_report_size(desc, dlen, hid_input, rep) == 0 |
308 | && hid_report_size(desc, dlen, hid_output, rep) == 0) |
309 | continue; |
310 | |
311 | bha.ba_vendor = vendor; |
312 | bha.ba_product = product; |
313 | bha.ba_desc = desc; |
314 | bha.ba_dlen = dlen; |
315 | bha.ba_input = bthidev_null; |
316 | bha.ba_feature = bthidev_null; |
317 | bha.ba_output = bthidev_output; |
318 | bha.ba_id = rep; |
319 | |
320 | locs[BTHIDBUSCF_REPORTID] = rep; |
321 | |
322 | dev = config_found_sm_loc(self, "bthidbus" , |
323 | locs, &bha, bthidev_print, config_stdsubmatch); |
324 | if (dev != NULL) { |
325 | hidev = device_private(dev); |
326 | hidev->sc_dev = dev; |
327 | hidev->sc_parent = self; |
328 | hidev->sc_id = rep; |
329 | hidev->sc_input = bha.ba_input; |
330 | hidev->sc_feature = bha.ba_feature; |
331 | LIST_INSERT_HEAD(&sc->sc_list, hidev, sc_next); |
332 | } |
333 | } |
334 | |
335 | pmf_device_register(self, NULL, NULL); |
336 | |
337 | /* |
338 | * start bluetooth connections |
339 | */ |
340 | mutex_enter(bt_lock); |
341 | if ((sc->sc_flags & BTHID_RECONNECT) == 0 |
342 | && (err = bthidev_listen(sc)) != 0) |
343 | aprint_error_dev(self, "failed to listen (%d)\n" , err); |
344 | |
345 | if (sc->sc_flags & BTHID_CONNECTING) |
346 | bthidev_connect(sc); |
347 | mutex_exit(bt_lock); |
348 | } |
349 | |
350 | static int |
351 | bthidev_detach(device_t self, int flags) |
352 | { |
353 | struct bthidev_softc *sc = device_private(self); |
354 | struct bthidev *hidev; |
355 | |
356 | mutex_enter(bt_lock); |
357 | sc->sc_flags = 0; /* disable reconnecting */ |
358 | |
359 | /* release interrupt listen */ |
360 | if (sc->sc_int_l != NULL) { |
361 | l2cap_detach_pcb(&sc->sc_int_l); |
362 | sc->sc_int_l = NULL; |
363 | } |
364 | |
365 | /* release control listen */ |
366 | if (sc->sc_ctl_l != NULL) { |
367 | l2cap_detach_pcb(&sc->sc_ctl_l); |
368 | sc->sc_ctl_l = NULL; |
369 | } |
370 | |
371 | /* close interrupt channel */ |
372 | if (sc->sc_int != NULL) { |
373 | l2cap_disconnect_pcb(sc->sc_int, 0); |
374 | l2cap_detach_pcb(&sc->sc_int); |
375 | sc->sc_int = NULL; |
376 | } |
377 | |
378 | /* close control channel */ |
379 | if (sc->sc_ctl != NULL) { |
380 | l2cap_disconnect_pcb(sc->sc_ctl, 0); |
381 | l2cap_detach_pcb(&sc->sc_ctl); |
382 | sc->sc_ctl = NULL; |
383 | } |
384 | |
385 | callout_halt(&sc->sc_reconnect, bt_lock); |
386 | callout_destroy(&sc->sc_reconnect); |
387 | |
388 | mutex_exit(bt_lock); |
389 | |
390 | pmf_device_deregister(self); |
391 | |
392 | /* kill off the input processor */ |
393 | if (sc->sc_lwp != NULL) { |
394 | mutex_enter(&sc->sc_lock); |
395 | sc->sc_detach = 1; |
396 | cv_signal(&sc->sc_cv); |
397 | mutex_exit(&sc->sc_lock); |
398 | kthread_join(sc->sc_lwp); |
399 | sc->sc_lwp = NULL; |
400 | } |
401 | |
402 | /* detach children */ |
403 | while ((hidev = LIST_FIRST(&sc->sc_list)) != NULL) { |
404 | LIST_REMOVE(hidev, sc_next); |
405 | config_detach(hidev->sc_dev, flags); |
406 | } |
407 | |
408 | MBUFQ_DRAIN(&sc->sc_inq); |
409 | cv_destroy(&sc->sc_cv); |
410 | mutex_destroy(&sc->sc_lock); |
411 | sockopt_destroy(&sc->sc_mode); |
412 | |
413 | return 0; |
414 | } |
415 | |
416 | /* |
417 | * bthidev config print |
418 | */ |
419 | static int |
420 | bthidev_print(void *aux, const char *pnp) |
421 | { |
422 | struct bthidev_attach_args *ba = aux; |
423 | |
424 | if (pnp != NULL) |
425 | aprint_normal("%s:" , pnp); |
426 | |
427 | if (ba->ba_id > 0) |
428 | aprint_normal(" reportid %d" , ba->ba_id); |
429 | |
430 | return UNCONF; |
431 | } |
432 | |
433 | /***************************************************************************** |
434 | * |
435 | * bluetooth(4) HID attach/detach routines |
436 | */ |
437 | |
438 | /* |
439 | * callouts are scheduled after connections have been lost, in order |
440 | * to clean up and reconnect. |
441 | */ |
442 | static void |
443 | bthidev_timeout(void *arg) |
444 | { |
445 | struct bthidev_softc *sc = arg; |
446 | |
447 | mutex_enter(bt_lock); |
448 | callout_ack(&sc->sc_reconnect); |
449 | |
450 | switch (sc->sc_state) { |
451 | case BTHID_CLOSED: |
452 | if (sc->sc_int != NULL) { |
453 | l2cap_disconnect_pcb(sc->sc_int, 0); |
454 | break; |
455 | } |
456 | |
457 | if (sc->sc_ctl != NULL) { |
458 | l2cap_disconnect_pcb(sc->sc_ctl, 0); |
459 | break; |
460 | } |
461 | |
462 | if (sc->sc_flags & BTHID_RECONNECT) { |
463 | sc->sc_flags |= BTHID_CONNECTING; |
464 | bthidev_connect(sc); |
465 | break; |
466 | } |
467 | |
468 | break; |
469 | |
470 | case BTHID_WAIT_CTL: |
471 | break; |
472 | |
473 | case BTHID_WAIT_INT: |
474 | break; |
475 | |
476 | case BTHID_OPEN: |
477 | break; |
478 | |
479 | default: |
480 | break; |
481 | } |
482 | mutex_exit(bt_lock); |
483 | } |
484 | |
485 | /* |
486 | * listen for our device |
487 | */ |
488 | static int |
489 | bthidev_listen(struct bthidev_softc *sc) |
490 | { |
491 | struct sockaddr_bt sa; |
492 | int err; |
493 | |
494 | memset(&sa, 0, sizeof(sa)); |
495 | sa.bt_len = sizeof(sa); |
496 | sa.bt_family = AF_BLUETOOTH; |
497 | bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); |
498 | |
499 | /* |
500 | * Listen on control PSM |
501 | */ |
502 | err = l2cap_attach_pcb(&sc->sc_ctl_l, &bthidev_ctl_proto, sc); |
503 | if (err) |
504 | return err; |
505 | |
506 | err = l2cap_setopt(sc->sc_ctl_l, &sc->sc_mode); |
507 | if (err) |
508 | return err; |
509 | |
510 | sa.bt_psm = sc->sc_ctlpsm; |
511 | err = l2cap_bind_pcb(sc->sc_ctl_l, &sa); |
512 | if (err) |
513 | return err; |
514 | |
515 | err = l2cap_listen_pcb(sc->sc_ctl_l); |
516 | if (err) |
517 | return err; |
518 | |
519 | /* |
520 | * Listen on interrupt PSM |
521 | */ |
522 | err = l2cap_attach_pcb(&sc->sc_int_l, &bthidev_int_proto, sc); |
523 | if (err) |
524 | return err; |
525 | |
526 | err = l2cap_setopt(sc->sc_int_l, &sc->sc_mode); |
527 | if (err) |
528 | return err; |
529 | |
530 | sa.bt_psm = sc->sc_intpsm; |
531 | err = l2cap_bind_pcb(sc->sc_int_l, &sa); |
532 | if (err) |
533 | return err; |
534 | |
535 | err = l2cap_listen_pcb(sc->sc_int_l); |
536 | if (err) |
537 | return err; |
538 | |
539 | sc->sc_state = BTHID_WAIT_CTL; |
540 | return 0; |
541 | } |
542 | |
543 | /* |
544 | * start connecting to our device |
545 | */ |
546 | static int |
547 | bthidev_connect(struct bthidev_softc *sc) |
548 | { |
549 | struct sockaddr_bt sa; |
550 | int err; |
551 | |
552 | if (sc->sc_attempts++ > 0) |
553 | aprint_verbose_dev(sc->sc_dev, "connect (#%d)\n" , sc->sc_attempts); |
554 | |
555 | memset(&sa, 0, sizeof(sa)); |
556 | sa.bt_len = sizeof(sa); |
557 | sa.bt_family = AF_BLUETOOTH; |
558 | |
559 | err = l2cap_attach_pcb(&sc->sc_ctl, &bthidev_ctl_proto, sc); |
560 | if (err) { |
561 | aprint_error_dev(sc->sc_dev, "l2cap_attach failed (%d)\n" , err); |
562 | return err; |
563 | } |
564 | |
565 | err = l2cap_setopt(sc->sc_ctl, &sc->sc_mode); |
566 | if (err) { |
567 | aprint_error_dev(sc->sc_dev, "l2cap_setopt failed (%d)\n" , err); |
568 | return err; |
569 | } |
570 | |
571 | bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); |
572 | err = l2cap_bind_pcb(sc->sc_ctl, &sa); |
573 | if (err) { |
574 | aprint_error_dev(sc->sc_dev, "l2cap_bind_pcb failed (%d)\n" , err); |
575 | return err; |
576 | } |
577 | |
578 | sa.bt_psm = sc->sc_ctlpsm; |
579 | bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr); |
580 | err = l2cap_connect_pcb(sc->sc_ctl, &sa); |
581 | if (err) { |
582 | aprint_error_dev(sc->sc_dev, "l2cap_connect_pcb failed (%d)\n" , err); |
583 | return err; |
584 | } |
585 | |
586 | sc->sc_state = BTHID_WAIT_CTL; |
587 | return 0; |
588 | } |
589 | |
590 | /* |
591 | * The LWP which processes input reports, forwarding to child devices. |
592 | * We are always either processing input reports, holding the lock, or |
593 | * waiting for a signal on condvar. |
594 | */ |
595 | static void |
596 | bthidev_process(void *arg) |
597 | { |
598 | struct bthidev_softc *sc = arg; |
599 | struct mbuf *m; |
600 | |
601 | mutex_enter(&sc->sc_lock); |
602 | while (sc->sc_detach == 0) { |
603 | MBUFQ_DEQUEUE(&sc->sc_inq, m); |
604 | if (m == NULL) { |
605 | cv_wait(&sc->sc_cv, &sc->sc_lock); |
606 | continue; |
607 | } |
608 | |
609 | mutex_exit(&sc->sc_lock); |
610 | bthidev_process_one(sc, m); |
611 | m_freem(m); |
612 | mutex_enter(&sc->sc_lock); |
613 | } |
614 | mutex_exit(&sc->sc_lock); |
615 | kthread_exit(0); |
616 | } |
617 | |
618 | static void |
619 | bthidev_process_one(struct bthidev_softc *sc, struct mbuf *m) |
620 | { |
621 | struct bthidev *hidev; |
622 | uint8_t *data; |
623 | int len; |
624 | |
625 | if (sc->sc_state != BTHID_OPEN) |
626 | return; |
627 | |
628 | if (m->m_pkthdr.len > m->m_len) |
629 | aprint_error_dev(sc->sc_dev, "truncating HID report\n" ); |
630 | |
631 | len = m->m_len; |
632 | data = mtod(m, uint8_t *); |
633 | |
634 | switch (BTHID_TYPE(data[0])) { |
635 | case BTHID_DATA: |
636 | /* |
637 | * data[0] == type / parameter |
638 | * data[1] == id |
639 | * data[2..len] == report |
640 | */ |
641 | if (len < 3) |
642 | break; |
643 | |
644 | LIST_FOREACH(hidev, &sc->sc_list, sc_next) |
645 | if (data[1] == hidev->sc_id) |
646 | break; |
647 | |
648 | if (hidev == NULL) { |
649 | aprint_error_dev(sc->sc_dev, |
650 | "report id %d, len = %d ignored\n" , data[1], len - 2); |
651 | |
652 | break; |
653 | } |
654 | |
655 | switch (BTHID_DATA_PARAM(data[0])) { |
656 | case BTHID_DATA_INPUT: |
657 | (*hidev->sc_input)(hidev, data + 2, len - 2); |
658 | break; |
659 | |
660 | case BTHID_DATA_FEATURE: |
661 | (*hidev->sc_feature)(hidev, data + 2, len - 2); |
662 | break; |
663 | |
664 | default: |
665 | break; |
666 | } |
667 | |
668 | break; |
669 | |
670 | case BTHID_CONTROL: |
671 | if (len < 1) |
672 | break; |
673 | |
674 | switch (BTHID_DATA_PARAM(data[0])) { |
675 | case BTHID_CONTROL_UNPLUG: |
676 | aprint_normal_dev(sc->sc_dev, "unplugged\n" ); |
677 | |
678 | mutex_enter(bt_lock); |
679 | /* close interrupt channel */ |
680 | if (sc->sc_int != NULL) { |
681 | l2cap_disconnect_pcb(sc->sc_int, 0); |
682 | l2cap_detach_pcb(&sc->sc_int); |
683 | sc->sc_int = NULL; |
684 | } |
685 | |
686 | /* close control channel */ |
687 | if (sc->sc_ctl != NULL) { |
688 | l2cap_disconnect_pcb(sc->sc_ctl, 0); |
689 | l2cap_detach_pcb(&sc->sc_ctl); |
690 | sc->sc_ctl = NULL; |
691 | } |
692 | mutex_exit(bt_lock); |
693 | |
694 | break; |
695 | |
696 | default: |
697 | break; |
698 | } |
699 | |
700 | break; |
701 | |
702 | default: |
703 | break; |
704 | } |
705 | } |
706 | |
707 | /***************************************************************************** |
708 | * |
709 | * bluetooth(9) callback methods for L2CAP |
710 | * |
711 | * All these are called from Bluetooth Protocol code, in a soft |
712 | * interrupt context at IPL_SOFTNET. |
713 | */ |
714 | |
715 | static void |
716 | bthidev_connecting(void *arg) |
717 | { |
718 | |
719 | /* dont care */ |
720 | } |
721 | |
722 | static void |
723 | bthidev_ctl_connected(void *arg) |
724 | { |
725 | struct sockaddr_bt sa; |
726 | struct bthidev_softc *sc = arg; |
727 | int err; |
728 | |
729 | if (sc->sc_state != BTHID_WAIT_CTL) |
730 | return; |
731 | |
732 | KASSERT(sc->sc_ctl != NULL); |
733 | KASSERT(sc->sc_int == NULL); |
734 | |
735 | if (sc->sc_flags & BTHID_CONNECTING) { |
736 | /* initiate connect on interrupt PSM */ |
737 | err = l2cap_attach_pcb(&sc->sc_int, &bthidev_int_proto, sc); |
738 | if (err) |
739 | goto fail; |
740 | |
741 | err = l2cap_setopt(sc->sc_int, &sc->sc_mode); |
742 | if (err) |
743 | goto fail; |
744 | |
745 | memset(&sa, 0, sizeof(sa)); |
746 | sa.bt_len = sizeof(sa); |
747 | sa.bt_family = AF_BLUETOOTH; |
748 | bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); |
749 | |
750 | err = l2cap_bind_pcb(sc->sc_int, &sa); |
751 | if (err) |
752 | goto fail; |
753 | |
754 | sa.bt_psm = sc->sc_intpsm; |
755 | bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr); |
756 | err = l2cap_connect_pcb(sc->sc_int, &sa); |
757 | if (err) |
758 | goto fail; |
759 | } |
760 | |
761 | sc->sc_state = BTHID_WAIT_INT; |
762 | return; |
763 | |
764 | fail: |
765 | l2cap_detach_pcb(&sc->sc_ctl); |
766 | sc->sc_ctl = NULL; |
767 | |
768 | aprint_error_dev(sc->sc_dev, "connect failed (%d)\n" , err); |
769 | } |
770 | |
771 | static void |
772 | bthidev_int_connected(void *arg) |
773 | { |
774 | struct bthidev_softc *sc = arg; |
775 | |
776 | if (sc->sc_state != BTHID_WAIT_INT) |
777 | return; |
778 | |
779 | KASSERT(sc->sc_ctl != NULL); |
780 | KASSERT(sc->sc_int != NULL); |
781 | |
782 | sc->sc_attempts = 0; |
783 | sc->sc_flags &= ~BTHID_CONNECTING; |
784 | sc->sc_state = BTHID_OPEN; |
785 | |
786 | aprint_normal_dev(sc->sc_dev, "connected\n" ); |
787 | } |
788 | |
789 | /* |
790 | * Disconnected |
791 | * |
792 | * Depending on our state, this could mean several things, but essentially |
793 | * we are lost. If both channels are closed, and we are marked to reconnect, |
794 | * schedule another try otherwise just give up. They will contact us. |
795 | */ |
796 | static void |
797 | bthidev_ctl_disconnected(void *arg, int err) |
798 | { |
799 | struct bthidev_softc *sc = arg; |
800 | |
801 | if (sc->sc_ctl != NULL) { |
802 | l2cap_detach_pcb(&sc->sc_ctl); |
803 | sc->sc_ctl = NULL; |
804 | } |
805 | |
806 | sc->sc_state = BTHID_CLOSED; |
807 | |
808 | if (sc->sc_int == NULL) { |
809 | aprint_normal_dev(sc->sc_dev, "disconnected (%d)\n" , err); |
810 | sc->sc_flags &= ~BTHID_CONNECTING; |
811 | |
812 | if (sc->sc_flags & BTHID_RECONNECT) |
813 | callout_schedule(&sc->sc_reconnect, |
814 | BTHID_RETRY_INTERVAL * hz); |
815 | else |
816 | sc->sc_state = BTHID_WAIT_CTL; |
817 | } else { |
818 | /* |
819 | * The interrupt channel should have been closed first, |
820 | * but its potentially unsafe to detach that from here. |
821 | * Give them a second to do the right thing or let the |
822 | * callout handle it. |
823 | */ |
824 | callout_schedule(&sc->sc_reconnect, hz); |
825 | } |
826 | } |
827 | |
828 | static void |
829 | bthidev_int_disconnected(void *arg, int err) |
830 | { |
831 | struct bthidev_softc *sc = arg; |
832 | |
833 | if (sc->sc_int != NULL) { |
834 | l2cap_detach_pcb(&sc->sc_int); |
835 | sc->sc_int = NULL; |
836 | } |
837 | |
838 | sc->sc_state = BTHID_CLOSED; |
839 | |
840 | if (sc->sc_ctl == NULL) { |
841 | aprint_normal_dev(sc->sc_dev, "disconnected (%d)\n" , err); |
842 | sc->sc_flags &= ~BTHID_CONNECTING; |
843 | |
844 | if (sc->sc_flags & BTHID_RECONNECT) |
845 | callout_schedule(&sc->sc_reconnect, |
846 | BTHID_RETRY_INTERVAL * hz); |
847 | else |
848 | sc->sc_state = BTHID_WAIT_CTL; |
849 | } else { |
850 | /* |
851 | * The control channel should be closing also, allow |
852 | * them a chance to do that before we force it. |
853 | */ |
854 | callout_schedule(&sc->sc_reconnect, hz); |
855 | } |
856 | } |
857 | |
858 | /* |
859 | * New Connections |
860 | * |
861 | * We give a new L2CAP handle back if this matches the BDADDR we are |
862 | * listening for and we are in the right state. bthidev_connected will |
863 | * be called when the connection is open, so nothing else to do here |
864 | */ |
865 | static void * |
866 | bthidev_ctl_newconn(void *arg, struct sockaddr_bt *laddr, |
867 | struct sockaddr_bt *raddr) |
868 | { |
869 | struct bthidev_softc *sc = arg; |
870 | |
871 | if (bdaddr_same(&raddr->bt_bdaddr, &sc->sc_raddr) == 0) |
872 | return NULL; |
873 | |
874 | if ((sc->sc_flags & BTHID_CONNECTING) |
875 | || sc->sc_state != BTHID_WAIT_CTL |
876 | || sc->sc_ctl != NULL |
877 | || sc->sc_int != NULL) { |
878 | aprint_verbose_dev(sc->sc_dev, "reject ctl newconn %s%s%s%s\n" , |
879 | (sc->sc_flags & BTHID_CONNECTING) ? " (CONNECTING)" : "" , |
880 | (sc->sc_state == BTHID_WAIT_CTL) ? " (WAITING)" : "" , |
881 | (sc->sc_ctl != NULL) ? " (GOT CONTROL)" : "" , |
882 | (sc->sc_int != NULL) ? " (GOT INTERRUPT)" : "" ); |
883 | |
884 | return NULL; |
885 | } |
886 | |
887 | l2cap_attach_pcb(&sc->sc_ctl, &bthidev_ctl_proto, sc); |
888 | return sc->sc_ctl; |
889 | } |
890 | |
891 | static void * |
892 | bthidev_int_newconn(void *arg, struct sockaddr_bt *laddr, |
893 | struct sockaddr_bt *raddr) |
894 | { |
895 | struct bthidev_softc *sc = arg; |
896 | |
897 | if (bdaddr_same(&raddr->bt_bdaddr, &sc->sc_raddr) == 0) |
898 | return NULL; |
899 | |
900 | if ((sc->sc_flags & BTHID_CONNECTING) |
901 | || sc->sc_state != BTHID_WAIT_INT |
902 | || sc->sc_ctl == NULL |
903 | || sc->sc_int != NULL) { |
904 | aprint_verbose_dev(sc->sc_dev, "reject int newconn %s%s%s%s\n" , |
905 | (sc->sc_flags & BTHID_CONNECTING) ? " (CONNECTING)" : "" , |
906 | (sc->sc_state == BTHID_WAIT_INT) ? " (WAITING)" : "" , |
907 | (sc->sc_ctl == NULL) ? " (NO CONTROL)" : "" , |
908 | (sc->sc_int != NULL) ? " (GOT INTERRUPT)" : "" ); |
909 | |
910 | return NULL; |
911 | } |
912 | |
913 | l2cap_attach_pcb(&sc->sc_int, &bthidev_int_proto, sc); |
914 | return sc->sc_int; |
915 | } |
916 | |
917 | static void |
918 | bthidev_complete(void *arg, int count) |
919 | { |
920 | |
921 | /* dont care */ |
922 | } |
923 | |
924 | static void |
925 | bthidev_linkmode(void *arg, int new) |
926 | { |
927 | struct bthidev_softc *sc = arg; |
928 | int mode; |
929 | |
930 | (void)sockopt_getint(&sc->sc_mode, &mode); |
931 | |
932 | if ((mode & L2CAP_LM_AUTH) && !(new & L2CAP_LM_AUTH)) |
933 | aprint_error_dev(sc->sc_dev, "auth failed\n" ); |
934 | else if ((mode & L2CAP_LM_ENCRYPT) && !(new & L2CAP_LM_ENCRYPT)) |
935 | aprint_error_dev(sc->sc_dev, "encrypt off\n" ); |
936 | else if ((mode & L2CAP_LM_SECURE) && !(new & L2CAP_LM_SECURE)) |
937 | aprint_error_dev(sc->sc_dev, "insecure\n" ); |
938 | else |
939 | return; |
940 | |
941 | if (sc->sc_int != NULL) |
942 | l2cap_disconnect_pcb(sc->sc_int, 0); |
943 | |
944 | if (sc->sc_ctl != NULL) |
945 | l2cap_disconnect_pcb(sc->sc_ctl, 0); |
946 | } |
947 | |
948 | /* |
949 | * Receive reports from the protocol stack. Because this will be called |
950 | * with bt_lock held, we queue the mbuf and process it with a kernel thread |
951 | */ |
952 | static void |
953 | bthidev_input(void *arg, struct mbuf *m) |
954 | { |
955 | struct bthidev_softc *sc = arg; |
956 | |
957 | if (sc->sc_state != BTHID_OPEN) { |
958 | m_freem(m); |
959 | return; |
960 | } |
961 | |
962 | mutex_enter(&sc->sc_lock); |
963 | MBUFQ_ENQUEUE(&sc->sc_inq, m); |
964 | cv_signal(&sc->sc_cv); |
965 | mutex_exit(&sc->sc_lock); |
966 | } |
967 | |
968 | /***************************************************************************** |
969 | * |
970 | * IO routines |
971 | */ |
972 | |
973 | static void |
974 | bthidev_null(struct bthidev *hidev, uint8_t *report, int len) |
975 | { |
976 | |
977 | /* |
978 | * empty routine just in case the device |
979 | * provided no method to handle this report |
980 | */ |
981 | } |
982 | |
983 | static int |
984 | bthidev_output(struct bthidev *hidev, uint8_t *report, int rlen) |
985 | { |
986 | struct bthidev_softc *sc = device_private(hidev->sc_parent); |
987 | struct mbuf *m; |
988 | int err; |
989 | |
990 | if (sc == NULL || sc->sc_state != BTHID_OPEN) |
991 | return ENOTCONN; |
992 | |
993 | KASSERT(sc->sc_ctl != NULL); |
994 | KASSERT(sc->sc_int != NULL); |
995 | |
996 | if (rlen == 0 || report == NULL) |
997 | return 0; |
998 | |
999 | if (rlen > MHLEN - 2) { |
1000 | aprint_error_dev(sc->sc_dev, |
1001 | "output report too long (%d)!\n" , rlen); |
1002 | return EMSGSIZE; |
1003 | } |
1004 | |
1005 | m = m_gethdr(M_DONTWAIT, MT_DATA); |
1006 | if (m == NULL) |
1007 | return ENOMEM; |
1008 | |
1009 | /* |
1010 | * data[0] = type / parameter |
1011 | * data[1] = id |
1012 | * data[2..N] = report |
1013 | */ |
1014 | mtod(m, uint8_t *)[0] = (uint8_t)((BTHID_DATA << 4) | BTHID_DATA_OUTPUT); |
1015 | mtod(m, uint8_t *)[1] = hidev->sc_id; |
1016 | memcpy(mtod(m, uint8_t *) + 2, report, rlen); |
1017 | m->m_pkthdr.len = m->m_len = rlen + 2; |
1018 | |
1019 | mutex_enter(bt_lock); |
1020 | err = l2cap_send_pcb(sc->sc_int, m); |
1021 | mutex_exit(bt_lock); |
1022 | |
1023 | return err; |
1024 | } |
1025 | |