diff options
-rw-r--r-- | sys/dev/usb/if_ubt.c | 1756 | ||||
-rw-r--r-- | sys/dev/usb/if_ubtreg.h | 207 | ||||
-rw-r--r-- | sys/dev/usb/ubt.c | 1508 |
3 files changed, 1508 insertions, 1963 deletions
diff --git a/sys/dev/usb/if_ubt.c b/sys/dev/usb/if_ubt.c deleted file mode 100644 index 8ce0ed58a08..00000000000 --- a/sys/dev/usb/if_ubt.c +++ /dev/null @@ -1,1756 +0,0 @@ -/* $OpenBSD: if_ubt.c,v 1.12 2007/05/28 15:48:02 fkr 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.12 2007/05/28 15:48:02 fkr 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_CLASS(ubt, DV_IFNET); - -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 *); - -int -ubt_match(struct device *parent, void *match, void *aux) -{ -#ifdef notyet - /* - * 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[] = { - }; -#endif - - /* - * 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. - */ - - Static struct usb_devno const ubt_broken_devices[] = { - { USB_VENDOR_CSR, USB_PRODUCT_CSR_BLUECORE }, - { USB_VENDOR_MSI, USB_PRODUCT_MSI_BLUETOOTH }, - { USB_VENDOR_MSI, USB_PRODUCT_MSI_BLUETOOTH_2 } - }; - - struct usb_attach_arg *uaa = aux; - usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device); - - if (uaa->iface == NULL) - return (UMATCH_NONE); -#ifdef notyet - if (usb_lookup(ubt_ignored_devices, uaa->vendor, uaa->product)) - return (UMATCH_NONE); -#endif - - 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); -} - -void -ubt_attach(struct device *parent, struct device *self, void *aux) -{ - struct ubt_softc *sc = (struct ubt_softc *)self; - struct usb_attach_arg *uaa = aux; - usb_config_descriptor_t *cd = NULL; - usb_interface_descriptor_t *id = NULL; - usb_endpoint_descriptor_t *ed = NULL; - char *devinfop; - 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; - - devinfop = usbd_devinfo_alloc(sc->sc_udev, 0); - printf("\n%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop); - usbd_devinfo_free(devinfop); - - /* - * 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); - return; - } - - id = usbd_get_interface_descriptor(sc->sc_iface0); - if (id == NULL) { - printf("%s: Could not get interface 0 descriptor\n", - USBDEVNAME(sc->sc_dev)); - return; - } - - 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); - return; - } - - 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)); - return; - } - if (sc->sc_bulk_in_ep == -1) { - printf("%s: Could not detect bulk-in endpoint\n", - USBDEVNAME(sc->sc_dev)); - return; - } - if (sc->sc_bulk_out_ep == -1) { - printf("%s: Could not detect bulk-out endpoint\n", - USBDEVNAME(sc->sc_dev)); - return; - } - - 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)); - return; - } - - 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); - return; - } - - id = usbd_get_interface_descriptor(sc->sc_iface1); - if (id == NULL) { - printf("%s: Could not get interface 1 descriptor\n", - USBDEVNAME(sc->sc_dev)); - return; - } - - /* - * 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); - return; - } - 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); - return; - } - - 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); - return; - } - - 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)); - return; - } - if (sc->sc_isoc_out_ep == -1) { - printf("%s: Could not detect isoc-out endpoint\n", - USBDEVNAME(sc->sc_dev)); - return; - } - if (sc->sc_isoc_size <= 0) { - printf("%s: Invalid isoc. packet size=%d\n", - USBDEVNAME(sc->sc_dev), sc->sc_isoc_size); - return; - } - - 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); - return; - } - - /* 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)); - return; - } - 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)); - return; - } - - 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)); - return; - } - 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)); - return; - } - - 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)); - return; - } - 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)); - return; - } - - 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)); - return; - } - 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)); - return; - } - - /* - * 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)); - return; - } - 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)); - return; - } - 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)); - return; - } - - 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)); - return; - } - 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)); - return; - } - 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)); - return; - } - - 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); - return; - } - - /* 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); - return; - } - - /* 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); - return; - } - -#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); - return; - } - - /* 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); - return; - } -#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); - return; - } - - /* 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); - return; - } - -#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); - return; - } -#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)); -} - -int -ubt_detach(struct device *self, int flags) -{ - struct ubt_softc *sc = (struct ubt_softc *)self; - 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=%lu, len=%d\n", - __func__, USBDEVNAME(sc->sc_dev), - (ulong)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 = letoh16(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 = splnet(); - 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; -} diff --git a/sys/dev/usb/if_ubtreg.h b/sys/dev/usb/if_ubtreg.h deleted file mode 100644 index f4d3bf0105a..00000000000 --- a/sys/dev/usb/if_ubtreg.h +++ /dev/null @@ -1,207 +0,0 @@ -/* $OpenBSD: if_ubtreg.h,v 1.1 2005/01/14 12:21:02 grange Exp $ */ - -/* - * ng_ubt_var.h - * - * 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_ubtreg.h,v 1.1 2005/01/14 12:21:02 grange Exp $ - * $FreeBSD: src/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h,v 1.4 2004/10/12 23:33:46 emax Exp $ - */ - -#ifndef _NG_UBT_VAR_H_ -#define _NG_UBT_VAR_H_ - -/************************************************************************** - ************************************************************************** - ** Netgraph node hook name, type name and type cookie and commands - ************************************************************************** - **************************************************************************/ - -#define NG_UBT_NODE_TYPE "ubt" -#define NG_UBT_HOOK "hook" - -#define NGM_UBT_COOKIE 1021837971 - -/* Debug levels */ -#define NG_UBT_ALERT_LEVEL 1 -#define NG_UBT_ERR_LEVEL 2 -#define NG_UBT_WARN_LEVEL 3 -#define NG_UBT_INFO_LEVEL 4 - -/************************************************************************** - ************************************************************************** - ** UBT node command/event parameters - ************************************************************************** - **************************************************************************/ - -#define NGM_UBT_NODE_SET_DEBUG 1 /* set debug level */ -#define NGM_UBT_NODE_GET_DEBUG 2 /* get debug level */ -typedef u_int16_t ng_ubt_node_debug_ep; - -#define NGM_UBT_NODE_SET_QLEN 3 /* set queue length */ -#define NGM_UBT_NODE_GET_QLEN 4 /* get queue length */ -typedef struct { - int32_t queue; /* queue index */ -#define NGM_UBT_NODE_QUEUE_CMD 1 /* commands */ -#define NGM_UBT_NODE_QUEUE_ACL 2 /* ACL data */ -#define NGM_UBT_NODE_QUEUE_SCO 3 /* SCO data */ - - int32_t qlen; /* queue length */ -} ng_ubt_node_qlen_ep; - -#define NGM_UBT_NODE_GET_STAT 5 /* get statistic */ -typedef struct { - u_int32_t pckts_recv; /* # of packets received */ - u_int32_t bytes_recv; /* # of bytes received */ - u_int32_t pckts_sent; /* # of packets sent */ - u_int32_t bytes_sent; /* # of bytes sent */ - u_int32_t oerrors; /* # of output errors */ - u_int32_t ierrors; /* # of input errors */ -} ng_ubt_node_stat_ep; - -#define NGM_UBT_NODE_RESET_STAT 6 /* reset statistic */ - -#define NGM_UBT_NODE_DEV_NODES 7 /* on/off device interface */ -typedef u_int16_t ng_ubt_node_dev_nodes_ep; - -/* pullup wrapper */ -#define NG_UBT_M_PULLUP(m, s) \ - do { \ - if ((m)->m_len < (s)) \ - (m) = m_pullup((m), (s)); \ - if ((m) == NULL) \ - NG_UBT_ALERT("%s: %s - m_pullup(%d) failed\n", \ - __func__, USBDEVNAME(sc->sc_dev), (s)); \ - } while (0) - -/* Debug printf's */ -#define NG_UBT_ALERT if (sc->sc_debug >= NG_UBT_ALERT_LEVEL) printf -#define NG_UBT_ERR if (sc->sc_debug >= NG_UBT_ERR_LEVEL) printf -#define NG_UBT_WARN if (sc->sc_debug >= NG_UBT_WARN_LEVEL) printf -#define NG_UBT_INFO if (sc->sc_debug >= NG_UBT_INFO_LEVEL) printf - -/* Bluetooth USB control request type */ -#define UBT_HCI_REQUEST 0x20 -#define UBT_DEFAULT_QLEN 12 - -/* USB device softc structure */ -struct ubt_softc { - USBBASEDEVICE sc_dev; /* pointer back to USB device */ - - /* State */ - ng_ubt_node_debug_ep sc_debug; /* debug level */ - u_int32_t sc_flags; /* device flags */ -#define UBT_NEED_FRAME_TYPE (1 << 0) /* device required frame type */ -#define UBT_HAVE_FRAME_TYPE UBT_NEED_FRAME_TYPE -#define UBT_CMD_XMIT (1 << 1) /* CMD xmit in progress */ -#define UBT_ACL_XMIT (1 << 2) /* ACL xmit in progress */ -#define UBT_SCO_XMIT (1 << 3) /* SCO xmit in progress */ -#define UBT_EVT_RECV (1 << 4) /* EVN recv in progress */ -#define UBT_ACL_RECV (1 << 5) /* ACL recv in progress */ -#define UBT_SCO_RECV (1 << 6) /* SCO recv in progress */ -#define UBT_CTRL_DEV (1 << 7) /* ctrl device is open */ -#define UBT_INTR_DEV (1 << 8) /* intr device is open */ -#define UBT_BULK_DEV (1 << 9) /* bulk device is open */ -#define UBT_ANY_DEV (UBT_CTRL_DEV|UBT_INTR_DEV|UBT_BULK_DEV) - - ng_ubt_node_stat_ep sc_stat; /* statistic */ -#define NG_UBT_STAT_PCKTS_SENT(s) (s).pckts_sent ++ -#define NG_UBT_STAT_BYTES_SENT(s, n) (s).bytes_sent += (n) -#define NG_UBT_STAT_PCKTS_RECV(s) (s).pckts_recv ++ -#define NG_UBT_STAT_BYTES_RECV(s, n) (s).bytes_recv += (n) -#define NG_UBT_STAT_OERROR(s) (s).oerrors ++ -#define NG_UBT_STAT_IERROR(s) (s).ierrors ++ -#define NG_UBT_STAT_RESET(s) bzero(&(s), sizeof((s))) - - /* USB device specific */ - usbd_device_handle sc_udev; /* USB device handle */ - - usbd_interface_handle sc_iface0; /* USB interface 0 */ - usbd_interface_handle sc_iface1; /* USB interface 1 */ - - /* Interrupt pipe (HCI events) */ - int sc_intr_ep; /* interrupt endpoint */ - usbd_pipe_handle sc_intr_pipe; /* interrupt pipe handle */ - usbd_xfer_handle sc_intr_xfer; /* intr xfer */ - struct mbuf *sc_intr_mbuf; /* interrupt mbuf */ - void *sc_intr_buffer; /* interrupt buffer */ -#define UBT_INTR_BUFFER_SIZE MCLBYTES - - /* Control pipe (HCI commands) */ - usbd_xfer_handle sc_ctrl_xfer; /* control xfer handle */ - void *sc_ctrl_buffer; /* control buffer */ - struct ng_bt_mbufq sc_cmdq; /* HCI command queue */ -#define UBT_CTRL_BUFFER_SIZE \ - (sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE) - - /* Bulk in pipe (ACL data) */ - int sc_bulk_in_ep; /* bulk-in enpoint */ - usbd_pipe_handle sc_bulk_in_pipe; /* bulk-in pipe */ - usbd_xfer_handle sc_bulk_in_xfer; /* bulk-in xfer */ - struct mbuf *sc_bulk_in_mbuf; /* bulk-in mbuf */ - void *sc_bulk_in_buffer; /* bulk-in buffer */ - - /* Bulk out pipe (ACL data) */ - int sc_bulk_out_ep; /* bulk-out endpoint */ - usbd_pipe_handle sc_bulk_out_pipe; /* bulk-out pipe */ - usbd_xfer_handle sc_bulk_out_xfer; /* bulk-out xfer */ - void *sc_bulk_out_buffer; /* bulk-out buffer */ - struct ng_bt_mbufq sc_aclq; /* ACL data queue */ -#define UBT_BULK_BUFFER_SIZE \ - MCLBYTES /* XXX should be big enough to hold one frame */ - - /* Isoc. in pipe (SCO data) */ - int sc_isoc_in_ep; /* isoc-in endpoint */ - usbd_pipe_handle sc_isoc_in_pipe; /* isoc-in pipe */ - usbd_xfer_handle sc_isoc_in_xfer; /* isoc-in xfer */ - void *sc_isoc_in_buffer; /* isoc-in buffer */ - u_int16_t *sc_isoc_in_frlen; /* isoc-in. frame length */ - - /* Isoc. out pipe (ACL data) */ - int sc_isoc_out_ep; /* isoc-out endpoint */ - usbd_pipe_handle sc_isoc_out_pipe; /* isoc-out pipe */ - usbd_xfer_handle sc_isoc_out_xfer; /* isoc-out xfer */ - void *sc_isoc_out_buffer; /* isoc-in buffer */ - u_int16_t *sc_isoc_out_frlen; /* isoc-out. frame length */ - struct ng_bt_mbufq sc_scoq; /* SCO data queue */ - - int sc_isoc_size; /* max. size of isoc. packet */ - u_int32_t sc_isoc_nframes; /* num. isoc. frames */ -#define UBT_ISOC_BUFFER_SIZE \ - (sizeof(ng_hci_scodata_pkt_t) + NG_HCI_SCO_PKT_SIZE) - -#if 0 - /* Netgraph specific */ - node_p sc_node; /* pointer back to node */ - hook_p sc_hook; /* upstream hook */ -#endif - - struct ifnet sc_if; -}; -typedef struct ubt_softc ubt_softc_t; -typedef struct ubt_softc * ubt_softc_p; - -#endif /* ndef _NG_UBT_VAR_H_ */ diff --git a/sys/dev/usb/ubt.c b/sys/dev/usb/ubt.c new file mode 100644 index 00000000000..474b706c1d1 --- /dev/null +++ b/sys/dev/usb/ubt.c @@ -0,0 +1,1508 @@ +/* $OpenBSD: ubt.c,v 1.1 2007/05/30 12:15:02 gwk 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. + * + * 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. + */ +/* + * 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> + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/ioctl.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/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> + +#include <netbt/bluetooth.h> +#include <netbt/hci.h> + +/******************************************************************************* + * + * debugging stuff + */ +#undef DPRINTF +#undef DPRINTFN + +#ifdef UBT_DEBUG +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) + +#else +#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) + +/* Transmit timeouts */ +#define UBT_CMD_TIMEOUT USBD_DEFAULT_TIMEOUT +#define UBT_ACL_TIMEOUT USBD_DEFAULT_TIMEOUT + +/* + * 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; + int sc_refcnt; + int sc_dying; + + /* Control Interface */ + usbd_interface_handle sc_iface0; + + /* 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; /* 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; /* 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; + + /* Successfully attached */ + int sc_ok; +}; + +/* + * Bluetooth unit/USB callback routines + */ +int ubt_enable(struct hci_unit *); +void ubt_disable(struct hci_unit *); + +void ubt_xmit_cmd_start(struct hci_unit *); +void ubt_xmit_cmd_complete(usbd_xfer_handle, + usbd_private_handle, usbd_status); + +void ubt_xmit_acl_start(struct hci_unit *); +void ubt_xmit_acl_complete(usbd_xfer_handle, + usbd_private_handle, usbd_status); + +void ubt_xmit_sco_start(struct hci_unit *); +void ubt_xmit_sco_start1(struct ubt_softc *, struct ubt_isoc_xfer *); +void ubt_xmit_sco_complete(usbd_xfer_handle, + usbd_private_handle, usbd_status); + +void ubt_recv_event(usbd_xfer_handle, + usbd_private_handle, usbd_status); + +void ubt_recv_acl_start(struct ubt_softc *); +void ubt_recv_acl_complete(usbd_xfer_handle, + usbd_private_handle, usbd_status); + +void ubt_recv_sco_start1(struct ubt_softc *, struct ubt_isoc_xfer *); +void ubt_recv_sco_complete(usbd_xfer_handle, + usbd_private_handle, usbd_status); + +USB_DECLARE_DRIVER(ubt); + +static int ubt_set_isoc_config(struct ubt_softc *); +static void ubt_abortdealloc(struct ubt_softc *); + +/* + * Match against the whole device, since we want to take + * both interfaces. If a device should be ignored then add + * + * { VendorID, ProductID } + * + * to the ubt_ignore list. + */ +static const struct usb_devno ubt_ignore[] = { + { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033NF }, + { 0, 0 } /* end of list */ +}; + +int +ubt_match(struct device *parent, void *match, void *aux) +{ + + struct usb_attach_arg *uaa = aux; + usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device); + + DPRINTFN(50, "ubt_match\n"); + + if (usb_lookup(ubt_ignore, uaa->vendor, uaa->product)) + return UMATCH_NONE; + + if (dd->bDeviceClass == UDCLASS_WIRELESS + && dd->bDeviceSubClass == UDSUBCLASS_RF + && dd->bDeviceProtocol == UDPROTO_BLUETOOTH) + return UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO; + + return UMATCH_NONE; +} + + +void +ubt_attach(struct device *parent, struct device *self, void *aux) +{ + struct ubt_softc *sc = (struct ubt_softc *)self; + struct usb_attach_arg *uaa = aux; + usb_config_descriptor_t *cd; + usb_endpoint_descriptor_t *ed; + 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); + printf("\n%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop); + usbd_devinfo_free(devinfop); + + /* + * Move the device into the configured state + */ + err = usbd_set_config_index(sc->sc_udev, 0, 1); + if (err) { + printf("%s: failed to set configuration idx 0: %s\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err)); + + return; + } + + /* + * 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 + */ + err = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface0); + if (err) { + printf("%s: Could not get interface 0 handle %s (%d)\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err), err); + + return; + } + + sc->sc_evt_addr = -1; + sc->sc_aclrd_addr = -1; + sc->sc_aclwr_addr = -1; + + count = 0; + (void)usbd_endpoint_count(sc->sc_iface0, &count); + + for (i = 0 ; i < count ; i++) { + int dir, type; + + ed = usbd_interface2endpoint_descriptor(sc->sc_iface0, i); + if (ed == NULL) { + printf("%s: could not read endpoint descriptor %d\n", + USBDEVNAME(sc->sc_dev), i); + + return; + } + + dir = UE_GET_DIR(ed->bEndpointAddress); + type = UE_GET_XFERTYPE(ed->bmAttributes); + + if (dir == UE_DIR_IN && type == UE_INTERRUPT) + sc->sc_evt_addr = ed->bEndpointAddress; + else if (dir == UE_DIR_IN && type == UE_BULK) + sc->sc_aclrd_addr = ed->bEndpointAddress; + else if (dir == UE_DIR_OUT && type == UE_BULK) + sc->sc_aclwr_addr = ed->bEndpointAddress; + } + + if (sc->sc_evt_addr == -1) { + printf("%s: missing INTERRUPT endpoint on interface 0\n", + USBDEVNAME(sc->sc_dev)); + + return; + } + if (sc->sc_aclrd_addr == -1) { + printf("%s: missing BULK IN endpoint on interface 0\n", + USBDEVNAME(sc->sc_dev)); + + return; + } + if (sc->sc_aclwr_addr == -1) { + printf("%s: missing BULK OUT endpoint on interface 0\n", + USBDEVNAME(sc->sc_dev)); + + return; + } + + /* + * 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, &sc->sc_iface1); + if (err) { + printf("%s: Could not get interface 1 handle %s (%d)\n", + USBDEVNAME(sc->sc_dev), usbd_errstr(err), err); + + return; + } + + cd = usbd_get_config_descriptor(sc->sc_udev); + if (cd == NULL) { + printf("%s: could not get config descriptor\n", + USBDEVNAME(sc->sc_dev)); + + return; + } + + sc->sc_alt_config = usbd_get_no_alts(cd, 1); + + /* set initial config */ + err = ubt_set_isoc_config(sc); + if (err) { + printf("%s: ISOC config failed\n", + USBDEVNAME(sc->sc_dev)); + + return; + } + + /* Attach HCI */ + sc->sc_unit.hci_softc = self; + 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)); + + sc->sc_ok = 1; + + return; +} + +int +ubt_detach(struct device *self, int flags) +{ + struct ubt_softc *sc = (struct ubt_softc *)self; + int s; + + DPRINTF("sc=%p flags=%d\n", sc, flags); + + sc->sc_dying = 1; + + if (!sc->sc_ok) + return 0; + + /* 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); + + /* wait for all processes to finish */ + s = splusb(); + if (sc->sc_refcnt-- > 0) + usb_detach_wait(USBDEV(sc->sc_dev)); + + splx(s); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + DPRINTFN(1, "driver detached\n"); + + return 0; +} + +int +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; + break; + + case DVACT_DEACTIVATE: + sc->sc_dying = 1; + break; + } + return error; +} + +/* set ISOC configuration */ +int +ubt_set_isoc_config(struct ubt_softc *sc) +{ + 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 != USBD_NORMAL_COMPLETION) { + printf( + "%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 == USBD_IN_USE ? EBUSY : EIO; + } + + /* + * 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) { + printf( + "%s: missing ISOC IN endpoint on interface config %d\n", + USBDEVNAME(sc->sc_dev), sc->sc_config); + + return ENOENT; + } + if (wr_addr == -1) { + printf( + "%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; +} + +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 + */ +int +ubt_enable(struct hci_unit *unit) +{ + struct ubt_softc *sc = (struct ubt_softc *)unit->hci_softc; + usbd_status err; + int i, error; + + DPRINTFN(1, "sc=%p\n", sc); + + if (unit->hci_flags & BTF_RUNNING) + return 0; + + /* Events */ + sc->sc_evt_buf = malloc(UBT_BUFSIZ_EVENT, M_USBDEV, M_NOWAIT); + if (sc->sc_evt_buf == NULL) { + error = ENOMEM; + goto bad; + } + 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, + USBD_DEFAULT_INTERVAL); + if (err != USBD_NORMAL_COMPLETION) { + error = EIO; + goto bad; + } + + /* 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; + } + + /* 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 bad; + } + sc->sc_aclrd_xfer = usbd_alloc_xfer(sc->sc_udev); + if (sc->sc_aclrd_xfer == NULL) { + error = ENOMEM; + goto bad; + } + sc->sc_aclrd_buf = usbd_alloc_buffer(sc->sc_aclrd_xfer, UBT_BUFSIZ_ACL); + if (sc->sc_aclrd_buf == NULL) { + error = ENOMEM; + goto bad; + } + sc->sc_aclrd_busy = 0; + ubt_recv_acl_start(sc); + + /* 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_aclwr_xfer = usbd_alloc_xfer(sc->sc_udev); + if (sc->sc_aclwr_xfer == NULL) { + error = ENOMEM; + goto bad; + } + sc->sc_aclwr_buf = usbd_alloc_buffer(sc->sc_aclwr_xfer, UBT_BUFSIZ_ACL); + if (sc->sc_aclwr_buf == NULL) { + error = ENOMEM; + goto bad; + } + + /* 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; + } + + 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]); + } + } + + /* 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; + } + + 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; + } + } + + unit->hci_flags &= ~BTF_XMIT; + unit->hci_flags |= BTF_RUNNING; + return 0; + +bad: + ubt_abortdealloc(sc); + return error; +} + +void +ubt_disable(struct hci_unit *unit) +{ + struct ubt_softc *sc = (struct ubt_softc *)unit->hci_softc; + + DPRINTFN(1, "sc=%p\n", sc); + + if ((unit->hci_flags & BTF_RUNNING) == 0) + return; + + ubt_abortdealloc(sc); + + unit->hci_flags &= ~BTF_RUNNING; +} + +void +ubt_xmit_cmd_start(struct hci_unit *unit) +{ + struct ubt_softc *sc = (struct ubt_softc *)unit->hci_softc; + usb_device_request_t req; + usbd_status status; + struct mbuf *m; + int len; + + if (sc->sc_dying) + return; + + if (IF_IS_EMPTY(&unit->hci_cmdq)) + return; + + IF_DEQUEUE(&unit->hci_cmdq, m); + + 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_cmd_xfer, + sc->sc_udev, + 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_cmd_xfer); + + 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_CMD; + } +} + +void +ubt_xmit_cmd_complete(usbd_xfer_handle xfer, + usbd_private_handle h, usbd_status status) +{ + struct hci_unit *unit = h; + struct ubt_softc *sc = (struct ubt_softc *)unit->hci_softc; + uint32_t count; + + 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; + + ubt_xmit_cmd_start(unit); +} + +void +ubt_xmit_acl_start(struct hci_unit *unit) +{ + struct ubt_softc *sc = (struct ubt_softc *)unit->hci_softc; + struct mbuf *m; + usbd_status status; + int len; + + if (sc->sc_dying) + return; + + if (IF_IS_EMPTY(&unit->hci_acltxq) == NULL) + return; + + sc->sc_refcnt++; + unit->hci_flags |= BTF_XMIT_ACL; + + IF_DEQUEUE(&unit->hci_acltxq, m); + + 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, + 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); + + 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; + } +} + +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 = (struct ubt_softc *)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 (sc->sc_dying) + return; + + 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); +} + +void +ubt_xmit_sco_start(struct hci_unit *unit) +{ + struct ubt_softc *sc = (struct ubt_softc *)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]); + } +} + +void +ubt_xmit_sco_start1(struct ubt_softc *sc, struct ubt_isoc_xfer *isoc) +{ + struct mbuf *m; + uint8_t *buf; + int num, len, size, space; + + 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) { + IF_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); +} + +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; + + if (status != USBD_NORMAL_COMPLETION) { + DPRINTF("status=%s (%d)\n", + usbd_errstr(status), status); + + 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; +} + +void +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; + + DPRINTFN(15, "sc=%p status=%s (%d)\n", + sc, usbd_errstr(status), status); + + if (status != USBD_NORMAL_COMPLETION || sc->sc_dying) + return; + + usbd_get_xfer_status(xfer, NULL, &buf, &count, NULL); + + if (count < sizeof(hci_event_hdr_t) - 1) { + DPRINTF("dumped undersized event (count = %d)\n", count); + sc->sc_unit.hci_stats.err_rx++; + return; + } + + sc->sc_unit.hci_stats.evt_rx++; + sc->sc_unit.hci_stats.byte_rx += count; + + 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++; +} + +void +ubt_recv_acl_start(struct ubt_softc *sc) +{ + usbd_status status; + + DPRINTFN(15, "sc=%p\n", sc); + + if (sc->sc_aclrd_busy || sc->sc_dying) { + DPRINTF("sc_aclrd_busy=%d, sc_dying=%d\n", + sc->sc_aclrd_busy, + sc->sc_dying); + + return; + } + + 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); + + KASSERT(status != USBD_NORMAL_COMPLETION); + + if (status != USBD_IN_PROGRESS) { + DPRINTF("usbd_transfer status=%s (%d)\n", + usbd_errstr(status), status); + + sc->sc_refcnt--; + sc->sc_aclrd_busy = 0; + } +} + +void +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; + + DPRINTFN(15, "sc=%p status=%s (%d)\n", + sc, usbd_errstr(status), status); + + 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 (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_aclrd_pipe); + else + return; + } else { + usbd_get_xfer_status(xfer, NULL, &buf, &count, NULL); + + if (count < sizeof(hci_acldata_hdr_t) - 1) { + DPRINTF("dumped undersized packet (%d)\n", count); + sc->sc_unit.hci_stats.err_rx++; + } else { + 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); +} + +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); +} + +void +ubt_recv_sco_complete(usbd_xfer_handle xfer, + usbd_private_handle h, usbd_status status) +{ + 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); +} |