diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/bluetooth/btdev.h | 68 | ||||
-rw-r--r-- | sys/dev/bluetooth/bthidev.c | 801 | ||||
-rw-r--r-- | sys/dev/bluetooth/bthidev.h | 22 | ||||
-rw-r--r-- | sys/dev/bluetooth/btkbd.c | 28 | ||||
-rw-r--r-- | sys/dev/bluetooth/btms.c | 21 | ||||
-rw-r--r-- | sys/dev/bluetooth/files.bluetooth | 21 | ||||
-rw-r--r-- | sys/dev/usb/usbhid.h | 5 | ||||
-rw-r--r-- | sys/dev/wscons/wsconsio.h | 4 |
8 files changed, 911 insertions, 59 deletions
diff --git a/sys/dev/bluetooth/btdev.h b/sys/dev/bluetooth/btdev.h index f284bfc548e..e158b9598e6 100644 --- a/sys/dev/bluetooth/btdev.h +++ b/sys/dev/bluetooth/btdev.h @@ -1,5 +1,5 @@ -/* $OpenBSD: btdev.h,v 1.2 2007/07/27 20:22:12 xsa Exp $ */ -/* $NetBSD: btdev.h,v 1.6 2007/04/21 06:15:22 plunky Exp $ */ +/* $OpenBSD: btdev.h,v 1.3 2007/09/01 17:06:26 xsa Exp $ */ +/* $NetBSD: btdev.h,v 1.1 2006/06/19 15:44:45 gdamore Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -35,25 +35,63 @@ #ifndef _DEV_BLUETOOTH_BTDEV_H_ #define _DEV_BLUETOOTH_BTDEV_H_ +/* + * Bluetooth Device attach arguments (from userland) + */ +struct btdev_attach_args { + bdaddr_t bd_laddr; /* local address */ + bdaddr_t bd_raddr; /* remote address */ + uint16_t bd_type; /* device type */ + + union { + struct { /* HID arguments */ + uint16_t hid_flags;/* HID flags */ + uint16_t hid_ctl; /* control PSM */ + uint16_t hid_int; /* interrupt PSM */ + void *hid_desc; /* HID descriptor */ + uint16_t hid_dlen; /* descriptor length */ + } bdd_hid; + + struct { /* HSET arguments */ + uint8_t hset_channel; /* RFCOMM channel */ + uint8_t hset_mtu; /* SCO mtu */ + } bdd_hset; + + struct { /* Advance Audio arguments */ + } bdd_a2dp; + } bdd; +}; + +#define bd_hid bdd.bdd_hid +#define bd_hset bdd.bdd_hset +#define bd_a2dp bdd.bdd_a2dp + +/* btdev type */ +#define BTDEV_HID 0x0001 +#define BTDEV_HSET 0x0002 + +/* bthid flags */ +#define BTHID_INITIATE (1 << 0) /* normally initiate */ +#define BTHID_CONNECT (1 << 1) /* initiate connect */ + /* btdev attach/detach ioctl's */ -#define BTDEV_ATTACH _IOW('b', 14, struct plistref) -#define BTDEV_DETACH _IOW('b', 15, struct plistref) - -/* btdev properties */ -#define BTDEVtype "device-type" -#define BTDEVladdr "local-bdaddr" -#define BTDEVraddr "remote-bdaddr" -#define BTDEVservice "service-name" -#define BTDEVmode "link-mode" -#define BTDEVauth "auth" -#define BTDEVencrypt "encrypt" -#define BTDEVsecure "secure" +#define BTDEV_ATTACH _IOW('b', 14, struct btdev_attach_args) +#define BTDEV_DETACH _IOW('b', 15, bdaddr_t) #ifdef _KERNEL + +/* + * Bluetooth device header + */ struct btdev { - struct device sc_dev; + struct device sc_dev; /* system device */ + bdaddr_t sc_addr; /* device bdaddr */ + LIST_ENTRY(btdev) sc_next; }; + +#define btdev_name(d) (((struct btdev *)(d))->sc_dev.dv_xname) + #endif /* _KERNEL */ #endif /* _DEV_BLUETOOTH_BTDEV_H_ */ diff --git a/sys/dev/bluetooth/bthidev.c b/sys/dev/bluetooth/bthidev.c new file mode 100644 index 00000000000..4304f6cc02b --- /dev/null +++ b/sys/dev/bluetooth/bthidev.c @@ -0,0 +1,801 @@ +/* $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 $ */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Iain Hibbert for Itronix Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/fcntl.h> +#include <sys/kernel.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/proc.h> +#include <sys/systm.h> + +#include <netbt/bluetooth.h> +#include <netbt/l2cap.h> + +#include <dev/usb/hid.h> +#include <dev/bluetooth/btdev.h> +#include <dev/bluetooth/bthid.h> +#include <dev/bluetooth/bthidev.h> + +#define MAX_DESCRIPTOR_LEN 1024 /* sanity check */ + +/* bthidev softc */ +struct bthidev_softc { + struct btdev sc_btdev; /* device+ */ + uint16_t sc_state; + uint16_t sc_flags; + + bdaddr_t sc_laddr; /* local address */ + bdaddr_t sc_raddr; /* remote address */ + + void *sc_desc; /* HID descriptor */ + int sc_dlen; /* descriptor length */ + + uint16_t sc_ctlpsm; /* control PSM */ + struct l2cap_channel *sc_ctl; /* control channel */ + struct l2cap_channel *sc_ctl_l; /* control listen */ + + uint16_t sc_intpsm; /* interrupt PSM */ + struct l2cap_channel *sc_int; /* interrupt channel */ + struct l2cap_channel *sc_int_l; /* interrupt listen */ + + LIST_HEAD(,bthidev) sc_list; /* child list */ + + struct timeout sc_reconnect; + int sc_attempts; /* connection attempts */ +}; + +/* device state */ +#define BTHID_CLOSED 0 +#define BTHID_WAIT_CTL 1 +#define BTHID_WAIT_INT 2 +#define BTHID_OPEN 3 +#define BTHID_DETACHING 4 + +#define BTHID_RETRY_INTERVAL 5 /* seconds between connection attempts */ + +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); + +int bthidev_match(struct device *, void *, void *); +void bthidev_attach(struct device *, struct device *, void *); +int bthidev_detach(struct device *, int); +int bthidev_print(void *, const char *); + +int bthidevsubmatch(struct device *parent, void *, void *); + +struct cfdriver bthidev_cd = { + NULL, "bthidev", DV_DULL +}; + +const struct cfattach bthidev_ca = { + sizeof(struct bthidev_softc), + bthidev_match, + bthidev_attach, + bthidev_detach, +}; + +/* bluetooth(9) protocol methods for L2CAP */ +void bthidev_connecting(void *); +void bthidev_ctl_connected(void *); +void bthidev_int_connected(void *); +void bthidev_ctl_disconnected(void *, int); +void bthidev_int_disconnected(void *, int); +void *bthidev_ctl_newconn(void *, struct sockaddr_bt *, + struct sockaddr_bt *); +void *bthidev_int_newconn(void *, struct sockaddr_bt *, + struct sockaddr_bt *); +void bthidev_complete(void *, int); +void bthidev_input(void *, struct mbuf *); + +const struct btproto bthidev_ctl_proto = { + bthidev_connecting, + bthidev_ctl_connected, + bthidev_ctl_disconnected, + bthidev_ctl_newconn, + bthidev_complete, + NULL, + bthidev_input, +}; + +const struct btproto bthidev_int_proto = { + bthidev_connecting, + bthidev_int_connected, + bthidev_int_disconnected, + bthidev_int_newconn, + bthidev_complete, + NULL, + bthidev_input, +}; + + +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) + return 0; + + return 1; +} + +void +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 hid_data *d; + struct hid_item h; + int maxid, rep, s; + + /* + * Init softc + */ + LIST_INIT(&sc->sc_list); + timeout_set(&sc->sc_reconnect, bthidev_timeout, sc); + sc->sc_state = BTHID_CLOSED; + + /* + * copy in our configuration info + */ + 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; + + sc->sc_flags = bda->bd_hid.hid_flags; + if (sc->sc_flags & BTHID_INITIATE) + sc->sc_flags |= BTHID_CONNECT; + + 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"); + 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. + */ + maxid = -1; + h.report_ID = 0; + d = hid_start_parse(sc->sc_desc, sc->sc_dlen, hid_none); + while (hid_get_item(d, &h)) { + if (h.report_ID > maxid) + maxid = h.report_ID; + } + hid_end_parse(d); + + if (maxid < 0) { + printf(" no reports found\n"); + return; + } + + printf("\n"); + + for (rep = 0 ; rep <= maxid ; rep++) { + if (hid_report_size(sc->sc_desc, sc->sc_dlen, hid_feature, rep) == 0 + && hid_report_size(sc->sc_desc, sc->sc_dlen, hid_input, rep) == 0 + && hid_report_size(sc->sc_desc, sc->sc_dlen, hid_output, rep) == 0) + continue; + + bha.ba_desc = sc->sc_desc; + bha.ba_dlen = sc->sc_dlen; + bha.ba_input = bthidev_null; + bha.ba_feature = bthidev_null; + bha.ba_output = bthidev_output; + bha.ba_id = rep; + + dev = (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); + } + } + + /* + * start bluetooth connections + */ + s = splsoftnet(); + if ((sc->sc_flags & BTHID_INITIATE) == 0) + bthidev_listen(sc); + + if (sc->sc_flags & BTHID_CONNECT) + bthidev_connect(sc); + splx(s); +} + +int +bthidev_detach(struct device *self, int flags) +{ + struct bthidev_softc *sc = (struct bthidev_softc *)self; + struct bthidev *dev; + int s; + + s = splsoftnet(); + sc->sc_flags = 0; /* disable reconnecting */ + + /* release interrupt listen */ + if (sc->sc_int_l != NULL) { + l2cap_detach(&sc->sc_int_l); + sc->sc_int_l = NULL; + } + + /* release control listen */ + if (sc->sc_ctl_l != NULL) { + l2cap_detach(&sc->sc_ctl_l); + sc->sc_ctl_l = NULL; + } + + /* close interrupt channel */ + if (sc->sc_int != NULL) { + l2cap_disconnect(sc->sc_int, 0); + l2cap_detach(&sc->sc_int); + sc->sc_int = NULL; + } + + /* close control channel */ + if (sc->sc_ctl != NULL) { + l2cap_disconnect(sc->sc_ctl, 0); + l2cap_detach(&sc->sc_ctl); + sc->sc_ctl = NULL; + } + + 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); + } + + /* release descriptor */ + if (sc->sc_desc != NULL) { + free(sc->sc_desc, M_BTHIDEV); + sc->sc_desc = NULL; + } + return 0; +} + +int +bthidev_print(void *aux, const char *pnp) +{ + struct bthidev_attach_args *ba = aux; + + if (pnp != NULL) + printf("%s:", pnp); + + if (ba->ba_id > 0) + printf(" reportid %d", ba->ba_id); + + return UNCONF; +} + +int +bthidevsubmatch(struct device *parent, void *match, void *aux) +{ + struct bthidev_attach_args *ba = aux; + struct cfdata *cf = match; + + if (cf->bthidevcf_reportid != BTHIDEV_UNK_REPORTID && + cf->bthidevcf_reportid != ba->ba_id) + return (0); + + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); +} + + +/***************************************************************************** + * + * bluetooth(4) HID attach/detach routines + */ + +/* + * callouts are scheduled to initiate outgoing connections + * after the connection has been lost. + */ +void +bthidev_timeout(void *arg) +{ + struct bthidev_softc *sc = arg; + int s, err; + + 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); + break; + + case BTHID_WAIT_CTL: + break; + + case BTHID_WAIT_INT: + break; + + case BTHID_OPEN: + break; + + case BTHID_DETACHING: + wakeup(sc); + break; + + default: + break; + } + splx(s); +} + +/* + * listen for our device + */ +int +bthidev_listen(struct bthidev_softc *sc) +{ + struct sockaddr_bt sa; + int err; + + memset(&sa, 0, sizeof(sa)); + sa.bt_len = sizeof(sa); + sa.bt_family = AF_BLUETOOTH; + bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); + + /* + * Listen on control PSM + */ + err = l2cap_attach(&sc->sc_ctl_l, &bthidev_ctl_proto, sc); + if (err) + return err; + + sa.bt_psm = sc->sc_ctlpsm; + err = l2cap_bind(sc->sc_ctl_l, &sa); + if (err) + return err; + + err = l2cap_listen(sc->sc_ctl_l); + if (err) + return err; + + /* + * Listen on interrupt PSM + */ + err = l2cap_attach(&sc->sc_int_l, &bthidev_int_proto, sc); + if (err) + return err; + + sa.bt_psm = sc->sc_intpsm; + err = l2cap_bind(sc->sc_int_l, &sa); + if (err) + return err; + + err = l2cap_listen(sc->sc_int_l); + if (err) + return err; + + sc->sc_state = BTHID_WAIT_CTL; + return 0; +} + +/* + * start connecting to our device + */ +int +bthidev_connect(struct bthidev_softc *sc) +{ + struct sockaddr_bt sa; + int err; + + if (sc->sc_attempts++ > 0) + printf("%s: connect (#%d)\n", + sc->sc_btdev.sc_dev.dv_xname, sc->sc_attempts); + + memset(&sa, 0, sizeof(sa)); + sa.bt_len = sizeof(sa); + sa.bt_family = AF_BLUETOOTH; + + err = l2cap_attach(&sc->sc_ctl, &bthidev_ctl_proto, sc); + if (err) { + printf("%s: l2cap_attach failed (%d)\n", + sc->sc_btdev.sc_dev.dv_xname, err); + return err; + } + + bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); + err = l2cap_bind(sc->sc_ctl, &sa); + if (err) { + printf("%s: l2cap_bind failed (%d)\n", + sc->sc_btdev.sc_dev.dv_xname, err); + return err; + } + + sa.bt_psm = sc->sc_ctlpsm; + bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr); + err = l2cap_connect(sc->sc_ctl, &sa); + if (err) { + printf("%s: l2cap_connect failed (%d)\n", + sc->sc_btdev.sc_dev.dv_xname, err); + return err; + } + + sc->sc_state = BTHID_WAIT_CTL; + return 0; +} + +/***************************************************************************** + * + * bluetooth(9) callback methods for L2CAP + * + * All these are called from Bluetooth Protocol code, in a soft + * interrupt context at IPL_SOFTNET. + */ + +void +bthidev_connecting(void *arg) +{ + + /* dont care */ +} + +void +bthidev_ctl_connected(void *arg) +{ + struct sockaddr_bt sa; + struct bthidev_softc *sc = arg; + int err; + + if (sc->sc_state != BTHID_WAIT_CTL) + return; + + KASSERT(sc->sc_ctl != NULL); + KASSERT(sc->sc_int == NULL); + + if (sc->sc_flags & BTHID_CONNECT) { + /* initiate connect on interrupt PSM */ + err = l2cap_attach(&sc->sc_int, &bthidev_int_proto, sc); + if (err) + goto fail; + + memset(&sa, 0, sizeof(sa)); + sa.bt_len = sizeof(sa); + sa.bt_family = AF_BLUETOOTH; + bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); + + err = l2cap_bind(sc->sc_int, &sa); + if (err) + goto fail; + + sa.bt_psm = sc->sc_intpsm; + bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr); + err = l2cap_connect(sc->sc_int, &sa); + if (err) + goto fail; + } + + sc->sc_state = BTHID_WAIT_INT; + return; + +fail: + l2cap_detach(&sc->sc_ctl); + sc->sc_ctl = NULL; + printf("%s: connect failed (%d)\n", + sc->sc_btdev.sc_dev.dv_xname, err); +} + +void +bthidev_int_connected(void *arg) +{ + struct bthidev_softc *sc = arg; + + if (sc->sc_state != BTHID_WAIT_INT) + return; + + KASSERT(sc->sc_ctl != NULL); + KASSERT(sc->sc_int != NULL); + + sc->sc_attempts = 0; + sc->sc_flags &= ~BTHID_CONNECT; + sc->sc_state = BTHID_OPEN; + + printf("%s: connected\n", sc->sc_btdev.sc_dev.dv_xname); +} + +/* + * Disconnected + * + * Depending on our state, this could mean several things, but essentially + * we are lost. + */ +void +bthidev_ctl_disconnected(void *arg, int err) +{ + struct bthidev_softc *sc = arg; + + if (sc->sc_ctl != NULL) { + l2cap_detach(&sc->sc_ctl); + sc->sc_ctl = NULL; + } + + sc->sc_state = BTHID_CLOSED; + + if (sc->sc_int == NULL) { + printf("%s: disconnected\n", sc->sc_btdev.sc_dev.dv_xname); + + if (sc->sc_flags & BTHID_INITIATE) + timeout_add(&sc->sc_reconnect, + BTHID_RETRY_INTERVAL * hz); + else + sc->sc_state = BTHID_WAIT_CTL; + } +} + +void +bthidev_int_disconnected(void *arg, int err) +{ + struct bthidev_softc *sc = arg; + + if (sc->sc_int != NULL) { + l2cap_detach(&sc->sc_int); + sc->sc_int = NULL; + } + + sc->sc_state = BTHID_CLOSED; + + if (sc->sc_ctl == NULL) { + printf("%s: disconnected\n", sc->sc_btdev.sc_dev.dv_xname); + + if (sc->sc_flags & BTHID_INITIATE) + timeout_add(&sc->sc_reconnect, + BTHID_RETRY_INTERVAL * hz); + else + sc->sc_state = BTHID_WAIT_CTL; + } +} + +/* + * New Connections + * + * We give a new L2CAP handle back if this matches the BDADDR we are + * listening for and we are in the right state. bthidev_connected will + * be called when the connection is open, so nothing else to do here + */ +void * +bthidev_ctl_newconn(void *arg, struct sockaddr_bt *laddr, + struct sockaddr_bt *raddr) +{ + struct bthidev_softc *sc = arg; + + if (bdaddr_same(&raddr->bt_bdaddr, &sc->sc_raddr) == 0 + || (sc->sc_flags & BTHID_INITIATE) + || sc->sc_state != BTHID_WAIT_CTL + || sc->sc_ctl != NULL + || sc->sc_int != NULL) + return NULL; + + l2cap_attach(&sc->sc_ctl, &bthidev_ctl_proto, sc); + return sc->sc_ctl; +} + +void * +bthidev_int_newconn(void *arg, struct sockaddr_bt *laddr, + struct sockaddr_bt *raddr) +{ + struct bthidev_softc *sc = arg; + + if (bdaddr_same(&raddr->bt_bdaddr, &sc->sc_raddr) == 0 + || (sc->sc_flags & BTHID_INITIATE) + || sc->sc_state != BTHID_WAIT_INT + || sc->sc_ctl == NULL + || sc->sc_int != NULL) + return NULL; + + l2cap_attach(&sc->sc_int, &bthidev_int_proto, sc); + return sc->sc_int; +} + +void +bthidev_complete(void *arg, int count) +{ + + /* dont care */ +} + +/* + * Receive reports from the protocol stack. + */ +void +bthidev_input(void *arg, struct mbuf *m) +{ + struct bthidev_softc *sc = arg; + struct bthidev *dev; + uint8_t *data; + int len; + + if (sc->sc_state != BTHID_OPEN) + goto release; + + if (m->m_pkthdr.len > m->m_len) + printf("%s: truncating HID report\n", + sc->sc_btdev.sc_dev.dv_xname); + + len = m->m_len; + data = mtod(m, uint8_t *); + + if (BTHID_TYPE(data[0]) == BTHID_DATA) { + /* + * data[0] == type / parameter + * data[1] == id + * data[2..len] == report + */ + if (len < 3) + goto release; + + LIST_FOREACH(dev, &sc->sc_list, sc_next) { + if (data[1] == dev->sc_id) { + switch (BTHID_DATA_PARAM(data[0])) { + case BTHID_DATA_INPUT: + (*dev->sc_input)(dev, data + 2, len - 2); + break; + + case BTHID_DATA_FEATURE: + (*dev->sc_feature)(dev, data + 2, len - 2); + break; + + default: + break; + } + + goto release; + } + } + printf("%s: report id %d, len = %d ignored\n", + sc->sc_btdev.sc_dev.dv_xname, data[1], len - 2); + + goto release; + } + + if (BTHID_TYPE(data[0]) == BTHID_CONTROL) { + if (len < 1) + goto release; + + if (BTHID_DATA_PARAM(data[0]) == BTHID_CONTROL_UNPLUG) { + printf("%s: unplugged\n", + sc->sc_btdev.sc_dev.dv_xname); + + /* close interrupt channel */ + if (sc->sc_int != NULL) { + l2cap_disconnect(sc->sc_int, 0); + l2cap_detach(&sc->sc_int); + sc->sc_int = NULL; + } + + /* close control channel */ + if (sc->sc_ctl != NULL) { + l2cap_disconnect(sc->sc_ctl, 0); + l2cap_detach(&sc->sc_ctl); + sc->sc_ctl = NULL; + } + } + + goto release; + } + +release: + m_freem(m); +} + +/***************************************************************************** + * + * IO routines + */ + +void +bthidev_null(struct bthidev *dev, uint8_t *report, int len) +{ + + /* + * empty routine just in case the device + * provided no method to handle this report + */ +} + +int +bthidev_output(struct bthidev *dev, uint8_t *report, int rlen) +{ + struct bthidev_softc *sc = (struct bthidev_softc *)dev->sc_parent; + struct mbuf *m; + int s, err; + + if (sc == NULL || sc->sc_state != BTHID_OPEN) + return ENOTCONN; + + KASSERT(sc->sc_ctl != NULL); + KASSERT(sc->sc_int != NULL); + + if (rlen == 0 || report == NULL) + return 0; + + if (rlen > MHLEN - 2) { + printf("%s: output report too long (%d)!\n", + sc->sc_btdev.sc_dev.dv_xname, rlen); + + return EMSGSIZE; + } + + m = m_gethdr(M_DONTWAIT, MT_DATA); + if (m == NULL) + return ENOMEM; + + /* + * data[0] = type / parameter + * data[1] = id + * 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; + memcpy(mtod(m, uint8_t *) + 2, report, rlen); + m->m_pkthdr.len = m->m_len = rlen + 2; + + s = splsoftnet(); + err = l2cap_send(sc->sc_int, m); + splx(s); + + return err; +} diff --git a/sys/dev/bluetooth/bthidev.h b/sys/dev/bluetooth/bthidev.h index 15fa38ab672..76765bfaf6d 100644 --- a/sys/dev/bluetooth/bthidev.h +++ b/sys/dev/bluetooth/bthidev.h @@ -1,5 +1,5 @@ -/* $OpenBSD: bthidev.h,v 1.1 2007/07/27 16:52:24 gwk Exp $ */ -/* $NetBSD: bthidev.h,v 1.3 2006/09/10 15:45:56 plunky Exp $ */ +/* $OpenBSD: bthidev.h,v 1.2 2007/09/01 17:06:26 xsa Exp $ */ +/* $NetBSD: bthidev.h,v 1.1 2006/06/19 15:44:45 gdamore Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. @@ -35,17 +35,15 @@ #ifndef _DEV_BLUETOOTH_BTHIDEV_H_ #define _DEV_BLUETOOTH_BTHIDEV_H_ -/* bthidev(4) properties */ -#define BTHIDEVcontrolpsm "control-psm" -#define BTHIDEVinterruptpsm "interrupt-psm" -#define BTHIDEVdescriptor "descriptor" -#define BTHIDEVreconnect "reconnect" +#define BTHIDBUSCF_REPORTID 0 +#define BTHIDBUSCF_REPORTID_DEFAULT -1 + +#define bthidevcf_reportid cf_loc[BTHIDBUSCF_REPORTID] +#define BTHIDEV_UNK_REPORTID BTHIDBUSCF_REPORTID_DEFAULT -#ifdef _KERNEL -/* HID device header */ struct bthidev { struct device sc_dev; - struct device *sc_parent; + struct btdev *sc_parent; int sc_id; /* report id */ int sc_len; /* report len */ @@ -59,9 +57,8 @@ struct bthidev { LIST_ENTRY(bthidev) sc_next; }; -/* HID device attach arguments */ struct bthidev_attach_args { - const void *ba_desc; /* descriptor */ + void *ba_desc; /* descriptor */ int ba_dlen; /* descriptor length */ int ba_id; /* report id */ @@ -72,6 +69,5 @@ struct bthidev_attach_args { int (*ba_output) /* output method */ (struct bthidev *, uint8_t *, int); }; -#endif /* _KERNEL */ #endif /* _DEV_BLUETOOTH_BTHIDEV_H_ */ diff --git a/sys/dev/bluetooth/btkbd.c b/sys/dev/bluetooth/btkbd.c index cd5958a2d86..0d65a8efc18 100644 --- a/sys/dev/bluetooth/btkbd.c +++ b/sys/dev/bluetooth/btkbd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: btkbd.c,v 1.1 2007/07/27 16:52:24 gwk Exp $ */ +/* $OpenBSD: btkbd.c,v 1.2 2007/09/01 17:06:26 xsa Exp $ */ /* $NetBSD: btkbd.c,v 1.7 2007/07/09 21:00:31 ad Exp $ */ /*- @@ -107,7 +107,7 @@ struct btkbd_softc { #endif }; -int btkbd_match(struct device *, struct cfdata *, void *); +int btkbd_match(struct device *, void *, void *); void btkbd_attach(struct device *, struct device *, void *); int btkbd_detach(struct device *, int); @@ -124,7 +124,7 @@ const struct cfattach btkbd_ca = { int btkbd_enable(void *, int); void btkbd_set_leds(void *, int); -int btkbd_ioctl(void *, unsigned long, void *, int, struct lwp *); +int btkbd_ioctl(void *, u_long, caddr_t, int, struct proc *); const struct wskbd_accessops btkbd_accessops = { btkbd_enable, @@ -150,7 +150,7 @@ const struct wskbd_mapdata btkbd_keymapdata = { void btkbd_input(struct bthidev *, uint8_t *, int); /* internal prototypes */ -const char *btkbd_parse_desc(struct btkbd_softc *, int, const void *, int); +const char *btkbd_parse_desc(struct btkbd_softc *, int, void *, int); #ifdef WSDISPLAY_COMPAT_RAWKBD #ifdef BTKBD_REPEAT @@ -160,7 +160,7 @@ void btkbd_repeat(void *); int -btkbd_match(struct device *self, struct cfdata *cfdata, void *aux) +btkbd_match(struct device *self, void *match, void *aux) { struct bthidev_attach_args *ba = aux; @@ -226,7 +226,7 @@ btkbd_detach(struct device *self, int flags) } const char * -btkbd_parse_desc(struct btkbd_softc *sc, int id, const void *desc, int dlen) +btkbd_parse_desc(struct btkbd_softc *sc, int id, void *desc, int dlen) { struct hid_data *d; struct hid_item h; @@ -326,22 +326,22 @@ btkbd_set_leds(void *self, int leds) } int -btkbd_ioctl(void *self, unsigned long cmd, void *data, int flag, struct lwp *l) +btkbd_ioctl(void *self, u_long cmd, caddr_t data, int flag, struct proc *p) { struct btkbd_softc *sc = (struct btkbd_softc *)self; switch (cmd) { case WSKBDIO_GTYPE: *(int *)data = WSKBD_TYPE_BLUETOOTH; - break; + return 0; case WSKBDIO_SETLEDS: btkbd_set_leds(sc, *(int *)data); - break; + return 0; case WSKBDIO_GETLEDS: *(int *)data = sc->sc_leds; - break; + return 0; #ifdef WSDISPLAY_COMPAT_RAWKBD case WSKBDIO_SETMODE: @@ -349,14 +349,10 @@ btkbd_ioctl(void *self, unsigned long cmd, void *data, int flag, struct lwp *l) #ifdef BTKBD_REPEAT timeout_del(&sc->sc_repeat); #endif - break; + return 0; #endif - - default: - return EPASSTHROUGH; } - - return 0; + return -1; } #ifdef WSDISPLAY_COMPAT_RAWKBD diff --git a/sys/dev/bluetooth/btms.c b/sys/dev/bluetooth/btms.c index fa49dcc570e..bfbd43fbbaa 100644 --- a/sys/dev/bluetooth/btms.c +++ b/sys/dev/bluetooth/btms.c @@ -1,4 +1,4 @@ -/* $OpenBSD: btms.c,v 1.1 2007/07/27 16:52:24 gwk Exp $ */ +/* $OpenBSD: btms.c,v 1.2 2007/09/01 17:06:26 xsa Exp $ */ /* $NetBSD: btms.c,v 1.6 2007/03/04 06:01:45 christos Exp $ */ /*- @@ -81,7 +81,7 @@ struct btms_softc { #define BTMS_HASZ (1 << 1) /* has Z direction */ #define BTMS_HASW (1 << 2) /* has W direction */ -int btms_match(struct device *, struct cfdata *, void *); +int btms_match(struct device *, void *, void *); void btms_attach(struct device *, struct device *, void *); int btms_detach(struct device *, int); @@ -98,7 +98,7 @@ const struct cfattach btms_ca = { /* wsmouse(4) accessops */ int btms_enable(void *); -int btms_ioctl(void *, unsigned long, void *, int, struct lwp *); +int btms_ioctl(void *, u_long, caddr_t, int, struct proc *); void btms_disable(void *); const struct wsmouse_accessops btms_accessops = { @@ -108,11 +108,11 @@ const struct wsmouse_accessops btms_accessops = { }; /* bthid methods */ -void btms_input(struct bthidev *, uint8_t *, int); +void btms_input(struct bthidev *, uint8_t *, int); int -btms_match(struct device *parent, struct cfdata *match, void *aux) +btms_match(struct device *parent, void *match, void *aux) { struct bthidev_attach_args *ba = aux; @@ -257,20 +257,17 @@ btms_enable(void *self) } int -btms_ioctl(void *self, unsigned long cmd, void *data, int flag, struct lwp *l) +btms_ioctl(void *self, u_long cmd, caddr_t data, int flag, struct proc *p) { /* struct btms_softc *sc = (struct btms_softc *)self; */ switch (cmd) { case WSMOUSEIO_GTYPE: - *(uint *)data = WSMOUSE_TYPE_BLUETOOTH; - break; - - default: - return EPASSTHROUGH; + *(u_int *)data = WSMOUSE_TYPE_BLUETOOTH; + return 0; } - return 0; + return -1; } void diff --git a/sys/dev/bluetooth/files.bluetooth b/sys/dev/bluetooth/files.bluetooth index e32ab21f4ae..4de928086af 100644 --- a/sys/dev/bluetooth/files.bluetooth +++ b/sys/dev/bluetooth/files.bluetooth @@ -1,4 +1,4 @@ -# $OpenBSD: files.bluetooth,v 1.4 2007/07/23 22:42:27 mk Exp $ +# $OpenBSD: files.bluetooth,v 1.5 2007/09/01 17:06:26 xsa Exp $ # # Config file and device description for machine-independent Bluetooth code. # Included by ports that support Bluetooth host controllers. @@ -6,3 +6,22 @@ device bthub {} attach bthub at btbus file dev/bluetooth/bthub.c bthub needs-flag + +# HID +# HID "bus" +define bthidbus {[ reportid = -1 ]} + +# HID Device +device bthidev: bluetooth, bthidbus, hid +attach bthidev at bthub +file dev/bluetooth/bthidev.c bthidev + +# HID Mice +device btms: hid, wsmousedev +attach btms at bthidbus +file dev/bluetooth/btms.c btms + +# HID Keyboard +device btkbd: hid, wskbddev +attach btkbd at bthidbus +file dev/bluetooth/btkbd.c btkbd diff --git a/sys/dev/usb/usbhid.h b/sys/dev/usb/usbhid.h index 1ce0cd31cbf..dad6a50b29b 100644 --- a/sys/dev/usb/usbhid.h +++ b/sys/dev/usb/usbhid.h @@ -1,4 +1,4 @@ -/* $OpenBSD: usbhid.h,v 1.9 2007/06/09 11:06:53 mbalmer Exp $ */ +/* $OpenBSD: usbhid.h,v 1.10 2007/09/01 17:06:26 xsa Exp $ */ /* $NetBSD: usbhid.h,v 1.11 2001/12/28 00:20:24 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbhid.h,v 1.7 1999/11/17 22:33:51 n_hibma Exp $ */ @@ -172,6 +172,9 @@ typedef struct usb_hid_descriptor { #define HUD_LED_COMPOSE 0x0004 #define HUD_LED_KANA 0x0005 +/* Usages, Consumer */ +#define HUC_AC_PAN 0x0238 + #define HID_USAGE2(p, u) (((p) << 16) | u) #define HID_GET_USAGE(u) ((u) & 0xffff) #define HID_GET_USAGE_PAGE(u) (((u) >> 16) & 0xffff) diff --git a/sys/dev/wscons/wsconsio.h b/sys/dev/wscons/wsconsio.h index 7bd7c4d95b6..3ac638ab784 100644 --- a/sys/dev/wscons/wsconsio.h +++ b/sys/dev/wscons/wsconsio.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wsconsio.h,v 1.45 2007/05/08 20:38:20 robert Exp $ */ +/* $OpenBSD: wsconsio.h,v 1.46 2007/09/01 17:06:26 xsa Exp $ */ /* $NetBSD: wsconsio.h,v 1.74 2005/04/28 07:15:44 martin Exp $ */ /* @@ -122,6 +122,7 @@ struct wscons_event { #define WSKBD_TYPE_LUNA 15 /* OMRON Luna */ #define WSKBD_TYPE_ZAURUS 16 /* Sharp Zaurus */ #define WSKBD_TYPE_DOMAIN 17 /* Apollo Domain */ +#define WSKBD_TYPE_BLUETOOTH 18 /* Bluetooth keyboard */ /* Manipulate the keyboard bell. */ struct wskbd_bell_data { @@ -203,6 +204,7 @@ struct wskbd_map_data { #define WSMOUSE_TYPE_HIL 10 /* HP HIL */ #define WSMOUSE_TYPE_LUNA 11 /* OMRON Luna */ #define WSMOUSE_TYPE_DOMAIN 12 /* Apollo Domain */ +#define WSMOUSE_TYPE_BLUETOOTH 13 /* Bluetooth mouse */ /* Set resolution. Not applicable to all mouse types. */ #define WSMOUSEIO_SRES _IOW('W', 33, u_int) |