diff options
Diffstat (limited to 'sys/dev/bluetooth/bthidev.c')
-rw-r--r-- | sys/dev/bluetooth/bthidev.c | 224 |
1 files changed, 164 insertions, 60 deletions
diff --git a/sys/dev/bluetooth/bthidev.c b/sys/dev/bluetooth/bthidev.c index 4304f6cc02b..2ac50931dd6 100644 --- a/sys/dev/bluetooth/bthidev.c +++ b/sys/dev/bluetooth/bthidev.c @@ -1,5 +1,5 @@ -/* $OpenBSD: bthidev.c,v 1.1 2007/09/01 17:06:26 xsa Exp $ */ -/* $NetBSD: bthidev.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $ */ +/* $OpenBSD: bthidev.c,v 1.2 2008/02/24 21:46:19 uwe Exp $ */ +/* $NetBSD: bthidev.c,v 1.13 2007/11/12 19:19:32 plunky Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -55,13 +55,13 @@ /* bthidev softc */ struct bthidev_softc { - struct btdev sc_btdev; /* device+ */ + struct btdev sc_btdev; /* base device */ uint16_t sc_state; uint16_t sc_flags; bdaddr_t sc_laddr; /* local address */ bdaddr_t sc_raddr; /* remote address */ - + int sc_mode; /* link mode */ void *sc_desc; /* HID descriptor */ int sc_dlen; /* descriptor length */ @@ -79,6 +79,10 @@ struct bthidev_softc { int sc_attempts; /* connection attempts */ }; +/* sc_flags */ +#define BTHID_RECONNECT (1 << 0) /* reconnect on link loss */ +#define BTHID_CONNECTING (1 << 1) /* we are connecting */ + /* device state */ #define BTHID_CLOSED 0 #define BTHID_WAIT_CTL 1 @@ -88,12 +92,14 @@ struct bthidev_softc { #define BTHID_RETRY_INTERVAL 5 /* seconds between connection attempts */ +/* bthidev internals */ void bthidev_timeout(void *); int bthidev_listen(struct bthidev_softc *); int bthidev_connect(struct bthidev_softc *); int bthidev_output(struct bthidev *, uint8_t *, int); void bthidev_null(struct bthidev *, uint8_t *, int); +/* autoconf(9) glue */ int bthidev_match(struct device *, void *, void *); void bthidev_attach(struct device *, struct device *, void *); int bthidev_detach(struct device *, int); @@ -123,6 +129,7 @@ void *bthidev_ctl_newconn(void *, struct sockaddr_bt *, void *bthidev_int_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *); void bthidev_complete(void *, int); +void bthidev_linkmode(void *, int); void bthidev_input(void *, struct mbuf *); const struct btproto bthidev_ctl_proto = { @@ -131,7 +138,7 @@ const struct btproto bthidev_ctl_proto = { bthidev_ctl_disconnected, bthidev_ctl_newconn, bthidev_complete, - NULL, + bthidev_linkmode, bthidev_input, }; @@ -141,22 +148,21 @@ const struct btproto bthidev_int_proto = { bthidev_int_disconnected, bthidev_int_newconn, bthidev_complete, - NULL, + bthidev_linkmode, bthidev_input, }; +/***************************************************************************** + * + * bthidev autoconf(9) routines + */ int bthidev_match(struct device *self, void *match, void *aux) { struct btdev_attach_args *bda = (struct btdev_attach_args *)aux; - if (bda->bd_type != BTDEV_HID - || L2CAP_PSM_INVALID(bda->bd_hid.hid_ctl) - || L2CAP_PSM_INVALID(bda->bd_hid.hid_int) - || bda->bd_hid.hid_desc == NULL - || bda->bd_hid.hid_dlen == 0 - || bda->bd_hid.hid_dlen > MAX_DESCRIPTOR_LEN) + if (bda->bd_type != BTDEV_HID) return 0; return 1; @@ -168,7 +174,7 @@ bthidev_attach(struct device *parent, struct device *self, void *aux) struct bthidev_softc *sc = (struct bthidev_softc *)self; struct btdev_attach_args *bda = (struct btdev_attach_args *)aux; struct bthidev_attach_args bha; - struct bthidev *dev; + struct bthidev *hidev; struct hid_data *d; struct hid_item h; int maxid, rep, s; @@ -179,6 +185,9 @@ bthidev_attach(struct device *parent, struct device *self, void *aux) LIST_INIT(&sc->sc_list); timeout_set(&sc->sc_reconnect, bthidev_timeout, sc); sc->sc_state = BTHID_CLOSED; + sc->sc_flags = BTHID_CONNECTING; + sc->sc_ctlpsm = L2CAP_PSM_HID_CNTL; + sc->sc_intpsm = L2CAP_PSM_HID_INTR; /* * copy in our configuration info @@ -186,20 +195,48 @@ bthidev_attach(struct device *parent, struct device *self, void *aux) bdaddr_copy(&sc->sc_laddr, &bda->bd_laddr); bdaddr_copy(&sc->sc_raddr, &bda->bd_raddr); - sc->sc_ctlpsm = bda->bd_hid.hid_ctl; - sc->sc_intpsm = bda->bd_hid.hid_int; + if (bda->bd_mode != BTDEV_MODE_NONE) { + if (bda->bd_mode == BTDEV_MODE_AUTH) { + sc->sc_mode = L2CAP_LM_AUTH; + printf(" auth"); + } else if (bda->bd_mode == BTDEV_MODE_ENCRYPT) { + sc->sc_mode = L2CAP_LM_ENCRYPT; + printf(" encrypt"); + } else if (bda->bd_mode == BTDEV_MODE_SECURE) { + sc->sc_mode = L2CAP_LM_SECURE; + printf(" secure"); + } else { + printf(" unknown link-mode: %d\n", bda->bd_mode); + return; + } + } + + if (!L2CAP_PSM_INVALID(bda->bd_hid.hid_ctl)) + sc->sc_ctlpsm = bda->bd_hid.hid_ctl; + + if (!L2CAP_PSM_INVALID(bda->bd_hid.hid_int)) + sc->sc_intpsm = bda->bd_hid.hid_int; - sc->sc_flags = bda->bd_hid.hid_flags; - if (sc->sc_flags & BTHID_INITIATE) - sc->sc_flags |= BTHID_CONNECT; + if (bda->bd_hid.hid_flags & BTHID_INITIATE) + sc->sc_flags |= BTHID_RECONNECT; + if (bda->bd_hid.hid_desc == NULL || + bda->bd_hid.hid_dlen == 0 || + bda->bd_hid.hid_dlen > MAX_DESCRIPTOR_LEN) { + printf(": no descriptor\n"); + return; + } sc->sc_dlen = bda->bd_hid.hid_dlen; sc->sc_desc = malloc(bda->bd_hid.hid_dlen, M_BTHIDEV, M_WAITOK); if (sc->sc_desc == NULL) { - printf(" no memory\n"); + printf(": no memory\n"); + return; + } + if (copyin(bda->bd_hid.hid_desc, sc->sc_desc, bda->bd_hid.hid_dlen)) { + free(sc->sc_desc, M_BTHIDEV); + printf(": no descriptor"); return; } - copyin(bda->bd_hid.hid_desc, sc->sc_desc, bda->bd_hid.hid_dlen); /* * Parse the descriptor and attach child devices, one per report. @@ -214,7 +251,7 @@ bthidev_attach(struct device *parent, struct device *self, void *aux) hid_end_parse(d); if (maxid < 0) { - printf(" no reports found\n"); + printf(": no reports found\n"); return; } @@ -233,14 +270,14 @@ bthidev_attach(struct device *parent, struct device *self, void *aux) bha.ba_output = bthidev_output; bha.ba_id = rep; - dev = (struct bthidev *)config_found_sm(self, &bha, + hidev = (struct bthidev *)config_found_sm(self, &bha, bthidev_print, bthidevsubmatch); - if (dev != NULL) { - dev->sc_parent = &sc->sc_btdev; - dev->sc_id = rep; - dev->sc_input = bha.ba_input; - dev->sc_feature = bha.ba_feature; - LIST_INSERT_HEAD(&sc->sc_list, dev, sc_next); + if (hidev != NULL) { + hidev->sc_parent = &sc->sc_btdev; + hidev->sc_id = rep; + hidev->sc_input = bha.ba_input; + hidev->sc_feature = bha.ba_feature; + LIST_INSERT_HEAD(&sc->sc_list, hidev, sc_next); } } @@ -248,10 +285,10 @@ bthidev_attach(struct device *parent, struct device *self, void *aux) * start bluetooth connections */ s = splsoftnet(); - if ((sc->sc_flags & BTHID_INITIATE) == 0) + if ((sc->sc_flags & BTHID_RECONNECT) == 0) bthidev_listen(sc); - if (sc->sc_flags & BTHID_CONNECT) + if (sc->sc_flags & BTHID_CONNECTING) bthidev_connect(sc); splx(s); } @@ -260,7 +297,7 @@ int bthidev_detach(struct device *self, int flags) { struct bthidev_softc *sc = (struct bthidev_softc *)self; - struct bthidev *dev; + struct bthidev *hidev; int s; s = splsoftnet(); @@ -292,15 +329,16 @@ bthidev_detach(struct device *self, int flags) sc->sc_ctl = NULL; } + /* remove timeout */ sc->sc_state = BTHID_DETACHING; timeout_del(&sc->sc_reconnect); splx(s); /* detach children */ - while ((dev = LIST_FIRST(&sc->sc_list)) != NULL) { - LIST_REMOVE(dev, sc_next); - config_detach(&dev->sc_dev, flags); + while ((hidev = LIST_FIRST(&sc->sc_list)) != NULL) { + LIST_REMOVE(hidev, sc_next); + config_detach(&hidev->sc_dev, flags); } /* release descriptor */ @@ -311,6 +349,9 @@ bthidev_detach(struct device *self, int flags) return 0; } +/* + * bthidev config print + */ int bthidev_print(void *aux, const char *pnp) { @@ -345,24 +386,35 @@ bthidevsubmatch(struct device *parent, void *match, void *aux) */ /* - * callouts are scheduled to initiate outgoing connections - * after the connection has been lost. + * callouts are scheduled after connections have been lost, in order + * to clean up and reconnect. */ void bthidev_timeout(void *arg) { struct bthidev_softc *sc = arg; - int s, err; + int s; s = splsoftnet(); switch (sc->sc_state) { case BTHID_CLOSED: - sc->sc_flags |= BTHID_CONNECT; - err = bthidev_connect(sc); - if (err) - printf("%s: connect failed (%d)\n", - sc->sc_btdev.sc_dev.dv_xname, err); + if (sc->sc_int != NULL) { + l2cap_disconnect(sc->sc_int, 0); + break; + } + + if (sc->sc_ctl != NULL) { + l2cap_disconnect(sc->sc_ctl, 0); + break; + } + + if (sc->sc_flags & BTHID_RECONNECT) { + sc->sc_flags |= BTHID_CONNECTING; + bthidev_connect(sc); + break; + } + break; case BTHID_WAIT_CTL: @@ -405,6 +457,10 @@ bthidev_listen(struct bthidev_softc *sc) if (err) return err; + err = l2cap_setopt(sc->sc_ctl_l, SO_L2CAP_LM, &sc->sc_mode); + if (err) + return err; + sa.bt_psm = sc->sc_ctlpsm; err = l2cap_bind(sc->sc_ctl_l, &sa); if (err) @@ -421,6 +477,10 @@ bthidev_listen(struct bthidev_softc *sc) if (err) return err; + err = l2cap_setopt(sc->sc_int_l, SO_L2CAP_LM, &sc->sc_mode); + if (err) + return err; + sa.bt_psm = sc->sc_intpsm; err = l2cap_bind(sc->sc_int_l, &sa); if (err) @@ -458,6 +518,10 @@ bthidev_connect(struct bthidev_softc *sc) return err; } + err = l2cap_setopt(sc->sc_ctl, SO_L2CAP_LM, &sc->sc_mode); + if (err) + return err; + bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); err = l2cap_bind(sc->sc_ctl, &sa); if (err) { @@ -490,8 +554,7 @@ bthidev_connect(struct bthidev_softc *sc) void bthidev_connecting(void *arg) { - - /* dont care */ + /* don't care */ } void @@ -507,12 +570,16 @@ bthidev_ctl_connected(void *arg) KASSERT(sc->sc_ctl != NULL); KASSERT(sc->sc_int == NULL); - if (sc->sc_flags & BTHID_CONNECT) { + if (sc->sc_flags & BTHID_CONNECTING) { /* initiate connect on interrupt PSM */ err = l2cap_attach(&sc->sc_int, &bthidev_int_proto, sc); if (err) goto fail; + err = l2cap_setopt(sc->sc_int, SO_L2CAP_LM, &sc->sc_mode); + if (err) + goto fail; + memset(&sa, 0, sizeof(sa)); sa.bt_len = sizeof(sa); sa.bt_family = AF_BLUETOOTH; @@ -551,7 +618,7 @@ bthidev_int_connected(void *arg) KASSERT(sc->sc_int != NULL); sc->sc_attempts = 0; - sc->sc_flags &= ~BTHID_CONNECT; + sc->sc_flags &= ~BTHID_CONNECTING; sc->sc_state = BTHID_OPEN; printf("%s: connected\n", sc->sc_btdev.sc_dev.dv_xname); @@ -561,7 +628,8 @@ bthidev_int_connected(void *arg) * Disconnected * * Depending on our state, this could mean several things, but essentially - * we are lost. + * we are lost. If both channels are closed, and we are marked to reconnect, + * schedule another try otherwise just give up. They will contact us. */ void bthidev_ctl_disconnected(void *arg, int err) @@ -578,11 +646,19 @@ bthidev_ctl_disconnected(void *arg, int err) if (sc->sc_int == NULL) { printf("%s: disconnected\n", sc->sc_btdev.sc_dev.dv_xname); - if (sc->sc_flags & BTHID_INITIATE) + if (sc->sc_flags & BTHID_RECONNECT) timeout_add(&sc->sc_reconnect, BTHID_RETRY_INTERVAL * hz); else sc->sc_state = BTHID_WAIT_CTL; + } else { + /* + * The interrupt channel should have been closed first, + * but its potentially unsafe to detach that from here. + * Give them a second to do the right thing or let the + * callout handle it. + */ + timeout_add(&sc->sc_reconnect, hz); } } @@ -600,12 +676,19 @@ bthidev_int_disconnected(void *arg, int err) if (sc->sc_ctl == NULL) { printf("%s: disconnected\n", sc->sc_btdev.sc_dev.dv_xname); + sc->sc_flags &= ~BTHID_CONNECTING; - if (sc->sc_flags & BTHID_INITIATE) + if (sc->sc_flags & BTHID_RECONNECT) timeout_add(&sc->sc_reconnect, BTHID_RETRY_INTERVAL * hz); else sc->sc_state = BTHID_WAIT_CTL; + } else { + /* + * The control channel should be closing also, allow + * them a chance to do that before we force it. + */ + timeout_add(&sc->sc_reconnect, hz); } } @@ -623,7 +706,7 @@ bthidev_ctl_newconn(void *arg, struct sockaddr_bt *laddr, struct bthidev_softc *sc = arg; if (bdaddr_same(&raddr->bt_bdaddr, &sc->sc_raddr) == 0 - || (sc->sc_flags & BTHID_INITIATE) + || (sc->sc_flags & BTHID_CONNECTING) || sc->sc_state != BTHID_WAIT_CTL || sc->sc_ctl != NULL || sc->sc_int != NULL) @@ -640,7 +723,7 @@ bthidev_int_newconn(void *arg, struct sockaddr_bt *laddr, struct bthidev_softc *sc = arg; if (bdaddr_same(&raddr->bt_bdaddr, &sc->sc_raddr) == 0 - || (sc->sc_flags & BTHID_INITIATE) + || (sc->sc_flags & BTHID_CONNECTING) || sc->sc_state != BTHID_WAIT_INT || sc->sc_ctl == NULL || sc->sc_int != NULL) @@ -657,6 +740,27 @@ bthidev_complete(void *arg, int count) /* dont care */ } +void +bthidev_linkmode(void *arg, int new) +{ + struct bthidev_softc *sc = arg; + + if ((sc->sc_mode & L2CAP_LM_AUTH) && !(new & L2CAP_LM_AUTH)) + printf("%s: auth failed\n", sc->sc_btdev.sc_dev.dv_xname); + else if ((sc->sc_mode & L2CAP_LM_ENCRYPT) && !(new & L2CAP_LM_ENCRYPT)) + printf("%s: encrypt off\n", sc->sc_btdev.sc_dev.dv_xname); + else if ((sc->sc_mode & L2CAP_LM_SECURE) && !(new & L2CAP_LM_SECURE)) + printf("%s: insecure\n", sc->sc_btdev.sc_dev.dv_xname); + else + return; + + if (sc->sc_int != NULL) + l2cap_disconnect(sc->sc_int, 0); + + if (sc->sc_ctl != NULL) + l2cap_disconnect(sc->sc_ctl, 0); +} + /* * Receive reports from the protocol stack. */ @@ -664,7 +768,7 @@ void bthidev_input(void *arg, struct mbuf *m) { struct bthidev_softc *sc = arg; - struct bthidev *dev; + struct bthidev *hidev; uint8_t *data; int len; @@ -687,15 +791,15 @@ bthidev_input(void *arg, struct mbuf *m) if (len < 3) goto release; - LIST_FOREACH(dev, &sc->sc_list, sc_next) { - if (data[1] == dev->sc_id) { + LIST_FOREACH(hidev, &sc->sc_list, sc_next) { + if (data[1] == hidev->sc_id) { switch (BTHID_DATA_PARAM(data[0])) { case BTHID_DATA_INPUT: - (*dev->sc_input)(dev, data + 2, len - 2); + (*hidev->sc_input)(hidev, data + 2, len - 2); break; case BTHID_DATA_FEATURE: - (*dev->sc_feature)(dev, data + 2, len - 2); + (*hidev->sc_feature)(hidev, data + 2, len - 2); break; default: @@ -747,7 +851,7 @@ release: */ void -bthidev_null(struct bthidev *dev, uint8_t *report, int len) +bthidev_null(struct bthidev *hidev, uint8_t *report, int len) { /* @@ -757,9 +861,9 @@ bthidev_null(struct bthidev *dev, uint8_t *report, int len) } int -bthidev_output(struct bthidev *dev, uint8_t *report, int rlen) +bthidev_output(struct bthidev *hidev, uint8_t *report, int rlen) { - struct bthidev_softc *sc = (struct bthidev_softc *)dev->sc_parent; + struct bthidev_softc *sc = (struct bthidev_softc *)hidev->sc_parent; struct mbuf *m; int s, err; @@ -789,7 +893,7 @@ bthidev_output(struct bthidev *dev, uint8_t *report, int rlen) * data[2..N] = report */ mtod(m, uint8_t *)[0] = (uint8_t)((BTHID_DATA << 4) | BTHID_DATA_OUTPUT); - mtod(m, uint8_t *)[1] = dev->sc_id; + mtod(m, uint8_t *)[1] = hidev->sc_id; memcpy(mtod(m, uint8_t *) + 2, report, rlen); m->m_pkthdr.len = m->m_len = rlen + 2; |