summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorgdamore <gdamore@NetBSD.org>2006-06-19 15:44:33 +0000
committergdamore <gdamore@NetBSD.org>2006-06-19 15:44:33 +0000
commita5c89047c05c5d03bdf7663f4585fb0bed64a924 (patch)
tree69f226b62ddfe2f9187d03b1f1f79cff67264727 /sys/dev
parent052a8ad2ad25caabbef9124f0a7c9f0980e20beb (diff)
Initial import of bluetooth stack on behalf of Iain Hibbert. (plunky@,
NetBSD Foundation Membership still pending.) This stack was written by Iain under sponsorship from Itronix Inc. The stack includes support for rfcomm networking (networking via your bluetooth enabled cell phone), hid devices (keyboards/mice), and headsets. Drivers for both PCMCIA and USB bluetooth controllers are included.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/DEVNAMES10
-rw-r--r--sys/dev/Makefile4
-rw-r--r--sys/dev/bluetooth/Makefile6
-rw-r--r--sys/dev/bluetooth/bluetooth.c58
-rw-r--r--sys/dev/bluetooth/bluetooth.h120
-rw-r--r--sys/dev/bluetooth/bt_hci.h127
-rw-r--r--sys/dev/bluetooth/btdev.h96
-rw-r--r--sys/dev/bluetooth/bthci.c648
-rw-r--r--sys/dev/bluetooth/bthci_util.c94
-rw-r--r--sys/dev/bluetooth/bthci_util.h39
-rw-r--r--sys/dev/bluetooth/bthcivar.h48
-rw-r--r--sys/dev/bluetooth/bthid.h89
-rw-r--r--sys/dev/bluetooth/bthidev.c793
-rw-r--r--sys/dev/bluetooth/bthidev.h70
-rw-r--r--sys/dev/bluetooth/bthset.c1079
-rw-r--r--sys/dev/bluetooth/bthset.h54
-rw-r--r--sys/dev/bluetooth/bthub.c245
-rw-r--r--sys/dev/bluetooth/btkbd.c566
-rw-r--r--sys/dev/bluetooth/btms.c328
-rw-r--r--sys/dev/bluetooth/files.bluetooth39
-rw-r--r--sys/dev/pcmcia/bt3c.c968
-rw-r--r--sys/dev/pcmcia/files.pcmcia6
-rw-r--r--sys/dev/pcmcia/pcmciadevs3
-rw-r--r--sys/dev/usb/FILES2
-rw-r--r--sys/dev/usb/files.usb8
-rw-r--r--sys/dev/usb/hid.c32
-rw-r--r--sys/dev/usb/ubt.c1673
-rw-r--r--sys/dev/usb/ubtreg.h40
-rw-r--r--sys/dev/usb/uhci.c13
-rw-r--r--sys/dev/wscons/wsconsio.h4
30 files changed, 5678 insertions, 1584 deletions
diff --git a/sys/dev/DEVNAMES b/sys/dev/DEVNAMES
index 328ca4e6c11..87cebd7e428 100644
--- a/sys/dev/DEVNAMES
+++ b/sys/dev/DEVNAMES
@@ -1,4 +1,4 @@
-# $NetBSD: DEVNAMES,v 1.199 2006/05/14 21:42:26 elad Exp $
+# $NetBSD: DEVNAMES,v 1.200 2006/06/19 15:44:44 gdamore Exp $
#
# This file contains all used device names and defined attributes in
# alphabetical order. New devices added to the system somewhere should first
@@ -169,9 +169,15 @@ boca MI
bonito algor
bpp MI
brgphy MI
+bt3c MI
bt_dac sparc Attribute
bt_dac sparc64 Attribute
+bthidev MI
+bthub MI
+bthset MI
+btkbd MI
btl arc
+btms MI
btnmgr hpcmips
btvmei MI
btvmeii MI
@@ -1282,6 +1288,8 @@ uaudio MI
uba MI
ubsa MI
ubsec MI
+ubt MI
+ubtbcmfw MI
ucb hpcmips
ucbif hpcmips
ucbio hpcmips
diff --git a/sys/dev/Makefile b/sys/dev/Makefile
index 348c6cb52b3..7e3c7aad600 100644
--- a/sys/dev/Makefile
+++ b/sys/dev/Makefile
@@ -1,6 +1,6 @@
-# $NetBSD: Makefile,v 1.22 2006/06/08 00:12:50 rpaulo Exp $
+# $NetBSD: Makefile,v 1.23 2006/06/19 15:44:44 gdamore Exp $
-SUBDIR= apm ata dec dmover hpc i2o ic ieee1394 ir isa microcode ofw \
+SUBDIR= apm ata bluetooth dec dmover hpc i2o ic ieee1394 ir isa microcode ofw \
pci pckbport pcmcia raidframe sbus scsipi sun tc usb vme wscons
INCSDIR= /usr/include/dev
diff --git a/sys/dev/bluetooth/Makefile b/sys/dev/bluetooth/Makefile
new file mode 100644
index 00000000000..060dced8912
--- /dev/null
+++ b/sys/dev/bluetooth/Makefile
@@ -0,0 +1,6 @@
+# $NetBSD: Makefile,v 1.1 2006/06/19 15:44:44 gdamore Exp $
+
+INCSDIR= /usr/include/dev/bluetooth
+INCS= btdev.h bthset.h
+
+.include <bsd.kinc.mk>
diff --git a/sys/dev/bluetooth/bluetooth.c b/sys/dev/bluetooth/bluetooth.c
deleted file mode 100644
index 9f582c65885..00000000000
--- a/sys/dev/bluetooth/bluetooth.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/* $NetBSD: bluetooth.c,v 1.4 2005/12/11 12:21:15 christos Exp $ */
-
-/*
- * Copyright (c) 2002 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Lennart Augustsson (lennart@augustsson.net).
- *
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``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 THE FOUNDATION OR CONTRIBUTORS
- * 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/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bluetooth.c,v 1.4 2005/12/11 12:21:15 christos Exp $");
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/device.h>
-
-#include <dev/bluetooth/bluetooth.h>
-
-int
-bt_print(void *aux, const char *pnp)
-{
- /*struct bt_attach_args *bt = aux;*/
-
- if (pnp != NULL)
- aprint_normal("bthci at %s", pnp);
-
- return (UNCONF);
-}
diff --git a/sys/dev/bluetooth/bluetooth.h b/sys/dev/bluetooth/bluetooth.h
deleted file mode 100644
index 85a87c235e5..00000000000
--- a/sys/dev/bluetooth/bluetooth.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/* $NetBSD: bluetooth.h,v 1.6 2005/12/11 12:21:15 christos Exp $ */
-
-/*
- * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Lennart Augustsson (lennart@augustsson.net) and
- * David Sainty (David.Sainty@dtsp.co.nz).
- *
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``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 THE FOUNDATION OR CONTRIBUTORS
- * 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.
- */
-
-struct btframe_buffer;
-
-struct btframe_channel {
- /*
- * Allocate and transmit a buffer for transmission to
- * Bluetooth device.
- */
-
- /*
- * bt_alloc() allocates a buffer suitable for passing to
- * bt_send() on this channel. The required buffer size should
- * be passed in, and the method returns a pointer to the
- * buffer space, and a handle for the buffer to be passed to
- * bt_send(). Returns NULL on error (invalid size, or
- * allocation error).
- */
- u_int8_t* (*bt_alloc)(void*, size_t, struct btframe_buffer **);
-
- /*
- * bt_send() sends the data stored in the given buffer with
- * the given size. Returns zero on success, or a standard
- * error number on failure.
- */
- int (*bt_send)(void*, struct btframe_buffer *, size_t);
-};
-
-struct btframe_methods {
- int (*bt_open)(void *h, int flag, int mode, struct proc *p);
- int (*bt_close)(void *h, int flag, int mode, struct proc *p);
-
- /* HCI channels */
- struct btframe_channel bt_control;
- struct btframe_channel bt_acldata;
- struct btframe_channel bt_scodata;
-
- /* Raise SPL to a level that will prevent callbacks */
- int (*bt_splraise)(void);
-};
-
-struct btframe_callback_methods {
- /* Received data */
- void (*bt_recveventdata)(void *, u_int8_t *, size_t);
- void (*bt_recvacldata)(void *, u_int8_t *, size_t);
- void (*bt_recvscodata)(void *, u_int8_t *, size_t);
-};
-
-struct bt_attach_args {
- struct btframe_methods const *bt_methods;
- struct btframe_callback_methods const **bt_cb;
- void *bt_handle;
-};
-
-int bt_print(void *aux, const char *pnp);
-
-#define BTGETW(x) (((u_int8_t const*)(x))[0] | ((u_int8_t const*)(x))[1] << 8)
-
-/* HCI interface constants */
-
-/* Maximum event packet length, including header */
-#define BTHCI_EVENT_MIN_LEN 2
-#define BTHCI_EVENT_MAX_LEN (0xff + BTHCI_EVENT_MIN_LEN)
-#define BTHCI_EVENT_LEN_OFFT 1
-#define BTHCI_EVENT_LEN_LENGTH 1
-
-/* Maximum command packet length, including header */
-#define BTHCI_COMMAND_MIN_LEN 3
-#define BTHCI_COMMAND_MAX_LEN (0xff + BTHCI_COMMAND_MIN_LEN)
-#define BTHCI_COMMAND_LEN_OFFT 2
-#define BTHCI_COMMAND_LEN_LENGTH 1
-
-/* Maximum ACL data packet length, including header */
-#define BTHCI_ACL_DATA_MIN_LEN 4
-#define BTHCI_ACL_DATA_MAX_LEN (0xffff + BTHCI_ACL_DATA_MIN_LEN)
-#define BTHCI_ACL_DATA_LEN_OFFT 2
-#define BTHCI_ACL_DATA_LEN_LENGTH 2
-
-/* HCI consumer interface constants */
-#define BTHCI_PKTID_COMMAND 1
-#define BTHCI_PKTID_ACL_DATA 2
-#define BTHCI_PKTID_SCO_DATA 3
-#define BTHCI_PKTID_EVENT 4
diff --git a/sys/dev/bluetooth/bt_hci.h b/sys/dev/bluetooth/bt_hci.h
deleted file mode 100644
index f2d7c106d1d..00000000000
--- a/sys/dev/bluetooth/bt_hci.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* $NetBSD: bt_hci.h,v 1.1 2003/01/11 05:44:08 dsainty Exp $ */
-
-/*
- * Copyright (c) 2003 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by David Sainty <David.Sainty@dtsp.co.nz>
- *
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``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 THE FOUNDATION OR CONTRIBUTORS
- * 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.
- */
-
-/*
- * Constants and structures defined by the Bluetooth specification of
- * the HCI protocol.
- */
-
-#ifndef _NETBT_BT_HCI_H_
-#define _NETBT_BT_HCI_H_
-
-/* Event codes */
-
-/*
- * Note: Names chosen to match FreeBSD, but without the NG prefix.
- * All taken from spec.
- */
-
-#define BT_HCI_EVENT_INQUIRY_COMPL 0x01
-#define BT_HCI_EVENT_INQUIRY_RESULT 0x02
-#define BT_HCI_EVENT_CON_COMPL 0x03
-#define BT_HCI_EVENT_CON_REQ 0x04
-#define BT_HCI_EVENT_DISCON_COMPL 0x05
-#define BT_HCI_EVENT_AUTH_COMPL 0x06
-#define BT_HCI_EVENT_REMOTE_NAME_REQ_COMPL 0x07
-#define BT_HCI_EVENT_ENCRYPTION_CHANGE 0x08
-#define BT_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL 0x09
-#define BT_HCI_EVENT_MASTER_LINK_KEY_COMPL 0x0a
-#define BT_HCI_EVENT_READ_REMOTE_FEATURES_COMPL 0x0b
-#define BT_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL 0x0c
-#define BT_HCI_EVENT_QOS_SETUP_COMPL 0x0d
-#define BT_HCI_EVENT_COMMAND_COMPL 0x0e
-#define BT_HCI_EVENT_COMMAND_STATUS 0x0f
-#define BT_HCI_EVENT_HARDWARE_ERROR 0x10
-#define BT_HCI_EVENT_FLUSH_OCCUR 0x11
-#define BT_HCI_EVENT_ROLE_CHANGE 0x12
-#define BT_HCI_EVENT_NUM_COMPL_PKTS 0x13
-#define BT_HCI_EVENT_MODE_CHANGE 0x14
-#define BT_HCI_EVENT_RETURN_LINK_KEYS 0x15
-#define BT_HCI_EVENT_PIN_CODE_REQ 0x16
-#define BT_HCI_EVENT_LINK_KEY_REQ 0x17
-#define BT_HCI_EVENT_LINK_KEY_NOTIFICATION 0x18
-#define BT_HCI_EVENT_LOOPBACK_COMMAND 0x19
-#define BT_HCI_EVENT_DATA_BUFFER_OVERFLOW 0x1a
-#define BT_HCI_EVENT_MAX_SLOT_CHANGE 0x1b
-#define BT_HCI_EVENT_READ_CLOCK_OFFSET_COMPL 0x1c
-#define BT_HCI_EVENT_CON_PKT_TYPE_CHANGED 0x1d
-#define BT_HCI_EVENT_QOS_VIOLATION 0x1e
-#define BT_HCI_EVENT_PAGE_SCAN_MODE_CHANGE 0x1f
-#define BT_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE 0x20
-#define BT_HCI_EVENT_BT_LOGO 0xfe
-#define BT_HCI_EVENT_VENDOR 0xff
-
-
-/* Mask constants */
-#define BT_HCI_EVMSK_ALL 0x00000000ffffffff
-#define BT_HCI_EVMSK_NONE 0x0000000000000000
-
-#define BT_HCI_EVMSK_INQUIRY_COMPL 0x0000000000000001
-#define BT_HCI_EVMSK_INQUIRY_RESULT 0x0000000000000002
-#define BT_HCI_EVMSK_CON_COMPL 0x0000000000000004
-#define BT_HCI_EVMSK_CON_REQ 0x0000000000000008
-#define BT_HCI_EVMSK_DISCON_COMPL 0x0000000000000010
-#define BT_HCI_EVMSK_AUTH_COMPL 0x0000000000000020
-#define BT_HCI_EVMSK_REMOTE_NAME_REQ_COMPL 0x0000000000000040
-#define BT_HCI_EVMSK_ENCRYPTION_CHANGE 0x0000000000000080
-#define BT_HCI_EVMSK_CHANGE_CON_LINK_KEY_COMPL 0x0000000000000100
-#define BT_HCI_EVMSK_MASTER_LINK_KEY_COMPL 0x0000000000000200
-#define BT_HCI_EVMSK_READ_REMOTE_FEATURES_COMPL 0x0000000000000400
-#define BT_HCI_EVMSK_READ_REMOTE_VER_INFO_COMPL 0x0000000000000800
-#define BT_HCI_EVMSK_QOS_SETUP_COMPL 0x0000000000001000
-#define BT_HCI_EVMSK_COMMAND_COMPL 0x0000000000002000
-#define BT_HCI_EVMSK_COMMAND_STATUS 0x0000000000004000
-#define BT_HCI_EVMSK_HARDWARE_ERROR 0x0000000000008000
-#define BT_HCI_EVMSK_FLUSH_OCCUR 0x0000000000010000
-#define BT_HCI_EVMSK_ROLE_CHANGE 0x0000000000020000
-#define BT_HCI_EVMSK_NUM_COMPL_PKTS 0x0000000000040000
-#define BT_HCI_EVMSK_MODE_CHANGE 0x0000000000080000
-#define BT_HCI_EVMSK_RETURN_LINK_KEYS 0x0000000000100000
-#define BT_HCI_EVMSK_PIN_CODE_REQ 0x0000000000200000
-#define BT_HCI_EVMSK_LINK_KEY_REQ 0x0000000000400000
-#define BT_HCI_EVMSK_LINK_KEY_NOTIFICATION 0x0000000000800000
-#define BT_HCI_EVMSK_LOOPBACK_COMMAND 0x0000000001000000
-#define BT_HCI_EVMSK_DATA_BUFFER_OVERFLOW 0x0000000002000000
-#define BT_HCI_EVMSK_MAX_SLOT_CHANGE 0x0000000004000000
-#define BT_HCI_EVMSK_READ_CLOCK_OFFSET_COMPL 0x0000000008000000
-#define BT_HCI_EVMSK_CON_PKT_TYPE_CHANGED 0x0000000010000000
-#define BT_HCI_EVMSK_QOS_VIOLATION 0x0000000020000000
-#define BT_HCI_EVMSK_PAGE_SCAN_MODE_CHANGE 0x0000000040000000
-#define BT_HCI_EVMSK_PAGE_SCAN_REP_MODE_CHANGE 0x0000000080000000
-
-#endif /* _NETBT_BT_HCI_H_ */
diff --git a/sys/dev/bluetooth/btdev.h b/sys/dev/bluetooth/btdev.h
new file mode 100644
index 00000000000..e8aa4ff8421
--- /dev/null
+++ b/sys/dev/bluetooth/btdev.h
@@ -0,0 +1,96 @@
+/* $NetBSD: btdev.h,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.
+ */
+
+#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 btdev_attach_args)
+#define BTDEV_DETACH _IOW('b', 15, bdaddr_t)
+
+#ifdef _KERNEL
+
+/*
+ * Bluetooth device header
+ */
+struct btdev {
+ 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/bthci.c b/sys/dev/bluetooth/bthci.c
deleted file mode 100644
index a7d3df95fec..00000000000
--- a/sys/dev/bluetooth/bthci.c
+++ /dev/null
@@ -1,648 +0,0 @@
-/* $NetBSD: bthci.c,v 1.20 2006/03/28 17:38:29 thorpej Exp $ */
-
-/*
- * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Lennart Augustsson (lennart@augustsson.net) and
- * David Sainty (David.Sainty@dtsp.co.nz).
- *
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``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 THE FOUNDATION OR CONTRIBUTORS
- * 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/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bthci.c,v 1.20 2006/03/28 17:38:29 thorpej Exp $");
-
-#include "bthcidrv.h"
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/ioctl.h>
-#include <sys/kernel.h>
-#include <sys/device.h>
-#include <sys/conf.h>
-#include <sys/malloc.h>
-#include <sys/poll.h>
-#include <sys/select.h>
-#include <sys/vnode.h>
-
-#include <dev/bluetooth/bluetooth.h>
-#include <dev/bluetooth/bthci_util.h>
-#include <dev/bluetooth/bthcivar.h>
-
-#ifdef BTHCI_DEBUG
-#define DPRINTF(x) if (bthcidebug) printf x
-#define DPRINTFN(n,x) if (bthcidebug>(n)) printf x
-#define Static
-int bthcidebug = 99;
-#else
-#define DPRINTF(x)
-#define DPRINTFN(n,x)
-#define Static static
-#endif
-
-struct bthci_softc {
- struct device sc_dev;
- struct btframe_methods const *sc_methods;
- void *sc_handle;
-
- u_int8_t *sc_rd_buf;
- size_t sc_rd_len;
- struct selinfo sc_rd_sel;
-
- int sc_refcnt;
- int sc_outputready;
- char sc_open;
- char sc_dying;
-};
-
-int bthci_match(struct device *parent, struct cfdata *match, void *aux);
-void bthci_attach(struct device *parent, struct device *self, void *aux);
-int bthci_activate(struct device *self, enum devact act);
-int bthci_detach(struct device *self, int flags);
-
-#if NBTHCIDRV == 0
-/* In case we just have tty attachment. */
-struct cfdriver bthci_cd = {
- NULL, "bthci", DV_DULL
-};
-#endif
-
-CFATTACH_DECL(bthci, sizeof(struct bthci_softc),
- bthci_match, bthci_attach, bthci_detach, bthci_activate);
-
-extern struct cfdriver bthci_cd;
-
-dev_type_open(bthciopen);
-dev_type_close(bthciclose);
-dev_type_poll(bthcipoll);
-dev_type_kqfilter(bthcikqfilter);
-
-static int bthciread(dev_t dev, struct uio *uio, int flag);
-static int bthciwrite(dev_t dev, struct uio *uio, int flag);
-
-static void bthci_recveventdata(void *h, u_int8_t *data, size_t len);
-static void bthci_recvacldata(void *h, u_int8_t *data, size_t len);
-static void bthci_recvscodata(void *h, u_int8_t *data, size_t len);
-
-const struct cdevsw bthci_cdevsw = {
- bthciopen, bthciclose, bthciread, bthciwrite, noioctl,
- nostop, notty, bthcipoll, nommap, bthcikqfilter,
-};
-
-#define BTHCIUNIT(dev) (minor(dev))
-
-struct btframe_callback_methods const bthci_callbacks = {
- bthci_recveventdata, bthci_recvacldata, bthci_recvscodata
-};
-
-int
-bthci_match(struct device *parent, struct cfdata *match, void *aux)
-{
- /*struct bt_attach_args *bt = aux;*/
-
- return (1);
-}
-
-void
-bthci_attach(struct device *parent, struct device *self, void *aux)
-{
- struct bthci_softc *sc = (struct bthci_softc *)self;
- struct bt_attach_args *bt = aux;
-
- sc->sc_methods = bt->bt_methods;
- sc->sc_handle = bt->bt_handle;
-
- *bt->bt_cb = &bthci_callbacks;
-
-#ifdef DIAGNOSTIC
- if (sc->sc_methods->bt_open == NULL ||
- sc->sc_methods->bt_close == NULL ||
-
- sc->sc_methods->bt_control.bt_alloc == NULL ||
- sc->sc_methods->bt_control.bt_send == NULL ||
-
- sc->sc_methods->bt_acldata.bt_alloc == NULL ||
- sc->sc_methods->bt_acldata.bt_send == NULL ||
-
- sc->sc_methods->bt_scodata.bt_alloc == NULL ||
- sc->sc_methods->bt_scodata.bt_send == NULL)
- panic("%s: missing methods", sc->sc_dev.dv_xname);
-#endif
-
- printf("\n");
-}
-
-int
-bthci_activate(struct device *self, enum devact act)
-{
- /*struct bthci_softc *sc = (struct bthci_softc *)self;*/
-
- switch (act) {
- case DVACT_ACTIVATE:
- return (EOPNOTSUPP);
- break;
-
- case DVACT_DEACTIVATE:
- break;
- }
- return (0);
-}
-
-static void
-bthci_abortdealloc(struct bthci_softc *sc)
-{
- DPRINTFN(0, ("%s: sc=%p\n", __func__, sc));
-
- if (sc->sc_rd_buf != NULL) {
- free(sc->sc_rd_buf, M_TEMP);
- sc->sc_rd_buf = NULL;
- }
-}
-
-int
-bthci_detach(struct device *self, int flags)
-{
- struct bthci_softc *sc = (struct bthci_softc *)self;
- int maj, mn;
-
- sc->sc_dying = 1;
- wakeup(&sc->sc_outputready);
-
- DPRINTFN(1, ("%s: waiting for refcount (%d)\n",
- __func__, sc->sc_refcnt));
-
- if (--sc->sc_refcnt >= 0)
- tsleep(&sc->sc_refcnt, PZERO, "bthcdt", 0);
-
- DPRINTFN(1, ("%s: refcount complete\n", __func__));
-
- bthci_abortdealloc(sc);
-
- /* locate the major number */
- maj = cdevsw_lookup_major(&bthci_cdevsw);
-
- /* Nuke the vnodes for any open instances (calls close). */
- mn = device_unit(self);
- vdevgone(maj, mn, mn, VCHR);
-
- DPRINTFN(1, ("%s: driver detached\n", __func__));
-
- return (0);
-}
-
-int
-bthciopen(dev_t dev, int flag, int mode, struct lwp *l)
-{
- struct bthci_softc *sc;
- struct proc *p = l->l_proc;
- int error;
-
- sc = device_lookup(&bthci_cd, BTHCIUNIT(dev));
- if (sc == NULL)
- return (ENXIO);
- if (! device_is_active(&sc->sc_dev))
- return (EIO);
- if (sc->sc_open)
- return (EBUSY);
-
- sc->sc_rd_buf = malloc(BTHCI_ACL_DATA_MAX_LEN + 1, M_TEMP, M_NOWAIT);
- if (sc->sc_rd_buf == NULL)
- return (ENOMEM);
-
- sc->sc_refcnt++;
-
- if (sc->sc_methods->bt_open != NULL) {
- error = sc->sc_methods->bt_open(sc->sc_handle, flag, mode, p);
- if (error)
- goto bad;
- }
-
- if (--sc->sc_refcnt < 0)
- wakeup(&sc->sc_refcnt);
-
- sc->sc_open = 1;
- return (0);
-
- bad:
- free(sc->sc_rd_buf, M_TEMP);
- sc->sc_rd_buf = NULL;
-
- return error;
-}
-
-int
-bthciclose(dev_t dev, int flag, int mode, struct lwp *l)
-{
- struct bthci_softc *sc;
- struct proc *p = l->l_proc;
- int error;
-
- sc = device_lookup(&bthci_cd, BTHCIUNIT(dev));
- if (sc == NULL)
- return (ENXIO);
-
- if (!sc->sc_open)
- return (0);
-
- sc->sc_refcnt++;
-
- sc->sc_open = 0;
-
- if (sc->sc_methods->bt_close != NULL)
- error = sc->sc_methods->bt_close(sc->sc_handle, flag, mode, p);
- else
- error = 0;
-
- bthci_abortdealloc(sc);
-
- if (--sc->sc_refcnt < 0)
- wakeup(&sc->sc_refcnt);
-
- return (error);
-}
-
-static int
-bthciread(dev_t dev, struct uio *uio, int flag)
-{
- struct bthci_softc *sc;
- int error;
- int s;
-
- sc = device_lookup(&bthci_cd, BTHCIUNIT(dev));
- if (sc == NULL)
- return (ENXIO);
-
- if (! device_is_active(&sc->sc_dev) ||
- !sc->sc_open || sc->sc_dying)
- return (EIO);
-
- DPRINTFN(1, ("%s: length=%lu\n", __func__,
- (unsigned long)uio->uio_resid));
-
- sc->sc_refcnt++;
-
- s = sc->sc_methods->bt_splraise();
- while (!sc->sc_outputready) {
- DPRINTFN(5,("%s: calling tsleep()\n", __func__));
- error = tsleep(&sc->sc_outputready, PZERO | PCATCH,
- "bthcrd", 0);
- if (sc->sc_dying)
- error = EIO;
- if (error) {
- splx(s);
- DPRINTFN(0, ("%s: tsleep() = %d\n",
- __func__, error));
- goto ret;
- }
- }
- splx(s);
-
- if (uio->uio_resid < sc->sc_rd_len) {
-#ifdef DIAGNOSTIC
- printf("bthciread: short read %ld < %ld\n",
- (long)uio->uio_resid, (long)sc->sc_rd_len);
-#endif
- error = EINVAL;
- goto ret;
- }
-
- error = uiomove(sc->sc_rd_buf, sc->sc_rd_len, uio);
-
- if (!error) {
- sc->sc_outputready = 0;
- sc->sc_rd_len = 0;
- }
-
- ret:
- if (--sc->sc_refcnt < 0)
- wakeup(&sc->sc_refcnt);
-
- return error;
-}
-
-static int
-bthcichanwrite(struct bthci_softc *sc,
- struct btframe_channel const *btchannel, struct uio *uio)
-{
- struct btframe_buffer *btframebuf;
- u_int8_t *btbuf;
- int error;
- size_t uiolen;
-
- sc->sc_refcnt++;
-
- uiolen = uio->uio_resid;
-
- btbuf = btchannel->bt_alloc(sc->sc_handle, uiolen, &btframebuf);
- if (btbuf == NULL) {
- error = ENOMEM;
- goto ret;
- }
-
- error = uiomove(btbuf, uiolen, uio);
- if (error)
- goto ret;
-
- error = btchannel->bt_send(sc->sc_handle, btframebuf, uiolen);
-
- ret:
- if (--sc->sc_refcnt < 0)
- wakeup(&sc->sc_refcnt);
-
- return error;
-}
-
-static int
-bthciwrite(dev_t dev, struct uio *uio, int flag)
-{
- struct bthci_softc *sc;
- struct btframe_channel const *btchannel;
- int error;
- size_t uiolen;
- u_int8_t bthcitype;
-
- sc = device_lookup(&bthci_cd, BTHCIUNIT(dev));
- if (sc == NULL)
- return (ENXIO);
-
- if (! device_is_active(&sc->sc_dev) || !sc->sc_open)
- return (EIO);
-
- uiolen = uio->uio_resid;
-
- DPRINTFN(1, ("%s: length=%lu\n", __func__, (unsigned long)uiolen));
-
- if (uiolen > BTHCI_ACL_DATA_MAX_LEN + 1) {
-#ifdef DIAGNOSTIC
- printf("bthciread: long write %ld\n", (long)uio->uio_resid);
-#endif
- return (EINVAL);
- }
-
- if (uiolen <= 1) {
-#ifdef DIAGNOSTIC
- printf("bthciread: short write %ld\n", (long)uio->uio_resid);
-#endif
- return (EINVAL);
- }
-
- error = uiomove(&bthcitype, 1, uio);
- if (error)
- return (error);
-
- if (bthcitype == BTHCI_PKTID_COMMAND) {
- /* Command */
- btchannel = &sc->sc_methods->bt_control;
- } else if (bthcitype == BTHCI_PKTID_ACL_DATA) {
- /* ACL data */
- btchannel = &sc->sc_methods->bt_acldata;
- } else if (bthcitype == BTHCI_PKTID_SCO_DATA) {
- /* SCO data */
- btchannel = &sc->sc_methods->bt_scodata;
- } else {
- /* Bad packet type */
- return (EINVAL);
- }
-
- return (bthcichanwrite(sc, btchannel, uio));
-}
-
-#if 0
-int
-bthciioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
-{
- struct bthci_softc *sc;
- void *vaddr = addr;
- int error;
-
- sc = device_lookup(&bthci_cd, BTHCIUNIT(dev));
- if (sc == NULL)
- return (ENXIO);
- if (! device_is_active(&sc->sc_dev) || !sc->sc_open)
- return (EIO);
-
- switch (cmd) {
- case FIONBIO:
- /* All handled in the upper FS layer. */
- error = 0;
- break;
-
- default:
- error = EINVAL;
- break;
- }
- return (error);
-}
-#endif
-
-int
-bthcipoll(dev_t dev, int events, struct lwp *l)
-{
- struct bthci_softc *sc;
- int revents;
- int s;
-
- sc = device_lookup(&bthci_cd, BTHCIUNIT(dev));
- if (sc == NULL)
- return (POLLERR);
-
- if (! device_is_active(&sc->sc_dev) ||
- !sc->sc_open || sc->sc_dying)
- return (POLLHUP);
-
- revents = events & (POLLOUT | POLLWRNORM);
-
- if (events & (POLLIN | POLLRDNORM)) {
- s = sc->sc_methods->bt_splraise();
- if (sc->sc_outputready) {
- DPRINTFN(2,("%s: have data\n", __func__));
- revents |= events & (POLLIN | POLLRDNORM);
- } else {
- DPRINTFN(2,("%s: recording read select\n",
- __func__));
- selrecord(l, &sc->sc_rd_sel);
- }
- splx(s);
- }
-
- return revents;
-}
-
-static void
-filt_bthcirdetach(struct knote *kn)
-{
- struct bthci_softc *sc = kn->kn_hook;
- int s;
-
- s = sc->sc_methods->bt_splraise();
- SLIST_REMOVE(&sc->sc_rd_sel.sel_klist, kn, knote, kn_selnext);
- splx(s);
-}
-
-/* ARGSUSED */
-static int
-filt_bthciread(struct knote *kn, long hint)
-{
- struct bthci_softc *sc = kn->kn_hook;
-
- kn->kn_data = sc->sc_rd_len;
- return (kn->kn_data > 0);
-}
-
-static const struct filterops bthciread_filtops =
- { 1, NULL, filt_bthcirdetach, filt_bthciread };
-
-int
-bthcikqfilter(dev_t dev, struct knote *kn)
-{
- struct bthci_softc *sc;
- struct klist *klist;
- int s;
-
- sc = device_lookup(&bthci_cd, BTHCIUNIT(dev));
- if (sc == NULL)
- return (ENXIO);
- if (! device_is_active(&sc->sc_dev) || !sc->sc_open)
- return (EIO);
-
- switch (kn->kn_filter) {
- case EVFILT_READ:
- klist = &sc->sc_rd_sel.sel_klist;
- kn->kn_fop = &bthciread_filtops;
- break;
- default:
- return (1);
- }
-
- kn->kn_hook = sc;
-
- s = sc->sc_methods->bt_splraise();
- SLIST_INSERT_HEAD(klist, kn, kn_selnext);
- splx(s);
-
- return (0);
-}
-
-static void
-bthci_recveventdata(void *h, u_int8_t *data, size_t len)
-{
- struct bthci_softc *sc = h;
- size_t pktlength;
- unsigned int ecode;
-
- if (len < BTHCI_EVENT_MIN_LEN || len > BTHCI_EVENT_MAX_LEN) {
- DPRINTFN(1,("%s: invalid sized event, size=%u\n",
- __func__, (unsigned int)len));
- return;
- }
-
- ecode = data[0];
- DPRINTFN(1,("%s: received event: %02x (%s)\n", __func__,
- ecode, bthci_eventstr(ecode)));
-
- pktlength = data[BTHCI_EVENT_LEN_OFFT] + BTHCI_EVENT_LEN_OFFT +
- BTHCI_EVENT_LEN_LENGTH;
- if (pktlength != len) {
- DPRINTFN(1,("%s: event length didn't match, "
- "pktlen=%u size=%u\n",
- __func__, (unsigned int)pktlength,
- (unsigned int)len));
- return;
- }
-
- if (!sc->sc_open)
- return;
-
- if (sc->sc_rd_len != 0) {
- DPRINTFN(1,("%s: dropping an event, size=%u\n",
- __func__, (unsigned int)len));
- return;
- }
-
- sc->sc_rd_buf[0] = BTHCI_PKTID_EVENT;
- memcpy(&sc->sc_rd_buf[1], data, len);
- sc->sc_rd_len = len + 1;
- sc->sc_outputready = 1;
-
- wakeup(&sc->sc_outputready);
- selnotify(&sc->sc_rd_sel, 0);
-}
-
-static void
-bthci_recvacldata(void *h, u_int8_t *data, size_t len)
-{
- struct bthci_softc *sc = h;
- size_t pktlength;
-
- if (!sc->sc_open)
- return;
-
- if (len < BTHCI_ACL_DATA_MIN_LEN || len > BTHCI_ACL_DATA_MAX_LEN) {
- DPRINTFN(1,("%s: invalid acl packet, size=%u\n",
- __func__, (unsigned int)len));
- return;
- }
-
- pktlength = BTGETW(&data[BTHCI_ACL_DATA_LEN_OFFT]) +
- BTHCI_ACL_DATA_LEN_OFFT +
- BTHCI_ACL_DATA_LEN_LENGTH;
-
- if (pktlength != len) {
- DPRINTFN(1,("%s: acl packet length didn't match, "
- "pktlen=%u size=%u\n",
- __func__, (unsigned int)pktlength,
- (unsigned int)len));
- return;
- }
-
- if (sc->sc_rd_len != 0) {
- DPRINTFN(1,("%s: dropping an acl packet, size=%u\n",
- __func__, (unsigned int)len));
- return;
- }
-
- sc->sc_rd_buf[0] = BTHCI_PKTID_ACL_DATA;
- memcpy(&sc->sc_rd_buf[1], data, len);
- sc->sc_rd_len = len + 1;
- sc->sc_outputready = 1;
-
- wakeup(&sc->sc_outputready);
- selnotify(&sc->sc_rd_sel, 0);
-}
-
-static void
-bthci_recvscodata(void *h, u_int8_t *data, size_t len)
-{
- struct bthci_softc *sc = h;
-
- if (!sc->sc_open)
- return;
-}
diff --git a/sys/dev/bluetooth/bthci_util.c b/sys/dev/bluetooth/bthci_util.c
deleted file mode 100644
index 42756264e82..00000000000
--- a/sys/dev/bluetooth/bthci_util.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/* $NetBSD: bthci_util.c,v 1.1 2003/01/11 05:41:57 dsainty Exp $ */
-
-/*
- * Copyright (c) 2003 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by David Sainty <David.Sainty@dtsp.co.nz>
- *
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``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 THE FOUNDATION OR CONTRIBUTORS
- * 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/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bthci_util.c,v 1.1 2003/01/11 05:41:57 dsainty Exp $");
-
-#include <dev/bluetooth/bt_hci.h>
-#include <dev/bluetooth/bthci_util.h>
-
-static char const * const hci_eventnames[] = {
- "NULL",
- "INQUIRY_COMPL",
- "INQUIRY_RESULT",
- "CON_COMPL",
- "CON_REQ",
- "DISCON_COMPL",
- "AUTH_COMPL",
- "REMOTE_NAME_REQ_COMPL",
- "ENCRYPTION_CHANGE",
- "CHANGE_CON_LINK_KEY_COMPL",
- "MASTER_LINK_KEY_COMPL",
- "READ_REMOTE_FEATURES_COMPL",
- "READ_REMOTE_VER_INFO_COMPL",
- "QOS_SETUP_COMPL",
- "COMMAND_COMPL",
- "COMMAND_STATUS",
- "HARDWARE_ERROR",
- "FLUSH_OCCUR",
- "ROLE_CHANGE",
- "NUM_COMPL_PKTS",
- "MODE_CHANGE",
- "RETURN_LINK_KEYS",
- "PIN_CODE_REQ",
- "LINK_KEY_REQ",
- "LINK_KEY_NOTIFICATION",
- "LOOPBACK_COMMAND",
- "DATA_BUFFER_OVERFLOW",
- "MAX_SLOT_CHANGE",
- "READ_CLOCK_OFFSET_COMPL",
- "CON_PKT_TYPE_CHANGED",
- "QOS_VIOLATION",
- "PAGE_SCAN_MODE_CHANGE",
- "PAGE_SCAN_REP_MODE_CHANGE"
-};
-
-char const*
-bthci_eventstr(unsigned int evtcode)
-{
- if (evtcode < (sizeof(hci_eventnames) / sizeof(*hci_eventnames)))
- return hci_eventnames[evtcode];
-
- if (evtcode == BT_HCI_EVENT_BT_LOGO)
- return "BT_LOGO";
-
- if (evtcode == BT_HCI_EVENT_VENDOR)
- return "VENDOR";
-
- return "UNRECOGNISED";
-}
diff --git a/sys/dev/bluetooth/bthci_util.h b/sys/dev/bluetooth/bthci_util.h
deleted file mode 100644
index 881aa08d2a0..00000000000
--- a/sys/dev/bluetooth/bthci_util.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* $NetBSD: bthci_util.h,v 1.1 2003/01/11 05:41:57 dsainty Exp $ */
-
-/*
- * Copyright (c) 2003 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by David Sainty <David.Sainty@dtsp.co.nz>
- *
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``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 THE FOUNDATION OR CONTRIBUTORS
- * 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.
- */
-
-char const* bthci_eventstr(unsigned int);
diff --git a/sys/dev/bluetooth/bthcivar.h b/sys/dev/bluetooth/bthcivar.h
deleted file mode 100644
index 4601f63e2df..00000000000
--- a/sys/dev/bluetooth/bthcivar.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* $NetBSD: bthcivar.h,v 1.4 2005/12/17 13:18:28 xtraeme Exp $ */
-
-/*
- * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Lennart Augustsson (lennart@augustsson.net).
- *
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``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 THE FOUNDATION OR CONTRIBUTORS
- * 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.
- */
-
-struct bthci_methods {
- int (*bt_open)(void *h, int flag, int mode, struct lwp *l);
- int (*bt_close)(void *h, int flag, int mode, struct lwp *l);
- int (*bt_read)(void *h, struct uio *uio, int flag);
- int (*bt_write)(void *h, struct uio *uio, int flag);
- int (*bt_poll)(void *h, int events, struct lwp *l);
- int (*bt_kqfilter)(void *h, struct knote *kn);
-};
-
-struct btframe_methods;
diff --git a/sys/dev/bluetooth/bthid.h b/sys/dev/bluetooth/bthid.h
new file mode 100644
index 00000000000..af6507c0729
--- /dev/null
+++ b/sys/dev/bluetooth/bthid.h
@@ -0,0 +1,89 @@
+/* $NetBSD: bthid.h,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.
+ */
+
+#ifndef _DEV_BLUETOOTH_BTHID_H_
+#define _DEV_BLUETOOTH_BTHID_H_
+
+/* Transaction Types */
+#define BTHID_HANDSHAKE 0x0
+#define BTHID_CONTROL 0x1
+#define BTHID_GET_REPORT 0x4
+#define BTHID_SET_REPORT 0x5
+#define BTHID_GET_PROTOCOL 0x6
+#define BTHID_SET_PROTOCOL 0x7
+#define BTHID_GET_IDLE 0x8
+#define BTHID_SET_IDLE 0x9
+#define BTHID_DATA 0xa
+#define BTHID_DATC 0xb
+
+#define BTHID_TYPE(b) (((b) & 0xf0) >> 4)
+
+/* HANDSHAKE Transaction Parameters */
+#define BTHID_HANDSHAKE_SUCCESS 0x0
+#define BTHID_HANDSHAKE_NOT_READY 0x1
+#define BTHID_HANDSHAKE_INVALID_ID 0x2
+#define BTHID_HANDSHAKE_UNSUPPORTED 0x3
+#define BTHID_HANDSHAKE_INVALID_PARAM 0x4
+#define BTHID_HANDSHAKE_UNKNOWN 0xe
+#define BTHID_HANDSHAKE_FATAL 0xf
+
+#define BTHID_HANDSHAKE_PARAM(b) ((b) & 0x0f)
+
+/* HID_CONTROL Transaction Parameters */
+#define BTHID_CONTROL_NOP 0x0
+#define BTHID_CONTROL_HARD_RESET 0x1
+#define BTHID_CONTROL_SOFT_RESET 0x2
+#define BTHID_CONTROL_SUSPEND 0x3
+#define BTHID_CONTROL_RESUME 0x4
+#define BTHID_CONTROL_UNPLUG 0x5
+
+#define BTHID_CONTROL_PARAM(b) ((b) & 0x0f)
+
+/* GET_REPORT Transaction Parameters */
+#define BTHID_CONTROL_SIZE 0x08
+
+/* GET_PROTOCOL Transaction Parameters */
+#define BTHID_PROTOCOL_REPORT 0
+#define BTHID_PROTOCOL_BOOT 1
+
+#define BTHID_PROTOCOL_PARAM(b) ((b) & 0x01)
+
+/* DATA, DATC Transaction Parameters */
+#define BTHID_DATA_OTHER 0
+#define BTHID_DATA_INPUT 1
+#define BTHID_DATA_OUTPUT 2
+#define BTHID_DATA_FEATURE 3
+
+#define BTHID_DATA_PARAM(b) ((b) & 0x03)
+
+#endif /* _DEV_BLUETOOTH_BTHID_H_ */
diff --git a/sys/dev/bluetooth/bthidev.c b/sys/dev/bluetooth/bthidev.c
new file mode 100644
index 00000000000..b24594b0cd2
--- /dev/null
+++ b/sys/dev/bluetooth/bthidev.c
@@ -0,0 +1,793 @@
+/* $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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: bthidev.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $");
+
+#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>
+
+#include "locators.h"
+
+/*****************************************************************************
+ *
+ * Bluetooth HID device
+ */
+
+MALLOC_DEFINE(M_BTHIDEV, "bthidev", "Bluetooth HID Memory");
+
+#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 callout 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 */
+
+/* bthidev internals */
+static void bthidev_timeout(void *);
+static int bthidev_listen(struct bthidev_softc *);
+static int bthidev_connect(struct bthidev_softc *);
+static int bthidev_output(struct bthidev *, uint8_t *, int);
+static void bthidev_null(struct bthidev *, uint8_t *, int);
+
+/* autoconf(9) glue */
+static int bthidev_match(struct device *, struct cfdata *, void *);
+static void bthidev_attach(struct device *, struct device *, void *);
+static int bthidev_detach(struct device *, int);
+static int bthidev_print(void *, const char *);
+
+CFATTACH_DECL(bthidev, sizeof(struct bthidev_softc),
+ bthidev_match, bthidev_attach, bthidev_detach, NULL);
+
+/* bluetooth(9) protocol methods for L2CAP */
+static void bthidev_connecting(void *);
+static void bthidev_ctl_connected(void *);
+static void bthidev_int_connected(void *);
+static void bthidev_ctl_disconnected(void *, int);
+static void bthidev_int_disconnected(void *, int);
+static void *bthidev_ctl_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
+static void *bthidev_int_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
+static void bthidev_complete(void *, int);
+static void bthidev_input(void *, struct mbuf *);
+
+static const struct btproto bthidev_ctl_proto = {
+ bthidev_connecting,
+ bthidev_ctl_connected,
+ bthidev_ctl_disconnected,
+ bthidev_ctl_newconn,
+ bthidev_complete,
+ bthidev_input,
+};
+
+static const struct btproto bthidev_int_proto = {
+ bthidev_connecting,
+ bthidev_int_connected,
+ bthidev_int_disconnected,
+ bthidev_int_newconn,
+ bthidev_complete,
+ bthidev_input,
+};
+
+/*****************************************************************************
+ *
+ * bthidev autoconf(9) routines
+ */
+
+static int
+bthidev_match(struct device *self, struct cfdata *cfdata, 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;
+}
+
+static 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 locs[BTHIDBUSCF_NLOCS];
+ int maxid, rep, s;
+
+ /*
+ * Init softc
+ */
+ LIST_INIT(&sc->sc_list);
+ callout_init(&sc->sc_reconnect);
+ callout_setfunc(&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) {
+ aprint_error(" 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) {
+ aprint_error(" no reports found\n");
+ return;
+ }
+
+ aprint_normal("\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;
+
+ locs[BTHIDBUSCF_REPORTID] = rep;
+
+ dev = (struct bthidev *)config_found_sm_loc((struct device *)sc, "bthidbus",
+ locs, &bha, bthidev_print, config_stdsubmatch);
+ 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);
+}
+
+static 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;
+ }
+
+ /* remove callout */
+ sc->sc_state = BTHID_DETACHING;
+ callout_stop(&sc->sc_reconnect);
+ if (callout_invoking(&sc->sc_reconnect))
+ tsleep(sc, PWAIT, "bthidetach", 0);
+
+ 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;
+}
+
+/*
+ * bthidev config print
+ */
+static int
+bthidev_print(void *aux, const char *pnp)
+{
+ struct bthidev_attach_args *ba = aux;
+
+ if (pnp != NULL)
+ aprint_normal("%s:", pnp);
+
+ if (ba->ba_id > 0)
+ aprint_normal(" reportid %d", ba->ba_id);
+
+ return UNCONF;
+}
+
+
+/*****************************************************************************
+ *
+ * bluetooth(4) HID attach/detach routines
+ */
+
+/*
+ * callouts are scheduled to initiate outgoing connections
+ * after the connection has been lost.
+ */
+static void
+bthidev_timeout(void *arg)
+{
+ struct bthidev_softc *sc = arg;
+ int s, err;
+
+ s = splsoftnet();
+ callout_ack(&sc->sc_reconnect);
+
+ switch (sc->sc_state) {
+ case BTHID_CLOSED:
+ sc->sc_flags |= BTHID_CONNECT;
+ err = bthidev_connect(sc);
+ if (err)
+ printf("%s: connect failed (%d)\n",
+ btdev_name(sc), 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
+ */
+static 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
+ */
+static int
+bthidev_connect(struct bthidev_softc *sc)
+{
+ struct sockaddr_bt sa;
+ int err;
+
+ if (sc->sc_attempts++ > 0)
+ printf("%s: connect (#%d)\n", btdev_name(sc), 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", btdev_name(sc), 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", btdev_name(sc), 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", btdev_name(sc), 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.
+ */
+
+static void
+bthidev_connecting(void *arg)
+{
+
+ /* dont care */
+}
+
+static 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", btdev_name(sc), err);
+}
+
+static 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", btdev_name(sc));
+}
+
+/*
+ * Disconnected
+ *
+ * Depending on our state, this could mean several things, but essentially
+ * we are lost.
+ */
+static 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", btdev_name(sc));
+
+ if (sc->sc_flags & BTHID_INITIATE)
+ callout_schedule(&sc->sc_reconnect,
+ BTHID_RETRY_INTERVAL * hz);
+ else
+ sc->sc_state = BTHID_WAIT_CTL;
+ }
+}
+
+static 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", btdev_name(sc));
+
+ if (sc->sc_flags & BTHID_INITIATE)
+ callout_schedule(&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
+ */
+static 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;
+}
+
+static 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;
+}
+
+static void
+bthidev_complete(void *arg, int count)
+{
+
+ /* dont care */
+}
+
+/*
+ * Receive reports from the protocol stack.
+ */
+static 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", btdev_name(sc));
+
+ 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",
+ btdev_name(sc), 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", btdev_name(sc));
+
+ /* 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
+ */
+
+static 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
+ */
+}
+
+static 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",
+ btdev_name(sc), 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
new file mode 100644
index 00000000000..e6d9e09ec81
--- /dev/null
+++ b/sys/dev/bluetooth/bthidev.h
@@ -0,0 +1,70 @@
+/* $NetBSD: bthidev.h,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.
+ */
+
+#ifndef _DEV_BLUETOOTH_BTHIDEV_H_
+#define _DEV_BLUETOOTH_BTHIDEV_H_
+
+/* HID device header */
+struct bthidev {
+ struct device sc_dev;
+ struct btdev *sc_parent;
+
+ int sc_id; /* report id */
+ int sc_len; /* report len */
+
+ void (*sc_input) /* input method */
+ (struct bthidev *, uint8_t *, int);
+
+ void (*sc_feature) /* feature method */
+ (struct bthidev *, uint8_t *, int);
+
+ LIST_ENTRY(bthidev) sc_next;
+};
+
+/* HID device attach arguments */
+struct bthidev_attach_args {
+ void *ba_desc; /* descriptor */
+ int ba_dlen; /* descriptor length */
+ int ba_id; /* report id */
+
+ void (*ba_input) /* input method */
+ (struct bthidev *, uint8_t *, int);
+ void (*ba_feature) /* feature method */
+ (struct bthidev *, uint8_t *, int);
+ int (*ba_output) /* output method */
+ (struct bthidev *, uint8_t *, int);
+};
+
+MALLOC_DECLARE(M_BTHIDEV);
+
+#endif /* _DEV_BLUETOOTH_BTHIDEV_H_ */
diff --git a/sys/dev/bluetooth/bthset.c b/sys/dev/bluetooth/bthset.c
new file mode 100644
index 00000000000..7dce0019e2e
--- /dev/null
+++ b/sys/dev/bluetooth/bthset.c
@@ -0,0 +1,1079 @@
+/* $NetBSD: bthset.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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: bthset.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $");
+
+#include <sys/param.h>
+#include <sys/audioio.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/rfcomm.h>
+#include <netbt/sco.h>
+
+#include <dev/audio_if.h>
+#include <dev/auconv.h>
+#include <dev/mulaw.h>
+
+#include <dev/bluetooth/btdev.h>
+#include <dev/bluetooth/bthset.h>
+
+#undef DPRINTF
+#undef DPRINTFN
+
+#ifdef BTHSET_DEBUG
+int bthset_debug = BTHSET_DEBUG;
+#define DPRINTF(fmt, args...) do { \
+ if (bthset_debug) \
+ printf("%s: "fmt, __func__ , ##args); \
+} while (/* CONSTCOND */0)
+
+#define DPRINTFN(n, fmt, args...) do { \
+ if (bthset_debug > (n)) \
+ printf("%s: "fmt, __func__ , ##args); \
+} while (/* CONSTCOND */0)
+#else
+#define DPRINTF(...)
+#define DPRINTFN(...)
+#endif
+
+/*****************************************************************************
+ *
+ * Bluetooth SCO Audio Headset device
+ */
+
+MALLOC_DEFINE(M_BTHSET, "bthset", "Bluetooth Headset Memory");
+
+/* bthset softc */
+struct bthset_softc {
+ struct btdev sc_btdev; /* device+ */
+ uint16_t sc_flags;
+
+ struct device *sc_audio; /* MI audio device */
+ void *sc_intr; /* interrupt cookie */
+
+ /* Bluetooth */
+ bdaddr_t sc_laddr; /* local address */
+ bdaddr_t sc_raddr; /* remote address */
+ uint16_t sc_state; /* link state */
+ struct sco_pcb *sc_sco; /* SCO handle */
+ int sc_mtu; /* SCO mtu */
+ uint8_t sc_channel; /* RFCOMM channel */
+ int sc_err; /* stored error */
+
+ /* Receive */
+ int sc_rx_want; /* bytes wanted */
+ uint8_t *sc_rx_block; /* receive block */
+ void (*sc_rx_intr)(void *); /* callback */
+ void *sc_rx_intrarg; /* callback arg */
+ struct mbuf *sc_rx_mbuf; /* leftover mbuf */
+
+ /* Transmit */
+ int sc_tx_size; /* bytes to send */
+ int sc_tx_pending; /* packets pending */
+ uint8_t *sc_tx_block; /* transmit block */
+ void (*sc_tx_intr)(void *); /* callback */
+ void *sc_tx_intrarg; /* callback arg */
+ void *sc_tx_buf; /* transmit buffer */
+ int sc_tx_refcnt; /* buffer refcnt */
+
+ /* mixer data */
+ int sc_vgs; /* speaker volume */
+ int sc_vgm; /* mic volume */
+};
+
+/* sc_state */
+#define BTHSET_CLOSED 0
+#define BTHSET_WAIT_CONNECT 1
+#define BTHSET_OPEN 2
+
+/* sc_flags */
+#define BTHSET_GETMTU (1 << 0)
+
+/* autoconf(9) glue */
+static int bthset_match(struct device *, struct cfdata *, void *);
+static void bthset_attach(struct device *, struct device *, void *);
+static int bthset_detach(struct device *, int);
+
+CFATTACH_DECL(bthset, sizeof(struct bthset_softc),
+ bthset_match, bthset_attach, bthset_detach, NULL);
+
+/* audio(9) glue */
+static int bthset_open(void *, int);
+static void bthset_close(void *);
+static int bthset_query_encoding(void *, struct audio_encoding *);
+static int bthset_set_params(void *, int, int, audio_params_t *, audio_params_t *,
+ stream_filter_list_t *, stream_filter_list_t *);
+static int bthset_round_blocksize(void *, int, int, const audio_params_t *);
+static int bthset_start_output(void *, void *, int, void (*)(void *), void *);
+static int bthset_start_input(void *, void *, int, void (*)(void *), void *);
+static int bthset_halt_output(void *);
+static int bthset_halt_input(void *);
+static int bthset_getdev(void *, struct audio_device *);
+static int bthset_setfd(void *, int);
+static int bthset_set_port(void *, mixer_ctrl_t *);
+static int bthset_get_port(void *, mixer_ctrl_t *);
+static int bthset_query_devinfo(void *, mixer_devinfo_t *);
+static void *bthset_allocm(void *, int, size_t, struct malloc_type *, int);
+static void bthset_freem(void *, void *, struct malloc_type *);
+static int bthset_get_props(void *);
+static int bthset_dev_ioctl(void *, u_long, caddr_t, int, struct lwp *);
+
+static const struct audio_hw_if bthset_if = {
+ bthset_open, /* open */
+ bthset_close, /* close */
+ NULL, /* drain */
+ bthset_query_encoding, /* query_encoding */
+ bthset_set_params, /* set_params */
+ bthset_round_blocksize, /* round_blocksize */
+ NULL, /* commit_settings */
+ NULL, /* init_output */
+ NULL, /* init_input */
+ bthset_start_output, /* start_output */
+ bthset_start_input, /* start_input */
+ bthset_halt_output, /* halt_output */
+ bthset_halt_input, /* halt_input */
+ NULL, /* speaker_ctl */
+ bthset_getdev, /* getdev */
+ bthset_setfd, /* setfd */
+ bthset_set_port, /* set_port */
+ bthset_get_port, /* get_port */
+ bthset_query_devinfo, /* query_devinfo */
+ bthset_allocm, /* allocm */
+ bthset_freem, /* freem */
+ NULL, /* round_buffersize */
+ NULL, /* mappage */
+ bthset_get_props, /* get_props */
+ NULL, /* trigger_output */
+ NULL, /* trigger_input */
+ bthset_dev_ioctl, /* dev_ioctl */
+ NULL, /* powerstate */
+};
+
+static const struct audio_device bthset_device = {
+ "Bluetooth Audio",
+ "",
+ "bthset"
+};
+
+/* bluetooth(9) glue for SCO */
+static void bthset_sco_connecting(void *);
+static void bthset_sco_connected(void *);
+static void bthset_sco_disconnected(void *, int);
+static void *bthset_sco_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
+static void bthset_sco_complete(void *, int);
+static void bthset_sco_input(void *, struct mbuf *);
+
+static const struct btproto bthset_sco_proto = {
+ bthset_sco_connecting,
+ bthset_sco_connected,
+ bthset_sco_disconnected,
+ bthset_sco_newconn,
+ bthset_sco_complete,
+ bthset_sco_input,
+};
+
+
+/*****************************************************************************
+ *
+ * bthset definitions
+ */
+
+/*
+ * bthset mixer class
+ */
+#define BTHSET_VGS 0
+#define BTHSET_VGM 1
+#define BTHSET_INPUT_CLASS 2
+#define BTHSET_OUTPUT_CLASS 3
+
+/* connect timeout */
+#define BTHSET_TIMEOUT (30 * hz)
+
+/* misc bthset functions */
+static void bthset_extfree(struct mbuf *, caddr_t, size_t, void *);
+static void bthset_intr(void *);
+
+
+/*****************************************************************************
+ *
+ * bthset autoconf(9) routines
+ */
+
+static int
+bthset_match(struct device *self, struct cfdata *cfdata, void *aux)
+{
+ struct btdev_attach_args *bda = (struct btdev_attach_args *)aux;
+
+ if (bda->bd_type != BTDEV_HSET
+ || bdaddr_any(&bda->bd_raddr)
+ || bda->bd_hset.hset_channel < RFCOMM_CHANNEL_MIN
+ || bda->bd_hset.hset_channel > RFCOMM_CHANNEL_MAX)
+ return 0;
+
+ return 1;
+}
+
+static void
+bthset_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct bthset_softc *sc = (struct bthset_softc *)self;
+ struct btdev_attach_args *bda = (struct btdev_attach_args *)aux;
+
+ /*
+ * Init softc
+ */
+ sc->sc_vgs = 200;
+ sc->sc_vgm = 200;
+ sc->sc_state = BTHSET_CLOSED;
+
+ /*
+ * copy in our configuration info
+ */
+ bdaddr_copy(&sc->sc_laddr, &bda->bd_laddr);
+ bdaddr_copy(&sc->sc_raddr, &bda->bd_raddr);
+ sc->sc_channel = bda->bd_hset.hset_channel;
+ sc->sc_mtu = bda->bd_hset.hset_mtu;
+ if (sc->sc_mtu == 0)
+ sc->sc_flags |= BTHSET_GETMTU;
+ else
+ aprint_verbose(" mtu %d", sc->sc_mtu);
+
+ aprint_normal("\n");
+
+ DPRINTF("sc=%p\n", sc);
+
+ /*
+ * set up transmit interrupt
+ */
+ sc->sc_intr = softintr_establish(IPL_SOFTNET, bthset_intr, sc);
+ if (sc->sc_intr == NULL) {
+ aprint_error("%s: softintr_establish failed\n", btdev_name(sc));
+ return;
+ }
+
+ /*
+ * attach audio device
+ */
+ sc->sc_audio = audio_attach_mi(&bthset_if, sc, &sc->sc_btdev.sc_dev);
+ if (sc->sc_audio == NULL) {
+ aprint_error("%s: audio_attach_mi failed\n", btdev_name(sc));
+ return;
+ }
+}
+
+static int
+bthset_detach(struct device *self, int flags)
+{
+ struct bthset_softc *sc = (struct bthset_softc *)self;
+ int s;
+
+ DPRINTF("sc=%p\n", sc);
+
+ if (sc->sc_sco != NULL) {
+ DPRINTF("sc_sco=%p\n", sc->sc_sco);
+ s = splsoftnet();
+ sco_disconnect(sc->sc_sco, 0);
+ sco_detach(&sc->sc_sco);
+ sc->sc_sco = NULL;
+ splx(s);
+ }
+
+ if (sc->sc_audio != NULL) {
+ DPRINTF("sc_audio=%p\n", sc->sc_audio);
+ config_detach(sc->sc_audio, flags);
+ sc->sc_audio = NULL;
+ }
+
+ if (sc->sc_intr != NULL) {
+ softintr_disestablish(sc->sc_intr);
+ sc->sc_intr = NULL;
+ }
+
+ if (sc->sc_rx_mbuf != NULL) {
+ m_freem(sc->sc_rx_mbuf);
+ sc->sc_rx_mbuf = NULL;
+ }
+
+ if (sc->sc_tx_refcnt > 0) {
+ printf("%s: tx_refcnt=%d!\n",
+ btdev_name(sc), sc->sc_tx_refcnt);
+
+ if ((flags & DETACH_FORCE) == 0)
+ return EAGAIN;
+ }
+
+ return 0;
+}
+
+
+/*****************************************************************************
+ *
+ * bluetooth(9) methods for SCO
+ *
+ * All these are called from Bluetooth Protocol code, in a soft
+ * interrupt context at IPL_SOFTNET.
+ */
+
+static void
+bthset_sco_connecting(void *arg)
+{
+// struct bthset_softc *sc = arg;
+
+ DPRINTF("%s\n", btdev_name(arg));
+
+ /* dont care */
+}
+
+static void
+bthset_sco_connected(void *arg)
+{
+ struct bthset_softc *sc = arg;
+
+ DPRINTF("%s\n", btdev_name(sc));
+
+ KASSERT(sc->sc_sco != NULL);
+ KASSERT(sc->sc_state == BTHSET_WAIT_CONNECT);
+
+ sc->sc_state = BTHSET_OPEN;
+ wakeup(sc);
+}
+
+static void
+bthset_sco_disconnected(void *arg, int err)
+{
+ struct bthset_softc *sc = arg;
+
+ DPRINTF("%s sc_state %d\n", btdev_name(sc), sc->sc_state);
+
+ KASSERT(sc->sc_sco != NULL);
+
+ sc->sc_err = err;
+ sco_detach(&sc->sc_sco);
+
+ switch (sc->sc_state) {
+ case BTHSET_CLOSED: /* dont think this can happen */
+ break;
+
+ case BTHSET_WAIT_CONNECT: /* connect failed */
+ wakeup(sc);
+ break;
+
+ case BTHSET_OPEN: /* link lost, XXX */
+ break;
+
+ default:
+ UNKNOWN(sc->sc_state);
+ }
+
+ sc->sc_state = BTHSET_CLOSED;
+}
+
+static void *
+bthset_sco_newconn(void *arg, struct sockaddr_bt *laddr, struct sockaddr_bt *raddr)
+{
+// struct bthset_softc *sc = arg;
+
+ DPRINTF("%s\n", btdev_name(arg));
+
+ return NULL;
+}
+
+static void
+bthset_sco_complete(void *arg, int count)
+{
+ struct bthset_softc *sc = arg;
+ int s;
+
+ DPRINTFN(10, "%s count %d\n", btdev_name(sc), count);
+
+ s = splaudio();
+ if (sc->sc_tx_pending > 0) {
+ sc->sc_tx_pending -= count;
+ if (sc->sc_tx_pending == 0)
+ (*sc->sc_tx_intr)(sc->sc_tx_intrarg);
+ }
+ splx(s);
+}
+
+static void
+bthset_sco_input(void *arg, struct mbuf *m)
+{
+ struct bthset_softc *sc = arg;
+ int len, s;
+
+ DPRINTFN(10, "%s len=%d\n", btdev_name(sc), m->m_pkthdr.len);
+
+ s = splaudio();
+ if (sc->sc_rx_want == 0) {
+ m_freem(m);
+ } else {
+ KASSERT(sc->sc_rx_intr != NULL);
+ KASSERT(sc->sc_rx_block != NULL);
+
+ len = MIN(sc->sc_rx_want, m->m_pkthdr.len);
+ m_copydata(m, 0, len, sc->sc_rx_block);
+
+ sc->sc_rx_want -= len;
+ sc->sc_rx_block += len;
+
+ if (len > m->m_pkthdr.len) {
+ if (sc->sc_rx_mbuf != NULL)
+ m_freem(sc->sc_rx_mbuf);
+
+ m_adj(m, len);
+ sc->sc_rx_mbuf = m;
+ } else {
+ m_freem(m);
+ }
+
+ if (sc->sc_rx_want == 0)
+ (*sc->sc_rx_intr)(sc->sc_rx_intrarg);
+ }
+ splx(s);
+}
+
+
+/*****************************************************************************
+ *
+ * audio(9) methods
+ *
+ */
+
+static int
+bthset_open(void *hdl, int flags)
+{
+ struct sockaddr_bt sa;
+ struct bthset_softc *sc = hdl;
+ int err, s;
+
+ DPRINTF("%s flags 0x%x\n", btdev_name(sc), flags);
+ // flags FREAD & FWRITE?
+
+ if (sc->sc_sco != NULL)
+ return EIO;
+
+ s = splsoftnet();
+
+ err = sco_attach(&sc->sc_sco, &bthset_sco_proto, sc);
+ if (err)
+ goto done;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.bt_len = sizeof(sa);
+ sa.bt_family = AF_BLUETOOTH;
+ bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr);
+
+ err = sco_bind(sc->sc_sco, &sa);
+ if (err) {
+ sco_detach(&sc->sc_sco);
+ goto done;
+ }
+
+ bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr);
+ err = sco_connect(sc->sc_sco, &sa);
+ if (err) {
+ sco_detach(&sc->sc_sco);
+ goto done;
+ }
+
+ sc->sc_state = BTHSET_WAIT_CONNECT;
+ while (err == 0 && sc->sc_state == BTHSET_WAIT_CONNECT)
+ err = tsleep(sc, PWAIT | PCATCH, "bthset", BTHSET_TIMEOUT);
+
+ switch (sc->sc_state) {
+ case BTHSET_CLOSED: /* disconnected */
+ sco_detach(&sc->sc_sco);
+ err = sc->sc_err;
+ break;
+
+ case BTHSET_WAIT_CONNECT: /* error */
+ sco_detach(&sc->sc_sco);
+ break;
+
+ case BTHSET_OPEN: /* hurrah */
+ if (sc->sc_flags & BTHSET_GETMTU)
+ sco_getopt(sc->sc_sco, SO_SCO_MTU, &sc->sc_mtu);
+
+ break;
+
+ default:
+ UNKNOWN(sc->sc_state);
+ break;
+ }
+
+done:
+ splx(s);
+
+ DPRINTF("done err=%d, sc_state=%d, sc_mtu=%d\n",
+ err, sc->sc_state, sc->sc_mtu);
+ return err;
+}
+
+static void
+bthset_close(void *hdl)
+{
+ struct bthset_softc *sc = hdl;
+ int s;
+
+ DPRINTF("%s\n", btdev_name(sc));
+
+ if (sc->sc_sco != NULL) {
+ s = splsoftnet();
+ sco_disconnect(sc->sc_sco, 0);
+ sco_detach(&sc->sc_sco);
+ splx(s);
+ }
+
+ if (sc->sc_rx_mbuf != NULL) {
+ m_freem(sc->sc_rx_mbuf);
+ sc->sc_rx_mbuf = NULL;
+ }
+
+ sc->sc_rx_want = 0;
+ sc->sc_rx_block = NULL;
+ sc->sc_rx_intr = NULL;
+ sc->sc_rx_intrarg = NULL;
+
+ sc->sc_tx_size = 0;
+ sc->sc_tx_block = NULL;
+ sc->sc_tx_pending = 0;
+ sc->sc_tx_intr = NULL;
+ sc->sc_tx_intrarg = NULL;
+}
+
+static int
+bthset_query_encoding(void *hdl, struct audio_encoding *ae)
+{
+// struct bthset_softc *sc = hdl;
+ int err = 0;
+
+ DPRINTF("%s index %d\n", btdev_name(hdl), ae->index);
+
+ switch (ae->index) {
+ case 0:
+ strcpy(ae->name, AudioEslinear_le);
+ ae->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ ae->precision = 16;
+ ae->flags = 0;
+ break;
+
+ default:
+ err = EINVAL;
+ }
+
+ return err;
+}
+
+static int
+bthset_set_params(void *hdl, int setmode, int usemode,
+ audio_params_t *play, audio_params_t *rec,
+ stream_filter_list_t *pfil, stream_filter_list_t *rfil)
+{
+// struct bthset_softc *sc = hdl;
+ audio_params_t hw;
+ int err = 0;
+
+ DPRINTF("%s setmode 0x%x usemode 0x%x\n", btdev_name(hdl), setmode, usemode);
+ DPRINTF("rate %d, precision %d, channels %d encoding %d\n",
+ play->sample_rate, play->precision, play->channels, play->encoding);
+
+ if ((play->precision != 16 && play->precision != 8)
+ || play->sample_rate < 7500
+ || play->sample_rate > 8500
+ || play->channels != 1)
+ return EINVAL;
+
+ play->sample_rate = 8000;
+ hw = *play;
+
+ switch (play->encoding) {
+ case AUDIO_ENCODING_ULAW:
+ hw.encoding = AUDIO_ENCODING_SLINEAR_LE;
+ pfil->append(pfil, mulaw_to_linear16, &hw);
+ break;
+
+ case AUDIO_ENCODING_ALAW:
+ hw.encoding = AUDIO_ENCODING_SLINEAR_LE;
+ pfil->append(pfil, alaw_to_linear16, &hw);
+ break;
+
+ case AUDIO_ENCODING_SLINEAR_LE:
+ break;
+
+ case AUDIO_ENCODING_SLINEAR_BE:
+ if (play->precision == 16) {
+ hw.encoding = AUDIO_ENCODING_SLINEAR_LE;
+ pfil->append(pfil, swap_bytes, &hw);
+ }
+ break;
+
+ case AUDIO_ENCODING_ULINEAR_LE:
+ case AUDIO_ENCODING_ULINEAR_BE:
+ default:
+ err = EINVAL;
+ }
+
+ return err;
+}
+
+/*
+ * If we have an MTU value to use, round the blocksize to that.
+ */
+static int
+bthset_round_blocksize(void *hdl, int bs, int mode, const audio_params_t *param)
+{
+ struct bthset_softc *sc = hdl;
+
+ if (sc->sc_mtu > 0)
+ bs = (bs / sc->sc_mtu) * sc->sc_mtu;
+
+ DPRINTF("%s mode=0x%x, bs=%d, sc_mtu=%d\n",
+ btdev_name(sc), mode, bs, sc->sc_mtu);
+
+ return bs;
+}
+
+/*
+ * Start Output
+ *
+ * We dont want to be calling the network stack at splaudio() so make
+ * a note of what is to be sent, and schedule an interrupt to bundle
+ * it up and queue it.
+ */
+static int
+bthset_start_output(void *hdl, void *block, int blksize,
+ void (*intr)(void *), void *intrarg)
+{
+ struct bthset_softc *sc = hdl;
+
+ DPRINTFN(5, "%s blksize %d\n", btdev_name(sc), blksize);
+
+ sc->sc_tx_block = block;
+ sc->sc_tx_pending = 0;
+ sc->sc_tx_size = blksize;
+ sc->sc_tx_intr = intr;
+ sc->sc_tx_intrarg = intrarg;
+
+ softintr_schedule(sc->sc_intr);
+ return 0;
+}
+
+/*
+ * Start Input
+ *
+ * When the SCO link is up, we are getting data in any case, so all we do
+ * is note what we want and where to put it and let the sco_input routine
+ * fill in the data.
+ *
+ * If there was any leftover data that didnt fit in the last block, retry
+ * it now.
+ */
+static int
+bthset_start_input(void *hdl, void *block, int blksize,
+ void (*intr)(void *), void *intrarg)
+{
+ struct bthset_softc *sc = hdl;
+ struct mbuf *m;
+
+ DPRINTFN(5, "%s blksize %d\n", btdev_name(sc), blksize);
+
+ if (sc->sc_sco == NULL)
+ return EINVAL;
+
+ sc->sc_rx_want = blksize;
+ sc->sc_rx_block = block;
+ sc->sc_rx_intr = intr;
+ sc->sc_rx_intrarg = intrarg;
+
+ if (sc->sc_rx_mbuf != NULL) {
+ m = sc->sc_rx_mbuf;
+ sc->sc_rx_mbuf = NULL;
+ bthset_sco_input(sc, m);
+ }
+
+ return 0;
+}
+
+/*
+ * Halt Output
+ *
+ * This doesnt really halt the output, but it will look
+ * that way to the audio driver. The current block will
+ * still be transmitted.
+ */
+static int
+bthset_halt_output(void *hdl)
+{
+ struct bthset_softc *sc = hdl;
+
+ DPRINTFN(5, "%s\n", btdev_name(sc));
+
+ sc->sc_tx_size = 0;
+ sc->sc_tx_block = NULL;
+ sc->sc_tx_pending = 0;
+ sc->sc_tx_intr = NULL;
+ sc->sc_tx_intrarg = NULL;
+
+ return 0;
+}
+
+/*
+ * Halt Input
+ *
+ * This doesnt really halt the input, but it will look
+ * that way to the audio driver. Incoming data will be
+ * discarded.
+ */
+static int
+bthset_halt_input(void *hdl)
+{
+ struct bthset_softc *sc = hdl;
+
+ DPRINTFN(5, "%s\n", btdev_name(sc));
+
+ sc->sc_rx_want = 0;
+ sc->sc_rx_block = NULL;
+ sc->sc_rx_intr = NULL;
+ sc->sc_rx_intrarg = NULL;
+
+ if (sc->sc_rx_mbuf != NULL) {
+ m_freem(sc->sc_rx_mbuf);
+ sc->sc_rx_mbuf = NULL;
+ }
+
+ return 0;
+}
+
+static int
+bthset_getdev(void *hdl, struct audio_device *ret)
+{
+
+ *ret = bthset_device;
+ return 0;
+}
+
+static int
+bthset_setfd(void *hdl, int fd)
+{
+// struct bthset_softc *sc = hdl;
+
+ DPRINTF("%s set %s duplex\n", btdev_name(hdl), fd ? "full" : "half");
+
+ return 0;
+}
+
+static int
+bthset_set_port(void *hdl, mixer_ctrl_t *mc)
+{
+ struct bthset_softc *sc = hdl;
+ int err = 0;
+
+ DPRINTF("%s dev %d type %d\n", btdev_name(sc), mc->dev, mc->type);
+
+ switch (mc->dev) {
+ case BTHSET_VGS:
+ if (mc->type != AUDIO_MIXER_VALUE ||
+ mc->un.value.num_channels != 1) {
+ err = EINVAL;
+ break;
+ }
+
+ sc->sc_vgs = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ break;
+
+ case BTHSET_VGM:
+ if (mc->type != AUDIO_MIXER_VALUE ||
+ mc->un.value.num_channels != 1) {
+ err = EINVAL;
+ break;
+ }
+
+ sc->sc_vgm = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+ break;
+
+ default:
+ err = EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static int
+bthset_get_port(void *hdl, mixer_ctrl_t *mc)
+{
+ struct bthset_softc *sc = hdl;
+ int err = 0;
+
+ DPRINTF("%s dev %d\n", btdev_name(sc), mc->dev);
+
+ switch (mc->dev) {
+ case BTHSET_VGS:
+ mc->type = AUDIO_MIXER_VALUE;
+ mc->un.value.num_channels = 1;
+ mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vgs;
+ break;
+
+ case BTHSET_VGM:
+ mc->type = AUDIO_MIXER_VALUE;
+ mc->un.value.num_channels = 1;
+ mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vgm;
+ break;
+
+ default:
+ err = EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static int
+bthset_query_devinfo(void *hdl, mixer_devinfo_t *di)
+{
+// struct bthset_softc *sc = hdl;
+ int err = 0;
+
+ DPRINTF("%s index %d\n", btdev_name(hdl), di->index);
+
+ switch(di->index) {
+ case BTHSET_VGS:
+ di->mixer_class = BTHSET_INPUT_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ strcpy(di->label.name, AudioNspeaker);
+ di->type = AUDIO_MIXER_VALUE;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ di->un.v.num_channels = 1;
+ di->un.v.delta = BTHSET_DELTA;
+ break;
+
+ case BTHSET_VGM:
+ di->mixer_class = BTHSET_INPUT_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ strcpy(di->label.name, AudioNmicrophone);
+ di->type = AUDIO_MIXER_VALUE;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ di->un.v.num_channels = 1;
+ di->un.v.delta = BTHSET_DELTA;
+ break;
+
+ case BTHSET_INPUT_CLASS:
+ di->mixer_class = BTHSET_INPUT_CLASS;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ strcpy(di->label.name, AudioCinputs);
+ di->type = AUDIO_MIXER_CLASS;
+ break;
+
+ default:
+ err = ENXIO;
+ break;
+ }
+
+ return err;
+}
+
+/*
+ * Allocate Ring Buffers.
+ */
+static void *
+bthset_allocm(void *hdl, int direction, size_t size,
+ struct malloc_type *type, int flags)
+{
+ struct bthset_softc *sc = hdl;
+ void *addr;
+
+ DPRINTF("%s: size %d direction %d\n",
+ btdev_name(hdl), size, direction);
+
+ addr = malloc(size, type, flags);
+
+ if (direction == AUMODE_PLAY) {
+ sc->sc_tx_buf = addr;
+ sc->sc_tx_refcnt = 0;
+ }
+
+ return addr;
+}
+
+/*
+ * Free Ring Buffers.
+ *
+ * Because we used external memory for the tx mbufs, we dont
+ * want to free the memory until all the mbufs are done with
+ *
+ * Just to be sure, dont free if something is still pending.
+ * This would be a memory leak but at least there is a warning..
+ */
+static void
+bthset_freem(void *hdl, void *addr, struct malloc_type *type)
+{
+ struct bthset_softc *sc = hdl;
+ int count = hz / 2;
+
+ if (addr == sc->sc_tx_buf) {
+ DPRINTF("%s: tx_refcnt=%d\n",
+ btdev_name(sc), sc->sc_tx_refcnt);
+
+ sc->sc_tx_buf = NULL;
+
+ while (sc->sc_tx_refcnt> 0 && count-- > 0)
+ tsleep(sc, PWAIT, "drain", 1);
+
+ if (sc->sc_tx_refcnt > 0) {
+ printf("%s: ring buffer unreleased!\n", btdev_name(sc));
+ return;
+ }
+ }
+
+ free(addr, type);
+}
+
+static int
+bthset_get_props(void *hdl)
+{
+
+ return AUDIO_PROP_FULLDUPLEX;
+}
+
+/*
+ * Handle private ioctl. We pass information out about how to talk
+ * to the device and mixer.
+ */
+static int
+bthset_dev_ioctl(void *hdl, u_long cmd, caddr_t addr, int flag, struct lwp *l)
+{
+ struct bthset_softc *sc = hdl;
+ struct bthset_info *bi = (struct bthset_info *)addr;
+ int err = 0;
+
+ DPRINTF("%s cmd 0x%lx flag %d\n", btdev_name(sc), cmd, flag);
+
+ switch (cmd) {
+ case BTHSET_GETINFO:
+ memset(bi, 0, sizeof(*bi));
+ bdaddr_copy(&bi->laddr, &sc->sc_laddr);
+ bdaddr_copy(&bi->raddr, &sc->sc_raddr);
+ bi->channel = sc->sc_channel;
+ bi->vgs = BTHSET_VGS;
+ bi->vgm = BTHSET_VGM;
+ break;
+
+ default:
+ err = EPASSTHROUGH;
+ break;
+ }
+
+ return err;
+}
+
+
+/*****************************************************************************
+ *
+ * misc bthset functions
+ *
+ */
+
+/*
+ * Our transmit interrupt. This is triggered when a new block is to be
+ * sent. We send mtu sized chunks of the block as mbufs with external
+ * storage to sco_send()
+ */
+static void
+bthset_intr(void *arg)
+{
+ struct bthset_softc *sc = arg;
+ struct mbuf *m;
+ uint8_t *block;
+ int mlen, size;
+
+ DPRINTFN(10, "%s block %p size %d\n",
+ btdev_name(sc), sc->sc_tx_block, sc->sc_tx_size);
+
+ block = sc->sc_tx_block;
+ size = sc->sc_tx_size;
+ sc->sc_tx_block = NULL;
+ sc->sc_tx_size = 0;
+
+ while (size > 0) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ break;
+
+ mlen = MIN(sc->sc_mtu, size);
+
+ /* I think M_DEVBUF is true but not relevant */
+ MEXTADD(m, block, mlen, M_DEVBUF, bthset_extfree, sc);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ break;
+ }
+ sc->sc_tx_refcnt++;
+
+ m->m_pkthdr.len = m->m_len = mlen;
+ sc->sc_tx_pending++;
+
+ if (sco_send(sc->sc_sco, m) > 0) {
+ sc->sc_tx_pending--;
+ m_free(m);
+ break;
+ }
+
+ block += mlen;
+ size -= mlen;
+ }
+}
+
+/*
+ * Release the mbuf, we keep a reference count on the tx buffer so
+ * that we dont release it before its free.
+ */
+static void
+bthset_extfree(struct mbuf *m, caddr_t addr, size_t size, void *arg)
+{
+ struct bthset_softc *sc = arg;
+
+ if (m != NULL)
+ pool_cache_put(&mbpool_cache, m);
+
+ sc->sc_tx_refcnt--;
+}
diff --git a/sys/dev/bluetooth/bthset.h b/sys/dev/bluetooth/bthset.h
new file mode 100644
index 00000000000..7ef82ba49a1
--- /dev/null
+++ b/sys/dev/bluetooth/bthset.h
@@ -0,0 +1,54 @@
+/* $NetBSD: bthset.h,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.
+ */
+
+#ifndef _DEV_BLUETOOTH_BTHSET_H_
+#define _DEV_BLUETOOTH_BTHSET_H_
+
+/*
+ * We need to provide a way to get the Headset contact information
+ * from the audio device so that the control channel can be set up
+ */
+
+struct bthset_info {
+ bdaddr_t laddr; /* controller */
+ bdaddr_t raddr; /* headset */
+ uint8_t channel; /* RFCOMM channel */
+ int vgs; /* mixer index speaker */
+ int vgm; /* mixer index mic */
+};
+
+#define BTHSET_GETINFO _IOR('b', 16, struct bthset_info)
+
+#define BTHSET_DELTA 16 /* volume delta */
+
+#endif /* _DEV_BLUETOOTH_BTHSET_H_ */
diff --git a/sys/dev/bluetooth/bthub.c b/sys/dev/bluetooth/bthub.c
new file mode 100644
index 00000000000..f9052c936b9
--- /dev/null
+++ b/sys/dev/bluetooth/bthub.c
@@ -0,0 +1,245 @@
+/* $NetBSD: bthub.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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: bthub.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $");
+
+#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/bluetooth/btdev.h>
+
+/*****************************************************************************
+ *
+ * Bluetooth HUB pseudo-device
+ *
+ * The Bluetooth hub is a conventent place to hang devices that are
+ * actually sitting on top of the protocol stack, and not necessarily
+ * attached to any particular bluetooth interface.
+ */
+
+struct bthub_softc {
+ struct device sc_dev;
+ LIST_HEAD(,btdev) sc_list;
+};
+
+/* our pseudo-device hook */
+static struct bthub_softc *hook;
+
+/* pseudo-device initialization */
+void bthubattach(int);
+
+/* autoconf(9) glue */
+static int bthub_match(struct device *, struct cfdata *, void *);
+static void bthub_attach(struct device *, struct device *, void *);
+static int bthub_detach(struct device *, int);
+static int bthub_print(void *, const char *);
+
+CFATTACH_DECL(bthub, sizeof(struct bthub_softc),
+ bthub_match, bthub_attach, bthub_detach, NULL);
+
+/* pseudo-device control */
+dev_type_ioctl(bthubioctl);
+
+const struct cdevsw bthub_cdevsw = {
+ nullopen, nullclose, noread, nowrite, bthubioctl,
+ nostop, notty, nopoll, nommap, nokqfilter,
+};
+
+/*****************************************************************************
+ *
+ * bthub init routine called at system boot
+ *
+ * we take this opportunity to hang a real device on our hook
+ * so that we can operate like a proper parent to our children.
+ */
+
+extern struct cfdriver bthub_cd;
+
+static struct cfdata bthub_cfdata = {
+ .cf_name = "bthub",
+ .cf_atname = "bthub",
+ .cf_unit = DVUNIT_ANY,
+ .cf_fstate = FSTATE_STAR,
+};
+
+void
+bthubattach(int num)
+{
+ int err;
+
+ err = config_cfattach_attach(bthub_cd.cd_name, &bthub_ca);
+ if (err) {
+ aprint_error("%s: unable to register cfattach (%d)\n",
+ bthub_cd.cd_name, err);
+
+ config_cfdriver_detach(&bthub_cd);
+ return;
+ }
+
+ hook = (struct bthub_softc *)config_attach_pseudo(&bthub_cfdata);
+ if (hook == NULL) {
+ aprint_error("%s: unable to attach bthub hook(%d)\n",
+ bthub_cd.cd_name, err);
+
+ config_cfattach_detach(bthub_cd.cd_name, &bthub_ca);
+ config_cfdriver_detach(&bthub_cd);
+ return;
+ }
+}
+
+/*****************************************************************************
+ *
+ * bthub autoconf(9) routines
+ *
+ * This is the real device hanging on our hook. Only the
+ * attach function will ever be called, I guess.
+ */
+
+static int
+bthub_match(struct device *self, struct cfdata *cfdata, void *arg)
+{
+
+ return 1;
+}
+
+static void
+bthub_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct bthub_softc *sc = (struct bthub_softc *)self;
+
+ LIST_INIT(&sc->sc_list);
+}
+
+static int
+bthub_detach(struct device *self, int flags)
+{
+ struct bthub_softc *sc = (struct bthub_softc *)self;
+ struct btdev *btdev;
+
+ while ((btdev = LIST_FIRST(&sc->sc_list)) != NULL) {
+ LIST_REMOVE(btdev, sc_next);
+ config_detach(&btdev->sc_dev, flags);
+ }
+ return 0;
+}
+
+/*****************************************************************************
+ *
+ * bthub control device ioctl(2)
+ *
+ * This is the method we use to control bluetooth devices,
+ * called from userland.
+ */
+
+int
+bthubioctl(dev_t devno, unsigned long cmd, caddr_t data, int flag, struct lwp *l)
+{
+ struct btdev_attach_args *bda;
+ struct btdev *btdev;
+ bdaddr_t *bdaddr;
+
+ if (hook == NULL)
+ return ENXIO;
+
+ switch (cmd) {
+ case BTDEV_ATTACH: /* attach BTDEV */
+ bda = (struct btdev_attach_args *)data;
+
+ /*
+ * validate our configuration
+ */
+ if (bdaddr_any(&bda->bd_raddr)
+ || bda->bd_type == 0)
+ return EINVAL;
+
+ LIST_FOREACH(btdev, &hook->sc_list, sc_next) {
+ if (bdaddr_same(&btdev->sc_addr, &bda->bd_raddr))
+ return EADDRINUSE;
+ }
+
+ btdev = (struct btdev *)config_found((struct device *)hook,
+ bda, bthub_print);
+ if (btdev == NULL)
+ return ENXIO;
+
+ bdaddr_copy(&btdev->sc_addr, &bda->bd_raddr);
+ LIST_INSERT_HEAD(&hook->sc_list, btdev, sc_next);
+ return 0;
+
+ case BTDEV_DETACH: /* detach BTDEV */
+ bdaddr = (bdaddr_t *)data;
+
+ LIST_FOREACH(btdev, &hook->sc_list, sc_next) {
+ if (bdaddr_same(bdaddr, &btdev->sc_addr) == 0)
+ continue;
+
+ LIST_REMOVE(btdev, sc_next);
+ config_detach((struct device *)btdev, DETACH_FORCE);
+ return 0;
+ }
+ return ENXIO;
+
+ default:
+ break;
+ }
+
+ return EPASSTHROUGH;
+}
+
+static int
+bthub_print(void *aux, const char *pnp)
+{
+ struct btdev_attach_args *bda = aux;
+
+ if (pnp != NULL)
+ aprint_normal("%s: ", pnp);
+
+ aprint_verbose(" bdaddr %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ bda->bd_raddr.b[5], bda->bd_raddr.b[4],
+ bda->bd_raddr.b[3], bda->bd_raddr.b[2],
+ bda->bd_raddr.b[1], bda->bd_raddr.b[0]);
+
+ return UNCONF;
+}
diff --git a/sys/dev/bluetooth/btkbd.c b/sys/dev/bluetooth/btkbd.c
new file mode 100644
index 00000000000..90421e9a9d7
--- /dev/null
+++ b/sys/dev/bluetooth/btkbd.c
@@ -0,0 +1,566 @@
+/* $NetBSD: btkbd.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.
+ */
+
+/*
+ * based on dev/usb/ukbd.c
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: btkbd.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $");
+
+#include <sys/param.h>
+#include <sys/callout.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+
+#include <netbt/bluetooth.h>
+
+#include <dev/bluetooth/bthid.h>
+#include <dev/bluetooth/bthidev.h>
+
+#include <dev/usb/hid.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wskbdvar.h>
+#include <dev/wscons/wsksymdef.h>
+#include <dev/wscons/wsksymvar.h>
+
+#include "opt_wsdisplay_compat.h"
+#include "opt_btkbd.h"
+
+#define MAXKEYCODE 6
+#define MAXMOD 8 /* max 32 */
+#define MAXKEYS (MAXMOD + (2 * MAXKEYCODE))
+
+struct btkbd_data {
+ uint32_t modifiers;
+ uint8_t keycode[MAXKEYCODE];
+};
+
+struct btkbd_mod {
+ uint32_t mask;
+ uint8_t key;
+};
+
+struct btkbd_softc {
+ struct bthidev sc_hidev; /* device+ */
+ struct device *sc_wskbd; /* child */
+ int sc_enabled;
+
+ int (*sc_output) /* output method */
+ (struct bthidev *, uint8_t *, int);
+
+ /* stored data */
+ struct btkbd_data sc_odata;
+ struct btkbd_data sc_ndata;
+
+ /* input reports */
+ int sc_nmod;
+ struct hid_location sc_modloc[MAXMOD];
+ struct btkbd_mod sc_mods[MAXMOD];
+
+ int sc_nkeycode;
+ struct hid_location sc_keycodeloc;
+
+ /* output reports */
+ struct hid_location sc_numloc;
+ struct hid_location sc_capsloc;
+ struct hid_location sc_scroloc;
+ int sc_leds;
+
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+ int sc_rawkbd;
+#ifdef BTKBD_REPEAT
+ struct callout sc_repeat;
+ int sc_nrep;
+ char sc_rep[MAXKEYS];
+#endif
+#endif
+};
+
+/* autoconf(9) methods */
+static int btkbd_match(struct device *, struct cfdata *, void *);
+static void btkbd_attach(struct device *, struct device *, void *);
+static int btkbd_detach(struct device *, int);
+
+CFATTACH_DECL(btkbd, sizeof(struct btkbd_softc),
+ btkbd_match, btkbd_attach, btkbd_detach, NULL);
+
+/* wskbd(4) accessops */
+static int btkbd_enable(void *, int);
+static void btkbd_set_leds(void *, int);
+static int btkbd_ioctl(void *, unsigned long, caddr_t, int, struct lwp *);
+
+static const struct wskbd_accessops btkbd_accessops = {
+ btkbd_enable,
+ btkbd_set_leds,
+ btkbd_ioctl
+};
+
+/* wskbd(4) keymap data */
+extern const struct wscons_keydesc ukbd_keydesctab[];
+
+const struct wskbd_mapdata btkbd_keymapdata = {
+ ukbd_keydesctab,
+#if defined(BTKBD_LAYOUT)
+ BTKBD_LAYOUT,
+#elif defined(PCKBD_LAYOUT)
+ PCKBD_LAYOUT,
+#else
+ KB_US,
+#endif
+};
+
+/* bthid methods */
+static void btkbd_input(struct bthidev *, uint8_t *, int);
+
+/* internal prototypes */
+static const char *btkbd_parse_desc(struct btkbd_softc *, int, void *, int);
+
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+#ifdef BTKBD_REPEAT
+static void btkbd_repeat(void *);
+#endif
+#endif
+
+/*****************************************************************************
+ *
+ * btkbd autoconf(9) routines
+ */
+
+static int
+btkbd_match(struct device *self, struct cfdata *cfdata, void *aux)
+{
+ struct bthidev_attach_args *ba = aux;
+
+ if (hid_is_collection(ba->ba_desc, ba->ba_dlen, ba->ba_id,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD)))
+ return 1;
+
+ return 0;
+}
+
+static void
+btkbd_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct btkbd_softc *sc = (struct btkbd_softc *)self;
+ struct bthidev_attach_args *ba = aux;
+ struct wskbddev_attach_args wska;
+ const char *parserr;
+
+ sc->sc_output = ba->ba_output;
+ ba->ba_input = btkbd_input;
+
+ parserr = btkbd_parse_desc(sc, ba->ba_id, ba->ba_desc, ba->ba_dlen);
+ if (parserr != NULL) {
+ aprint_error("%s\n", parserr);
+ return;
+ }
+
+ aprint_normal("\n");
+
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+#ifdef BTKBD_REPEAT
+ callout_init(&sc->sc_repeat);
+ callout_setfunc(&sc->sc_repeat, btkbd_repeat, sc);
+#endif
+#endif
+
+ wska.console = 0;
+ wska.keymap = &btkbd_keymapdata;
+ wska.accessops = &btkbd_accessops;
+ wska.accesscookie = sc;
+
+ sc->sc_wskbd = config_found((struct device *)sc, &wska, wskbddevprint);
+}
+
+static int
+btkbd_detach(struct device *self, int flags)
+{
+ struct btkbd_softc *sc = (struct btkbd_softc *)self;
+ int err = 0;
+
+ if (sc->sc_wskbd != NULL) {
+ err = config_detach(sc->sc_wskbd, flags);
+ sc->sc_wskbd = NULL;
+ }
+
+ return err;
+}
+
+static const char *
+btkbd_parse_desc(struct btkbd_softc *sc, int id, void *desc, int dlen)
+{
+ struct hid_data *d;
+ struct hid_item h;
+ int imod;
+
+ imod = 0;
+ sc->sc_nkeycode = 0;
+ d = hid_start_parse(desc, dlen, hid_input);
+ while (hid_get_item(d, &h)) {
+ if (h.kind != hid_input || (h.flags & HIO_CONST) ||
+ HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD ||
+ h.report_ID != id)
+ continue;
+
+ if (h.flags & HIO_VARIABLE) {
+ if (h.loc.size != 1)
+ return ("bad modifier size");
+
+ /* Single item */
+ if (imod < MAXMOD) {
+ sc->sc_modloc[imod] = h.loc;
+ sc->sc_mods[imod].mask = 1 << imod;
+ sc->sc_mods[imod].key = HID_GET_USAGE(h.usage);
+ imod++;
+ } else
+ return ("too many modifier keys");
+ } else {
+ /* Array */
+ if (h.loc.size != 8)
+ return ("key code size != 8");
+
+ if (h.loc.count > MAXKEYCODE)
+ return ("too many key codes");
+
+ if (h.loc.pos % 8 != 0)
+ return ("key codes not on byte boundary");
+
+ if (sc->sc_nkeycode != 0)
+ return ("multiple key code arrays\n");
+
+ sc->sc_keycodeloc = h.loc;
+ sc->sc_nkeycode = h.loc.count;
+ }
+ }
+ sc->sc_nmod = imod;
+ hid_end_parse(d);
+
+ hid_locate(desc, dlen, HID_USAGE2(HUP_LEDS, HUD_LED_NUM_LOCK),
+ id, hid_output, &sc->sc_numloc, NULL);
+
+ hid_locate(desc, dlen, HID_USAGE2(HUP_LEDS, HUD_LED_CAPS_LOCK),
+ id, hid_output, &sc->sc_capsloc, NULL);
+
+ hid_locate(desc, dlen, HID_USAGE2(HUP_LEDS, HUD_LED_SCROLL_LOCK),
+ id, hid_output, &sc->sc_scroloc, NULL);
+
+ return (NULL);
+}
+
+/*****************************************************************************
+ *
+ * wskbd(9) accessops
+ */
+
+static int
+btkbd_enable(void *self, int on)
+{
+ struct btkbd_softc *sc = (struct btkbd_softc *)self;
+
+ sc->sc_enabled = on;
+ return 0;
+}
+
+static void
+btkbd_set_leds(void *self, int leds)
+{
+ struct btkbd_softc *sc = (struct btkbd_softc *)self;
+ uint8_t report;
+
+ if (sc->sc_leds == leds)
+ return;
+
+ sc->sc_leds = leds;
+
+ /*
+ * This is not totally correct, since we did not check the
+ * report size from the descriptor but for keyboards it should
+ * just be a single byte with the relevant bits set.
+ */
+ report = 0;
+ if ((leds & WSKBD_LED_SCROLL) && sc->sc_scroloc.size == 1)
+ report |= 1 << sc->sc_scroloc.pos;
+
+ if ((leds & WSKBD_LED_NUM) && sc->sc_numloc.size == 1)
+ report |= 1 << sc->sc_numloc.pos;
+
+ if ((leds & WSKBD_LED_CAPS) && sc->sc_capsloc.size == 1)
+ report |= 1 << sc->sc_capsloc.pos;
+
+ if (sc->sc_output)
+ (*sc->sc_output)(&sc->sc_hidev, &report, sizeof(report));
+}
+
+static int
+btkbd_ioctl(void *self, unsigned long cmd, caddr_t data, int flag, struct lwp *l)
+{
+ struct btkbd_softc *sc = (struct btkbd_softc *)self;
+
+ switch (cmd) {
+ case WSKBDIO_GTYPE:
+ *(int *)data = WSKBD_TYPE_BLUETOOTH;
+ break;
+
+ case WSKBDIO_SETLEDS:
+ btkbd_set_leds(sc, *(int *)data);
+ break;
+
+ case WSKBDIO_GETLEDS:
+ *(int *)data = sc->sc_leds;
+ break;
+
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+ case WSKBDIO_SETMODE:
+ sc->sc_rawkbd = (*(int *)data == WSKBD_RAW);
+#ifdef BTKBD_REPEAT
+ callout_stop(&sc->sc_repeat);
+#endif
+ break;
+#endif
+
+ default:
+ return EPASSTHROUGH;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ *
+ * btkbd input routine, called from our parent
+ */
+
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+#define NN 0 /* no translation */
+/*
+ * Translate USB keycodes to US keyboard XT scancodes.
+ * Scancodes >= 0x80 represent EXTENDED keycodes.
+ *
+ * See http://www.microsoft.com/HWDEV/TECH/input/Scancode.asp
+ */
+static const u_int8_t btkbd_trtab[256] = {
+ NN, NN, NN, NN, 0x1e, 0x30, 0x2e, 0x20, /* 00 - 07 */
+ 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, /* 08 - 0f */
+ 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, /* 10 - 17 */
+ 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03, /* 18 - 1f */
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, /* 20 - 27 */
+ 0x1c, 0x01, 0x0e, 0x0f, 0x39, 0x0c, 0x0d, 0x1a, /* 28 - 2f */
+ 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34, /* 30 - 37 */
+ 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, /* 38 - 3f */
+ 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xaa, 0x46, /* 40 - 47 */
+ 0x7f, 0xd2, 0xc7, 0xc9, 0xd3, 0xcf, 0xd1, 0xcd, /* 48 - 4f */
+ 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e, /* 50 - 57 */
+ 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, /* 58 - 5f */
+ 0x48, 0x49, 0x52, 0x53, 0x56, 0xdd, NN, 0x59, /* 60 - 67 */
+ 0x5d, 0x5e, 0x5f, NN, NN, NN, NN, NN, /* 68 - 6f */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 70 - 77 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 78 - 7f */
+ NN, NN, NN, NN, NN, 0x7e, NN, 0x73, /* 80 - 87 */
+ 0x70, 0x7d, 0x79, 0x7b, 0x5c, NN, NN, NN, /* 88 - 8f */
+ NN, NN, 0x78, 0x77, 0x76, NN, NN, NN, /* 90 - 97 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9f */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* a0 - a7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* a8 - af */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* b0 - b7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* b8 - bf */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* c0 - c7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* c8 - cf */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* d0 - d7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* d8 - df */
+ 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc, /* e0 - e7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* e8 - ef */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* f0 - f7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* f8 - ff */
+};
+#endif
+
+#define KEY_ERROR 0x01
+#define PRESS 0x000
+#define RELEASE 0x100
+#define CODEMASK 0x0ff
+#define ADDKEY(c) ibuf[nkeys++] = (c)
+#define REP_DELAY1 400
+#define REP_DELAYN 100
+
+static void
+btkbd_input(struct bthidev *self, uint8_t *data, int len)
+{
+ struct btkbd_softc *sc = (struct btkbd_softc *)self;
+ struct btkbd_data *ud = &sc->sc_ndata;
+ uint16_t ibuf[MAXKEYS];
+ uint32_t mod, omod;
+ int nkeys, i, j;
+ int key;
+ int s;
+
+ if (sc->sc_wskbd == NULL || sc->sc_enabled == 0)
+ return;
+
+ /* extract key modifiers */
+ ud->modifiers = 0;
+ for (i = 0 ; i < sc->sc_nmod ; i++)
+ if (hid_get_data(data, &sc->sc_modloc[i]))
+ ud->modifiers |= sc->sc_mods[i].mask;
+
+ /* extract keycodes */
+ memcpy(ud->keycode, data + (sc->sc_keycodeloc.pos / 8),
+ sc->sc_nkeycode);
+
+ if (ud->keycode[0] == KEY_ERROR)
+ return; /* ignore */
+
+ nkeys = 0;
+ mod = ud->modifiers;
+ omod = sc->sc_odata.modifiers;
+ if (mod != omod)
+ for (i = 0 ; i < sc->sc_nmod ; i++)
+ if ((mod & sc->sc_mods[i].mask) !=
+ (omod & sc->sc_mods[i].mask))
+ ADDKEY(sc->sc_mods[i].key |
+ (mod & sc->sc_mods[i].mask
+ ? PRESS : RELEASE));
+
+ if (memcmp(ud->keycode, sc->sc_odata.keycode, sc->sc_nkeycode) != 0) {
+ /* Check for released keys. */
+ for (i = 0 ; i < sc->sc_nkeycode ; i++) {
+ key = sc->sc_odata.keycode[i];
+ if (key == 0)
+ continue;
+
+ for (j = 0 ; j < sc->sc_nkeycode ; j++)
+ if (key == ud->keycode[j])
+ goto rfound;
+
+ ADDKEY(key | RELEASE);
+
+ rfound:
+ ;
+ }
+
+ /* Check for pressed keys. */
+ for (i = 0 ; i < sc->sc_nkeycode ; i++) {
+ key = ud->keycode[i];
+ if (key == 0)
+ continue;
+
+ for (j = 0; j < sc->sc_nkeycode; j++)
+ if (key == sc->sc_odata.keycode[j])
+ goto pfound;
+
+ ADDKEY(key | PRESS);
+ pfound:
+ ;
+ }
+ }
+ sc->sc_odata = *ud;
+
+ if (nkeys == 0)
+ return;
+
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+ if (sc->sc_rawkbd) {
+ u_char cbuf[MAXKEYS * 2];
+ int c;
+ int npress;
+
+ for (npress = i = j = 0 ; i < nkeys ; i++) {
+ key = ibuf[i];
+ c = btkbd_trtab[key & CODEMASK];
+ if (c == NN)
+ continue;
+
+ if (c & 0x80)
+ cbuf[j++] = 0xe0;
+
+ cbuf[j] = c & 0x7f;
+ if (key & RELEASE)
+ cbuf[j] |= 0x80;
+#ifdef BTKBD_REPEAT
+ else {
+ /* remember pressed keys for autorepeat */
+ if (c & 0x80)
+ sc->sc_rep[npress++] = 0xe0;
+
+ sc->sc_rep[npress++] = c & 0x7f;
+ }
+#endif
+
+ j++;
+ }
+
+ s = spltty();
+ wskbd_rawinput(sc->sc_wskbd, cbuf, j);
+ splx(s);
+#ifdef BTKBD_REPEAT
+ callout_stop(&sc->sc_repeat);
+ if (npress != 0) {
+ sc->sc_nrep = npress;
+ callout_schedule(&sc->sc_repeat, hz * REP_DELAY1 / 1000);
+ }
+#endif
+ return;
+ }
+#endif
+
+ s = spltty();
+ for (i = 0 ; i < nkeys ; i++) {
+ key = ibuf[i];
+ wskbd_input(sc->sc_wskbd,
+ key & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN,
+ key & CODEMASK);
+ }
+ splx(s);
+}
+
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+#ifdef BTKBD_REPEAT
+static void
+btkbd_repeat(void *arg)
+{
+ struct btkbd_softc *sc = arg;
+ int s;
+
+ s = spltty();
+ wskbd_rawinput(sc->sc_wskbd, sc->sc_rep, sc->sc_nrep);
+ splx(s);
+ callout_schedule(&sc->sc_repeat, hz * REP_DELAYN / 1000);
+}
+#endif
+#endif
diff --git a/sys/dev/bluetooth/btms.c b/sys/dev/bluetooth/btms.c
new file mode 100644
index 00000000000..f22a3b57bc7
--- /dev/null
+++ b/sys/dev/bluetooth/btms.c
@@ -0,0 +1,328 @@
+/* $NetBSD: btms.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.
+ */
+
+/*
+ * based on dev/usb/ums.c
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: btms.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+
+#include <netbt/bluetooth.h>
+
+#include <dev/bluetooth/bthid.h>
+#include <dev/bluetooth/bthidev.h>
+
+#include <dev/usb/hid.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsmousevar.h>
+
+#define MAX_BUTTONS 31
+#define BUTTON(n) (1 << (((n) == 1 || (n) == 2) ? 3 - (n) : (n)))
+#define NOTMOUSE(f) (((f) & (HIO_CONST | HIO_RELATIVE)) != HIO_RELATIVE)
+
+struct btms_softc {
+ struct bthidev sc_hidev; /* device+ */
+
+ struct device *sc_wsmouse; /* child */
+ int sc_enabled;
+ uint16_t sc_flags;
+
+ /* locators */
+ struct hid_location sc_loc_x;
+ struct hid_location sc_loc_y;
+ struct hid_location sc_loc_z;
+ struct hid_location sc_loc_w;
+ struct hid_location sc_loc_button[MAX_BUTTONS];
+
+ int sc_num_buttons;
+ uint32_t sc_buttons;
+};
+
+/* sc_flags */
+#define BTMS_REVZ (1 << 0) /* reverse Z direction */
+#define BTMS_HASZ (1 << 1) /* has Z direction */
+
+/* autoconf(9) methods */
+static int btms_match(struct device *, struct cfdata *, void *);
+static void btms_attach(struct device *, struct device *, void *);
+static int btms_detach(struct device *, int);
+
+CFATTACH_DECL(btms, sizeof(struct btms_softc),
+ btms_match, btms_attach, btms_detach, NULL);
+
+/* wsmouse(4) accessops */
+static int btms_wsmouse_enable(void *);
+static int btms_wsmouse_ioctl(void *, unsigned long, caddr_t, int, struct lwp *);
+static void btms_wsmouse_disable(void *);
+
+static const struct wsmouse_accessops btms_wsmouse_accessops = {
+ btms_wsmouse_enable,
+ btms_wsmouse_ioctl,
+ btms_wsmouse_disable,
+};
+
+/* bthid methods */
+static void btms_input(struct bthidev *, uint8_t *, int);
+
+/*****************************************************************************
+ *
+ * btms autoconf(9) routines
+ */
+
+static int
+btms_match(struct device *parent, struct cfdata *match, void *aux)
+{
+ struct bthidev_attach_args *ba = aux;
+
+ if (hid_is_collection(ba->ba_desc, ba->ba_dlen, ba->ba_id,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
+ return 1;
+
+ return 0;
+}
+
+static void
+btms_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+ struct bthidev_attach_args *ba = aux;
+ struct wsmousedev_attach_args wsma;
+ struct hid_location *zloc;
+ uint32_t flags;
+ int i, hl;
+
+ ba->ba_input = btms_input;
+
+ /* control the horizontal */
+ hl = hid_locate(ba->ba_desc,
+ ba->ba_dlen,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
+ ba->ba_id,
+ hid_input,
+ &sc->sc_loc_x,
+ &flags);
+
+ if (hl == 0 || NOTMOUSE(flags)) {
+ printf("\n%s: X report 0x%04x not supported\n",
+ sc->sc_hidev.sc_dev.dv_xname, flags);
+
+ return;
+ }
+
+ /* control the vertical */
+ hl = hid_locate(ba->ba_desc,
+ ba->ba_dlen,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
+ ba->ba_id,
+ hid_input,
+ &sc->sc_loc_y,
+ &flags);
+
+ if (hl == 0 || NOTMOUSE(flags)) {
+ printf("\n%s: Y report 0x%04x not supported\n",
+ sc->sc_hidev.sc_dev.dv_xname, flags);
+
+ return;
+ }
+
+ /* Try the wheel first as the Z activator since it's tradition. */
+ hl = hid_locate(ba->ba_desc,
+ ba->ba_dlen,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
+ ba->ba_id,
+ hid_input,
+ &sc->sc_loc_z,
+ &flags);
+
+ zloc = &sc->sc_loc_z;
+ if (hl) {
+ if (NOTMOUSE(flags)) {
+ printf("\n%s: Wheel report 0x%04x not supported\n",
+ sc->sc_hidev.sc_dev.dv_xname, flags);
+
+ /* ignore Bad Z coord */
+ sc->sc_loc_z.size = 0;
+ } else {
+ sc->sc_flags |= BTMS_HASZ;
+ /* Wheels need the Z axis reversed. */
+ sc->sc_flags ^= BTMS_REVZ;
+ /* Put Z on the W coordinate */
+ zloc = &sc->sc_loc_w;
+ }
+ }
+
+ hl = hid_locate(ba->ba_desc,
+ ba->ba_dlen,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
+ ba->ba_id,
+ hid_input,
+ zloc,
+ &flags);
+
+ if (hl) {
+ if (NOTMOUSE(flags))
+ zloc->size = 0; /* ignore Z */
+ else
+ sc->sc_flags |= BTMS_HASZ;
+ }
+
+ for (i = 1 ; i <= MAX_BUTTONS ; i++) {
+ hl = hid_locate(ba->ba_desc,
+ ba->ba_dlen,
+ HID_USAGE2(HUP_BUTTON, i),
+ ba->ba_id,
+ hid_input,
+ &sc->sc_loc_button[i - 1],
+ NULL);
+
+ if (hl == 0)
+ break;
+ }
+ sc->sc_num_buttons = i - 1;
+
+ aprint_normal(": %d button%s%s.\n",
+ sc->sc_num_buttons,
+ sc->sc_num_buttons == 1 ? "" : "s",
+ sc->sc_flags & BTMS_HASZ ? " and Z dir" : "");
+
+ wsma.accessops = &btms_wsmouse_accessops;
+ wsma.accesscookie = sc;
+
+ sc->sc_wsmouse = config_found((struct device *)sc, &wsma, wsmousedevprint);
+}
+
+static int
+btms_detach(struct device *self, int flags)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+ int err = 0;
+
+ if (sc->sc_wsmouse != NULL) {
+ err = config_detach(sc->sc_wsmouse, flags);
+ sc->sc_wsmouse = NULL;
+ }
+
+ return err;
+}
+
+/*****************************************************************************
+ *
+ * wsmouse(4) accessops
+ */
+
+static int
+btms_wsmouse_enable(void *self)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+
+ if (sc->sc_enabled)
+ return EBUSY;
+
+ sc->sc_enabled = 1;
+ return 0;
+}
+
+static int
+btms_wsmouse_ioctl(void *self, unsigned long cmd, caddr_t data, int flag, struct lwp *l)
+{
+ /* struct btms_softc *sc = (struct btms_softc *)self; */
+
+ switch (cmd) {
+ case WSMOUSEIO_GTYPE:
+ *(uint *)data = WSMOUSE_TYPE_BLUETOOTH;
+ break;
+
+ default:
+ return EPASSTHROUGH;
+ }
+
+ return 0;
+}
+
+static void
+btms_wsmouse_disable(void *self)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+
+ sc->sc_enabled = 0;
+}
+
+/*****************************************************************************
+ *
+ * btms input routine, called from our parent
+ */
+
+static void
+btms_input(struct bthidev *self, uint8_t *data, int len)
+{
+ struct btms_softc *sc = (struct btms_softc *)self;
+ int dx, dy, dz, dw;
+ uint32_t buttons;
+ int i, s;
+
+ if (sc->sc_wsmouse == NULL || sc->sc_enabled == 0)
+ return;
+
+ dx = hid_get_data(data, &sc->sc_loc_x);
+ dy = -hid_get_data(data, &sc->sc_loc_y);
+ dz = hid_get_data(data, &sc->sc_loc_z);
+ dw = hid_get_data(data, &sc->sc_loc_w);
+
+ if (sc->sc_flags & BTMS_REVZ)
+ dz = -dz;
+
+ buttons = 0;
+ for (i = 0 ; i < sc->sc_num_buttons ; i++)
+ if (hid_get_data(data, &sc->sc_loc_button[i]))
+ buttons |= BUTTON(i);
+
+ if (dx != 0 || dy != 0 || dz != 0 || dw != 0 || buttons != sc->sc_buttons) {
+ sc->sc_buttons = buttons;
+
+ s = spltty();
+ wsmouse_input_xyzw(sc->sc_wsmouse, buttons,
+ dx, dy, dz, dw,
+ WSMOUSE_INPUT_DELTA);
+ splx(s);
+ }
+}
diff --git a/sys/dev/bluetooth/files.bluetooth b/sys/dev/bluetooth/files.bluetooth
index c981c8c1f71..a6a261e91da 100644
--- a/sys/dev/bluetooth/files.bluetooth
+++ b/sys/dev/bluetooth/files.bluetooth
@@ -1,12 +1,33 @@
-# $NetBSD: files.bluetooth,v 1.5 2005/12/11 12:21:15 christos Exp $
+# $NetBSD: files.bluetooth,v 1.6 2006/06/19 15:44:45 gdamore Exp $
+#
+# Config file for machine independent Bluetooth devices that attach
+# to pseudo-device hub
-define btbus { }
+# Hub driver
+defpseudo bthub { }
+file dev/bluetooth/bthub.c bthub
-# Bluetooth host controller driver
-define bthcidrv
-device bthci { }: bthcidrv
-attach bthci at btbus
+# HID "bus"
+define bthidbus {[ reportid = -1 ]}
-file dev/bluetooth/bluetooth.c btbus
-file dev/bluetooth/bthci.c bthcidrv needs-flag
-file dev/bluetooth/bthci_util.c bthcidrv
+# HID Device
+device bthidev { }: bluetooth, bthidbus, hid
+attach bthidev at bthub
+file dev/bluetooth/bthidev.c bthidev
+
+# HID Keyboards
+defflag opt_btkbd.h BTKBD_REPEAT
+defparam opt_btkbd.h BTKBD_LAYOUT
+device btkbd { }: hid, wskbddev
+attach btkbd at bthidbus
+file dev/bluetooth/btkbd.c btkbd
+
+# HID Mice
+device btms { }: hid, wsmousedev
+attach btms at bthidbus
+file dev/bluetooth/btms.c btms
+
+# Audio Headsets
+device bthset { }: bluetooth, audiobus, auconv, mulaw, aurateconv
+attach bthset at bthub
+file dev/bluetooth/bthset.c bthset
diff --git a/sys/dev/pcmcia/bt3c.c b/sys/dev/pcmcia/bt3c.c
new file mode 100644
index 00000000000..957d1a07033
--- /dev/null
+++ b/sys/dev/pcmcia/bt3c.c
@@ -0,0 +1,968 @@
+/* $NetBSD: bt3c.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $ */
+
+/*-
+ * Copyright (c) 2005 Iain D. Hibbert,
+ * All rights reserved.
+ *
+ * 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 the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+/*
+ * Driver for the 3Com Bluetooth PC Card 3CRWB6096, written with reference to
+ * FreeBSD and BlueZ drivers for same, with credit for those going to:
+ *
+ * Maksim Yevmenkin <m_evmenkin@yahoo.com> (FreeBSD)
+ * Marcel Holtmann <marcel@holtmann.org> (BlueZ)
+ * Jose Orlando Pereira <jop@di.uminho.pt> (BlueZ)
+ * David Hinds <dahinds@users.sourceforge.net> (Original Code)
+ */
+
+/*
+ * The CIS info from my card:
+ *
+ * pcmcia1: CIS tuple chain:
+ * CISTPL_DEVICE type=null speed=null
+ * 01 03 00 00 ff
+ * CISTPL_VERS_1
+ * 15 24 05 00 33 43 4f 4d 00 33 43 52 57 42 36 30
+ * 2d 41 00 42 6c 75 65 74 6f 6f 74 68 20 50 43 20
+ * 43 61 72 64 00 ff
+ * CISTPL_MANFID
+ * 20 04 01 01 40 00
+ * CISTPL_FUNCID
+ * 21 02 02 01
+ * CISTPL_CONFIG
+ * 1a 06 05 30 20 03 17 00
+ * CISTPL_CFTABLE_ENTRY
+ * 1b 09 f0 41 18 a0 40 07 30 ff ff
+ * unhandled CISTPL 80
+ * 80 0a 02 01 40 00 2d 00 00 00 00 ff
+ * CISTPL_NO_LINK
+ * 14 00
+ * CISTPL_END
+ * ff
+ * pcmcia1: CIS version PC Card Standard 5.0
+ * pcmcia1: CIS info: 3COM, 3CRWB60-A, Bluetooth PC Card
+ * pcmcia1: Manufacturer code 0x101, product 0x40
+ * pcmcia1: function 0: serial port, ccr addr 320 mask 17
+ * pcmcia1: function 0, config table entry 48: I/O card; irq mask ffff; iomask 0, iospace 0-7; rdybsy_active io8 irqlevel
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: bt3c.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $");
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+
+#include <machine/cpu.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmciadevs.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+
+#include <dev/firmload.h>
+#define BT3C_FIRMWARE_FILE "BT3CPCC.bin"
+
+/**************************************************************************
+ *
+ * bt3c autoconfig glue
+ */
+
+struct bt3c_softc {
+ struct device sc_dev; /* required */
+
+ struct pcmcia_function *sc_pf; /* our PCMCIA function */
+ struct pcmcia_io_handle sc_pcioh; /* PCMCIA i/o space info */
+ int sc_iow; /* our i/o window */
+ void *sc_powerhook; /* power hook descriptor */
+ int sc_flags; /* flags */
+
+ struct hci_unit sc_unit; /* Bluetooth HCI Unit */
+
+ /* hardware interrupt */
+ void *sc_intr; /* cookie */
+ int sc_state; /* receive state */
+ int sc_want; /* how much we want */
+ struct mbuf *sc_rxp; /* incoming packet */
+ struct mbuf *sc_txp; /* outgoing packet */
+};
+
+/* sc_state */ /* receiving */
+#define BT3C_RECV_PKT_TYPE 0 /* packet type */
+#define BT3C_RECV_ACL_HDR 1 /* acl header */
+#define BT3C_RECV_SCO_HDR 2 /* sco header */
+#define BT3C_RECV_EVENT_HDR 3 /* event header */
+#define BT3C_RECV_ACL_DATA 4 /* acl packet data */
+#define BT3C_RECV_SCO_DATA 5 /* sco packet data */
+#define BT3C_RECV_EVENT_DATA 6 /* event packet data */
+
+/* sc_flags */
+#define BT3C_SLEEPING (1 << 0) /* but not with the fishes */
+
+static int bt3c_match(struct device *, struct cfdata *, void *);
+static void bt3c_attach(struct device *, struct device *, void *);
+static int bt3c_detach(struct device *, int);
+static int bt3c_activate(struct device *, enum devact);
+static void bt3c_power(int, void *);
+
+CFATTACH_DECL(bt3c, sizeof(struct bt3c_softc),
+ bt3c_match, bt3c_attach, bt3c_detach, bt3c_activate);
+
+static void bt3c_start(struct hci_unit *);
+static int bt3c_enable(struct hci_unit *);
+static void bt3c_disable(struct hci_unit *);
+
+/**************************************************************************
+ *
+ * Hardware Definitions & IO routines
+ *
+ * I made up the names for most of these defs since we dont have
+ * manufacturers recommendations, but I dont like raw numbers..
+ *
+ * all hardware routines are running at IPL_TTY
+ *
+ */
+#define BT3C_ISR 0x7001 /* Interrupt Status Register */
+#define BT3C_ISR_RXRDY (1<<0) /* Device has data */
+#define BT3C_ISR_TXRDY (1<<1) /* Finished sending data */
+#define BT3C_ISR_ANTENNA (1<<5) /* Antenna position changed */
+
+#define BT3C_CSR 0x7002 /* Card Status Register */
+#define BT3C_CSR_ANTENNA (1<<4) /* Antenna position */
+
+#define BT3C_TX_COUNT 0x7005 /* Tx fifo contents */
+#define BT3C_TX_FIFO 0x7080 /* Transmit Fifo */
+#define BT3C_RX_COUNT 0x7006 /* Rx fifo contents */
+#define BT3C_RX_FIFO 0x7480 /* Receive Fifo */
+#define BT3C_FIFO_SIZE 256
+
+/* IO Registers */
+#define BT3C_IOR_DATA_L 0x00 /* data low byte */
+#define BT3C_IOR_DATA_H 0x01 /* data high byte */
+#define BT3C_IOR_ADDR_L 0x02 /* address low byte */
+#define BT3C_IOR_ADDR_H 0x03 /* address high byte */
+#define BT3C_IOR_CNTL 0x04 /* control byte */
+#define BT3C_IOR_CNTL_BOOT (1<<6) /* Boot Card */
+#define BT3C_IOR_CNTL_INTR (1<<7) /* Interrupt Requested */
+#define BT3C_IOR_LEN 0x05
+
+static inline uint16_t
+bt3c_get(struct bt3c_softc *sc)
+{
+ uint16_t data;
+
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_READ);
+ data = bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ BT3C_IOR_DATA_L);
+ data |= bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ BT3C_IOR_DATA_H) << 8;
+
+ return data;
+}
+
+static inline void
+bt3c_put(struct bt3c_softc *sc, uint16_t data)
+{
+
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_WRITE);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ BT3C_IOR_DATA_L, data & 0xff);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ BT3C_IOR_DATA_H, (data >> 8) & 0xff);
+}
+
+static inline uint8_t
+bt3c_read_control(struct bt3c_softc *sc)
+{
+
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_READ);
+ return bus_space_read_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ BT3C_IOR_CNTL);
+}
+
+static inline void
+bt3c_write_control(struct bt3c_softc *sc, uint8_t data)
+{
+
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_WRITE);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ BT3C_IOR_CNTL, data);
+}
+
+static inline void
+bt3c_set_address(struct bt3c_softc *sc, uint16_t addr)
+{
+
+ bus_space_barrier(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ 0, BT3C_IOR_LEN, BUS_SPACE_BARRIER_WRITE);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ BT3C_IOR_ADDR_L, addr & 0xff);
+ bus_space_write_1(sc->sc_pcioh.iot, sc->sc_pcioh.ioh,
+ BT3C_IOR_ADDR_H, (addr >> 8) & 0xff);
+}
+
+static inline uint16_t
+bt3c_read(struct bt3c_softc *sc, uint16_t addr)
+{
+
+ bt3c_set_address(sc, addr);
+ return bt3c_get(sc);
+}
+
+static inline void
+bt3c_write(struct bt3c_softc *sc, uint16_t addr, uint16_t data)
+{
+
+ bt3c_set_address(sc, addr);
+ bt3c_put(sc, data);
+}
+
+/*
+ * receive incoming data from device, store in mbuf chain and
+ * pass on complete packets to bt device
+ */
+static void
+bt3c_receive(struct bt3c_softc *sc)
+{
+ struct mbuf *m = sc->sc_rxp;
+ int space = 0;
+ uint16_t count;
+ uint8_t b;
+
+ /*
+ * If we already started a packet, find the
+ * trailing end of it.
+ */
+ if (m) {
+ while (m->m_next)
+ m = m->m_next;
+
+ space = M_TRAILINGSPACE(m);
+ }
+
+ count = bt3c_read(sc, BT3C_RX_COUNT);
+ bt3c_set_address(sc, BT3C_RX_FIFO);
+
+ while (count > 0) {
+ if (space == 0) {
+ if (m == NULL) {
+ /* new packet */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ printf("%s: out of memory\n",
+ sc->sc_dev.dv_xname);
+ ++sc->sc_unit.hci_stats.err_rx;
+ goto out; /* (lost sync) */
+ }
+
+ sc->sc_rxp = m;
+ m->m_pkthdr.len = m->m_len = 0;
+ space = MHLEN;
+
+ sc->sc_state = BT3C_RECV_PKT_TYPE;
+ sc->sc_want = 1;
+ } else {
+ /* extend mbuf */
+ MGET(m->m_next, M_DONTWAIT, MT_DATA);
+ if (m->m_next == NULL) {
+ printf("%s: out of memory\n",
+ sc->sc_dev.dv_xname);
+ ++sc->sc_unit.hci_stats.err_rx;
+ goto out; /* (lost sync) */
+ }
+
+ m = m->m_next;
+ m->m_len = 0;
+ space = MLEN;
+
+ if (sc->sc_want > MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT)
+ space = MCLBYTES;
+ }
+ }
+ }
+
+ b = bt3c_get(sc);
+ mtod(m, uint8_t *)[m->m_len++] = b;
+ count--;
+ space--;
+ sc->sc_rxp->m_pkthdr.len++;
+ sc->sc_unit.hci_stats.byte_rx++;
+
+ sc->sc_want--;
+ if (sc->sc_want > 0)
+ continue; /* want more */
+
+ switch (sc->sc_state) {
+ case BT3C_RECV_PKT_TYPE: /* Got packet type */
+
+ switch (b) {
+ case HCI_ACL_DATA_PKT:
+ sc->sc_state = BT3C_RECV_ACL_HDR;
+ sc->sc_want = sizeof(hci_acldata_hdr_t) - 1;
+ break;
+
+ case HCI_SCO_DATA_PKT:
+ sc->sc_state = BT3C_RECV_SCO_HDR;
+ sc->sc_want = sizeof(hci_scodata_hdr_t) - 1;
+ break;
+
+ case HCI_EVENT_PKT:
+ sc->sc_state = BT3C_RECV_EVENT_HDR;
+ sc->sc_want = sizeof(hci_event_hdr_t) - 1;
+ break;
+
+ default:
+ printf("%s: Unknown packet type=%#x!\n",
+ sc->sc_dev.dv_xname, b);
+ ++sc->sc_unit.hci_stats.err_rx;
+ m_freem(sc->sc_rxp);
+ sc->sc_rxp = NULL;
+ goto out; /* (lost sync) */
+ }
+
+ break;
+
+ /*
+ * we assume (correctly of course :) that the packet headers
+ * all fit into a single pkthdr mbuf
+ */
+ case BT3C_RECV_ACL_HDR: /* Got ACL Header */
+ sc->sc_state = BT3C_RECV_ACL_DATA;
+ sc->sc_want = mtod(m, hci_acldata_hdr_t *)->length;
+ sc->sc_want = le16toh(sc->sc_want);
+ break;
+
+ case BT3C_RECV_SCO_HDR: /* Got SCO Header */
+ sc->sc_state = BT3C_RECV_SCO_DATA;
+ sc->sc_want = mtod(m, hci_scodata_hdr_t *)->length;
+ break;
+
+ case BT3C_RECV_EVENT_HDR: /* Got Event Header */
+ sc->sc_state = BT3C_RECV_EVENT_DATA;
+ sc->sc_want = mtod(m, hci_event_hdr_t *)->length;
+ break;
+
+ case BT3C_RECV_ACL_DATA: /* ACL Packet Complete */
+ hci_input_acl(&sc->sc_unit, sc->sc_rxp);
+ sc->sc_unit.hci_stats.acl_rx++;
+ sc->sc_rxp = m = NULL;
+ space = 0;
+ break;
+
+ case BT3C_RECV_SCO_DATA: /* SCO Packet Complete */
+ hci_input_sco(&sc->sc_unit, sc->sc_rxp);
+ sc->sc_unit.hci_stats.sco_rx++;
+ sc->sc_rxp = m = NULL;
+ space = 0;
+ break;
+
+ case BT3C_RECV_EVENT_DATA: /* Event Packet Complete */
+ sc->sc_unit.hci_stats.evt_rx++;
+ hci_input_event(&sc->sc_unit, sc->sc_rxp);
+ sc->sc_rxp = m = NULL;
+ space = 0;
+ break;
+
+ default:
+ panic("%s: invalid state %d!\n",
+ sc->sc_dev.dv_xname, sc->sc_state);
+ }
+ }
+
+out:
+ bt3c_write(sc, BT3C_RX_COUNT, 0x0000);
+}
+
+/*
+ * write data from current packet to Transmit FIFO.
+ * restart when done.
+ */
+static void
+bt3c_transmit(struct bt3c_softc *sc)
+{
+ struct mbuf *m;
+ int count, rlen;
+ uint8_t *rptr;
+
+ m = sc->sc_txp;
+ if (m == NULL) {
+ sc->sc_unit.hci_flags &= ~BTF_XMIT;
+ bt3c_start(&sc->sc_unit);
+ return;
+ }
+
+ count = 0;
+ rlen = 0;
+ rptr = mtod(m, uint8_t *);
+
+ bt3c_set_address(sc, BT3C_TX_FIFO);
+
+ for(;;) {
+ if (rlen >= m->m_len) {
+ m = m->m_next;
+ if (m == NULL) {
+ m = sc->sc_txp;
+ sc->sc_txp = NULL;
+
+ if (M_GETCTX(m, void *) == NULL)
+ m_freem(m);
+ else
+ hci_complete_sco(&sc->sc_unit, m);
+
+ break;
+ }
+
+ rlen = 0;
+ rptr = mtod(m, uint8_t *);
+ continue;
+ }
+
+ if (count >= BT3C_FIFO_SIZE) {
+ m_adj(m, rlen);
+ break;
+ }
+
+ bt3c_put(sc, *rptr++);
+ rlen++;
+ count++;
+ }
+
+ bt3c_write(sc, BT3C_TX_COUNT, count);
+ sc->sc_unit.hci_stats.byte_tx += count;
+}
+
+/*
+ * interrupt routine
+ */
+static int
+bt3c_intr(void *arg)
+{
+ struct bt3c_softc *sc = arg;
+ uint16_t control, isr;
+
+ control = bt3c_read_control(sc);
+ if (control & BT3C_IOR_CNTL_INTR) {
+ isr = bt3c_read(sc, BT3C_ISR);
+ if ((isr & 0xff) == 0x7f) {
+ printf("%s: bt3c_intr got strange ISR=%04x\n",
+ sc->sc_dev.dv_xname, isr);
+ } else if ((isr & 0xff) != 0xff) {
+
+ if (isr & BT3C_ISR_RXRDY)
+ bt3c_receive(sc);
+
+ if (isr & BT3C_ISR_TXRDY)
+ bt3c_transmit(sc);
+
+#ifdef DIAGNOSTIC
+ if (isr & BT3C_ISR_ANTENNA) {
+ if (bt3c_read(sc, BT3C_CSR) & BT3C_CSR_ANTENNA)
+ printf("%s: Antenna Out\n",
+ sc->sc_dev.dv_xname);
+ else
+ printf("%s: Antenna In\n",
+ sc->sc_dev.dv_xname);
+ }
+#endif
+
+ bt3c_write(sc, BT3C_ISR, 0x0000);
+ bt3c_write_control(sc, control);
+
+ return 1; /* handled */
+ }
+ }
+
+ return 0; /* not handled */
+}
+
+/*
+ * load firmware for the device
+ *
+ * The firmware file is a plain ASCII file containing lines in the format:
+ *
+ * S<Digit><Len><Address><Data1><Data2>...<DataN><Checksum>
+ *
+ * <Digit>: 0 start ?
+ * 3 data line
+ * 7 finish ?
+ *
+ * <Len>: 1 byte, and is the number of bytes in the rest of the line
+ * <Address>: 4 byte address (only 2 bytes are valid for bt3c I think)
+ * <Data>: 2 byte data word to be written to the address
+ * <Checksum>: checksum of all bytes in the line including <Len>
+ *
+ * all bytes are in hexadecimal
+ */
+static inline int32_t
+hex(const uint8_t *p, int n)
+{
+ uint32_t val = 0;
+
+ while (n > 0) {
+ val <<= 4;
+
+ if ('0' <= *p && *p <= '9')
+ val += (*p - '0');
+ else if ('a' <= *p && *p <= 'f')
+ val += (*p - 'a' + 0xa);
+ else if ('A' <= *p && *p <= 'F')
+ val += (*p - 'A' + 0xa);
+ else
+ return -1;
+
+ p++;
+ n--;
+ }
+
+ return val;
+}
+
+static int
+bt3c_load_firmware(struct bt3c_softc *sc)
+{
+ uint8_t *buf, *line, *next, *p;
+ int32_t addr, data;
+ int err, sum, len;
+ firmware_handle_t fh;
+ size_t size;
+
+ err = firmware_open(sc->sc_dev.dv_cfdata->cf_name,
+ BT3C_FIRMWARE_FILE, &fh);
+ if (err) {
+ printf("%s: Cannot open firmware %s/%s\n", sc->sc_dev.dv_xname,
+ sc->sc_dev.dv_cfdata->cf_name, BT3C_FIRMWARE_FILE);
+ return err;
+ }
+
+ size = (size_t)firmware_get_size(fh);
+#ifdef DIAGNOSTIC
+ if (size < 0 || size > 10 * 1024) { /* sanity check */
+ printf("%s: firmware file seems WAY too big!\n",
+ sc->sc_dev.dv_xname);
+ return EFBIG;
+ }
+#endif
+
+ buf = firmware_malloc(size);
+ KASSERT(buf != NULL);
+
+ err = firmware_read(fh, 0, buf, size);
+ if (err) {
+ printf("%s: Firmware read failed (%d)\n",
+ sc->sc_dev.dv_xname, err);
+ goto out;
+ }
+
+ /* Reset */
+ bt3c_write(sc, 0x8040, 0x0404);
+ bt3c_write(sc, 0x8040, 0x0400);
+ DELAY(1);
+ bt3c_write(sc, 0x8040, 0x0404);
+ DELAY(17);
+
+ next = buf;
+ err = EFTYPE;
+
+ while (next < buf + size) {
+ line = next;
+
+ while (*next != '\r' && *next != '\n') {
+ if (next >= buf + size)
+ goto out;
+
+ next++;
+ }
+
+ /* 14 covers address and checksum minimum */
+ if (next - line < 14)
+ goto out;
+
+ if (line[0] != 'S')
+ goto out;
+
+ /* verify line length */
+ len = hex(line + 2, 2);
+ if (len < 0 || next - line != len * 2 + 4)
+ goto out;
+
+ /* checksum the line */
+ sum = 0;
+ for (p = line + 2 ; p < next ; p += 2)
+ sum += hex(p, 2);
+
+ if ((sum & 0xff) != 0xff)
+ goto out;
+
+ /* extract relevant data */
+ switch (line[1]) {
+ case '0':
+ /* I dont know what this is */
+ break;
+
+ case '3':
+ /* find number of data words */
+ len = (len - 5) / 2;
+ if (len > 15)
+ goto out;
+
+ addr = hex(line + 8, 4);
+ if (addr < 0)
+ goto out;
+
+ bt3c_set_address(sc, addr);
+
+ for (p = line + 12 ; p + 4 < next ; p += 4) {
+ data = hex(p, 4);
+ if (data < 0)
+ goto out;
+
+ bt3c_put(sc, data);
+ }
+ break;
+
+ case '7':
+ /* I dont know what this is */
+ break;
+
+ default:
+ goto out;
+ }
+
+ /* skip to start of next line */
+ while (next < buf + size && (*next == '\r' || *next == '\n'))
+ next++;
+ }
+
+ err = 0;
+ DELAY(17);
+
+ /* Boot */
+ bt3c_set_address(sc, 0x3000);
+ bt3c_write_control(sc, (bt3c_read_control(sc) | BT3C_IOR_CNTL_BOOT));
+ DELAY(17);
+
+ /* Clear Registers */
+ bt3c_write(sc, BT3C_RX_COUNT, 0x0000);
+ bt3c_write(sc, BT3C_TX_COUNT, 0x0000);
+ bt3c_write(sc, BT3C_ISR, 0x0000);
+ DELAY(1000);
+
+out:
+ firmware_free(buf, size);
+ firmware_close(fh);
+ return err;
+}
+
+/**************************************************************************
+ *
+ * bt device callbacks (all called at IPL_TTY)
+ */
+
+/*
+ * start sending on bt3c
+ * this should be called only when BTF_XMIT is not set, and
+ * we only send cmd packets that are clear to send
+ */
+static void
+bt3c_start(struct hci_unit *unit)
+{
+ struct bt3c_softc *sc = unit->hci_softc;
+ struct mbuf *m;
+
+ KASSERT((unit->hci_flags & BTF_XMIT) == 0);
+ KASSERT(sc->sc_txp == NULL);
+
+ if (MBUFQ_FIRST(&unit->hci_cmdq)) {
+ MBUFQ_DEQUEUE(&unit->hci_cmdq, m);
+ unit->hci_stats.cmd_tx++;
+ M_SETCTX(m, NULL);
+ goto start;
+ }
+
+ if (MBUFQ_FIRST(&unit->hci_scotxq)) {
+ MBUFQ_DEQUEUE(&unit->hci_scotxq, m);
+ unit->hci_stats.sco_tx++;
+ goto start;
+ }
+
+ if (MBUFQ_FIRST(&unit->hci_acltxq)) {
+ MBUFQ_DEQUEUE(&unit->hci_acltxq, m);
+ unit->hci_stats.acl_tx++;
+ M_SETCTX(m, NULL);
+ goto start;
+ }
+
+ /* Nothing to send */
+ return;
+
+start:
+ sc->sc_txp = m;
+ unit->hci_flags |= BTF_XMIT;
+ bt3c_transmit(sc);
+}
+
+/*
+ * enable device
+ * turn on card
+ * load firmware
+ * establish interrupts
+ */
+static int
+bt3c_enable(struct hci_unit *unit)
+{
+ struct bt3c_softc *sc = unit->hci_softc;
+ int err;
+
+ if (unit->hci_flags & BTF_RUNNING)
+ return 0;
+
+ sc->sc_intr = pcmcia_intr_establish(sc->sc_pf, IPL_TTY, bt3c_intr, sc);
+ if (sc->sc_intr == NULL) {
+ err = EIO;
+ goto bad;
+ }
+
+ err = pcmcia_function_enable(sc->sc_pf);
+ if (err)
+ goto bad1;
+
+ err = bt3c_load_firmware(sc);
+ if (err)
+ goto bad2;
+
+ unit->hci_flags |= BTF_RUNNING;
+ unit->hci_flags &= ~BTF_XMIT;
+
+ /*
+ * 3Com card will send a Command_Status packet when its
+ * ready to receive commands
+ */
+ unit->hci_num_cmd_pkts = 0;
+
+ return 0;
+
+bad2:
+ pcmcia_function_disable(sc->sc_pf);
+bad1:
+ pcmcia_intr_disestablish(sc->sc_pf, sc->sc_intr);
+ sc->sc_intr = NULL;
+bad:
+ return err;
+}
+
+/*
+ * disable device
+ * shut down card
+ * disestablish interrupts
+ * free held packets
+ */
+static void
+bt3c_disable(struct hci_unit *unit)
+{
+ struct bt3c_softc *sc = unit->hci_softc;
+
+ if ((unit->hci_flags & BTF_RUNNING) == 0)
+ return;
+
+ pcmcia_function_disable(sc->sc_pf);
+
+ if (sc->sc_intr) {
+ pcmcia_intr_disestablish(sc->sc_pf, sc->sc_intr);
+ sc->sc_intr = NULL;
+ }
+
+ if (sc->sc_rxp) {
+ m_freem(sc->sc_rxp);
+ sc->sc_rxp = NULL;
+ }
+
+ if (sc->sc_txp) {
+ m_freem(sc->sc_txp);
+ sc->sc_txp = NULL;
+ }
+
+ unit->hci_flags &= ~BTF_RUNNING;
+}
+
+/**************************************************************************
+ *
+ * bt3c PCMCIA autoconfig glue
+ */
+
+static int
+bt3c_match(struct device *parent, struct cfdata *match, void *aux)
+{
+ struct pcmcia_attach_args *pa = aux;
+
+ if (pa->manufacturer == PCMCIA_VENDOR_3COM &&
+ pa->product == PCMCIA_PRODUCT_3COM_3CRWB6096)
+ return 10; /* 'com' also claims this, so trump them */
+
+ return 0;
+}
+
+static void
+bt3c_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct bt3c_softc *sc = (struct bt3c_softc *)self;
+ struct pcmcia_attach_args *pa = aux;
+ struct pcmcia_config_entry *cfe;
+
+ sc->sc_pf = pa->pf;
+
+ /* Find a PCMCIA config entry we can use */
+ SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) {
+ if (cfe->num_memspace != 0)
+ continue;
+
+ if (cfe->num_iospace != 1)
+ continue;
+
+ if (pcmcia_io_alloc(pa->pf, cfe->iospace[0].start,
+ cfe->iospace[0].length, 0, &sc->sc_pcioh) == 0)
+ break;
+ }
+
+ if (cfe == 0) {
+ aprint_error("bt3c_attach: cannot allocate io space\n");
+ goto no_config_entry;
+ }
+
+ /* Initialise it */
+ pcmcia_function_init(pa->pf, cfe);
+
+ /* Map in the io space */
+ if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO,
+ &sc->sc_pcioh, &sc->sc_iow)) {
+ aprint_error("bt3c_attach: cannot map io space\n");
+ goto iomap_failed;
+ }
+
+ /* Attach Bluetooth unit */
+ sc->sc_unit.hci_softc = sc;
+ sc->sc_unit.hci_devname = sc->sc_dev.dv_xname;
+ sc->sc_unit.hci_enable = bt3c_enable;
+ sc->sc_unit.hci_disable = bt3c_disable;
+ sc->sc_unit.hci_start_cmd = bt3c_start;
+ sc->sc_unit.hci_start_acl = bt3c_start;
+ sc->sc_unit.hci_start_sco = bt3c_start;
+ sc->sc_unit.hci_ipl = IPL_TTY;
+ hci_attach(&sc->sc_unit);
+
+ /* establish a power change hook */
+ sc->sc_powerhook = powerhook_establish(bt3c_power, sc);
+ return;
+
+iomap_failed:
+ /* unmap io space */
+ pcmcia_io_free(pa->pf, &sc->sc_pcioh);
+
+no_config_entry:
+ sc->sc_iow = -1;
+}
+
+static int
+bt3c_detach(struct device *self, int flags)
+{
+ struct bt3c_softc *sc = (struct bt3c_softc *)self;
+ int err = 0;
+
+ bt3c_disable(&sc->sc_unit);
+
+ if (sc->sc_powerhook) {
+ powerhook_disestablish(sc->sc_powerhook);
+ sc->sc_powerhook = NULL;
+ }
+
+ hci_detach(&sc->sc_unit);
+
+ if (sc->sc_iow != -1) {
+ pcmcia_io_unmap(sc->sc_pf, sc->sc_iow);
+ pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh);
+ sc->sc_iow = -1;
+ }
+
+ return err;
+}
+
+static int
+bt3c_activate(struct device *self, enum devact act)
+{
+ // struct bt3c_softc *sc = (struct bt3c_softc *)self;
+ int err = 0;
+
+ switch(act) {
+ case DVACT_ACTIVATE:
+ err = EOPNOTSUPP;
+ break;
+
+ case DVACT_DEACTIVATE:
+ // could notify unit somehow?
+ break;
+ }
+
+ return err;
+}
+
+static void
+bt3c_power(int why, void *arg)
+{
+ struct bt3c_softc *sc = arg;
+
+ switch(why) {
+ case PWR_SUSPEND:
+ case PWR_STANDBY:
+ if (sc->sc_unit.hci_flags & BTF_RUNNING) {
+ printf_nolog("%s: sleeping\n", sc->sc_dev.dv_xname);
+ bt3c_disable(&sc->sc_unit);
+ sc->sc_flags |= BT3C_SLEEPING;
+ }
+ break;
+
+ case PWR_RESUME:
+ if (sc->sc_flags & BT3C_SLEEPING) {
+ printf_nolog("%s: waking up\n", sc->sc_dev.dv_xname);
+ bt3c_enable(&sc->sc_unit);
+ sc->sc_flags &= ~BT3C_SLEEPING;
+ }
+ break;
+
+ case PWR_SOFTSUSPEND:
+ case PWR_SOFTSTANDBY:
+ case PWR_SOFTRESUME:
+ break;
+ }
+}
diff --git a/sys/dev/pcmcia/files.pcmcia b/sys/dev/pcmcia/files.pcmcia
index e0cb389c25f..bf6a9b6dfd5 100644
--- a/sys/dev/pcmcia/files.pcmcia
+++ b/sys/dev/pcmcia/files.pcmcia
@@ -1,4 +1,4 @@
-# $NetBSD: files.pcmcia,v 1.48 2006/04/26 16:19:52 rpaulo Exp $
+# $NetBSD: files.pcmcia,v 1.49 2006/06/19 15:44:45 gdamore Exp $
#
# Config.new file and device description for machine-independent PCMCIA code.
# Included by ports that need it.
@@ -141,3 +141,7 @@ file dev/pcmcia/esl.c esl
attach opl at esl with opl_esl
file dev/pcmcia/opl_esl.c opl_esl
+# 3Com Bluetooth controller 3CRW6096
+device bt3c { }: bluetooth, firmload
+attach bt3c at pcmcia
+file dev/pcmcia/bt3c.c bt3c needs-flag
diff --git a/sys/dev/pcmcia/pcmciadevs b/sys/dev/pcmcia/pcmciadevs
index dde4804575f..0191018c132 100644
--- a/sys/dev/pcmcia/pcmciadevs
+++ b/sys/dev/pcmcia/pcmciadevs
@@ -1,4 +1,4 @@
-$NetBSD: pcmciadevs,v 1.215 2006/03/23 02:14:42 christos Exp $
+$NetBSD: pcmciadevs,v 1.216 2006/06/19 15:44:45 gdamore Exp $
/* $FreeBSD: src/sys/dev/pccard/pccarddevs,v 1.20 2001/11/19 05:02:55 imp Exp $*/
/*-
@@ -127,6 +127,7 @@ product 3COM 3CRWE737A 0x0001 3Com AirConnect Wireless LAN
product 3COM 3CXM056BNW 0x002f 3Com/NoteWorthy 3CXM056-BNW 56K Modem
product 3COM 3CXEM556 0x0035 3Com/Megahertz 3CXEM556 Ethernet/Modem
product 3COM 3CXEM556INT 0x003d 3Com/Megahertz 3CXEM556-INT Ethernet/Modem
+product 3COM 3CRWB6096 0x0040 3Com 3CRWB60-A Bluetooth PC Card
product 3COM 3CCFEM556BI 0x0556 3Com/Megahertz 3CCFEM556BI Ethernet/Modem
product 3COM 3C562 0x0562 3Com 3c562 33.6 Modem/10Mbps Ethernet
product 3COM 3C589 0x0589 3Com 3c589 10Mbps Ethernet
diff --git a/sys/dev/usb/FILES b/sys/dev/usb/FILES
index aeaae64cc38..0b8bb34171a 100644
--- a/sys/dev/usb/FILES
+++ b/sys/dev/usb/FILES
@@ -26,6 +26,8 @@ ohcireg.h Hardware definitions for OHCI
ohcivar.h API for ohci.c
uaudio.c USB audio class driver
uaudioreg.h and definitions for it
+ubt.c USB Bluetooth driver
+ubtbcmfw.c Firmware loader for Broadcom BCM2033 Bluetooth devices
ucom.c TTY emulation for two bulk pipes
ucomvar.h TTY emulation interface
uftdi.c USB FTDI serial driver
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index f125cbc22a1..f866bba82e0 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -1,4 +1,4 @@
-# $NetBSD: files.usb,v 1.66 2006/04/03 08:15:48 scw Exp $
+# $NetBSD: files.usb,v 1.67 2006/06/19 15:44:45 gdamore Exp $
#
# Config file and device description for machine-independent USB code.
# Included by ports that need it. Ports that use it must provide
@@ -76,7 +76,7 @@ defparam UKBD_LAYOUT
device ukbd: hid, wskbddev
attach ukbd at uhidbus
file dev/usb/ukbd.c ukbd needs-flag
-file dev/usb/ukbdmap.c ukbd
+file dev/usb/ukbdmap.c ukbd | btkbd
# Mice
device ums: hid, wsmousedev
@@ -119,8 +119,8 @@ attach ustir at uhub
file dev/usb/ustir.c ustir
-# Bluetooth bridges
-device ubt: btbus
+# Bluetooth controllers
+device ubt: bluetooth
attach ubt at uhub
file dev/usb/ubt.c ubt
diff --git a/sys/dev/usb/hid.c b/sys/dev/usb/hid.c
index 9378992b548..f3071647521 100644
--- a/sys/dev/usb/hid.c
+++ b/sys/dev/usb/hid.c
@@ -1,4 +1,4 @@
-/* $NetBSD: hid.c,v 1.24 2006/04/14 17:18:59 christos Exp $ */
+/* $NetBSD: hid.c,v 1.25 2006/06/19 15:44:45 gdamore Exp $ */
/* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */
/*
@@ -39,7 +39,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hid.c,v 1.24 2006/04/14 17:18:59 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hid.c,v 1.25 2006/06/19 15:44:45 gdamore Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -461,6 +461,25 @@ hid_get_data(u_char *buf, struct hid_location *loc)
return (data);
}
+/*
+ * hid_is_collection(desc, size, id, usage)
+ *
+ * This function is broken in the following way.
+ *
+ * It used to discover if the given 'id' is part of 'usage' collection
+ * in the descriptor in order to match report id against device type.
+ *
+ * The semantics of hid_start_parse() means though, that only a single
+ * kind of report is considered. The current HID code that uses this for
+ * matching is actually only looking for input reports, so this works
+ * for now.
+ *
+ * This function could try all report kinds (input, output and feature)
+ * consecutively if necessary, but it may be better to integrate the
+ * libusbhid code which can consider multiple report kinds simultaneously
+ *
+ * Needs some thought.
+ */
int
hid_is_collection(void *desc, int size, u_int8_t id, u_int32_t usage)
{
@@ -468,7 +487,7 @@ hid_is_collection(void *desc, int size, u_int8_t id, u_int32_t usage)
struct hid_item hi;
u_int32_t coll_usage = ~0;
- hd = hid_start_parse(desc, size, hid_none);
+ hd = hid_start_parse(desc, size, hid_input);
if (hd == NULL)
return (0);
@@ -477,10 +496,15 @@ hid_is_collection(void *desc, int size, u_int8_t id, u_int32_t usage)
DPRINTFN(2,("hid_is_collection: kind=%d id=%d usage=0x%x"
"(0x%x)\n",
hi.kind, hi.report_ID, hi.usage, coll_usage));
+
if (hi.kind == hid_collection &&
hi.collection == HCOLL_APPLICATION)
coll_usage = hi.usage;
- if (hi.kind == hid_endcollection &&
+
+ if (hi.kind == hid_endcollection)
+ coll_usage = ~0;
+
+ if (hi.kind == hid_input &&
coll_usage == usage &&
hi.report_ID == id) {
DPRINTFN(2,("hid_is_collection: found\n"));
diff --git a/sys/dev/usb/ubt.c b/sys/dev/usb/ubt.c
index 811b2abc439..a353464e07e 100644
--- a/sys/dev/usb/ubt.c
+++ b/sys/dev/usb/ubt.c
@@ -1,5 +1,35 @@
-/* $NetBSD: ubt.c,v 1.13 2005/12/11 12:24:01 christos Exp $ */
+/* $NetBSD: ubt.c,v 1.14 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.
+ */
/*
* Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
* All rights reserved.
@@ -36,292 +66,486 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
+/*
+ * This driver originally written by Lennart Augustsson and David Sainty,
+ * but was mostly rewritten for the NetBSD Bluetooth protocol stack by
+ * Iain Hibbert for Itronix, Inc using the FreeBSD ng_ubt.c driver as a
+ * reference.
+ */
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ubt.c,v 1.13 2005/12/11 12:24:01 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ubt.c,v 1.14 2006/06/19 15:44:45 gdamore Exp $");
#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/malloc.h>
#include <sys/device.h>
-#include <sys/lock.h>
#include <sys/ioctl.h>
-#include <sys/conf.h>
-#include <sys/file.h>
-#include <sys/poll.h>
-#include <sys/select.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
-#include <dev/usb/ubtreg.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbdevs.h>
-#include <dev/bluetooth/bluetooth.h>
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+
+/*******************************************************************************
+ *
+ * debugging stuff
+ */
+#undef DPRINTF
+#undef DPRINTFN
#ifdef UBT_DEBUG
-#define DPRINTF(x) if (ubtdebug) logprintf x
-#define DPRINTFN(n,x) if (ubtdebug>(n)) logprintf x
-int ubtdebug = 49;
+int ubt_debug = UBT_DEBUG;
+
+#define DPRINTF(fmt, args...) do { \
+ if (ubt_debug) \
+ printf("%s: "fmt, __func__ , ##args); \
+} while (/* CONSTCOND */0)
+
+#define DPRINTFN(n, fmt, args...) do { \
+ if (ubt_debug > (n)) \
+ printf("%s: "fmt, __func__ , ##args); \
+} while (/* CONSTCOND */0)
+
+SYSCTL_SETUP(sysctl_hw_ubt_debug_setup, "sysctl hw.ubt_debug setup")
+{
+
+ sysctl_createv(NULL, 0, NULL, NULL,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "hw",
+ NULL,
+ NULL, 0,
+ NULL, 0,
+ CTL_HW, CTL_EOL);
+
+ sysctl_createv(NULL, 0, NULL, NULL,
+ CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
+ CTLTYPE_INT, "ubt_debug",
+ SYSCTL_DESCR("ubt debug level"),
+ NULL, 0,
+ &ubt_debug, sizeof(ubt_debug),
+ CTL_HW, CTL_CREATE, CTL_EOL);
+}
#else
-#define DPRINTF(x)
-#define DPRINTFN(n,x)
+#define DPRINTF(...)
+#define DPRINTFN(...)
#endif
+/*******************************************************************************
+ *
+ * ubt softc structure
+ *
+ */
+
+/* buffer sizes */
+/*
+ * NB: although ACL packets can extend to 65535 bytes, most devices
+ * have max_acl_size at much less (largest I have seen is 384)
+ */
+#define UBT_BUFSIZ_CMD (HCI_CMD_PKT_SIZE - 1)
+#define UBT_BUFSIZ_ACL (2048 - 1)
+#define UBT_BUFSIZ_EVENT (HCI_EVENT_PKT_SIZE - 1)
+
+/* Interrupt Interval from (Bluetooth spec) */
+#define UBT_EVENT_INTERVAL 1 /* 1ms */
+
+/* Transmit timeouts */
+#define UBT_CMD_TIMEOUT USBD_DEFAULT_TIMEOUT
+#define UBT_ACL_TIMEOUT USBD_DEFAULT_TIMEOUT
+
/*
- * Protocol related definitions
+ * ISOC transfers
+ *
+ * xfer buffer size depends on the frame size, and the number
+ * of frames per transfer is fixed, as each frame should be
+ * 1ms worth of data. This keeps the rate that xfers complete
+ * fairly constant. We use multiple xfers to keep the hardware
+ * busy
*/
+#define UBT_NXFERS 3 /* max xfers to queue */
+#define UBT_NFRAMES 10 /* frames per xfer */
+
+struct ubt_isoc_xfer {
+ struct ubt_softc *softc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ uint16_t size[UBT_NFRAMES];
+ int busy;
+};
struct ubt_softc {
- USBBASEDEVICE sc_dev;
- usbd_device_handle sc_udev;
- usbd_interface_handle sc_ctl_iface;
- usbd_interface_handle sc_isoc_iface;
+ USBBASEDEVICE sc_dev;
+ usbd_device_handle sc_udev;
+ int sc_refcnt;
+ int sc_dying;
- /* Control */
- usbd_pipe_handle sc_ctl_pipe;
- usbd_xfer_handle sc_ctl_xfer;
- u_int8_t *sc_ctl_buf;
+ /* Control Interface */
+ usbd_interface_handle sc_iface0;
- /* Events */
- int sc_evt_addr;
- usbd_pipe_handle sc_evt_pipe;
- usbd_xfer_handle sc_evt_xfer;
- u_int8_t *sc_evt_buf;
+ /* Commands (control) */
+ usbd_xfer_handle sc_cmd_xfer;
+ uint8_t *sc_cmd_buf;
+
+ /* Events (interrupt) */
+ int sc_evt_addr; /* endpoint address */
+ usbd_pipe_handle sc_evt_pipe;
+ uint8_t *sc_evt_buf;
/* ACL data (in) */
- int sc_aclrd_addr;
- usbd_pipe_handle sc_aclrd_pipe;
- usbd_xfer_handle sc_aclrd_xfer;
- u_int8_t *sc_aclrd_buf;
- int sc_aclrd_running;
+ int sc_aclrd_addr; /* endpoint address */
+ usbd_pipe_handle sc_aclrd_pipe; /* read pipe */
+ usbd_xfer_handle sc_aclrd_xfer; /* read xfer */
+ uint8_t *sc_aclrd_buf; /* read buffer */
+ int sc_aclrd_busy; /* reading */
/* ACL data (out) */
- int sc_aclwr_addr;
- usbd_pipe_handle sc_aclwr_pipe;
- usbd_xfer_handle sc_aclwr_xfer;
- u_int8_t *sc_aclwr_buf;
+ int sc_aclwr_addr; /* endpoint address */
+ usbd_pipe_handle sc_aclwr_pipe; /* write pipe */
+ usbd_xfer_handle sc_aclwr_xfer; /* write xfer */
+ uint8_t *sc_aclwr_buf; /* write buffer */
+
+ /* ISOC interface */
+ usbd_interface_handle sc_iface1; /* ISOC interface */
+ struct sysctllog *sc_log; /* sysctl log */
+ int sc_config; /* current config no */
+ int sc_alt_config; /* no of alternates */
+
+ /* SCO data (in) */
+ int sc_scord_addr; /* endpoint address */
+ usbd_pipe_handle sc_scord_pipe; /* read pipe */
+ int sc_scord_size; /* frame length */
+ struct ubt_isoc_xfer sc_scord[UBT_NXFERS];
+ struct mbuf *sc_scord_mbuf; /* current packet */
+
+ /* SCO data (out) */
+ int sc_scowr_addr; /* endpoint address */
+ usbd_pipe_handle sc_scowr_pipe; /* write pipe */
+ int sc_scowr_size; /* frame length */
+ struct ubt_isoc_xfer sc_scowr[UBT_NXFERS];
+ struct mbuf *sc_scowr_mbuf; /* current packet */
+
+ /* Protocol structure */
+ struct hci_unit sc_unit;
+};
- struct device *sc_child;
- struct btframe_callback_methods const *sc_cb;
+/*******************************************************************************
+ *
+ * Bluetooth unit/USB callback routines
+ *
+ */
+static int ubt_enable(struct hci_unit *);
+static void ubt_disable(struct hci_unit *);
- int sc_refcnt;
- char sc_dying;
-};
+static void ubt_xmit_cmd_start(struct hci_unit *);
+static void ubt_xmit_cmd_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
-static int ubt_open(void *h, int flag, int mode, usb_proc_ptr p);
-static int ubt_close(void *h, int flag, int mode, usb_proc_ptr p);
+static void ubt_xmit_acl_start(struct hci_unit *);
+static void ubt_xmit_acl_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
-static u_int8_t* ubt_alloc_control(void*, size_t, struct btframe_buffer**);
-static int ubt_send_control(void*, struct btframe_buffer*, size_t);
+static void ubt_xmit_sco_start(struct hci_unit *);
+static void ubt_xmit_sco_start1(struct ubt_softc *, struct ubt_isoc_xfer *);
+static void ubt_xmit_sco_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
-static u_int8_t* ubt_alloc_acldata(void*, size_t, struct btframe_buffer**);
-static int ubt_send_acldata(void*, struct btframe_buffer*, size_t);
+static void ubt_recv_event(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
-static u_int8_t* ubt_alloc_scodata(void*, size_t, struct btframe_buffer**);
-static int ubt_send_scodata(void*, struct btframe_buffer*, size_t);
+static void ubt_recv_acl_start(struct ubt_softc *);
+static void ubt_recv_acl_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
-static int ubt_splraise(void);
+static void ubt_recv_sco_start1(struct ubt_softc *, struct ubt_isoc_xfer *);
+static void ubt_recv_sco_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
-static void ubt_event_cb(usbd_xfer_handle, usbd_private_handle, usbd_status);
-static void ubt_aclrd_cb(usbd_xfer_handle, usbd_private_handle, usbd_status);
-static void ubt_aclrd_request(struct ubt_softc *);
-static struct btframe_methods const ubt_methods = {
- ubt_open, ubt_close,
- {ubt_alloc_control, ubt_send_control},
- {ubt_alloc_acldata, ubt_send_acldata},
- {ubt_alloc_scodata, ubt_send_scodata},
- ubt_splraise
-};
+/*******************************************************************************
+ *
+ * USB Autoconfig stuff
+ *
+ */
USB_DECLARE_DRIVER(ubt);
+static int ubt_set_isoc_config(struct ubt_softc *);
+static int ubt_sysctl_config(SYSCTLFN_PROTO);
+static void ubt_abortdealloc(struct ubt_softc *);
+
USB_MATCH(ubt)
{
USB_MATCH_START(ubt, uaa);
usb_interface_descriptor_t *id;
- DPRINTFN(50,("ubt_match\n"));
+ DPRINTFN(50, "ubt_match\n");
if (uaa->iface == NULL)
- return (UMATCH_NONE);
+ return UMATCH_NONE;
id = usbd_get_interface_descriptor(uaa->iface);
- if (id != NULL &&
- id->bInterfaceClass == UICLASS_WIRELESS &&
- id->bInterfaceSubClass == UISUBCLASS_RF &&
- id->bInterfaceProtocol == UIPROTO_BLUETOOTH)
- return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
- return (UMATCH_NONE);
+ if (id != NULL
+ && id->bInterfaceClass == UICLASS_WIRELESS
+ && id->bInterfaceSubClass == UISUBCLASS_RF
+ && id->bInterfaceProtocol == UIPROTO_BLUETOOTH)
+ return UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO;
+
+ return UMATCH_NONE;
}
USB_ATTACH(ubt)
{
USB_ATTACH_START(ubt, sc, uaa);
- usbd_device_handle dev = uaa->device;
- usbd_interface_handle iface = uaa->iface;
- struct bt_attach_args bt;
- usb_interface_descriptor_t const *id;
- char *devinfop;
- usb_endpoint_descriptor_t const *ed;
- u_int8_t epcount;
- int i;
-
- DPRINTFN(10,("ubt_attach: sc=%p\n", sc));
-
- devinfop = usbd_devinfo_alloc(dev, 0);
+ usbd_interface_handle iface;
+ usb_config_descriptor_t *cd;
+ usb_endpoint_descriptor_t *ed;
+ const struct sysctlnode *node;
+ char *devinfop;
+ int err;
+ uint8_t count, i;
+
+ DPRINTFN(50, "ubt_attach: sc=%p\n", sc);
+
+ sc->sc_udev = uaa->device;
+
+ devinfop = usbd_devinfo_alloc(sc->sc_udev, 0);
USB_ATTACH_SETUP;
- printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop);
+ aprint_normal("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop);
usbd_devinfo_free(devinfop);
- sc->sc_udev = dev;
- sc->sc_ctl_iface = iface;
+ /*
+ * We must have at least 2 interfaces.
+ */
+ if (uaa->nifaces < 2) {
+ aprint_error("%s: need 2 interfaces (got %d)\n",
+ USBDEVNAME(sc->sc_dev), uaa->nifaces);
+
+ USB_ATTACH_ERROR_RETURN;
+ }
/*
- * The control interface comes before the isoc interface
- * according to the spec, so we find it first.
+ * Interface 0 must have 3 endpoints
+ * 1) Interrupt endpoint to receive HCI events
+ * 2) Bulk IN endpoint to receive ACL data
+ * 3) Bulk OUT endpoint to send ACL data
*/
- epcount = 0;
- (void)usbd_endpoint_count(iface, &epcount);
+ err = usbd_device2interface_handle(sc->sc_udev, 0, &iface);
+ if (err) {
+ aprint_error("%s: Could not get interface 0 handle %s (%d)\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err), err);
+
+ USB_ATTACH_ERROR_RETURN;
+ }
sc->sc_evt_addr = -1;
sc->sc_aclrd_addr = -1;
sc->sc_aclwr_addr = -1;
- for (i = 0; i < epcount; i++) {
+ count = 0;
+ (void)usbd_endpoint_count(iface, &count);
+
+ for (i = 0 ; i < count ; i++) {
+ int dir, type;
+
ed = usbd_interface2endpoint_descriptor(iface, i);
if (ed == NULL) {
- printf("%s: couldn't get ep %d\n",
+ aprint_error("%s: could not read endpoint descriptor %d\n",
USBDEVNAME(sc->sc_dev), i);
+
USB_ATTACH_ERROR_RETURN;
}
- DPRINTFN(10, ("%s: addr=%d attr=%d\n", __func__,
- (int)ed->bEndpointAddress,
- (int)ed->bmAttributes));
+ dir = UE_GET_DIR(ed->bEndpointAddress);
+ type = UE_GET_XFERTYPE(ed->bmAttributes);
- if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
- UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
+ if (dir == UE_DIR_IN && type == UE_INTERRUPT)
sc->sc_evt_addr = ed->bEndpointAddress;
- } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
- UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ else if (dir == UE_DIR_IN && type == UE_BULK)
sc->sc_aclrd_addr = ed->bEndpointAddress;
- } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
- UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
+ else if (dir == UE_DIR_OUT && type == UE_BULK)
sc->sc_aclwr_addr = ed->bEndpointAddress;
- }
}
- if (sc->sc_evt_addr == -1 ||
- sc->sc_aclrd_addr == -1 || sc->sc_aclwr_addr == -1) {
- printf("%s: missing endpoint\n", USBDEVNAME(sc->sc_dev));
+ if (sc->sc_evt_addr == -1) {
+ aprint_error("%s: missing INTERRUPT endpoint on interface 0\n",
+ USBDEVNAME(sc->sc_dev));
+
USB_ATTACH_ERROR_RETURN;
}
+ if (sc->sc_aclrd_addr == -1) {
+ aprint_error("%s: missing BULK IN endpoint on interface 0\n",
+ USBDEVNAME(sc->sc_dev));
- /* XXX works because isoc comes after ctl */
- /* Grab isoc interface as well. */
- for (i = 0; i < uaa->nifaces; i++) {
- if (uaa->ifaces[i] == NULL)
- continue;
- id = usbd_get_interface_descriptor(uaa->ifaces[i]);
- if (id != NULL &&
- id->bInterfaceClass == UICLASS_WIRELESS &&
- id->bInterfaceSubClass == UISUBCLASS_RF &&
- id->bInterfaceProtocol == UIPROTO_BLUETOOTH) {
- sc->sc_isoc_iface = uaa->ifaces[i];
- uaa->ifaces[i] = NULL;
- }
+ USB_ATTACH_ERROR_RETURN;
}
+ if (sc->sc_aclwr_addr == -1) {
+ aprint_error("%s: missing BULK OUT endpoint on interface 0\n",
+ USBDEVNAME(sc->sc_dev));
- printf("%s: has%s isoc data\n", USBDEVNAME(sc->sc_dev),
- sc->sc_isoc_iface != NULL ? "" : " no");
-
- usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
- USBDEV(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
- bt.bt_methods = &ubt_methods;
- bt.bt_cb = &sc->sc_cb;
- bt.bt_handle = sc;
+ /* Interface 0 Ok */
+ sc->sc_iface0 = iface;
+ uaa->ifaces[0] = NULL;
- sc->sc_child = config_found(self, &bt, bt_print);
+ /*
+ * Interface 1 must have 2 endpoints
+ * 1) Isochronous IN endpoint to receive SCO data
+ * 2) Isochronous OUT endpoint to send SCO data
+ *
+ * and will have several configurations, which can be selected
+ * via a sysctl variable. We select config 0 to start, which
+ * means that no SCO data will be available.
+ */
+ err = usbd_device2interface_handle(sc->sc_udev, 1, &iface);
+ if (err) {
+ aprint_error("%s: Could not get interface 1 handle %s (%d)\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(err), err);
- USB_ATTACH_SUCCESS_RETURN;
-}
+ USB_ATTACH_ERROR_RETURN;
+ }
-static void
-ubt_abortdealloc(struct ubt_softc *sc)
-{
- DPRINTFN(0, ("%s: sc=%p\n", __func__, sc));
+ cd = usbd_get_config_descriptor(sc->sc_udev);
+ if (cd == NULL) {
+ aprint_error("%s: could not get config descriptor\n",
+ USBDEVNAME(sc->sc_dev));
- if (sc->sc_evt_pipe != NULL) {
- usbd_abort_pipe(sc->sc_evt_pipe);
- usbd_close_pipe(sc->sc_evt_pipe);
- sc->sc_evt_pipe = NULL;
- }
- if (sc->sc_evt_buf != NULL) {
- free(sc->sc_evt_buf, M_USBDEV);
- sc->sc_evt_buf = NULL;
- }
- if (sc->sc_aclrd_pipe != NULL) {
- usbd_abort_pipe(sc->sc_aclrd_pipe);
- usbd_close_pipe(sc->sc_aclrd_pipe);
- sc->sc_aclrd_pipe = NULL;
- }
- if (sc->sc_aclwr_pipe != NULL) {
- usbd_abort_pipe(sc->sc_aclwr_pipe);
- usbd_close_pipe(sc->sc_aclwr_pipe);
- sc->sc_aclwr_pipe = NULL;
+ USB_ATTACH_ERROR_RETURN;
}
- if (sc->sc_aclrd_xfer != NULL) {
- usbd_free_xfer(sc->sc_aclrd_xfer);
- sc->sc_aclrd_xfer = NULL;
- sc->sc_aclrd_buf = NULL;
+
+ sc->sc_alt_config = usbd_get_no_alts(cd, 1);
+
+ /* Interface 1 Ok */
+ sc->sc_iface1 = iface;
+ uaa->ifaces[1] = NULL;
+
+ /* set initial config */
+ err = ubt_set_isoc_config(sc);
+ if (err) {
+ aprint_error("%s: ISOC config failed\n",
+ USBDEVNAME(sc->sc_dev));
+
+ USB_ATTACH_ERROR_RETURN;
}
- if (sc->sc_aclwr_xfer != NULL) {
- usbd_free_xfer(sc->sc_aclwr_xfer);
- sc->sc_aclwr_xfer = NULL;
- sc->sc_aclwr_buf = NULL;
+
+ /* Attach HCI */
+ sc->sc_unit.hci_softc = sc;
+ sc->sc_unit.hci_devname = USBDEVNAME(sc->sc_dev);
+ sc->sc_unit.hci_enable = ubt_enable;
+ sc->sc_unit.hci_disable = ubt_disable;
+ sc->sc_unit.hci_start_cmd = ubt_xmit_cmd_start;
+ sc->sc_unit.hci_start_acl = ubt_xmit_acl_start;
+ sc->sc_unit.hci_start_sco = ubt_xmit_sco_start;
+ sc->sc_unit.hci_ipl = IPL_USB; /* XXX: IPL_SOFTUSB ?? */
+ hci_attach(&sc->sc_unit);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ /* sysctl set-up for alternate configs */
+ sysctl_createv(&sc->sc_log, 0, NULL, NULL,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "hw",
+ NULL,
+ NULL, 0,
+ NULL, 0,
+ CTL_HW, CTL_EOL);
+
+ sysctl_createv(&sc->sc_log, 0, NULL, &node,
+ 0,
+ CTLTYPE_NODE, USBDEVNAME(sc->sc_dev),
+ SYSCTL_DESCR("ubt driver information"),
+ NULL, 0,
+ NULL, 0,
+ CTL_HW,
+ CTL_CREATE, CTL_EOL);
+
+ if (node != NULL) {
+ sysctl_createv(&sc->sc_log, 0, NULL, NULL,
+ CTLFLAG_READWRITE,
+ CTLTYPE_INT, "config",
+ SYSCTL_DESCR("configuration number"),
+ ubt_sysctl_config, 0,
+ sc, 0,
+ CTL_HW, node->sysctl_num,
+ CTL_CREATE, CTL_EOL);
+
+ sysctl_createv(&sc->sc_log, 0, NULL, NULL,
+ CTLFLAG_READONLY,
+ CTLTYPE_INT, "alt_config",
+ SYSCTL_DESCR("number of alternate configurations"),
+ NULL, 0,
+ &sc->sc_alt_config, sizeof(sc->sc_alt_config),
+ CTL_HW, node->sysctl_num,
+ CTL_CREATE, CTL_EOL);
+
+ sysctl_createv(&sc->sc_log, 0, NULL, NULL,
+ CTLFLAG_READONLY,
+ CTLTYPE_INT, "sco_rxsize",
+ SYSCTL_DESCR("max SCO receive size"),
+ NULL, 0,
+ &sc->sc_scord_size, sizeof(sc->sc_scord_size),
+ CTL_HW, node->sysctl_num,
+ CTL_CREATE, CTL_EOL);
+
+ sysctl_createv(&sc->sc_log, 0, NULL, NULL,
+ CTLFLAG_READONLY,
+ CTLTYPE_INT, "sco_txsize",
+ SYSCTL_DESCR("max SCO transmit size"),
+ NULL, 0,
+ &sc->sc_scowr_size, sizeof(sc->sc_scowr_size),
+ CTL_HW, node->sysctl_num,
+ CTL_CREATE, CTL_EOL);
}
+
+ USB_ATTACH_SUCCESS_RETURN;
}
USB_DETACH(ubt)
{
USB_DETACH_START(ubt, sc);
int s;
- int rv = 0;
- DPRINTF(("%s: sc=%p flags=%d\n", __func__, sc, flags));
+ DPRINTF("sc=%p flags=%d\n", sc, flags);
sc->sc_dying = 1;
- /* Abort all pipes. Causes processes waiting for transfer to wake. */
+ /* delete sysctl nodes */
+ sysctl_teardown(&sc->sc_log);
+
+ /* Detach HCI interface */
+ hci_detach(&sc->sc_unit);
+
+ /*
+ * Abort all pipes. Causes processes waiting for transfer to wake.
+ *
+ * Actually, hci_detach() above will call ubt_disable() which may
+ * call ubt_abortdealloc(), but lets be sure since doing it twice
+ * wont cause an error.
+ */
ubt_abortdealloc(sc);
- DPRINTFN(1, ("%s: waiting for USB detach\n", __func__));
+ /* wait for all processes to finish */
s = splusb();
- if (--sc->sc_refcnt >= 0) {
- /* Wait for processes to go away. */
+ if (sc->sc_refcnt-- > 0)
usb_detach_wait(USBDEV(sc->sc_dev));
- }
- splx(s);
- DPRINTFN(1, ("%s: USB detach complete\n", __func__));
- if (sc->sc_child != NULL) {
- DPRINTFN(1, ("%s: waiting for child detach\n", __func__));
- rv = config_detach(sc->sc_child, flags);
- sc->sc_child = NULL;
- DPRINTFN(1, ("%s: child detach complete\n", __func__));
- }
+ splx(s);
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
USBDEV(sc->sc_dev));
- DPRINTFN(1, ("%s: driver detached\n", __func__));
+ DPRINTFN(1, "driver detached\n");
- return (rv);
+ return 0;
}
int
@@ -330,354 +554,1047 @@ ubt_activate(device_ptr_t self, enum devact act)
struct ubt_softc *sc = (struct ubt_softc *)self;
int error = 0;
+ DPRINTFN(1, "ubt_activate: sc=%p, act=%d\n", sc, act);
+
switch (act) {
case DVACT_ACTIVATE:
- return (EOPNOTSUPP);
+ return EOPNOTSUPP;
break;
case DVACT_DEACTIVATE:
sc->sc_dying = 1;
- if (sc->sc_child != NULL)
- error = config_deactivate(sc->sc_child);
break;
}
- return (error);
+ return error;
}
+/* set ISOC configuration */
static int
-ubt_open(void *h, int flag, int mode, usb_proc_ptr p)
+ubt_set_isoc_config(struct ubt_softc *sc)
{
- struct ubt_softc *sc = h;
- int error;
+ usb_endpoint_descriptor_t *ed;
+ int rd_addr, wr_addr, rd_size, wr_size;
+ uint8_t count, i;
+ int err;
+
+ err = usbd_set_interface(sc->sc_iface1, sc->sc_config);
+ if (err) {
+ aprint_error(
+ "%s: Could not set config %d on ISOC interface. %s (%d)\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_config, usbd_errstr(err), err);
+
+ return err;
+ }
+
+ /*
+ * We wont get past the above if there are any pipes open, so no
+ * need to worry about buf/xfer/pipe deallocation. If we get an
+ * error after this, the frame quantities will be 0 and no SCO
+ * data will be possible.
+ */
+
+ sc->sc_scord_size = rd_size = 0;
+ sc->sc_scord_addr = rd_addr = -1;
+
+ sc->sc_scowr_size = wr_size = 0;
+ sc->sc_scowr_addr = wr_addr = -1;
+
+ count = 0;
+ (void)usbd_endpoint_count(sc->sc_iface1, &count);
+
+ for (i = 0 ; i < count ; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface1, i);
+ if (ed == NULL) {
+ printf("%s: could not read endpoint descriptor %d\n",
+ USBDEVNAME(sc->sc_dev), i);
+
+ return EIO;
+ }
+
+ DPRINTFN(5, "%s: endpoint type %02x (%02x) addr %02x (%s)\n",
+ USBDEVNAME(sc->sc_dev),
+ UE_GET_XFERTYPE(ed->bmAttributes),
+ UE_GET_ISO_TYPE(ed->bmAttributes),
+ ed->bEndpointAddress,
+ UE_GET_DIR(ed->bEndpointAddress) ? "in" : "out");
+
+ if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
+ continue;
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
+ rd_addr = ed->bEndpointAddress;
+ rd_size = UGETW(ed->wMaxPacketSize);
+ } else {
+ wr_addr = ed->bEndpointAddress;
+ wr_size = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ if (rd_addr == -1) {
+ aprint_error(
+ "%s: missing ISOC IN endpoint on interface config %d\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_config);
+
+ return ENOENT;
+ }
+ if (wr_addr == -1) {
+ aprint_error(
+ "%s: missing ISOC OUT endpoint on interface config %d\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_config);
+
+ return ENOENT;
+ }
+
+#ifdef DIAGNOSTIC
+ if (rd_size > MLEN) {
+ printf("%s: rd_size=%d exceeds MLEN\n",
+ USBDEVNAME(sc->sc_dev), rd_size);
+
+ return EOVERFLOW;
+ }
+
+ if (wr_size > MLEN) {
+ printf("%s: wr_size=%d exceeds MLEN\n",
+ USBDEVNAME(sc->sc_dev), wr_size);
+
+ return EOVERFLOW;
+ }
+#endif
+
+ sc->sc_scord_size = rd_size;
+ sc->sc_scord_addr = rd_addr;
+
+ sc->sc_scowr_size = wr_size;
+ sc->sc_scowr_addr = wr_addr;
+
+ return 0;
+}
+
+/* sysctl helper to set alternate configurations */
+static int
+ubt_sysctl_config(SYSCTLFN_ARGS)
+{
+ struct sysctlnode node;
+ struct ubt_softc *sc;
+ int t, error;
+
+ node = *rnode;
+ sc = node.sysctl_data;
+
+ t = sc->sc_config;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ if (t < 0 || t >= sc->sc_alt_config)
+ return EINVAL;
+
+ sc->sc_config = t;
+ return ubt_set_isoc_config(sc);
+}
+
+static void
+ubt_abortdealloc(struct ubt_softc *sc)
+{
+ int i;
+
+ DPRINTFN(1, "sc=%p\n", sc);
+
+ /* Abort all pipes */
+ if (sc->sc_evt_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_evt_pipe);
+ usbd_close_pipe(sc->sc_evt_pipe);
+ sc->sc_evt_pipe = NULL;
+ }
+
+ if (sc->sc_aclrd_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_aclrd_pipe);
+ usbd_close_pipe(sc->sc_aclrd_pipe);
+ sc->sc_aclrd_pipe = NULL;
+ }
+
+ if (sc->sc_aclwr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_aclwr_pipe);
+ usbd_close_pipe(sc->sc_aclwr_pipe);
+ sc->sc_aclwr_pipe = NULL;
+ }
+
+ if (sc->sc_scord_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_scord_pipe);
+ usbd_close_pipe(sc->sc_scord_pipe);
+ sc->sc_scord_pipe = NULL;
+ }
+
+ if (sc->sc_scowr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_scowr_pipe);
+ usbd_close_pipe(sc->sc_scowr_pipe);
+ sc->sc_scowr_pipe = NULL;
+ }
+
+ /* Free event buffer */
+ if (sc->sc_evt_buf != NULL) {
+ free(sc->sc_evt_buf, M_USBDEV);
+ sc->sc_evt_buf = NULL;
+ }
+
+ /* Free all xfers and xfer buffers (implicit) */
+ if (sc->sc_cmd_xfer != NULL) {
+ usbd_free_xfer(sc->sc_cmd_xfer);
+ sc->sc_cmd_xfer = NULL;
+ sc->sc_cmd_buf = NULL;
+ }
+
+ if (sc->sc_aclrd_xfer != NULL) {
+ usbd_free_xfer(sc->sc_aclrd_xfer);
+ sc->sc_aclrd_xfer = NULL;
+ sc->sc_aclrd_buf = NULL;
+ }
+
+ if (sc->sc_aclwr_xfer != NULL) {
+ usbd_free_xfer(sc->sc_aclwr_xfer);
+ sc->sc_aclwr_xfer = NULL;
+ sc->sc_aclwr_buf = NULL;
+ }
+
+ for (i = 0 ; i < UBT_NXFERS ; i++) {
+ if (sc->sc_scord[i].xfer != NULL) {
+ usbd_free_xfer(sc->sc_scord[i].xfer);
+ sc->sc_scord[i].xfer = NULL;
+ sc->sc_scord[i].buf = NULL;
+ }
+
+ if (sc->sc_scowr[i].xfer != NULL) {
+ usbd_free_xfer(sc->sc_scowr[i].xfer);
+ sc->sc_scowr[i].xfer = NULL;
+ sc->sc_scowr[i].buf = NULL;
+ }
+ }
+
+ /* Free partial SCO packets */
+ if (sc->sc_scord_mbuf != NULL) {
+ m_freem(sc->sc_scord_mbuf);
+ sc->sc_scord_mbuf = NULL;
+ }
+
+ if (sc->sc_scowr_mbuf != NULL) {
+ m_freem(sc->sc_scowr_mbuf);
+ sc->sc_scowr_mbuf = NULL;
+ }
+}
+
+/*******************************************************************************
+ *
+ * Bluetooth Unit/USB callbacks
+ *
+ * All of this will be called at the IPL_ we specified above
+ */
+static int
+ubt_enable(struct hci_unit *unit)
+{
+ struct ubt_softc *sc = unit->hci_softc;
usbd_status err;
+ int i, error;
+
+ DPRINTFN(1, "sc=%p\n", sc);
- DPRINTFN(0, ("%s: sc=%p\n", __func__, sc));
+ if (unit->hci_flags & BTF_RUNNING)
+ return 0;
- sc->sc_evt_buf = malloc(BTHCI_EVENT_MAX_LEN, M_USBDEV, M_NOWAIT);
+ /* Events */
+ sc->sc_evt_buf = malloc(UBT_BUFSIZ_EVENT, M_USBDEV, M_NOWAIT);
if (sc->sc_evt_buf == NULL) {
error = ENOMEM;
- goto bad0;
+ goto bad;
}
-
- err = usbd_open_pipe_intr(sc->sc_ctl_iface, sc->sc_evt_addr,
- USBD_SHORT_XFER_OK, &sc->sc_evt_pipe,
- sc, sc->sc_evt_buf, BTHCI_EVENT_MAX_LEN,
- ubt_event_cb, UBT_EVENT_EP_INTERVAL);
+ err = usbd_open_pipe_intr(sc->sc_iface0,
+ sc->sc_evt_addr,
+ USBD_SHORT_XFER_OK,
+ &sc->sc_evt_pipe,
+ sc,
+ sc->sc_evt_buf,
+ UBT_BUFSIZ_EVENT,
+ ubt_recv_event,
+ UBT_EVENT_INTERVAL);
if (err != USBD_NORMAL_COMPLETION) {
error = EIO;
- goto bad1;
+ goto bad;
}
- err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_aclrd_addr,
- 0, &sc->sc_aclrd_pipe);
- if (err != USBD_NORMAL_COMPLETION) {
- error = EIO;
- goto bad2;
+ /* Commands */
+ sc->sc_cmd_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_cmd_xfer == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ sc->sc_cmd_buf = usbd_alloc_buffer(sc->sc_cmd_xfer, UBT_BUFSIZ_CMD);
+ if (sc->sc_cmd_buf == NULL) {
+ error = ENOMEM;
+ goto bad;
}
- err = usbd_open_pipe(sc->sc_ctl_iface, sc->sc_aclwr_addr,
- 0, &sc->sc_aclwr_pipe);
+ /* ACL read */
+ err = usbd_open_pipe(sc->sc_iface0, sc->sc_aclrd_addr,
+ USBD_EXCLUSIVE_USE, &sc->sc_aclrd_pipe);
if (err != USBD_NORMAL_COMPLETION) {
error = EIO;
- goto bad3;
- }
-
- sc->sc_ctl_xfer = usbd_alloc_xfer(sc->sc_udev);
- if (sc->sc_ctl_xfer == NULL) {
- error = ENOMEM;
- goto bad4;
+ goto bad;
}
sc->sc_aclrd_xfer = usbd_alloc_xfer(sc->sc_udev);
if (sc->sc_aclrd_xfer == NULL) {
error = ENOMEM;
- goto bad5;
+ goto bad;
}
- sc->sc_aclwr_xfer = usbd_alloc_xfer(sc->sc_udev);
- if (sc->sc_aclwr_xfer == NULL) {
+ sc->sc_aclrd_buf = usbd_alloc_buffer(sc->sc_aclrd_xfer, UBT_BUFSIZ_ACL);
+ if (sc->sc_aclrd_buf == NULL) {
error = ENOMEM;
- goto bad6;
+ goto bad;
}
+ sc->sc_aclrd_busy = 0;
+ ubt_recv_acl_start(sc);
- /* Buffers */
- sc->sc_ctl_buf = usbd_alloc_buffer(sc->sc_ctl_xfer,
- BTHCI_COMMAND_MAX_LEN);
- if (sc->sc_ctl_buf == NULL) {
- error = ENOMEM;
- goto bad7;
+ /* ACL write */
+ err = usbd_open_pipe(sc->sc_iface0, sc->sc_aclwr_addr,
+ USBD_EXCLUSIVE_USE, &sc->sc_aclwr_pipe);
+ if (err != USBD_NORMAL_COMPLETION) {
+ error = EIO;
+ goto bad;
}
- sc->sc_aclrd_buf = usbd_alloc_buffer(sc->sc_aclrd_xfer,
- BTHCI_ACL_DATA_MAX_LEN);
- if (sc->sc_aclrd_buf == NULL) {
+ sc->sc_aclwr_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_aclwr_xfer == NULL) {
error = ENOMEM;
- goto bad7;
+ goto bad;
}
- sc->sc_aclwr_buf = usbd_alloc_buffer(sc->sc_aclwr_xfer,
- BTHCI_ACL_DATA_MAX_LEN);
+ sc->sc_aclwr_buf = usbd_alloc_buffer(sc->sc_aclwr_xfer, UBT_BUFSIZ_ACL);
if (sc->sc_aclwr_buf == NULL) {
error = ENOMEM;
- goto bad7;
+ goto bad;
}
- /* Start reading */
- ubt_aclrd_request(sc);
+ /* SCO read */
+ if (sc->sc_scord_size > 0) {
+ err = usbd_open_pipe(sc->sc_iface1, sc->sc_scord_addr,
+ USBD_EXCLUSIVE_USE, &sc->sc_scord_pipe);
+ if (err != USBD_NORMAL_COMPLETION) {
+ error = EIO;
+ goto bad;
+ }
- return 0;
+ for (i = 0 ; i < UBT_NXFERS ; i++) {
+ sc->sc_scord[i].xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_scord[i].xfer == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ sc->sc_scord[i].buf = usbd_alloc_buffer(sc->sc_scord[i].xfer,
+ sc->sc_scord_size * UBT_NFRAMES);
+ if (sc->sc_scord[i].buf == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ sc->sc_scord[i].softc = sc;
+ sc->sc_scord[i].busy = 0;
+ ubt_recv_sco_start1(sc, &sc->sc_scord[i]);
+ }
+ }
- bad7:
- usbd_free_xfer(sc->sc_aclwr_xfer);
- sc->sc_aclwr_xfer = NULL;
- bad6:
- usbd_free_xfer(sc->sc_aclrd_xfer);
- sc->sc_aclrd_xfer = NULL;
- bad5:
- usbd_free_xfer(sc->sc_ctl_xfer);
- sc->sc_ctl_xfer = NULL;
- bad4:
- usbd_close_pipe(sc->sc_aclwr_pipe);
- sc->sc_aclwr_pipe = NULL;
- bad3:
- usbd_close_pipe(sc->sc_aclrd_pipe);
- sc->sc_aclrd_pipe = NULL;
- bad2:
- usbd_close_pipe(sc->sc_evt_pipe);
- sc->sc_evt_pipe = NULL;
- bad1:
- free(sc->sc_evt_buf, M_USBDEV);
- sc->sc_evt_buf = NULL;
- bad0:
- return error;
-}
+ /* SCO write */
+ if (sc->sc_scowr_size > 0) {
+ err = usbd_open_pipe(sc->sc_iface1, sc->sc_scowr_addr,
+ USBD_EXCLUSIVE_USE, &sc->sc_scowr_pipe);
+ if (err != USBD_NORMAL_COMPLETION) {
+ error = EIO;
+ goto bad;
+ }
-static int
-ubt_close(void *h, int flag, int mode, usb_proc_ptr p)
-{
- struct ubt_softc *sc = h;
+ for (i = 0 ; i < UBT_NXFERS ; i++) {
+ sc->sc_scowr[i].xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_scowr[i].xfer == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ sc->sc_scowr[i].buf = usbd_alloc_buffer(sc->sc_scowr[i].xfer,
+ sc->sc_scowr_size * UBT_NFRAMES);
+ if (sc->sc_scowr[i].buf == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ sc->sc_scowr[i].softc = sc;
+ sc->sc_scowr[i].busy = 0;
+ }
+ }
- DPRINTFN(0, ("%s: sc=%p\n", __func__, sc));
+ unit->hci_flags &= ~BTF_XMIT;
+ unit->hci_flags |= BTF_RUNNING;
+ return 0;
+bad:
ubt_abortdealloc(sc);
-
- return 0;
+ return error;
}
-static u_int8_t*
-ubt_alloc_control(void *h, size_t len, struct btframe_buffer **buf)
+static void
+ubt_disable(struct hci_unit *unit)
{
- struct ubt_softc *sc = h;
+ struct ubt_softc *sc = unit->hci_softc;
- /*
- * We should be catching this earlier, but at the moment a
- * user request can generate oversized allocations.
- */
- if (len > BTHCI_COMMAND_MAX_LEN)
- return NULL;
+ DPRINTFN(1, "sc=%p\n", sc);
+
+ if ((unit->hci_flags & BTF_RUNNING) == 0)
+ return;
+
+ ubt_abortdealloc(sc);
- *buf = (struct btframe_buffer*)sc->sc_ctl_buf;
- return sc->sc_ctl_buf;
+ unit->hci_flags &= ~BTF_RUNNING;
}
-static int
-ubt_send_control(void *h, struct btframe_buffer *buf, size_t len)
+static void
+ubt_xmit_cmd_start(struct hci_unit *unit)
{
- struct ubt_softc *sc = h;
+ struct ubt_softc *sc = unit->hci_softc;
usb_device_request_t req;
usbd_status status;
+ struct mbuf *m;
+ int len;
- DPRINTFN(1,("%s: sc=%p\n", __func__, sc));
+ if (sc->sc_dying)
+ return;
-#ifdef DIAGNOSTIC
- if ((u_int8_t*)buf != sc->sc_ctl_buf)
- panic("ubt_control() called with wrong buffer");
-#endif
+ if (MBUFQ_FIRST(&unit->hci_cmdq) == NULL)
+ return;
- if (sc->sc_dying)
- return EIO;
+ MBUFQ_DEQUEUE(&unit->hci_cmdq, m);
+ KASSERT(m != NULL);
- if (len < BTHCI_COMMAND_MIN_LEN || len > BTHCI_COMMAND_MAX_LEN)
- return EINVAL;
+ DPRINTFN(15, "%s: xmit CMD packet (%d bytes)\n",
+ unit->hci_devname, m->m_pkthdr.len);
sc->sc_refcnt++;
+ unit->hci_flags |= BTF_XMIT_CMD;
+
+ len = m->m_pkthdr.len - 1;
+ m_copydata(m, 1, len, sc->sc_cmd_buf);
+ m_freem(m);
memset(&req, 0, sizeof(req));
req.bmRequestType = UT_WRITE_CLASS_DEVICE;
USETW(req.wLength, len);
- usbd_setup_default_xfer(sc->sc_ctl_xfer,
+ usbd_setup_default_xfer(sc->sc_cmd_xfer,
sc->sc_udev,
- sc,
- USBD_DEFAULT_TIMEOUT,
- &req, sc->sc_ctl_buf, len,
- USBD_SYNCHRONOUS | USBD_NO_COPY, NULL);
+ unit,
+ UBT_CMD_TIMEOUT,
+ &req,
+ sc->sc_cmd_buf,
+ len,
+ USBD_NO_COPY | USBD_FORCE_SHORT_XFER,
+ ubt_xmit_cmd_complete);
- status = usbd_transfer(sc->sc_ctl_xfer);
+ status = usbd_transfer(sc->sc_cmd_xfer);
- if (--sc->sc_refcnt < 0)
- usb_detach_wakeup(USBDEV(sc->sc_dev));
+ KASSERT(status != USBD_NORMAL_COMPLETION);
- if (status != USBD_NORMAL_COMPLETION)
- return EIO;
+ if (status != USBD_IN_PROGRESS) {
+ DPRINTF("usbd_transfer status=%s (%d)\n",
+ usbd_errstr(status), status);
- return 0;
+ sc->sc_refcnt--;
+ unit->hci_flags &= ~BTF_XMIT_CMD;
+ }
}
-static u_int8_t*
-ubt_alloc_acldata(void *h, size_t len, struct btframe_buffer **buf)
+static void
+ubt_xmit_cmd_complete(usbd_xfer_handle xfer,
+ usbd_private_handle h, usbd_status status)
{
- struct ubt_softc *sc = h;
+ struct hci_unit *unit = h;
+ struct ubt_softc *sc = unit->hci_softc;
+ uint32_t count;
- /*
- * We should be catching this earlier, but at the moment a
- * user request can generate oversized allocations.
- */
- if (len > BTHCI_ACL_DATA_MAX_LEN)
- return NULL;
+ DPRINTFN(15, "%s: CMD complete status=%s (%d)\n",
+ unit->hci_devname, usbd_errstr(status), status);
+
+ unit->hci_flags &= ~BTF_XMIT_CMD;
+
+ if (--sc->sc_refcnt < 0) {
+ DPRINTF("sc_refcnt=%d\n", sc->sc_refcnt);
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return;
+ }
+
+ if (sc->sc_dying) {
+ DPRINTF("sc_dying\n");
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF("status=%s (%d)\n",
+ usbd_errstr(status), status);
+
+ unit->hci_stats.err_tx++;
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ unit->hci_stats.cmd_tx++;
+ unit->hci_stats.byte_tx += count;
- *buf = (struct btframe_buffer*)sc->sc_aclwr_buf;
- return sc->sc_aclwr_buf;
+ ubt_xmit_cmd_start(unit);
}
-static int
-ubt_send_acldata(void *h, struct btframe_buffer *buf, size_t len)
+static void
+ubt_xmit_acl_start(struct hci_unit *unit)
{
- struct ubt_softc *sc = h;
+ struct ubt_softc *sc = unit->hci_softc;
+ struct mbuf *m;
usbd_status status;
-
- DPRINTFN(1,("%s: sc=%p\n", __func__, sc));
-
-#ifdef DIAGNOSTIC
- if ((u_int8_t*)buf != sc->sc_aclwr_buf)
- panic("ubt_sendacldata() called with wrong buffer");
-#endif
+ int len;
if (sc->sc_dying)
- return EIO;
+ return;
- if (len < BTHCI_ACL_DATA_MIN_LEN || len > BTHCI_ACL_DATA_MAX_LEN)
- return EINVAL;
+ if (MBUFQ_FIRST(&unit->hci_acltxq) == NULL)
+ return;
sc->sc_refcnt++;
+ unit->hci_flags |= BTF_XMIT_ACL;
+
+ MBUFQ_DEQUEUE(&unit->hci_acltxq, m);
+ KASSERT(m != NULL);
+
+ DPRINTFN(15, "%s: xmit ACL packet (%d bytes)\n",
+ unit->hci_devname, m->m_pkthdr.len);
+
+ len = m->m_pkthdr.len - 1;
+ if (len > UBT_BUFSIZ_ACL) {
+ DPRINTF("%s: truncating ACL packet (%d => %d)!\n",
+ unit->hci_devname, len, UBT_BUFSIZ_ACL);
+
+ len = UBT_BUFSIZ_ACL;
+ }
+
+ m_copydata(m, 1, len, sc->sc_aclwr_buf);
+ m_freem(m);
+
+ unit->hci_stats.acl_tx++;
+ unit->hci_stats.byte_tx += len;
usbd_setup_xfer(sc->sc_aclwr_xfer,
sc->sc_aclwr_pipe,
- (usbd_private_handle)sc,
- sc->sc_aclwr_buf, len,
- USBD_SYNCHRONOUS | USBD_NO_COPY,
- USBD_DEFAULT_TIMEOUT,
- NULL);
+ unit,
+ sc->sc_aclwr_buf,
+ len,
+ USBD_NO_COPY | USBD_FORCE_SHORT_XFER,
+ UBT_ACL_TIMEOUT,
+ ubt_xmit_acl_complete);
status = usbd_transfer(sc->sc_aclwr_xfer);
- if (--sc->sc_refcnt < 0)
+ KASSERT(status != USBD_NORMAL_COMPLETION);
+
+ if (status != USBD_IN_PROGRESS) {
+ DPRINTF("usbd_transfer status=%s (%d)\n",
+ usbd_errstr(status), status);
+
+ sc->sc_refcnt--;
+ unit->hci_flags &= ~BTF_XMIT_ACL;
+ }
+}
+
+static void
+ubt_xmit_acl_complete(usbd_xfer_handle xfer,
+ usbd_private_handle h, usbd_status status)
+{
+ struct hci_unit *unit = h;
+ struct ubt_softc *sc = unit->hci_softc;
+
+ DPRINTFN(15, "%s: ACL complete status=%s (%d)\n",
+ unit->hci_devname, usbd_errstr(status), status);
+
+ unit->hci_flags &= ~BTF_XMIT_ACL;
+
+ if (--sc->sc_refcnt < 0) {
usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return;
+ }
- if (status != USBD_NORMAL_COMPLETION)
- return EIO;
+ if (sc->sc_dying)
+ return;
- return 0;
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF("status=%s (%d)\n",
+ usbd_errstr(status), status);
+
+ unit->hci_stats.err_tx++;
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_aclwr_pipe);
+ else
+ return;
+ }
+
+ ubt_xmit_acl_start(unit);
}
-static u_int8_t*
-ubt_alloc_scodata(void *h, size_t len, struct btframe_buffer **buf)
+static void
+ubt_xmit_sco_start(struct hci_unit *unit)
{
- return NULL;
+ struct ubt_softc *sc = unit->hci_softc;
+ int i;
+
+ if (sc->sc_dying || sc->sc_scowr_size == 0)
+ return;
+
+ for (i = 0 ; i < UBT_NXFERS ; i++) {
+ if (sc->sc_scowr[i].busy)
+ continue;
+
+ ubt_xmit_sco_start1(sc, &sc->sc_scowr[i]);
+ }
}
-static int
-ubt_send_scodata(void *h, struct btframe_buffer *buf, size_t len)
+static void
+ubt_xmit_sco_start1(struct ubt_softc *sc, struct ubt_isoc_xfer *isoc)
{
- struct ubt_softc *sc = h;
+ struct mbuf *m;
+ uint8_t *buf;
+ int num, len, size, space;
- DPRINTFN(1,("%s: sc=%p\n", __func__, sc));
+ space = sc->sc_scowr_size * UBT_NFRAMES;
+ buf = isoc->buf;
+ len = 0;
+
+ /*
+ * Fill the request buffer with data from the queue,
+ * keeping any leftover packet on our private hook.
+ *
+ * Complete packets are passed back up to the stack
+ * for disposal, since we can't rely on the controller
+ * to tell us when it has finished with them.
+ */
+
+ m = sc->sc_scowr_mbuf;
+ while (space > 0) {
+ if (m == NULL) {
+ MBUFQ_DEQUEUE(&sc->sc_unit.hci_scotxq, m);
+ if (m == NULL)
+ break;
+
+ m_adj(m, 1); /* packet type */
+ }
+
+ if (m->m_pkthdr.len > 0) {
+ size = MIN(m->m_pkthdr.len, space);
+
+ m_copydata(m, 0, size, buf);
+ m_adj(m, size);
+
+ buf += size;
+ len += size;
+ space -= size;
+ }
+
+ if (m->m_pkthdr.len == 0) {
+ sc->sc_unit.hci_stats.sco_tx++;
+ hci_complete_sco(&sc->sc_unit, m);
+ m = NULL;
+ }
+ }
+ sc->sc_scowr_mbuf = m;
+
+ DPRINTFN(15, "isoc=%p, len=%d, space=%d\n", isoc, len, space);
+
+ if (len == 0) /* nothing to send */
+ return;
+
+ sc->sc_refcnt++;
+ sc->sc_unit.hci_flags |= BTF_XMIT_SCO;
+ sc->sc_unit.hci_stats.byte_tx += len;
+ isoc->busy = 1;
+
+ /*
+ * calculate number of isoc frames and sizes
+ */
+
+ for (num = 0 ; len > 0 ; num++) {
+ size = MIN(sc->sc_scowr_size, len);
+
+ isoc->size[num] = size;
+ len -= size;
+ }
+
+ usbd_setup_isoc_xfer(isoc->xfer,
+ sc->sc_scowr_pipe,
+ isoc,
+ isoc->size,
+ num,
+ USBD_NO_COPY | USBD_FORCE_SHORT_XFER,
+ ubt_xmit_sco_complete);
+
+ usbd_transfer(isoc->xfer);
+}
+
+static void
+ubt_xmit_sco_complete(usbd_xfer_handle xfer,
+ usbd_private_handle h, usbd_status status)
+{
+ struct ubt_isoc_xfer *isoc = h;
+ struct ubt_softc *sc;
+ int i;
+
+ KASSERT(xfer == isoc->xfer);
+ sc = isoc->softc;
+
+ DPRINTFN(15, "isoc=%p, status=%s (%d)\n",
+ isoc, usbd_errstr(status), status);
+
+ isoc->busy = 0;
+
+ for (i = 0 ; ; i++) {
+ if (i == UBT_NXFERS) {
+ sc->sc_unit.hci_flags &= ~BTF_XMIT_SCO;
+ break;
+ }
+
+ if (sc->sc_scowr[i].busy)
+ break;
+ }
+
+ if (--sc->sc_refcnt < 0) {
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return;
+ }
if (sc->sc_dying)
- return EIO;
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF("status=%s (%d)\n",
+ usbd_errstr(status), status);
- return ENXIO;
+ sc->sc_unit.hci_stats.err_tx++;
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_scowr_pipe);
+ else
+ return;
+ }
+
+ ubt_xmit_sco_start(&sc->sc_unit);
+}
+
+/*
+ * load incoming data into an mbuf with
+ * leading type byte
+ */
+static struct mbuf *
+ubt_mbufload(uint8_t *buf, int count, uint8_t type)
+{
+ struct mbuf *m;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return NULL;
+
+ *mtod(m, uint8_t *) = type;
+ m->m_pkthdr.len = m->m_len = MHLEN;
+ m_copyback(m, 1, count, buf); // (extends if necessary)
+ if (m->m_pkthdr.len != MAX(MHLEN, count + 1)) {
+ m_free(m);
+ return NULL;
+ }
+
+ m->m_pkthdr.len = count + 1;
+ m->m_len = MIN(MHLEN, m->m_pkthdr.len);
+
+ return m;
}
static void
-ubt_event_cb(usbd_xfer_handle xfer, usbd_private_handle h, usbd_status status)
+ubt_recv_event(usbd_xfer_handle xfer, usbd_private_handle h, usbd_status status)
{
struct ubt_softc *sc = h;
+ struct mbuf *m;
+ uint32_t count;
void *buf;
- u_int32_t size;
- DPRINTFN(1,("%s: sc=%p status=%s\n", __func__, sc,
- usbd_errstr(status)));
+ DPRINTFN(15, "sc=%p status=%s (%d)\n",
+ sc, usbd_errstr(status), status);
- if (status != USBD_NORMAL_COMPLETION || sc->sc_dying ||
- sc->sc_child == NULL)
+ if (status != USBD_NORMAL_COMPLETION || sc->sc_dying)
return;
- usbd_get_xfer_status(xfer, NULL, &buf, &size, NULL);
+ usbd_get_xfer_status(xfer, NULL, &buf, &count, NULL);
+
+ sc->sc_unit.hci_stats.evt_rx++;
+ sc->sc_unit.hci_stats.byte_rx += count;
- sc->sc_cb->bt_recveventdata(sc->sc_child, buf, (size_t)size);
+ m = ubt_mbufload(buf, count, HCI_EVENT_PKT);
+ if (m != NULL)
+ hci_input_event(&sc->sc_unit, m);
+ else
+ sc->sc_unit.hci_stats.err_rx++;
}
static void
-ubt_aclrd_request(struct ubt_softc *sc)
+ubt_recv_acl_start(struct ubt_softc *sc)
{
usbd_status status;
- int s;
- DPRINTFN(1,("%s: sc=%p\n", __func__, sc));
+ DPRINTFN(15, "sc=%p\n", sc);
- if (sc->sc_dying)
- return;
+ if (sc->sc_aclrd_busy || sc->sc_dying) {
+ DPRINTF("sc_aclrd_busy=%d, sc_dying=%d\n",
+ sc->sc_aclrd_busy,
+ sc->sc_dying);
- s = splusb();
- if (sc->sc_aclrd_running) {
- splx(s);
return;
}
- sc->sc_aclrd_running = 1;
- splx(s);
- usbd_setup_xfer(sc->sc_aclrd_xfer, sc->sc_aclrd_pipe,
- sc, sc->sc_aclrd_buf, BTHCI_ACL_DATA_MAX_LEN,
- USBD_SHORT_XFER_OK | USBD_NO_COPY,
- USBD_NO_TIMEOUT, ubt_aclrd_cb);
+ sc->sc_refcnt++;
+ sc->sc_aclrd_busy = 1;
+
+ usbd_setup_xfer(sc->sc_aclrd_xfer,
+ sc->sc_aclrd_pipe,
+ sc,
+ sc->sc_aclrd_buf,
+ UBT_BUFSIZ_ACL,
+ USBD_NO_COPY | USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT,
+ ubt_recv_acl_complete);
status = usbd_transfer(sc->sc_aclrd_xfer);
- /* Cancellation is normal on device shutdown */
- if (status == USBD_IN_PROGRESS || status == USBD_CANCELLED)
- return;
+ KASSERT(status != USBD_NORMAL_COMPLETION);
- DPRINTFN(1,("%s: read request failed: %s\n", __func__,
- usbd_errstr(status)));
+ if (status != USBD_IN_PROGRESS) {
+ DPRINTF("usbd_transfer status=%s (%d)\n",
+ usbd_errstr(status), status);
- sc->sc_aclrd_running = 0;
- /* XXX now what!? */
+ sc->sc_refcnt--;
+ sc->sc_aclrd_busy = 0;
+ }
}
static void
-ubt_aclrd_cb(usbd_xfer_handle xfer, usbd_private_handle h, usbd_status status)
+ubt_recv_acl_complete(usbd_xfer_handle xfer,
+ usbd_private_handle h, usbd_status status)
{
struct ubt_softc *sc = h;
+ struct mbuf *m;
+ uint32_t count;
void *buf;
- u_int32_t size;
- DPRINTFN(1,("%s: sc=%p status=%s\n", __func__, sc,
- usbd_errstr(status)));
+ DPRINTFN(15, "sc=%p status=%s (%d)\n",
+ sc, usbd_errstr(status), status);
- sc->sc_aclrd_running = 0;
+ sc->sc_aclrd_busy = 0;
+
+ if (--sc->sc_refcnt < 0) {
+ DPRINTF("refcnt = %d\n", sc->sc_refcnt);
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return;
+ }
- if (status != USBD_NORMAL_COMPLETION || sc->sc_dying ||
- sc->sc_child == NULL)
+ if (sc->sc_dying) {
+ DPRINTF("sc_dying\n");
return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF("status=%s (%d)\n",
+ usbd_errstr(status), status);
- usbd_get_xfer_status(xfer, NULL, &buf, &size, NULL);
+ sc->sc_unit.hci_stats.err_rx++;
- sc->sc_cb->bt_recvacldata(sc->sc_child, buf, (size_t)size);
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_aclrd_pipe);
+ else
+ return;
+ } else {
+ usbd_get_xfer_status(xfer, NULL, &buf, &count, NULL);
- /* Re-issue the request */
- if (!sc->sc_dying)
- ubt_aclrd_request(sc);
+ sc->sc_unit.hci_stats.acl_rx++;
+ sc->sc_unit.hci_stats.byte_rx += count;
+
+ m = ubt_mbufload(buf, count, HCI_ACL_DATA_PKT);
+ if (m != NULL)
+ hci_input_acl(&sc->sc_unit, m);
+ else
+ sc->sc_unit.hci_stats.err_rx++;
+ }
+
+ /* and restart */
+ ubt_recv_acl_start(sc);
}
-static int
-ubt_splraise(void)
+static void
+ubt_recv_sco_start1(struct ubt_softc *sc, struct ubt_isoc_xfer *isoc)
+{
+ int i;
+
+ DPRINTFN(15, "sc=%p, isoc=%p\n", sc, isoc);
+
+ if (isoc->busy || sc->sc_dying || sc->sc_scord_size == 0) {
+ DPRINTF("%s%s%s\n",
+ isoc->busy ? " busy" : "",
+ sc->sc_dying ? " dying" : "",
+ sc->sc_scord_size == 0 ? " size=0" : "");
+
+ return;
+ }
+
+ sc->sc_refcnt++;
+ isoc->busy = 1;
+
+ for (i = 0 ; i < UBT_NFRAMES ; i++)
+ isoc->size[i] = sc->sc_scord_size;
+
+ usbd_setup_isoc_xfer(isoc->xfer,
+ sc->sc_scord_pipe,
+ isoc,
+ isoc->size,
+ UBT_NFRAMES,
+ USBD_NO_COPY | USBD_SHORT_XFER_OK,
+ ubt_recv_sco_complete);
+
+ usbd_transfer(isoc->xfer);
+}
+
+static void
+ubt_recv_sco_complete(usbd_xfer_handle xfer,
+ usbd_private_handle h, usbd_status status)
{
- return splusb();
+ struct ubt_isoc_xfer *isoc = h;
+ struct ubt_softc *sc;
+ struct mbuf *m;
+ uint32_t count;
+ uint8_t *ptr, *frame;
+ int i, size, got, want;
+
+ KASSERT(isoc != NULL);
+ KASSERT(isoc->xfer == xfer);
+
+ sc = isoc->softc;
+ isoc->busy = 0;
+
+ if (--sc->sc_refcnt < 0) {
+ DPRINTF("refcnt=%d\n", sc->sc_refcnt);
+ usb_detach_wakeup(USBDEV(sc->sc_dev));
+ return;
+ }
+
+ if (sc->sc_dying) {
+ DPRINTF("sc_dying\n");
+ return;
+ }
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF("status=%s (%d)\n",
+ usbd_errstr(status), status);
+
+ sc->sc_unit.hci_stats.err_rx++;
+
+ if (status == USBD_STALLED) {
+ usbd_clear_endpoint_stall_async(sc->sc_scord_pipe);
+ goto restart;
+ }
+
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
+ if (count == 0)
+ goto restart;
+
+ DPRINTFN(15, "sc=%p, isoc=%p, count=%u\n",
+ sc, isoc, count);
+
+ sc->sc_unit.hci_stats.byte_rx += count;
+
+ /*
+ * Extract SCO packets from ISOC frames. The way we have it,
+ * no SCO packet can be bigger than MHLEN. This is unlikely
+ * to actually happen, but if we ran out of mbufs and lost
+ * sync then we may get spurious data that makes it seem that
+ * way, so we discard data that wont fit. This doesnt really
+ * help with the lost sync situation alas.
+ */
+
+ m = sc->sc_scord_mbuf;
+ if (m != NULL) {
+ sc->sc_scord_mbuf = NULL;
+ ptr = mtod(m, uint8_t *) + m->m_pkthdr.len;
+ got = m->m_pkthdr.len;
+ want = sizeof(hci_scodata_hdr_t);
+ if (got >= want)
+ want += mtod(m, hci_scodata_hdr_t *)->length ;
+ } else {
+ ptr = NULL;
+ got = 0;
+ want = 0;
+ }
+
+ for (i = 0 ; i < UBT_NFRAMES ; i++) {
+ frame = isoc->buf + (i * sc->sc_scord_size);
+
+ while (isoc->size[i] > 0) {
+ size = isoc->size[i];
+
+ if (m == NULL) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ printf("%s: out of memory (xfer halted)\n",
+ USBDEVNAME(sc->sc_dev));
+
+ sc->sc_unit.hci_stats.err_rx++;
+ return; /* lost sync */
+ }
+
+ ptr = mtod(m, uint8_t *);
+ *ptr++ = HCI_SCO_DATA_PKT;
+ got = 1;
+ want = sizeof(hci_scodata_hdr_t);
+ }
+
+ if (got + size > want)
+ size = want - got;
+
+ if (got + size > MHLEN)
+ memcpy(ptr, frame, MHLEN - got);
+ else
+ memcpy(ptr, frame, size);
+
+ ptr += size;
+ got += size;
+ frame += size;
+
+ if (got == want) {
+ /*
+ * If we only got a header, add the packet
+ * length to our want count. Send complete
+ * packets up to protocol stack.
+ */
+ if (want == sizeof(hci_scodata_hdr_t))
+ want += mtod(m, hci_scodata_hdr_t *)->length;
+
+ if (got == want) {
+ m->m_pkthdr.len = m->m_len = got;
+ sc->sc_unit.hci_stats.sco_rx++;
+ hci_input_sco(&sc->sc_unit, m);
+ m = NULL;
+ }
+ }
+
+ isoc->size[i] -= size;
+ }
+ }
+
+ if (m != NULL) {
+ m->m_pkthdr.len = m->m_len = got;
+ sc->sc_scord_mbuf = m;
+ }
+
+restart: /* and restart */
+ ubt_recv_sco_start1(sc, isoc);
}
diff --git a/sys/dev/usb/ubtreg.h b/sys/dev/usb/ubtreg.h
deleted file mode 100644
index 1c604d5e501..00000000000
--- a/sys/dev/usb/ubtreg.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* $NetBSD: ubtreg.h,v 1.1 2003/01/05 05:20:20 dsainty Exp $ */
-
-/*
- * Copyright (c) 2003 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by David Sainty <David.Sainty@dtsp.co.nz>
- *
- * 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``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 THE FOUNDATION OR CONTRIBUTORS
- * 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.
- */
-
-/* Event endpoint interval */
-#define UBT_EVENT_EP_INTERVAL 1
diff --git a/sys/dev/usb/uhci.c b/sys/dev/usb/uhci.c
index 91a1561551a..88f3907b571 100644
--- a/sys/dev/usb/uhci.c
+++ b/sys/dev/usb/uhci.c
@@ -1,4 +1,4 @@
-/* $NetBSD: uhci.c,v 1.196 2006/05/12 01:25:00 mrg Exp $ */
+/* $NetBSD: uhci.c,v 1.197 2006/06/19 15:44:45 gdamore Exp $ */
/* $FreeBSD: src/sys/dev/usb/uhci.c,v 1.33 1999/11/17 22:33:41 n_hibma Exp $ */
/*
@@ -49,7 +49,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.196 2006/05/12 01:25:00 mrg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.197 2006/06/19 15:44:45 gdamore Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -2676,7 +2676,8 @@ uhci_device_isoc_done(usbd_xfer_handle xfer)
{
uhci_intr_info_t *ii = &UXFER(xfer)->iinfo;
- DPRINTFN(4, ("uhci_isoc_done: length=%d\n", xfer->actlen));
+ DPRINTFN(4, ("uhci_isoc_done: length=%d, busy_free=0x%08x\n",
+ xfer->actlen, xfer->busy_free));
if (ii->xfer != xfer)
/* Not on interrupt list, ignore it. */
@@ -2686,12 +2687,6 @@ uhci_device_isoc_done(usbd_xfer_handle xfer)
return;
#ifdef DIAGNOSTIC
- if (xfer->busy_free != XFER_BUSY) {
- printf("uhci_device_isoc_done: xfer=%p not busy 0x%08x\n",
- xfer, xfer->busy_free);
- return;
- }
-
if (ii->stdend == NULL) {
printf("uhci_device_isoc_done: xfer=%p stdend==NULL\n", xfer);
#ifdef UHCI_DEBUG
diff --git a/sys/dev/wscons/wsconsio.h b/sys/dev/wscons/wsconsio.h
index d55ec5bda29..b223101b880 100644
--- a/sys/dev/wscons/wsconsio.h
+++ b/sys/dev/wscons/wsconsio.h
@@ -1,4 +1,4 @@
-/* $NetBSD: wsconsio.h,v 1.79 2006/02/18 18:56:05 jmcneill Exp $ */
+/* $NetBSD: wsconsio.h,v 1.80 2006/06/19 15:44:45 gdamore Exp $ */
/*
* Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved.
@@ -102,6 +102,7 @@ struct wscons_event {
#define WSKBD_TYPE_SGI 18 /* SGI keyboard */
#define WSKBD_TYPE_MATRIXKP 19 /* Matrix keypads/buttons */
#define WSKBD_TYPE_EWS4800 20 /* NEC EWS4800 */
+#define WSKBD_TYPE_BLUETOOTH 21 /* Bluetooth keyboard */
/* Manipulate the keyboard bell. */
struct wskbd_bell_data {
@@ -200,6 +201,7 @@ struct wskbd_scroll_data {
#define WSMOUSE_TYPE_MAXINE 11 /* DEC maxine mouse */
#define WSMOUSE_TYPE_MAPLE 12 /* Dreamcast Maple mouse */
#define WSMOUSE_TYPE_SGI 13 /* SGI mouse */
+#define WSMOUSE_TYPE_BLUETOOTH 14 /* Bluetooth mouse */
/* Set resolution. Not applicable to all mouse types. */
#define WSMOUSEIO_SRES _IOW('W', 33, u_int)