summaryrefslogtreecommitdiff
path: root/sys/dev/usb/if_ubt.c
diff options
context:
space:
mode:
authorAlexander Yurchenko <grange@cvs.openbsd.org>2005-01-14 12:21:03 +0000
committerAlexander Yurchenko <grange@cvs.openbsd.org>2005-01-14 12:21:03 +0000
commitb0b0183bfdf4a33fc8d502894ebad9057e810fa0 (patch)
tree06533dac97b0e2531fd103c3b3385b087a9e2419 /sys/dev/usb/if_ubt.c
parent08b6f0f68c7520cadd2a4adba1c4dffad2359e2f (diff)
ubt(4): USB Bluetooth driver, commented out in all kernel config
files for now. Adopted from FreeBSD for our netbt stack. Help in testing by many, ok markus@
Diffstat (limited to 'sys/dev/usb/if_ubt.c')
-rw-r--r--sys/dev/usb/if_ubt.c1755
1 files changed, 1755 insertions, 0 deletions
diff --git a/sys/dev/usb/if_ubt.c b/sys/dev/usb/if_ubt.c
new file mode 100644
index 00000000000..71995da8489
--- /dev/null
+++ b/sys/dev/usb/if_ubt.c
@@ -0,0 +1,1755 @@
+/* $OpenBSD: if_ubt.c,v 1.1 2005/01/14 12:21:02 grange Exp $ */
+
+/*
+ * ng_ubt.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: if_ubt.c,v 1.1 2005/01/14 12:21:02 grange Exp $
+ * $FreeBSD: src/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c,v 1.20 2004/10/12 23:33:46 emax Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/endian.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/poll.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+
+#include <netbt/bluetooth.h>
+#include <netbt/hci.h>
+#include <netbt/l2cap.h>
+#include <netbt/bt.h>
+#include <netbt/hci_var.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/usb/if_ubtreg.h>
+
+USB_DECLARE_DRIVER(ubt);
+
+Static usbd_status ubt_request_start(struct ubt_softc *);
+Static void ubt_request_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_intr_start(struct ubt_softc *);
+Static void ubt_intr_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_bulk_in_start(struct ubt_softc *);
+Static void ubt_bulk_in_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_bulk_out_start(struct ubt_softc *);
+Static void ubt_bulk_out_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_isoc_in_start(struct ubt_softc *);
+Static void ubt_isoc_in_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static usbd_status ubt_isoc_out_start(struct ubt_softc *);
+Static void ubt_isoc_out_complete(usbd_xfer_handle,
+ usbd_private_handle, usbd_status);
+
+Static void ubt_reset(struct ubt_softc *);
+Static void ubt_stop(struct ubt_softc *);
+
+Static int ubt_rcvdata(struct ubt_softc *, struct mbuf *);
+
+Static void ubt_if_start(struct ifnet *);
+Static int ubt_if_ioctl(struct ifnet *, u_long, caddr_t);
+Static int ubt_if_init(struct ifnet *);
+Static void ubt_if_watchdog(struct ifnet *);
+
+USB_MATCH(ubt)
+{
+ /*
+ * If for some reason device should not be attached then put
+ * VendorID/ProductID pair into the list below. Currently I
+ * do not know of any such devices. The format is as follows:
+ *
+ * { VENDOR_ID, PRODUCT_ID },
+ *
+ * where VENDOR_ID and PRODUCT_ID are hex numbers.
+ */
+
+ Static struct usb_devno const ubt_ignored_devices[] = {
+ { 0, 0 } /* This should be the last item in the list */
+ };
+
+ /*
+ * If device violates Bluetooth specification and has bDeviceClass,
+ * bDeviceSubClass and bDeviceProtocol set to wrong values then you
+ * could try to put VendorID/ProductID pair into the list below.
+ * Currently I do not know of any such devices.
+ */
+
+ Static struct usb_devno const ubt_broken_devices[] = {
+ { USB_VENDOR_CSR, USB_PRODUCT_CSR_BLUECORE },
+ { 0, 0 } /* This should be the last item in the list */
+ };
+
+ USB_MATCH_START(ubt, uaa);
+
+ usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
+
+ if (uaa->iface == NULL ||
+ usb_lookup(ubt_ignored_devices, uaa->vendor, uaa->product))
+ return (UMATCH_NONE);
+
+ if (dd->bDeviceClass == UDCLASS_WIRELESS &&
+ dd->bDeviceSubClass == UDSUBCLASS_RF &&
+ dd->bDeviceProtocol == UDPROTO_BLUETOOTH)
+ return (UMATCH_DEVCLASS_DEVSUBCLASS);
+
+ if (usb_lookup(ubt_broken_devices, uaa->vendor, uaa->product))
+ return (UMATCH_VENDOR_PRODUCT);
+
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(ubt)
+{
+ USB_ATTACH_START(ubt, sc, uaa);
+ usb_config_descriptor_t *cd = NULL;
+ usb_interface_descriptor_t *id = NULL;
+ usb_endpoint_descriptor_t *ed = NULL;
+ char devinfo[1024];
+ usbd_status error;
+ int i, ai, alt_no, isoc_in, isoc_out,
+ isoc_isize, isoc_osize;
+ struct ifnet *ifp = &sc->sc_if;
+
+ /* Get USB device info */
+ sc->sc_udev = uaa->device;
+ usbd_devinfo(sc->sc_udev, 0, devinfo, sizeof(devinfo));
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ /*
+ * Initialize device softc structure
+ */
+
+ /* State */
+ sc->sc_debug = NG_UBT_WARN_LEVEL;
+ sc->sc_flags = 0;
+ NG_UBT_STAT_RESET(sc->sc_stat);
+
+ /* Interfaces */
+ sc->sc_iface0 = sc->sc_iface1 = NULL;
+
+ /* Interrupt pipe */
+ sc->sc_intr_ep = -1;
+ sc->sc_intr_pipe = NULL;
+ sc->sc_intr_xfer = NULL;
+ sc->sc_intr_buffer = NULL;
+
+ /* Control pipe */
+ sc->sc_ctrl_xfer = NULL;
+ sc->sc_ctrl_buffer = NULL;
+ NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
+
+ /* Bulk-in pipe */
+ sc->sc_bulk_in_ep = -1;
+ sc->sc_bulk_in_pipe = NULL;
+ sc->sc_bulk_in_xfer = NULL;
+ sc->sc_bulk_in_buffer = NULL;
+
+ /* Bulk-out pipe */
+ sc->sc_bulk_out_ep = -1;
+ sc->sc_bulk_out_pipe = NULL;
+ sc->sc_bulk_out_xfer = NULL;
+ sc->sc_bulk_out_buffer = NULL;
+ NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
+
+ /* Isoc-in pipe */
+ sc->sc_isoc_in_ep = -1;
+ sc->sc_isoc_in_pipe = NULL;
+ sc->sc_isoc_in_xfer = NULL;
+
+ /* Isoc-out pipe */
+ sc->sc_isoc_out_ep = -1;
+ sc->sc_isoc_out_pipe = NULL;
+ sc->sc_isoc_out_xfer = NULL;
+ sc->sc_isoc_size = -1;
+ NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
+
+ /*
+ * XXX set configuration?
+ *
+ * Configure Bluetooth USB device. Discover all required USB interfaces
+ * and endpoints.
+ *
+ * USB device must present two interfaces:
+ * 1) Interface 0 that has 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
+ *
+ * 2) Interface 1 then has 2 endpoints
+ * 1) Isochronous IN endpoint to receive SCO data
+ * 2) Isochronous OUT endpoint to send SCO data
+ *
+ * Interface 1 (with isochronous endpoints) has several alternate
+ * configurations with different packet size.
+ */
+
+ /*
+ * Interface 0
+ */
+
+ error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface0);
+ if (error || sc->sc_iface0 == NULL) {
+ printf("%s: Could not get interface 0 handle. %s (%d), " \
+ "handle=%p\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(error), error, sc->sc_iface0);
+ goto bad;
+ }
+
+ id = usbd_get_interface_descriptor(sc->sc_iface0);
+ if (id == NULL) {
+ printf("%s: Could not get interface 0 descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ for (i = 0; i < id->bNumEndpoints; i ++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface0, i);
+ if (ed == NULL) {
+ printf("%s: Could not read endpoint descriptor for " \
+ "interface 0, i=%d\n", USBDEVNAME(sc->sc_dev),
+ i);
+ goto bad;
+ }
+
+ switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
+ case UE_BULK:
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+ sc->sc_bulk_in_ep = ed->bEndpointAddress;
+ else
+ sc->sc_bulk_out_ep = ed->bEndpointAddress;
+ break;
+
+ case UE_INTERRUPT:
+ sc->sc_intr_ep = ed->bEndpointAddress;
+ break;
+ }
+ }
+
+ /* Check if we got everything we wanted on Interface 0 */
+ if (sc->sc_intr_ep == -1) {
+ printf("%s: Could not detect interrupt endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_bulk_in_ep == -1) {
+ printf("%s: Could not detect bulk-in endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_bulk_out_ep == -1) {
+ printf("%s: Could not detect bulk-out endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ printf("%s: Interface 0 endpoints: interrupt=%#x, bulk-in=%#x, " \
+ "bulk-out=%#x\n", USBDEVNAME(sc->sc_dev),
+ sc->sc_intr_ep, sc->sc_bulk_in_ep, sc->sc_bulk_out_ep);
+
+ /*
+ * Interface 1
+ */
+
+ cd = usbd_get_config_descriptor(sc->sc_udev);
+ if (cd == NULL) {
+ printf("%s: Could not get device configuration descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ error = usbd_device2interface_handle(sc->sc_udev, 1, &sc->sc_iface1);
+ if (error || sc->sc_iface1 == NULL) {
+ printf("%s: Could not get interface 1 handle. %s (%d), " \
+ "handle=%p\n", USBDEVNAME(sc->sc_dev),
+ usbd_errstr(error), error, sc->sc_iface1);
+ goto bad;
+ }
+
+ id = usbd_get_interface_descriptor(sc->sc_iface1);
+ if (id == NULL) {
+ printf("%s: Could not get interface 1 descriptor\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ /*
+ * Scan all alternate configurations for interface 1
+ */
+
+ alt_no = -1;
+
+ for (ai = 0; ai < usbd_get_no_alts(cd, 1); ai++) {
+ error = usbd_set_interface(sc->sc_iface1, ai);
+ if (error) {
+ printf("%s: [SCAN] Could not set alternate " \
+ "configuration %d for interface 1. %s (%d)\n",
+ USBDEVNAME(sc->sc_dev), ai, usbd_errstr(error),
+ error);
+ goto bad;
+ }
+ id = usbd_get_interface_descriptor(sc->sc_iface1);
+ if (id == NULL) {
+ printf("%s: Could not get interface 1 descriptor for " \
+ "alternate configuration %d\n",
+ USBDEVNAME(sc->sc_dev), ai);
+ goto bad;
+ }
+
+ isoc_in = isoc_out = -1;
+ isoc_isize = isoc_osize = 0;
+
+ for (i = 0; i < id->bNumEndpoints; i ++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface1, i);
+ if (ed == NULL) {
+ printf("%s: Could not read endpoint " \
+ "descriptor for interface 1, " \
+ "alternate configuration %d, i=%d\n",
+ USBDEVNAME(sc->sc_dev), ai, i);
+ goto bad;
+ }
+
+ if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS)
+ continue;
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
+ isoc_in = ed->bEndpointAddress;
+ isoc_isize = UGETW(ed->wMaxPacketSize);
+ } else {
+ isoc_out = ed->bEndpointAddress;
+ isoc_osize = UGETW(ed->wMaxPacketSize);
+ }
+ }
+
+ /*
+ * Make sure that configuration looks sane and if so
+ * update current settings
+ */
+
+ if (isoc_in != -1 && isoc_out != -1 &&
+ isoc_isize > 0 && isoc_osize > 0 &&
+ isoc_isize == isoc_osize && isoc_isize > sc->sc_isoc_size) {
+ sc->sc_isoc_in_ep = isoc_in;
+ sc->sc_isoc_out_ep = isoc_out;
+ sc->sc_isoc_size = isoc_isize;
+ alt_no = ai;
+ }
+ }
+
+ /* Check if we got everything we wanted on Interface 0 */
+ if (sc->sc_isoc_in_ep == -1) {
+ printf("%s: Could not detect isoc-in endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_isoc_out_ep == -1) {
+ printf("%s: Could not detect isoc-out endpoint\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ if (sc->sc_isoc_size <= 0) {
+ printf("%s: Invalid isoc. packet size=%d\n",
+ USBDEVNAME(sc->sc_dev), sc->sc_isoc_size);
+ goto bad;
+ }
+
+ error = usbd_set_interface(sc->sc_iface1, alt_no);
+ if (error) {
+ printf("%s: Could not set alternate configuration " \
+ "%d for interface 1. %s (%d)\n", USBDEVNAME(sc->sc_dev),
+ alt_no, usbd_errstr(error), error);
+ goto bad;
+ }
+
+ /* Allocate USB transfer handles and buffers */
+ sc->sc_ctrl_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_ctrl_xfer == NULL) {
+ printf("%s: Could not allocate control xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_ctrl_buffer = usbd_alloc_buffer(sc->sc_ctrl_xfer,
+ UBT_CTRL_BUFFER_SIZE);
+ if (sc->sc_ctrl_buffer == NULL) {
+ printf("%s: Could not allocate control buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_intr_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_intr_xfer == NULL) {
+ printf("%s: Could not allocate interrupt xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_intr_buffer = usbd_alloc_buffer(sc->sc_intr_xfer,
+ UBT_INTR_BUFFER_SIZE);
+ if (sc->sc_intr_buffer == NULL) {
+ printf("%s: Could not allocate interrupt buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_bulk_in_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulk_in_xfer == NULL) {
+ printf("%s: Could not allocate bulk-in xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_bulk_in_buffer = usbd_alloc_buffer(sc->sc_bulk_in_xfer,
+ UBT_BULK_BUFFER_SIZE);
+ if (sc->sc_bulk_in_buffer == NULL) {
+ printf("%s: Could not allocate bulk-in buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_bulk_out_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_bulk_out_xfer == NULL) {
+ printf("%s: Could not allocate bulk-out xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_bulk_out_buffer = usbd_alloc_buffer(sc->sc_bulk_out_xfer,
+ UBT_BULK_BUFFER_SIZE);
+ if (sc->sc_bulk_out_buffer == NULL) {
+ printf("%s: Could not allocate bulk-out buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ /*
+ * Allocate buffers for isoc. transfers
+ */
+
+ sc->sc_isoc_nframes = (UBT_ISOC_BUFFER_SIZE / sc->sc_isoc_size) + 1;
+
+ sc->sc_isoc_in_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_isoc_in_xfer == NULL) {
+ printf("%s: Could not allocate isoc-in xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_in_buffer = usbd_alloc_buffer(sc->sc_isoc_in_xfer,
+ sc->sc_isoc_nframes * sc->sc_isoc_size);
+ if (sc->sc_isoc_in_buffer == NULL) {
+ printf("%s: Could not allocate isoc-in buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_in_frlen = malloc(sizeof(u_int16_t) * sc->sc_isoc_nframes,
+ M_USBDEV, M_NOWAIT);
+ if (sc->sc_isoc_in_frlen == NULL) {
+ printf("%s: Could not allocate isoc-in frame sizes buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ sc->sc_isoc_out_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_isoc_out_xfer == NULL) {
+ printf("%s: Could not allocate isoc-out xfer handle\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_out_buffer = usbd_alloc_buffer(sc->sc_isoc_out_xfer,
+ sc->sc_isoc_nframes * sc->sc_isoc_size);
+ if (sc->sc_isoc_out_buffer == NULL) {
+ printf("%s: Could not allocate isoc-out buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+ sc->sc_isoc_out_frlen = malloc(sizeof(u_int16_t) * sc->sc_isoc_nframes,
+ M_USBDEV, M_NOWAIT);
+ if (sc->sc_isoc_out_frlen == NULL) {
+ printf("%s: Could not allocate isoc-out frame sizes buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto bad;
+ }
+
+ printf("%s: Interface 1 (alt.config %d) endpoints: isoc-in=%#x, " \
+ "isoc-out=%#x; wMaxPacketSize=%d; nframes=%d, buffer size=%d\n",
+ USBDEVNAME(sc->sc_dev), alt_no, sc->sc_isoc_in_ep,
+ sc->sc_isoc_out_ep, sc->sc_isoc_size, sc->sc_isoc_nframes,
+ (sc->sc_isoc_nframes * sc->sc_isoc_size));
+
+ /*
+ * Open pipes
+ */
+
+ /* Interrupt */
+ error = usbd_open_pipe(sc->sc_iface0, sc->sc_intr_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_intr_pipe);
+ if (error != USBD_NORMAL_COMPLETION) {
+ printf("%s: %s - Could not open interrupt pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(error),
+ error);
+ goto bad;
+ }
+
+ /* Bulk-in */
+ error = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_in_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulk_in_pipe);
+ if (error != USBD_NORMAL_COMPLETION) {
+ printf("%s: %s - Could not open bulk-in pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(error),
+ error);
+ goto bad;
+ }
+
+ /* Bulk-out */
+ error = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_out_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_bulk_out_pipe);
+ if (error != USBD_NORMAL_COMPLETION) {
+ printf("%s: %s - Could not open bulk-out pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(error),
+ error);
+ goto bad;
+ }
+
+#if __broken__ /* XXX FIXME */
+ /* Isoc-in */
+ error = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_in_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_isoc_in_pipe);
+ if (error != USBD_NORMAL_COMPLETION) {
+ printf("%s: %s - Could not open isoc-in pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(error),
+ error);
+ goto bad;
+ }
+
+ /* Isoc-out */
+ error = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_out_ep,
+ USBD_EXCLUSIVE_USE, &sc->sc_isoc_out_pipe);
+ if (error != USBD_NORMAL_COMPLETION) {
+ printf("%s: %s - Could not open isoc-out pipe. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(error),
+ error);
+ goto bad;
+ }
+#endif /* __broken__ */
+
+ /* Start intr transfer */
+ error = ubt_intr_start(sc);
+ if (error != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ALERT(
+"%s: %s - Could not start interrupt transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(error),
+ error);
+ goto bad;
+ }
+
+ /* Start bulk-in transfer */
+ error = ubt_bulk_in_start(sc);
+ if (error != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ALERT(
+"%s: %s - Could not start bulk-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(error),
+ error);
+ goto bad;
+ }
+
+#if __broken__ /* XXX FIXME */
+ /* Start isoc-in transfer */
+ error = ubt_isoc_in_start(sc);
+ if (error != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ALERT(
+"%s: %s - Could not start isoc-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(error),
+ error);
+ goto bad;
+ }
+#endif /* __broken__ */
+
+ /* Claim all interfaces on the device */
+ for (i = 0; i < uaa->nifaces; i++)
+ uaa->ifaces[i] = NULL;
+
+ /* Attach network interface */
+ bzero(ifp, sizeof(*ifp));
+ strlcpy(ifp->if_xname, USBDEVNAME(sc->sc_dev), sizeof(ifp->if_xname));
+ ifp->if_softc = sc;
+ ifp->if_mtu = 65536;
+ ifp->if_start = ubt_if_start;
+ ifp->if_ioctl = ubt_if_ioctl;
+ ifp->if_init = ubt_if_init;
+ ifp->if_watchdog = ubt_if_watchdog;
+ ifp->if_type = IFT_BLUETOOTH;
+ IFQ_SET_READY(&ifp->if_snd);
+ if_attach(ifp);
+ if_alloc_sadl(ifp);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ USB_ATTACH_SUCCESS_RETURN;
+bad:
+#if 0
+ ubt_detach(self);
+#endif
+ USB_ATTACH_ERROR_RETURN;
+}
+
+USB_DETACH(ubt)
+{
+ USB_DETACH_START(ubt, sc);
+ struct ifnet *ifp = &sc->sc_if;
+
+ /* Close pipes */
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_close_pipe(sc->sc_intr_pipe);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ if (sc->sc_bulk_in_pipe != NULL) {
+ usbd_close_pipe(sc->sc_bulk_in_pipe);
+ sc->sc_bulk_in_pipe = NULL;
+ }
+ if (sc->sc_bulk_out_pipe != NULL) {
+ usbd_close_pipe(sc->sc_bulk_out_pipe);
+ sc->sc_bulk_out_pipe = NULL;
+ }
+
+ if (sc->sc_isoc_in_pipe != NULL) {
+ usbd_close_pipe(sc->sc_isoc_in_pipe);
+ sc->sc_isoc_in_pipe = NULL;
+ }
+ if (sc->sc_isoc_out_pipe != NULL) {
+ usbd_close_pipe(sc->sc_isoc_out_pipe);
+ sc->sc_isoc_out_pipe = NULL;
+ }
+
+ /* Destroy USB transfer handles */
+ if (sc->sc_ctrl_xfer != NULL) {
+ usbd_free_xfer(sc->sc_ctrl_xfer);
+ sc->sc_ctrl_xfer = NULL;
+ }
+
+ if (sc->sc_intr_xfer != NULL) {
+ usbd_free_xfer(sc->sc_intr_xfer);
+ sc->sc_intr_xfer = NULL;
+ }
+
+ if (sc->sc_bulk_in_xfer != NULL) {
+ usbd_free_xfer(sc->sc_bulk_in_xfer);
+ sc->sc_bulk_in_xfer = NULL;
+ }
+ if (sc->sc_bulk_out_xfer != NULL) {
+ usbd_free_xfer(sc->sc_bulk_out_xfer);
+ sc->sc_bulk_out_xfer = NULL;
+ }
+
+ if (sc->sc_isoc_in_xfer != NULL) {
+ usbd_free_xfer(sc->sc_isoc_in_xfer);
+ sc->sc_isoc_in_xfer = NULL;
+ }
+ if (sc->sc_isoc_out_xfer != NULL) {
+ usbd_free_xfer(sc->sc_isoc_out_xfer);
+ sc->sc_isoc_out_xfer = NULL;
+ }
+
+ /* Destroy isoc. frame size buffers */
+ if (sc->sc_isoc_in_frlen != NULL) {
+ free(sc->sc_isoc_in_frlen, M_USBDEV);
+ sc->sc_isoc_in_frlen = NULL;
+ }
+ if (sc->sc_isoc_out_frlen != NULL) {
+ free(sc->sc_isoc_out_frlen, M_USBDEV);
+ sc->sc_isoc_out_frlen = NULL;
+ }
+
+#if 0
+ /* Destroy queues */
+ NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
+#endif
+
+ if_detach(ifp);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ return (0);
+}
+
+int
+ubt_activate(device_ptr_t self, enum devact act)
+{
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ case DVACT_DEACTIVATE:
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Start USB control request (HCI command).
+ */
+Static usbd_status
+ubt_request_start(struct ubt_softc * sc)
+{
+ usb_device_request_t req;
+ struct mbuf *m = NULL;
+ usbd_status status;
+
+ KASSERT(!(sc->sc_flags & UBT_CMD_XMIT), (
+"%s: %s - Another control request is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev)));
+
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
+ if (m == NULL) {
+ NG_UBT_INFO(
+"%s: %s - HCI command queue is empty\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ /*
+ * Check HCI command frame size and copy it back to
+ * linear USB transfer buffer.
+ */
+
+ if (m->m_pkthdr.len > UBT_CTRL_BUFFER_SIZE)
+ panic(
+"%s: %s - HCI command frame too big, size=%zd, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), UBT_CTRL_BUFFER_SIZE,
+ m->m_pkthdr.len);
+
+ m_copydata(m, 0, m->m_pkthdr.len, sc->sc_ctrl_buffer);
+
+ /* Initialize a USB control request and then schedule it */
+ bzero(&req, sizeof(req));
+ req.bmRequestType = UBT_HCI_REQUEST;
+ USETW(req.wLength, m->m_pkthdr.len);
+
+ NG_UBT_INFO(
+"%s: %s - Sending control request, bmRequestType=%#x, wLength=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), req.bmRequestType,
+ UGETW(req.wLength));
+
+ usbd_setup_default_xfer(
+ sc->sc_ctrl_xfer,
+ sc->sc_udev,
+ (usbd_private_handle) sc,
+ USBD_DEFAULT_TIMEOUT, /* XXX */
+ &req,
+ sc->sc_ctrl_buffer,
+ m->m_pkthdr.len,
+ USBD_NO_COPY,
+ ubt_request_complete);
+
+ status = usbd_transfer(sc->sc_ctrl_xfer);
+ if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Could not start control request. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ usbd_errstr(status), status);
+
+ NG_BT_MBUFQ_DROP(&sc->sc_cmdq);
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ /* XXX FIXME should we try to resubmit another request? */
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Control request has been started\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+
+ sc->sc_flags |= UBT_CMD_XMIT;
+ status = USBD_NORMAL_COMPLETION;
+ }
+
+ NG_FREE_M(m);
+
+ return (status);
+} /* ubt_request_start */
+
+/*
+ * USB control request callback
+ */
+Static void
+ubt_request_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ struct ubt_softc * sc = p;
+
+ if (sc == NULL)
+ return;
+
+ KASSERT((sc->sc_flags & UBT_CMD_XMIT), (
+"%s: %s - No control request is pending\n", __func__, USBDEVNAME(sc->sc_dev)));
+
+ sc->sc_flags &= ~UBT_CMD_XMIT;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Control request cancelled\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_ERR(
+"%s: %s - Control request failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(h->pipe);
+
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Sent %d bytes to control pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
+ NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ }
+
+ if (NG_BT_MBUFQ_LEN(&sc->sc_cmdq) > 0)
+ ubt_request_start(sc);
+} /* ubt_request_complete2 */
+
+/*
+ * Start interrupt transfer. Must be called when node is locked
+ */
+
+Static usbd_status
+ubt_intr_start(struct ubt_softc * sc)
+{
+ struct mbuf *m = NULL;
+ usbd_status status;
+
+ KASSERT(!(sc->sc_flags & UBT_EVT_RECV), (
+"%s: %s - Another interrupt request is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev)));
+
+ /* Allocate new mbuf cluster */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (USBD_NOMEM);
+
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ NG_FREE_M(m);
+ return (USBD_NOMEM);
+ }
+
+ if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
+ *mtod(m, u_int8_t *) = NG_HCI_EVENT_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
+ } else
+ m->m_pkthdr.len = m->m_len = 0;
+
+ /* Initialize a USB transfer and then schedule it */
+ usbd_setup_xfer(
+ sc->sc_intr_xfer,
+ sc->sc_intr_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_intr_buffer,
+ UBT_INTR_BUFFER_SIZE,
+ USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT,
+ ubt_intr_complete);
+
+ status = usbd_transfer(sc->sc_intr_xfer);
+ if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Failed to start intrerrupt transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ NG_FREE_M(m);
+
+ return (status);
+ }
+
+ sc->sc_flags |= UBT_EVT_RECV;
+ sc->sc_intr_mbuf = m;
+
+ return (USBD_NORMAL_COMPLETION);
+} /* ubt_intr_start */
+
+/*
+ * Process interrupt from USB device (We got data from interrupt pipe)
+ */
+Static void
+ubt_intr_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ struct ubt_softc * sc = p;
+ struct mbuf *m = NULL;
+ ng_hci_event_pkt_t *hdr = NULL;
+ int error = 0;
+
+ if (sc == NULL)
+ return;
+
+ KASSERT((sc->sc_flags & UBT_EVT_RECV), (
+"%s: %s - No interrupt request is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev)));
+
+ sc->sc_flags &= ~UBT_EVT_RECV;
+
+ m = sc->sc_intr_mbuf;
+ bcopy(sc->sc_intr_buffer, mtod(m, void *) + m->m_len, h->actlen);
+
+ hdr = mtod(m, ng_hci_event_pkt_t *);
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Interrupt xfer cancelled\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ NG_FREE_M(m);
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Interrupt xfer failed, %s (%d). No new xfer will be submitted!\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+
+ return; /* XXX FIXME we should restart after some delay */
+ }
+
+ NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+ m->m_pkthdr.len += h->actlen;
+ m->m_len += h->actlen;
+
+ NG_UBT_INFO(
+"%s: %s - Got %d bytes from interrupt pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ if (m->m_pkthdr.len < sizeof(*hdr)) {
+ NG_FREE_M(m);
+ goto done;
+ }
+
+ if (hdr->length == m->m_pkthdr.len - sizeof(*hdr)) {
+ NG_UBT_INFO(
+"%s: %s - Got complete HCI event frame, pktlen=%d, length=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len,
+ hdr->length);
+
+ NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+
+#if 0
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+#endif
+ ng_btsocket_hci_raw_node_rcvdata(&sc->sc_if, m);
+ if (error != 0)
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ } else {
+ NG_UBT_ERR(
+"%s: %s - Invalid HCI event frame size, length=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), hdr->length,
+ m->m_pkthdr.len);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+done:
+ ubt_intr_start(sc);
+} /* ubt_intr_complete */
+
+/*
+ * Start bulk-in USB transfer (ACL data). Must be called when node is locked
+ */
+Static usbd_status
+ubt_bulk_in_start(struct ubt_softc * sc)
+{
+ struct mbuf *m = NULL;
+ usbd_status status;
+
+ KASSERT(!(sc->sc_flags & UBT_ACL_RECV), (
+"%s: %s - Another bulk-in request is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev)));
+
+ /* Allocate new mbuf cluster */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return (USBD_NOMEM);
+
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ NG_FREE_M(m);
+ return (USBD_NOMEM);
+ }
+
+ if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
+ *mtod(m, u_int8_t *) = NG_HCI_ACL_DATA_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
+ } else
+ m->m_pkthdr.len = m->m_len = 0;
+
+ /* Initialize a bulk-in USB transfer and then schedule it */
+ usbd_setup_xfer(
+ sc->sc_bulk_in_xfer,
+ sc->sc_bulk_in_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_bulk_in_buffer,
+ UBT_BULK_BUFFER_SIZE,
+ USBD_SHORT_XFER_OK,
+ USBD_NO_TIMEOUT,
+ ubt_bulk_in_complete);
+
+ status = usbd_transfer(sc->sc_bulk_in_xfer);
+ if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Failed to start bulk-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ NG_FREE_M(m);
+
+ return (status);
+ }
+
+ sc->sc_flags |= UBT_ACL_RECV;
+ sc->sc_bulk_in_mbuf = m;
+
+ return (USBD_NORMAL_COMPLETION);
+} /* ubt_bulk_in_start */
+
+/*
+ * USB bulk-in transfer callback
+ */
+Static void
+ubt_bulk_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ struct ubt_softc * sc = p;
+ struct mbuf *m = NULL;
+ ng_hci_acldata_pkt_t *hdr = NULL;
+ int len;
+
+ if (sc == NULL)
+ return;
+
+ KASSERT((sc->sc_flags & UBT_ACL_RECV), (
+"%s: %s - No bulk-in request is pending\n", __func__, USBDEVNAME(sc->sc_dev)));
+
+ sc->sc_flags &= ~UBT_ACL_RECV;
+
+ m = sc->sc_bulk_in_mbuf;
+ bcopy(sc->sc_intr_buffer, mtod(m, void *) + m->m_len, h->actlen);
+
+ hdr = mtod(m, ng_hci_acldata_pkt_t *);
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Bulk-in xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_bulk_in_pipe);
+
+ NG_FREE_M(m);
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Bulk-in xfer failed, %s (%d). No new xfer will be submitted!\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulk_in_pipe);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+
+ return; /* XXX FIXME we should restart after some delay */
+ }
+
+ NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+ m->m_pkthdr.len += h->actlen;
+ m->m_len += h->actlen;
+
+ NG_UBT_INFO(
+"%s: %s - Got %d bytes from bulk-in pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ if (m->m_pkthdr.len < sizeof(*hdr)) {
+ NG_FREE_M(m);
+ goto done;
+ }
+
+ len = le16toh(hdr->length);
+ if (len == m->m_pkthdr.len - sizeof(*hdr)) {
+ NG_UBT_INFO(
+"%s: %s - Got complete ACL data frame, pktlen=%d, length=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len, len);
+
+ NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+
+#if 0
+ NG_SEND_DATA_ONLY(len, sc->sc_hook, m);
+#endif
+ if (len != 0)
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ } else {
+ NG_UBT_ERR(
+"%s: %s - Invalid ACL frame size, length=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), len,
+ m->m_pkthdr.len);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+done:
+ ubt_bulk_in_start(sc);
+} /* ubt_bulk_in_complete2 */
+
+/*
+ * Start bulk-out USB transfer. Must be called with node locked
+ */
+
+Static usbd_status
+ubt_bulk_out_start(struct ubt_softc * sc)
+{
+ struct mbuf *m = NULL;
+ usbd_status status;
+
+ KASSERT(!(sc->sc_flags & UBT_ACL_XMIT), (
+"%s: %s - Another bulk-out request is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev)));
+
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
+ if (m == NULL) {
+ NG_UBT_INFO(
+"%s: %s - ACL data queue is empty\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ /*
+ * Check ACL data frame size and copy it back to linear USB
+ * transfer buffer.
+ */
+
+ if (m->m_pkthdr.len > UBT_BULK_BUFFER_SIZE)
+ panic(
+"%s: %s - ACL data frame too big, size=%d, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), UBT_BULK_BUFFER_SIZE,
+ m->m_pkthdr.len);
+
+ m_copydata(m, 0, m->m_pkthdr.len, sc->sc_bulk_out_buffer);
+
+ /* Initialize a bulk-out USB transfer and then schedule it */
+ usbd_setup_xfer(
+ sc->sc_bulk_out_xfer,
+ sc->sc_bulk_out_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_bulk_out_buffer,
+ m->m_pkthdr.len,
+ USBD_NO_COPY,
+ USBD_DEFAULT_TIMEOUT, /* XXX */
+ ubt_bulk_out_complete);
+
+ status = usbd_transfer(sc->sc_bulk_out_xfer);
+ if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Could not start bulk-out transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ NG_BT_MBUFQ_DROP(&sc->sc_aclq);
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+
+ /* XXX FIXME should we try to start another transfer? */
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Bulk-out transfer has been started, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len);
+
+ sc->sc_flags |= UBT_ACL_XMIT;
+ status = USBD_NORMAL_COMPLETION;
+ }
+
+ NG_FREE_M(m);
+
+ return (status);
+} /* ubt_bulk_out_start */
+
+/*
+ * USB bulk-out transfer callback
+ */
+Static void
+ubt_bulk_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ struct ubt_softc * sc = p;
+
+ if (sc == NULL)
+ return;
+
+ KASSERT((sc->sc_flags & UBT_ACL_XMIT), (
+"%s: %s - No bulk-out request is pending\n", __func__, USBDEVNAME(sc->sc_dev)));
+
+ sc->sc_flags &= ~UBT_ACL_XMIT;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Bulk-out xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_bulk_out_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Bulk-out xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_bulk_out_pipe);
+
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Sent %d bytes to bulk-out pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
+ NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ }
+
+ if (NG_BT_MBUFQ_LEN(&sc->sc_aclq) > 0)
+ ubt_bulk_out_start(sc);
+} /* ubt_bulk_out_complete2 */
+
+/*
+ * Start Isochronous-in USB transfer. Must be called with node locked
+ */
+
+Static usbd_status
+ubt_isoc_in_start(struct ubt_softc * sc)
+{
+ usbd_status status;
+ int i;
+
+ KASSERT(!(sc->sc_flags & UBT_SCO_RECV), (
+"%s: %s - Another isoc-in request is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev)));
+
+ /* Initialize a isoc-in USB transfer and then schedule it */
+ for (i = 0; i < sc->sc_isoc_nframes; i++)
+ sc->sc_isoc_in_frlen[i] = sc->sc_isoc_size;
+
+ usbd_setup_isoc_xfer(
+ sc->sc_isoc_in_xfer,
+ sc->sc_isoc_in_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_isoc_in_frlen,
+ sc->sc_isoc_nframes,
+ USBD_NO_COPY, /* XXX flags */
+ ubt_isoc_in_complete);
+
+ status = usbd_transfer(sc->sc_isoc_in_xfer);
+ if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Failed to start isoc-in transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ usbd_errstr(status), status);
+
+ return (status);
+ }
+
+ sc->sc_flags |= UBT_SCO_RECV;
+
+ return (USBD_NORMAL_COMPLETION);
+} /* ubt_isoc_in_start */
+
+/*
+ * USB isochronous transfer callback
+ */
+Static void
+ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ struct ubt_softc * sc = p;
+ struct mbuf *m = NULL;
+ ng_hci_scodata_pkt_t *hdr = NULL;
+ u_int8_t *b = NULL;
+ int i;
+
+ if (sc == NULL)
+ return;
+
+ KASSERT((sc->sc_flags & UBT_SCO_RECV), (
+"%s: %s - No isoc-in request is pending\n", __func__, USBDEVNAME(sc->sc_dev)));
+
+ sc->sc_flags &= ~UBT_SCO_RECV;
+
+#if 0
+ if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) {
+ NG_UBT_INFO(
+"%s: %s - No upstream hook\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ return;
+ }
+#endif
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Isoc-in xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_isoc_in_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Isoc-in xfer failed, %s (%d). No new xfer will be submitted!\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_isoc_in_pipe);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+
+ return; /* XXX FIXME we should restart after some delay */
+ }
+
+ NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen);
+
+ NG_UBT_INFO(
+"%s: %s - Got %d bytes from isoc-in pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ /* Copy SCO data frame to mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ NG_UBT_ALERT(
+"%s: %s - Could not allocate mbuf\n",
+ __func__, USBDEVNAME(sc->sc_dev));
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ goto done;
+ }
+
+ /* Fix SCO data frame header if required */
+ if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) {
+ *mtod(m, u_int8_t *) = NG_HCI_SCO_DATA_PKT;
+ m->m_pkthdr.len = 1;
+ m->m_len = min(MHLEN, h->actlen + 1); /* XXX m_copyback */
+ } else {
+ m->m_pkthdr.len = 0;
+ m->m_len = min(MHLEN, h->actlen); /* XXX m_copyback */
+ }
+
+ /*
+ * XXX FIXME how do we know how many frames we have received?
+ * XXX use frlen for now. is that correct?
+ */
+
+ b = (u_int8_t *) sc->sc_isoc_in_buffer;
+
+ for (i = 0; i < sc->sc_isoc_nframes; i++) {
+ b += (i * sc->sc_isoc_size);
+
+ if (sc->sc_isoc_in_frlen[i] > 0)
+ m_copyback(m, m->m_pkthdr.len,
+ sc->sc_isoc_in_frlen[i], b);
+ }
+
+ if (m->m_pkthdr.len < sizeof(*hdr))
+ goto done;
+
+ hdr = mtod(m, ng_hci_scodata_pkt_t *);
+
+ if (hdr->length == m->m_pkthdr.len - sizeof(*hdr)) {
+ NG_UBT_INFO(
+"%s: %s - Got complete SCO data frame, pktlen=%d, length=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len,
+ hdr->length);
+
+ NG_UBT_STAT_PCKTS_RECV(sc->sc_stat);
+
+#if 0
+ NG_SEND_DATA_ONLY(i, sc->sc_hook, m);
+#endif
+ if (i != 0)
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ } else {
+ NG_UBT_ERR(
+"%s: %s - Invalid SCO frame size, length=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), hdr->length,
+ m->m_pkthdr.len);
+
+ NG_UBT_STAT_IERROR(sc->sc_stat);
+ NG_FREE_M(m);
+ }
+done:
+ ubt_isoc_in_start(sc);
+} /* ubt_isoc_in_complete2 */
+
+/*
+ * Start isochronous-out USB transfer. Must be called with node locked
+ */
+
+Static usbd_status
+ubt_isoc_out_start(struct ubt_softc * sc)
+{
+ struct mbuf *m = NULL;
+ u_int8_t *b = NULL;
+ int i, len, nframes;
+ usbd_status status;
+
+ KASSERT(!(sc->sc_flags & UBT_SCO_XMIT), (
+"%s: %s - Another isoc-out request is pending\n",
+ __func__, USBDEVNAME(sc->sc_dev)));
+
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
+ if (m == NULL) {
+ NG_UBT_INFO(
+"%s: %s - SCO data queue is empty\n", __func__, USBDEVNAME(sc->sc_dev));
+
+ return (USBD_NORMAL_COMPLETION);
+ }
+
+ /* Copy entire SCO frame into USB transfer buffer and start transfer */
+ b = (u_int8_t *) sc->sc_isoc_out_buffer;
+ nframes = 0;
+
+ for (i = 0; i < sc->sc_isoc_nframes; i++) {
+ b += (i * sc->sc_isoc_size);
+
+ len = min(m->m_pkthdr.len, sc->sc_isoc_size);
+ if (len > 0) {
+ m_copydata(m, 0, len, b);
+ m_adj(m, len);
+ nframes ++;
+ }
+
+ sc->sc_isoc_out_frlen[i] = len;
+ }
+
+ if (m->m_pkthdr.len > 0)
+ panic(
+"%s: %s - SCO data frame is too big, nframes=%d, size=%d, len=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), sc->sc_isoc_nframes,
+ sc->sc_isoc_size, m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+
+ /* Initialize a isoc-out USB transfer and then schedule it */
+ usbd_setup_isoc_xfer(
+ sc->sc_isoc_out_xfer,
+ sc->sc_isoc_out_pipe,
+ (usbd_private_handle) sc,
+ sc->sc_isoc_out_frlen,
+ nframes,
+ USBD_NO_COPY,
+ ubt_isoc_out_complete);
+
+ status = usbd_transfer(sc->sc_isoc_out_xfer);
+ if (status != USBD_NORMAL_COMPLETION && status != USBD_IN_PROGRESS) {
+ NG_UBT_ERR(
+"%s: %s - Could not start isoc-out transfer. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status),
+ status);
+
+ NG_BT_MBUFQ_DROP(&sc->sc_scoq);
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Isoc-out transfer has been started, nframes=%d, size=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), nframes,
+ sc->sc_isoc_size);
+
+ sc->sc_flags |= UBT_SCO_XMIT;
+ status = USBD_NORMAL_COMPLETION;
+ }
+
+ return (status);
+} /* ubt_isoc_out_start */
+
+/*
+ * USB isoc-out. transfer callback
+ */
+Static void
+ubt_isoc_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s)
+{
+ struct ubt_softc * sc = p;
+
+ if (sc == NULL)
+ return;
+
+ KASSERT((sc->sc_flags & UBT_SCO_XMIT), (
+"%s: %s - No isoc-out request is pending\n", __func__, USBDEVNAME(sc->sc_dev)));
+
+ sc->sc_flags &= ~UBT_SCO_XMIT;
+
+ if (s == USBD_CANCELLED) {
+ NG_UBT_INFO(
+"%s: %s - Isoc-out xfer cancelled, pipe=%p\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ sc->sc_isoc_out_pipe);
+
+ return;
+ }
+
+ if (s != USBD_NORMAL_COMPLETION) {
+ NG_UBT_WARN(
+"%s: %s - Isoc-out xfer failed. %s (%d)\n",
+ __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(s), s);
+
+ if (s == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_isoc_out_pipe);
+
+ NG_UBT_STAT_OERROR(sc->sc_stat);
+ } else {
+ NG_UBT_INFO(
+"%s: %s - Sent %d bytes to isoc-out pipe\n",
+ __func__, USBDEVNAME(sc->sc_dev), h->actlen);
+
+ NG_UBT_STAT_BYTES_SENT(sc->sc_stat, h->actlen);
+ NG_UBT_STAT_PCKTS_SENT(sc->sc_stat);
+ }
+
+ if (NG_BT_MBUFQ_LEN(&sc->sc_scoq) > 0)
+ ubt_isoc_out_start(sc);
+} /* ubt_isoc_out_complete2 */
+
+/*
+ * Abort transfers on all USB pipes
+ */
+
+Static void
+ubt_reset(struct ubt_softc * sc)
+{
+ /* Interrupt */
+ if (sc->sc_intr_pipe != NULL)
+ usbd_abort_pipe(sc->sc_intr_pipe);
+
+ /* Bulk-in/out */
+ if (sc->sc_bulk_in_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulk_in_pipe);
+ if (sc->sc_bulk_out_pipe != NULL)
+ usbd_abort_pipe(sc->sc_bulk_out_pipe);
+
+ /* Isoc-in/out */
+ if (sc->sc_isoc_in_pipe != NULL)
+ usbd_abort_pipe(sc->sc_isoc_in_pipe);
+ if (sc->sc_isoc_out_pipe != NULL)
+ usbd_abort_pipe(sc->sc_isoc_out_pipe);
+
+#if 0
+ /* Cleanup queues */
+ NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
+#endif
+} /* ubt_reset */
+
+/*
+ * Process data
+ */
+Static int
+ubt_rcvdata(struct ubt_softc *sc, struct mbuf *m)
+{
+ usbd_status (*f)(struct ubt_softc *) = NULL;
+ struct ng_bt_mbufq *q = NULL;
+ int b, error = 0;
+
+ if (sc == NULL) {
+ error = EHOSTDOWN;
+ goto done;
+ }
+
+#if 0
+ if (hook != sc->sc_hook) {
+ error = EINVAL;
+ goto done;
+ }
+#endif
+
+#if 0
+ /* Deatch mbuf and get HCI frame type */
+ NGI_GET_M(item, m);
+#endif
+
+ /* Process HCI frame */
+ switch (*mtod(m, u_int8_t *)) { /* XXX call m_pullup ? */
+ case NG_HCI_CMD_PKT:
+ f = ubt_request_start;
+ q = &sc->sc_cmdq;
+ b = UBT_CMD_XMIT;
+ break;
+
+ case NG_HCI_ACL_DATA_PKT:
+ f = ubt_bulk_out_start;
+ q = &sc->sc_aclq;
+ b = UBT_ACL_XMIT;
+ break;
+
+#if __broken__ /* XXX FIXME */
+ case NG_HCI_SCO_DATA_PKT:
+ f = ubt_isoc_out_start;
+ q = &sc->sc_scoq;
+ b = UBT_SCO_XMIT;
+ break;
+#endif /* __broken__ */
+
+ default:
+ NG_UBT_ERR(
+"%s: %s - Dropping unknown/unsupported HCI frame, type=%d, pktlen=%d\n",
+ __func__, USBDEVNAME(sc->sc_dev), *mtod(m, u_int8_t *),
+ m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+ error = EINVAL;
+
+ goto done;
+ /* NOT REACHED */
+ }
+
+ /* Loose frame type, if required */
+ if (!(sc->sc_flags & UBT_NEED_FRAME_TYPE))
+ m_adj(m, sizeof(u_int8_t));
+
+ if (NG_BT_MBUFQ_FULL(q)) {
+ NG_UBT_ERR(
+"%s: %s - Dropping HCI frame %#x, len=%d. Queue full\n",
+ __func__, USBDEVNAME(sc->sc_dev),
+ *mtod(m, u_int8_t *), m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+ } else
+ NG_BT_MBUFQ_ENQUEUE(q, m);
+
+ if (!(sc->sc_flags & b))
+ if ((*f)(sc) != USBD_NORMAL_COMPLETION)
+ error = EIO;
+done:
+#if 0
+ NG_FREE_ITEM(item);
+#endif
+
+ return (error);
+} /* ubt_rcvdata */
+
+void
+ubt_stop(struct ubt_softc *sc)
+{
+ /* nothing yet */
+ return;
+}
+
+void
+ubt_if_start(struct ifnet *ifp)
+{
+ struct ubt_softc *sc = ifp->if_softc;
+ struct mbuf *m0;
+
+ for (;;) {
+ IFQ_POLL(&ifp->if_snd, m0);
+ if (m0 == NULL)
+ break;
+
+ IFQ_DEQUEUE(&ifp->if_snd, m0);
+ ubt_rcvdata(sc, m0);
+ }
+}
+
+int
+ubt_if_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct ubt_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s;
+ int error = 0;
+
+ s = splimp();
+ switch(command) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ ubt_if_init(ifp);
+ break;
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu > 65536)
+ error = EINVAL;
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ ubt_if_init(ifp);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ ubt_stop(sc);
+ }
+ error = 0;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ splx(s);
+
+ return(error);
+}
+
+int
+ubt_if_init(struct ifnet *ifp)
+{
+ /* nothing yet */
+ return (0);
+}
+
+void
+ubt_if_watchdog(struct ifnet *ifp)
+{
+ /* nothing yet */
+ return;
+}