summaryrefslogtreecommitdiff
path: root/sys/dev/bluetooth/bthidev.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/bluetooth/bthidev.c')
-rw-r--r--sys/dev/bluetooth/bthidev.c224
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;