diff options
Diffstat (limited to 'sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c')
-rw-r--r-- | sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c | 1639 |
1 files changed, 1093 insertions, 546 deletions
diff --git a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c index 603d722..d2ef0f1 100644 --- a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c +++ b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt.c @@ -25,22 +25,22 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ng_ubt.c,v 1.1 2002/11/09 19:09:02 max Exp $ + * $Id: ng_ubt.c,v 1.14 2003/04/14 23:00:50 max Exp $ * $FreeBSD$ */ #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> +#include <sys/conf.h> #include <sys/endian.h> -#include <sys/interrupt.h> +#include <sys/filio.h> +#include <sys/fcntl.h> #include <sys/mbuf.h> #include <sys/malloc.h> #include <sys/kernel.h> - -#include <sys/socket.h> -#include <net/if.h> -#include <net/if_var.h> +#include <sys/poll.h> +#include <sys/vnode.h> #include <dev/usb/usb.h> #include <dev/usb/usbdi.h> @@ -62,33 +62,39 @@ USB_DECLARE_DRIVER(ubt); -Static int ubt_modevent (module_t, int, void *); +Static int ubt_modevent (module_t, int, void *); -Static usbd_status ubt_request_start (ubt_softc_p, struct mbuf *); -Static void ubt_request_complete (usbd_xfer_handle, - usbd_private_handle, usbd_status); +Static usbd_status ubt_request_start (ubt_softc_p); +Static void ubt_request_complete (usbd_xfer_handle, + usbd_private_handle, usbd_status); +Static void ubt_request_complete2 (node_p, hook_p, void *, int); -Static usbd_status ubt_intr_start (ubt_softc_p); -Static void ubt_intr_complete (usbd_xfer_handle, - usbd_private_handle, usbd_status); +Static usbd_status ubt_intr_start (ubt_softc_p); +Static void ubt_intr_complete (usbd_xfer_handle, + usbd_private_handle, usbd_status); +Static void ubt_intr_complete2 (node_p, hook_p, void *, int); -Static usbd_status ubt_bulk_in_start (ubt_softc_p); -Static void ubt_bulk_in_complete (usbd_xfer_handle, - usbd_private_handle, usbd_status); +Static usbd_status ubt_bulk_in_start (ubt_softc_p); +Static void ubt_bulk_in_complete (usbd_xfer_handle, + usbd_private_handle, usbd_status); +Static void ubt_bulk_in_complete2 (node_p, hook_p, void *, int); -Static usbd_status ubt_bulk_out_start (ubt_softc_p, struct mbuf *); -Static void ubt_bulk_out_complete (usbd_xfer_handle, - usbd_private_handle, usbd_status); +Static usbd_status ubt_bulk_out_start (ubt_softc_p); +Static void ubt_bulk_out_complete (usbd_xfer_handle, + usbd_private_handle, usbd_status); +Static void ubt_bulk_out_complete2 (node_p, hook_p, void *, int); -Static usbd_status ubt_isoc_in_start (ubt_softc_p); -Static void ubt_isoc_in_complete (usbd_xfer_handle, - usbd_private_handle, usbd_status); +Static usbd_status ubt_isoc_in_start (ubt_softc_p); +Static void ubt_isoc_in_complete (usbd_xfer_handle, + usbd_private_handle, usbd_status); +Static void ubt_isoc_in_complete2 (node_p, hook_p, void *, int); -Static usbd_status ubt_isoc_out_start (ubt_softc_p, struct mbuf *); -Static void ubt_isoc_out_complete (usbd_xfer_handle, - usbd_private_handle, usbd_status); +Static usbd_status ubt_isoc_out_start (ubt_softc_p); +Static void ubt_isoc_out_complete (usbd_xfer_handle, + usbd_private_handle, usbd_status); +Static void ubt_isoc_out_complete2 (node_p, hook_p, void *, int); -Static void ubt_swi_intr (void *); +Static void ubt_reset (ubt_softc_p); /* * Netgraph methods @@ -101,7 +107,6 @@ Static ng_connect_t ng_ubt_connect; Static ng_disconnect_t ng_ubt_disconnect; Static ng_rcvmsg_t ng_ubt_rcvmsg; Static ng_rcvdata_t ng_ubt_rcvdata; -Static void ng_ubt_reset (ubt_softc_p); /* Queue length */ Static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = @@ -175,6 +180,13 @@ Static const struct ng_cmdlist ng_ubt_cmdlist[] = { NULL, NULL }, +{ + NGM_UBT_COOKIE, + NGM_UBT_NODE_DEV_NODES, + "dev_nodes", + &ng_parse_uint16_type, + NULL +}, { 0, } }; @@ -191,7 +203,42 @@ Static struct ng_type typestruct = { ng_ubt_connect, /* connect hook */ ng_ubt_rcvdata, /* data */ ng_ubt_disconnect, /* disconnect hook */ - ng_ubt_cmdlist /* node command list */ + ng_ubt_cmdlist, /* node command list */ +}; + +/* + * Device methods + */ + +#define UBT_UNIT(n) ((minor(n) >> 4) & 0xf) +#define UBT_ENDPOINT(n) (minor(n) & 0xf) +#define UBT_MINOR(u, e) (((u) << 4) | (e)) +#define UBT_BSIZE 1024 + +Static d_open_t ubt_open; +Static d_close_t ubt_close; +Static d_read_t ubt_read; +Static d_write_t ubt_write; +Static d_ioctl_t ubt_ioctl; +Static d_poll_t ubt_poll; +Static void ubt_create_device_nodes (ubt_softc_p); +Static void ubt_destroy_device_nodes (ubt_softc_p); + +#if __FreeBSD_version < 500104 +#define CDEV_MAJOR 222 +#else +#define CDEV_MAJOR MAJOR_AUTO +#endif + +Static struct cdevsw ubt_cdevsw = { + .d_open = ubt_open, + .d_close = ubt_close, + .d_read = ubt_read, + .d_write = ubt_write, + .d_ioctl = ubt_ioctl, + .d_poll = ubt_poll, + .d_name = "ubt", + .d_maj = CDEV_MAJOR, }; /* @@ -221,8 +268,9 @@ ubt_modevent(module_t mod, int event, void *data) case MOD_LOAD: error = ng_newtype(&typestruct); if (error != 0) - printf("%s: Could not register Netgraph node type, " \ - "error=%d\n", NG_UBT_NODE_TYPE, error); + printf( +"%s: Could not register Netgraph node type, error=%d\n", + NG_UBT_NODE_TYPE, error); else error = usbd_driver_load(mod, event, data); break; @@ -247,23 +295,48 @@ ubt_modevent(module_t mod, int event, void *data) USB_MATCH(ubt) { - Static struct usb_devno const ubt_devices[] = { - { USB_VENDOR_3COM, USB_PRODUCT_3COM_3CREB96 }, - { USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_BT_DONGLE }, - { USB_VENDOR_TDK, USB_PRODUCT_TDK_BT_DONGLE }, - { USB_VENDOR_MSI, USB_PRODUCT_MSI_BT_DONGLE }, - { USB_VENDOR_BROADCOM, USB_PRODUCT_DBW_120M_BT_DONGLE }, - { USB_VENDOR_EPOX, USB_PRODUCT_BT_DG02_DONGLE }, - { 0, 0 } + /* + * 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[] = { + { 0, 0 } /* This should be the last item in the list */ }; USB_MATCH_START(ubt, uaa); - if (uaa->iface == NULL || - usb_lookup(ubt_devices, uaa->vendor, uaa->product) == NULL) + 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_VENDOR_PRODUCT); + return (UMATCH_NONE); } /* USB_MATCH(ubt) */ /* @@ -276,7 +349,7 @@ USB_ATTACH(ubt) usb_config_descriptor_t *cd = NULL; usb_interface_descriptor_t *id = NULL; usb_endpoint_descriptor_t *ed = NULL; - char devinfo[1024]; + char devinfo[UBT_BSIZE]; usbd_status error; int i, ai, alt_no, isoc_in, isoc_out, isoc_isize, isoc_osize; @@ -299,11 +372,6 @@ USB_ATTACH(ubt) /* Interfaces */ sc->sc_iface0 = sc->sc_iface1 = NULL; - /* Input queue */ - bzero(&sc->sc_inq, sizeof(sc->sc_inq)); - sc->sc_inq.ifq_maxlen = UBT_DEFAULT_QLEN; - mtx_init(&sc->sc_inq.ifq_mtx, "UBT inq", NULL, MTX_DEF); - /* Interrupt pipe */ sc->sc_intr_ep = -1; sc->sc_intr_pipe = NULL; @@ -313,9 +381,7 @@ USB_ATTACH(ubt) /* Control pipe */ sc->sc_ctrl_xfer = NULL; sc->sc_ctrl_buffer = NULL; - bzero(&sc->sc_cmdq, sizeof(sc->sc_cmdq)); - sc->sc_cmdq.ifq_maxlen = UBT_DEFAULT_QLEN; - mtx_init(&sc->sc_cmdq.ifq_mtx, "UBT cmdq", NULL, MTX_DEF); + NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); /* Bulk-in pipe */ sc->sc_bulk_in_ep = -1; @@ -328,9 +394,7 @@ USB_ATTACH(ubt) sc->sc_bulk_out_pipe = NULL; sc->sc_bulk_out_xfer = NULL; sc->sc_bulk_out_buffer = NULL; - bzero(&sc->sc_aclq, sizeof(sc->sc_aclq)); - sc->sc_aclq.ifq_maxlen = UBT_DEFAULT_QLEN; - mtx_init(&sc->sc_aclq.ifq_mtx, "UBT aclq", NULL, MTX_DEF); + NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); /* Isoc-in pipe */ sc->sc_isoc_in_ep = -1; @@ -342,21 +406,15 @@ USB_ATTACH(ubt) sc->sc_isoc_out_pipe = NULL; sc->sc_isoc_out_xfer = NULL; sc->sc_isoc_size = -1; - bzero(&sc->sc_scoq, sizeof(sc->sc_scoq)); - sc->sc_scoq.ifq_maxlen = UBT_DEFAULT_QLEN; - mtx_init(&sc->sc_scoq.ifq_mtx, "UBT scoq", NULL, MTX_DEF); + NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); /* Netgraph part */ sc->sc_node = NULL; sc->sc_hook = NULL; - /* Attach SWI handler to TTY SWI thread */ - sc->sc_ith = NULL; - if (swi_add(&tty_ithd, USBDEVNAME(sc->sc_dev), - ubt_swi_intr, sc, SWI_TTY, 0, &sc->sc_ith) < 0) { - printf("%s: Could not setup SWI ISR\n", USBDEVNAME(sc->sc_dev)); - goto bad; - } + /* Device part */ + sc->sc_ctrl_dev = sc->sc_intr_dev = sc->sc_bulk_dev = NODEV; + sc->sc_refcnt = sc->sc_dying = 0; /* * XXX set configuration? @@ -396,16 +454,6 @@ USB_ATTACH(ubt) USBDEVNAME(sc->sc_dev)); goto bad; } - if (id->bInterfaceClass != UICLASS_WIRELESS_CONTROLLER || - id->bInterfaceSubClass != UISUBCLASS_RF_CONTROLLER || - id->bInterfaceProtocol != UIPROTO_BLUETOOTH) { - printf("%s: Interface 0 is not supported, " \ - "bInterfaceClass=%#x, bInterfaceSubClass=%#x, "\ - "bInterfaceProtocol=%#x\n", USBDEVNAME(sc->sc_dev), - id->bInterfaceClass, id->bInterfaceSubClass, - id->bInterfaceProtocol); - goto bad; - } for (i = 0; i < id->bNumEndpoints; i ++) { ed = usbd_interface2endpoint_descriptor(sc->sc_iface0, i); @@ -476,16 +524,6 @@ USB_ATTACH(ubt) USBDEVNAME(sc->sc_dev)); goto bad; } - if (id->bInterfaceClass != UICLASS_WIRELESS_CONTROLLER || - id->bInterfaceSubClass != UISUBCLASS_RF_CONTROLLER || - id->bInterfaceProtocol != UIPROTO_BLUETOOTH) { - printf("%s: Interface 1 is not supported, " \ - "bInterfaceClass=%#x, bInterfaceSubClass=%#x, "\ - "bInterfaceProtocol=%#x\n", USBDEVNAME(sc->sc_dev), - id->bInterfaceClass, id->bInterfaceSubClass, - id->bInterfaceProtocol); - goto bad; - } /* * Scan all alternate configurations for interface 1 @@ -596,13 +634,6 @@ USB_ATTACH(ubt) 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) { @@ -610,13 +641,6 @@ USB_ATTACH(ubt) 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) { @@ -686,6 +710,62 @@ USB_ATTACH(ubt) 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__ */ + /* Create Netgraph node */ if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { printf("%s: Could not create Netgraph node\n", @@ -704,15 +784,15 @@ USB_ATTACH(ubt) } NG_NODE_SET_PRIVATE(sc->sc_node, sc); + NG_NODE_FORCE_WRITER(sc->sc_node); - /* - * XXX Is that correct? - * Claim all interfaces on the device - */ - + /* Claim all interfaces on the device */ for (i = 0; i < uaa->nifaces; i++) uaa->ifaces[i] = NULL; + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + USB_ATTACH_SUCCESS_RETURN; bad: ubt_detach(self); @@ -728,7 +808,9 @@ USB_DETACH(ubt) { USB_DETACH_START(ubt, sc); - ng_ubt_reset(sc); + sc->sc_dying = 1; + + ubt_destroy_device_nodes(sc); /* XXX FIXME locking? */ /* Destroy Netgraph node */ if (sc->sc_node != NULL) { @@ -737,6 +819,30 @@ USB_DETACH(ubt) sc->sc_node = NULL; } + /* 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); @@ -776,70 +882,43 @@ USB_DETACH(ubt) sc->sc_isoc_out_frlen = NULL; } - if (sc->sc_ith != NULL) { - ithread_remove_handler(sc->sc_ith); - sc->sc_ith = NULL; - } - /* Destroy queues */ - IF_DRAIN(&sc->sc_cmdq); - IF_DRAIN(&sc->sc_aclq); - IF_DRAIN(&sc->sc_scoq); - IF_DRAIN(&sc->sc_inq); + NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); + NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); + NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); - mtx_destroy(&sc->sc_cmdq.ifq_mtx); - mtx_destroy(&sc->sc_aclq.ifq_mtx); - mtx_destroy(&sc->sc_scoq.ifq_mtx); - mtx_destroy(&sc->sc_inq.ifq_mtx); + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); return (0); } /* USB_DETACH(ubt) */ /* - * Start USB control request (HCI command) + * Start USB control request (HCI command). Must be called with node locked */ Static usbd_status -ubt_request_start(ubt_softc_p sc, struct mbuf *m) +ubt_request_start(ubt_softc_p sc) { - usb_device_request_t req; - usbd_status status = USBD_NORMAL_COMPLETION; - - IF_LOCK(&sc->sc_cmdq); - - if (m != NULL) { - if (_IF_QFULL(&sc->sc_cmdq)) { - NG_UBT_ERR( -"%s: %s - Dropping HCI command frame, len=%d. Queue full\n", - __func__, USBDEVNAME(sc->sc_dev), - m->m_pkthdr.len); - - _IF_DROP(&sc->sc_cmdq); - NG_UBT_STAT_OERROR(sc->sc_stat); - - NG_FREE_M(m); - } else - _IF_ENQUEUE(&sc->sc_cmdq, m); - } else - sc->sc_flags &= ~UBT_CMD_XMIT; + usb_device_request_t req; + struct mbuf *m = NULL; + usbd_status status; - if (sc->sc_flags & UBT_CMD_XMIT) { - NG_UBT_INFO( + KASSERT(!(sc->sc_flags & UBT_CMD_XMIT), ( "%s: %s - Another control request is pending\n", - __func__, USBDEVNAME(sc->sc_dev)); - goto done; - } + __func__, USBDEVNAME(sc->sc_dev))); - _IF_DEQUEUE(&sc->sc_cmdq, m); + 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)); - goto done; + + return (USBD_NORMAL_COMPLETION); } /* - * Check HCI command frame size and copy it back - * to linear USB transfer buffer. + * Check HCI command frame size and copy it back to + * linear USB transfer buffer. */ if (m->m_pkthdr.len > UBT_CTRL_BUFFER_SIZE) @@ -851,7 +930,6 @@ ubt_request_start(ubt_softc_p sc, struct mbuf *m) 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); @@ -864,38 +942,39 @@ ubt_request_start(ubt_softc_p sc, struct mbuf *m) usbd_setup_default_xfer( sc->sc_ctrl_xfer, sc->sc_udev, - (usbd_private_handle) sc, - USBD_DEFAULT_TIMEOUT, /* XXX */ + (usbd_private_handle) sc->sc_node, + USBD_DEFAULT_TIMEOUT, /* XXX */ &req, sc->sc_ctrl_buffer, m->m_pkthdr.len, USBD_NO_COPY, ubt_request_complete); + NG_NODE_REF(sc->sc_node); + status = usbd_transfer(sc->sc_ctrl_xfer); - if (status && status != USBD_IN_PROGRESS) { + 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); - _IF_DROP(&sc->sc_cmdq); /* XXX */ - NG_UBT_STAT_OERROR(sc->sc_stat); + NG_NODE_UNREF(sc->sc_node); - /* XXX FIXME: should we try to resubmit another request? */ + 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); -done: - IF_UNLOCK(&sc->sc_cmdq); return (status); } /* ubt_request_start */ @@ -907,7 +986,24 @@ done: Static void ubt_request_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) { - ubt_softc_p sc = (ubt_softc_p) p; + ng_send_fn((node_p) p, NULL, ubt_request_complete2, (void *) h, s); + NG_NODE_UNREF((node_p) p); +} /* ubt_request_complete */ + +Static void +ubt_request_complete2(node_p node, hook_p hook, void *arg1, int arg2) +{ + ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node); + usbd_xfer_handle h = (usbd_xfer_handle) arg1; + usbd_status s = (usbd_status) arg2; + + 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( @@ -934,39 +1030,71 @@ ubt_request_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); } - ubt_request_start(sc, NULL /* completed request */); -} /* ubt_request_complete */ + if (NG_BT_MBUFQ_LEN(&sc->sc_cmdq) > 0) + ubt_request_start(sc); +} /* ubt_request_complete2 */ /* - * Start interrupt transfer + * Start interrupt transfer. Must be called when node is locked */ Static usbd_status ubt_intr_start(ubt_softc_p sc) { - usbd_status status; + 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_COPY, + (usbd_private_handle) sc->sc_node, + (void *)(mtod(m, u_int8_t *) + m->m_len), + MCLBYTES - m->m_len, + USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ubt_intr_complete); + NG_NODE_REF(sc->sc_node); + status = usbd_transfer(sc->sc_intr_xfer); - if (status && status != USBD_IN_PROGRESS) { + 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_NODE_UNREF(sc->sc_node); + + NG_FREE_M(m); + return (status); } + sc->sc_flags |= UBT_EVT_RECV; + sc->sc_intr_buffer = m; + return (USBD_NORMAL_COMPLETION); } /* ubt_intr_start */ @@ -977,65 +1105,77 @@ ubt_intr_start(ubt_softc_p sc) Static void ubt_intr_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) { - ubt_softc_p sc = (ubt_softc_p) p; + ng_send_fn((node_p) p, NULL, ubt_intr_complete2, (void *) h, s); + NG_NODE_UNREF((node_p) p); +} /* ubt_intr_complete */ + +Static void +ubt_intr_complete2(node_p node, hook_p hook, void *arg1, int arg2) +{ + ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node); + usbd_xfer_handle h = (usbd_xfer_handle) arg1; + usbd_status s = (usbd_status) arg2; struct mbuf *m = NULL; ng_hci_event_pkt_t *hdr = NULL; - int off; + int error; + + 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_buffer; + sc->sc_intr_buffer = NULL; + + hdr = mtod(m, ng_hci_event_pkt_t *); + + 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)); + + NG_FREE_M(m); + return; + } 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)\n", +"%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); - goto done; + 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 (h->actlen < sizeof(*hdr)) - goto done; - - /* Copy HCI event 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); + if (m->m_pkthdr.len < sizeof(*hdr)) { + NG_FREE_M(m); goto done; } - /* - * Copy data from USB buffer into mbuf and check if we got - * full HCI event frame. Fix HCI event frame if required. - */ - - if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) { - off = 1; - *mtod(m, u_int8_t *) = NG_HCI_EVENT_PKT; - } else - off = 0; - - m->m_pkthdr.len = 0; - m->m_len = min(MHLEN, h->actlen + off); /* XXX m_copyback is stupid */ - m_copyback(m, off, h->actlen, sc->sc_intr_buffer); - - hdr = mtod(m, ng_hci_event_pkt_t *); if (hdr->length == m->m_pkthdr.len - sizeof(*hdr)) { NG_UBT_INFO( "%s: %s - Got complete HCI event frame, pktlen=%d, length=%d\n", @@ -1044,23 +1184,9 @@ ubt_intr_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); - IF_LOCK(&sc->sc_inq); - if (_IF_QFULL(&sc->sc_inq)) { - NG_UBT_ERR( -"%s: %s -Incoming queue is full. Dropping mbuf, len=%d\n", - __func__, USBDEVNAME(sc->sc_dev), - m->m_pkthdr.len); - - _IF_DROP(&sc->sc_inq); + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (error != 0) NG_UBT_STAT_IERROR(sc->sc_stat); - - NG_FREE_M(m); - } else - _IF_ENQUEUE(&sc->sc_inq, m); - IF_UNLOCK(&sc->sc_inq); - - /* Schedule SWI */ - swi_sched(sc->sc_ith, 0); } else { NG_UBT_ERR( "%s: %s - Invalid HCI event frame size, length=%d, pktlen=%d\n", @@ -1072,38 +1198,69 @@ ubt_intr_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) } done: ubt_intr_start(sc); -} /* ubt_intr_complete */ +} /* ubt_intr_complete2 */ /* - * Start bulk-in USB transfer (ACL data) + * Start bulk-in USB transfer (ACL data). Must be called when node is locked */ Static usbd_status ubt_bulk_in_start(ubt_softc_p sc) { - usbd_status status; + 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_COPY, + (usbd_private_handle) sc->sc_node, + (void *)(mtod(m, u_int8_t *) + m->m_len), + MCLBYTES - m->m_len, + USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ubt_bulk_in_complete); + NG_NODE_REF(sc->sc_node); + status = usbd_transfer(sc->sc_bulk_in_xfer); - if (status && status != USBD_IN_PROGRESS) { + 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_NODE_UNREF(sc->sc_node); + + NG_FREE_M(m); + return (status); } + sc->sc_flags |= UBT_ACL_RECV; + sc->sc_bulk_in_buffer = m; + return (USBD_NORMAL_COMPLETION); } /* ubt_bulk_in_start */ @@ -1114,92 +1271,88 @@ ubt_bulk_in_start(ubt_softc_p sc) Static void ubt_bulk_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) { - ubt_softc_p sc = (ubt_softc_p) p; + ng_send_fn((node_p) p, NULL, ubt_bulk_in_complete2, (void *) h, s); + NG_NODE_UNREF((node_p) p); +} /* ubt_bulk_in_complete */ + +Static void +ubt_bulk_in_complete2(node_p node, hook_p hook, void *arg1, int arg2) +{ + ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node); + usbd_xfer_handle h = (usbd_xfer_handle) arg1; + usbd_status s = (usbd_status) arg2; 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_buffer; + sc->sc_bulk_in_buffer = NULL; + + hdr = mtod(m, ng_hci_acldata_pkt_t *); + + 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)); + + NG_FREE_M(m); + return; + } + 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)\n", +"%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); - goto done; + 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 (h->actlen < sizeof(*hdr)) - goto done; - - 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); + if (m->m_pkthdr.len < sizeof(*hdr)) { + NG_FREE_M(m); goto done; } - /* - * Copy data from linear USB buffer into mbuf and check if we got - * full ACL data frame. Fix ACL data frame header if required. - */ - - if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) { - len = 1; - *mtod(m, u_int8_t *) = NG_HCI_ACL_DATA_PKT; - } else - len = 0; - - m->m_pkthdr.len = 0; - m->m_len = min(MHLEN, h->actlen + len); /* XXX m_copyback is stupid */ - m_copyback(m, len, h->actlen, sc->sc_bulk_in_buffer); - - hdr = mtod(m, ng_hci_acldata_pkt_t *); 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); + __func__, USBDEVNAME(sc->sc_dev), m->m_pkthdr.len, len); NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); - IF_LOCK(&sc->sc_inq); - if (_IF_QFULL(&sc->sc_inq)) { - NG_UBT_ERR( -"%s: %s -Incoming queue is full. Dropping mbuf, len=%d\n", - __func__, USBDEVNAME(sc->sc_dev), - m->m_pkthdr.len); - - _IF_DROP(&sc->sc_inq); + NG_SEND_DATA_ONLY(len, sc->sc_hook, m); + if (len != 0) NG_UBT_STAT_IERROR(sc->sc_stat); - - NG_FREE_M(m); - } else - _IF_ENQUEUE(&sc->sc_inq, m); - IF_UNLOCK(&sc->sc_inq); - - /* Schedule SWI */ - swi_sched(sc->sc_ith, 0); } else { NG_UBT_ERR( "%s: %s - Invalid ACL frame size, length=%d, pktlen=%d\n", @@ -1211,47 +1364,28 @@ ubt_bulk_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) } done: ubt_bulk_in_start(sc); -} /* ubt_bulk_in_complete */ +} /* ubt_bulk_in_complete2 */ /* - * Start bulk-out USB transfer + * Start bulk-out USB transfer. Must be called with node locked */ Static usbd_status -ubt_bulk_out_start(ubt_softc_p sc, struct mbuf *m) +ubt_bulk_out_start(ubt_softc_p sc) { - usbd_status status = USBD_NORMAL_COMPLETION; - - IF_LOCK(&sc->sc_aclq); - - if (m != NULL) { - if (_IF_QFULL(&sc->sc_aclq)) { - NG_UBT_ERR( -"%s: %s - Dropping HCI ACL frame, len=%d. Queue full\n", - __func__, USBDEVNAME(sc->sc_dev), - m->m_pkthdr.len); - - _IF_DROP(&sc->sc_aclq); - NG_UBT_STAT_OERROR(sc->sc_stat); + struct mbuf *m = NULL; + usbd_status status; - NG_FREE_M(m); - } else - _IF_ENQUEUE(&sc->sc_aclq, m); - } else - sc->sc_flags &= ~UBT_ACL_XMIT; + KASSERT(!(sc->sc_flags & UBT_ACL_XMIT), ( +"%s: %s - Another bulk-out request is pending\n", + __func__, USBDEVNAME(sc->sc_dev))); - if (sc->sc_flags & UBT_ACL_XMIT) { - NG_UBT_INFO( -"%s: %s - Another bulk-out transfer is pending\n", - __func__, USBDEVNAME(sc->sc_dev)); - goto done; - } - - _IF_DEQUEUE(&sc->sc_aclq, m); + 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)); - goto done; + + return (USBD_NORMAL_COMPLETION); } /* @@ -1271,37 +1405,38 @@ ubt_bulk_out_start(ubt_softc_p sc, struct mbuf *m) usbd_setup_xfer( sc->sc_bulk_out_xfer, sc->sc_bulk_out_pipe, - (usbd_private_handle) sc, + (usbd_private_handle) sc->sc_node, sc->sc_bulk_out_buffer, m->m_pkthdr.len, USBD_NO_COPY, USBD_DEFAULT_TIMEOUT, /* XXX */ ubt_bulk_out_complete); + NG_NODE_REF(sc->sc_node); + status = usbd_transfer(sc->sc_bulk_out_xfer); - if (status && status != USBD_IN_PROGRESS) { + 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); - _IF_DROP(&sc->sc_aclq); /* XXX */ + NG_NODE_UNREF(sc->sc_node); + + NG_BT_MBUFQ_DROP(&sc->sc_aclq); NG_UBT_STAT_OERROR(sc->sc_stat); - /* XXX FIXME: should we try to start another transfer? */ + /* 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); -done: - IF_UNLOCK(&sc->sc_aclq); return (status); } /* ubt_bulk_out_start */ @@ -1313,13 +1448,29 @@ done: Static void ubt_bulk_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) { - ubt_softc_p sc = (ubt_softc_p) p; + ng_send_fn((node_p) p, NULL, ubt_bulk_out_complete2, (void *) h, s); + NG_NODE_UNREF((node_p) p); +} /* ubt_bulk_out_complete */ + +Static void +ubt_bulk_out_complete2(node_p node, hook_p hook, void *arg1, int arg2) +{ + ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node); + usbd_xfer_handle h = (usbd_xfer_handle) arg1; + usbd_status s = (usbd_status) arg2; + + 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); + __func__, USBDEVNAME(sc->sc_dev), sc->sc_bulk_out_pipe); return; } @@ -1342,11 +1493,12 @@ ubt_bulk_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); } - ubt_bulk_out_start(sc, NULL /* completed request */); -} /* ubt_bulk_out_complete */ + if (NG_BT_MBUFQ_LEN(&sc->sc_aclq) > 0) + ubt_bulk_out_start(sc); +} /* ubt_bulk_out_complete2 */ /* - * Start Isochronous-in USB transfer + * Start Isochronous-in USB transfer. Must be called with node locked */ Static usbd_status @@ -1355,30 +1507,39 @@ ubt_isoc_in_start(ubt_softc_p sc) usbd_status status; int i; - /* Initialize an isoc-in USB transfer and then schedule it. */ + 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, + (usbd_private_handle) sc->sc_node, sc->sc_isoc_in_frlen, sc->sc_isoc_nframes, USBD_NO_COPY, /* XXX flags */ ubt_isoc_in_complete); + NG_NODE_REF(sc->sc_node); + status = usbd_transfer(sc->sc_isoc_in_xfer); - if (status && status != USBD_IN_PROGRESS) { + 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); + NG_NODE_UNREF(sc->sc_node); + return (status); } + sc->sc_flags |= UBT_SCO_RECV; + return (USBD_NORMAL_COMPLETION); } /* ubt_isoc_in_start */ @@ -1389,12 +1550,36 @@ ubt_isoc_in_start(ubt_softc_p sc) Static void ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) { - ubt_softc_p sc = (ubt_softc_p) p; + ng_send_fn((node_p) p, NULL, ubt_isoc_in_complete2, (void *) h, s); + NG_NODE_UNREF((node_p) p); +} /* ubt_isoc_in_complete */ + +Static void +ubt_isoc_in_complete2(node_p node, hook_p hook, void *arg1, int arg2) +{ + ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node); + usbd_xfer_handle h = (usbd_xfer_handle) arg1; + usbd_status s = (usbd_status) arg2; 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 (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; + } + if (s == USBD_CANCELLED) { NG_UBT_INFO( "%s: %s - Isoc-in xfer cancelled, pipe=%p\n", @@ -1405,14 +1590,15 @@ ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) if (s != USBD_NORMAL_COMPLETION) { NG_UBT_WARN( -"%s: %s - Isoc-in xfer failed. %s (%d)\n", +"%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); - goto done; + + return; /* XXX FIXME we should restart after some delay */ } NG_UBT_STAT_BYTES_RECV(sc->sc_stat, h->actlen); @@ -1421,11 +1607,7 @@ ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) "%s: %s - Got %d bytes from isoc-in pipe\n", __func__, USBDEVNAME(sc->sc_dev), h->actlen); - if (h->actlen < sizeof(*hdr)) - goto done; - /* Copy SCO data frame to mbuf */ - MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { NG_UBT_ALERT( @@ -1437,18 +1619,18 @@ ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) } /* Fix SCO data frame header if required */ - - if (sc->sc_flags & UBT_HAVE_FRAME_TYPE) { - m->m_pkthdr.len = m->m_len = 0; - } else { + if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) { *mtod(m, u_int8_t *) = NG_HCI_SCO_DATA_PKT; - m->m_pkthdr.len = m->m_len = 1; + 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. - * XXX is that correct? + * 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; @@ -1461,11 +1643,8 @@ ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) sc->sc_isoc_in_frlen[i], b); } - NG_UBT_M_PULLUP(m, sizeof(*hdr)); - if (m == NULL) { - NG_UBT_STAT_IERROR(sc->sc_stat); + if (m->m_pkthdr.len < sizeof(*hdr)) goto done; - } hdr = mtod(m, ng_hci_scodata_pkt_t *); @@ -1477,23 +1656,9 @@ ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); - IF_LOCK(&sc->sc_inq); - if (_IF_QFULL(&sc->sc_inq)) { - NG_UBT_ERR( -"%s: %s -Incoming queue is full. Dropping mbuf, len=%d\n", - __func__, USBDEVNAME(sc->sc_dev), - m->m_pkthdr.len); - - _IF_DROP(&sc->sc_inq); + NG_SEND_DATA_ONLY(i, sc->sc_hook, m); + if (i != 0) NG_UBT_STAT_IERROR(sc->sc_stat); - - NG_FREE_M(m); - } else - _IF_ENQUEUE(&sc->sc_inq, m); - IF_UNLOCK(&sc->sc_inq); - - /* Schedule SWI */ - swi_sched(sc->sc_ith, 0); } else { NG_UBT_ERR( "%s: %s - Invalid SCO frame size, length=%d, pktlen=%d\n", @@ -1505,53 +1670,33 @@ ubt_isoc_in_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) } done: ubt_isoc_in_start(sc); -} /* ubt_bulk_in_complete */ +} /* ubt_isoc_in_complete2 */ /* - * Start isochronous-out USB transfer + * Start isochronous-out USB transfer. Must be called with node locked */ Static usbd_status -ubt_isoc_out_start(ubt_softc_p sc, struct mbuf *m) +ubt_isoc_out_start(ubt_softc_p sc) { + struct mbuf *m = NULL; u_int8_t *b = NULL; int i, len, nframes; - usbd_status status = USBD_NORMAL_COMPLETION; - - IF_LOCK(&sc->sc_scoq); + usbd_status status; - if (m != NULL) { - if (_IF_QFULL(&sc->sc_scoq)) { - NG_UBT_ERR( -"%s: %s - Dropping HCI SCO frame, len=%d. Queue full\n", - __func__, USBDEVNAME(sc->sc_dev), - m->m_pkthdr.len); + KASSERT(!(sc->sc_flags & UBT_SCO_XMIT), ( +"%s: %s - Another isoc-out request is pending\n", + __func__, USBDEVNAME(sc->sc_dev))); - _IF_DROP(&sc->sc_scoq); - NG_UBT_STAT_OERROR(sc->sc_stat); - - NG_FREE_M(m); - } else - _IF_ENQUEUE(&sc->sc_scoq, m); - } else - sc->sc_flags &= ~UBT_SCO_XMIT; - - if (sc->sc_flags & UBT_SCO_XMIT) { - NG_UBT_INFO( -"%s: %s - Another isoc-out transfer is pending\n", - __func__, USBDEVNAME(sc->sc_dev)); - goto done; - } - - _IF_DEQUEUE(&sc->sc_scoq, m); + 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)); - goto done; + + 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; @@ -1576,25 +1721,28 @@ ubt_isoc_out_start(ubt_softc_p sc, struct mbuf *m) NG_FREE_M(m); - /* Initialize an isoc-out USB transfer and then schedule it. */ - + /* 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, + (usbd_private_handle) sc->sc_node, sc->sc_isoc_out_frlen, nframes, USBD_NO_COPY, ubt_isoc_out_complete); + NG_NODE_REF(sc->sc_node); + status = usbd_transfer(sc->sc_isoc_out_xfer); - if (status && status != USBD_IN_PROGRESS) { + 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); - _IF_DROP(&sc->sc_scoq); /* XXX */ + NG_NODE_UNREF(sc->sc_node); + + NG_BT_MBUFQ_DROP(&sc->sc_scoq); NG_UBT_STAT_OERROR(sc->sc_stat); } else { NG_UBT_INFO( @@ -1603,11 +1751,8 @@ ubt_isoc_out_start(ubt_softc_p sc, struct mbuf *m) sc->sc_isoc_size); sc->sc_flags |= UBT_SCO_XMIT; - status = USBD_NORMAL_COMPLETION; } -done: - IF_UNLOCK(&sc->sc_scoq); return (status); } /* ubt_isoc_out_start */ @@ -1619,7 +1764,24 @@ done: Static void ubt_isoc_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) { - ubt_softc_p sc = (ubt_softc_p) p; + ng_send_fn((node_p) p, NULL, ubt_isoc_out_complete2, (void *) h, s); + NG_NODE_UNREF((node_p) p); +} /* ubt_isoc_out_complete */ + +Static void +ubt_isoc_out_complete2(node_p node, hook_p hook, void *arg1, int arg2) +{ + ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node); + usbd_xfer_handle h = (usbd_xfer_handle) arg1; + usbd_status s = (usbd_status) arg2; + + 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( @@ -1648,43 +1810,38 @@ ubt_isoc_out_complete(usbd_xfer_handle h, usbd_private_handle p, usbd_status s) NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); } - ubt_isoc_out_start(sc, NULL /* completed request */); -} /* ubt_isoc_out_complete */ + if (NG_BT_MBUFQ_LEN(&sc->sc_scoq) > 0) + ubt_isoc_out_start(sc); +} /* ubt_isoc_out_complete2 */ /* - * SWI interrupt handler + * Abort transfers on all USB pipes */ Static void -ubt_swi_intr(void *context) +ubt_reset(ubt_softc_p sc) { - ubt_softc_p sc = (ubt_softc_p) context; - struct mbuf *m = NULL; - int error; + /* Interrupt */ + if (sc->sc_intr_pipe != NULL) + usbd_abort_pipe(sc->sc_intr_pipe); - if (sc->sc_hook != NULL && NG_HOOK_IS_VALID(sc->sc_hook)) { - for (;;) { - IF_DEQUEUE(&sc->sc_inq, m); - if (m == NULL) - break; + /* 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); - NG_SEND_DATA_ONLY(error, sc->sc_hook, m); - if (error != 0) - NG_UBT_STAT_IERROR(sc->sc_stat); - } - } else { - IF_LOCK(&sc->sc_inq); - for (;;) { - _IF_DEQUEUE(&sc->sc_inq, m); - if (m == NULL) - break; + /* 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); - NG_UBT_STAT_IERROR(sc->sc_stat); - NG_FREE_M(m); - } - IF_UNLOCK(&sc->sc_inq); - } -} /* ubt_swi_intr */ + /* Cleanup queues */ + NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); + NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); + NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); +} /* ubt_reset */ /**************************************************************************** **************************************************************************** @@ -1736,6 +1893,7 @@ ng_ubt_shutdown(node_p node) } NG_NODE_SET_PRIVATE(sc->sc_node, sc); + NG_NODE_FORCE_WRITER(sc->sc_node); done: return (0); } /* ng_ubt_shutdown */ @@ -1748,7 +1906,11 @@ Static int ng_ubt_newhook(node_p node, hook_p hook, char const *name) { ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node); - usbd_status status; + + /* Refuse to create new hook if device interface is active */ + if (sc->sc_ctrl_dev != NODEV || sc->sc_intr_dev != NODEV || + sc->sc_bulk_dev != NODEV) + return (EBUSY); if (strcmp(name, NG_UBT_HOOK) != 0) return (EINVAL); @@ -1758,69 +1920,7 @@ ng_ubt_newhook(node_p node, hook_p hook, char const *name) sc->sc_hook = hook; - /* Interrupt */ - status = usbd_open_pipe(sc->sc_iface0, sc->sc_intr_ep, - USBD_EXCLUSIVE_USE, &sc->sc_intr_pipe); - if (status) { - NG_UBT_ALERT( -"%s: %s - Could not open interrupt pipe. %s (%d)\n", - __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status), - status); - goto fail; - } - - /* Bulk-in */ - status = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_in_ep, - USBD_EXCLUSIVE_USE, &sc->sc_bulk_in_pipe); - if (status) { - NG_UBT_ALERT( -"%s: %s - Could not open bulk-in pipe. %s (%d)\n", - __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status), - status); - goto fail; - } - - /* Bulk-out */ - status = usbd_open_pipe(sc->sc_iface0, sc->sc_bulk_out_ep, - USBD_EXCLUSIVE_USE, &sc->sc_bulk_out_pipe); - if (status) { - NG_UBT_ALERT( -"%s: %s - Could not open bulk-out pipe. %s (%d)\n", - __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status), - status); - goto fail; - } - -#if __broken__ /* XXX FIXME */ - /* Isoc-in */ - status = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_in_ep, - USBD_EXCLUSIVE_USE, &sc->sc_isoc_in_pipe); - if (status) { - NG_UBT_ALERT( -"%s: %s - Could not open isoc-in pipe. %s (%d)\n", - __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status), - status); - goto fail; - } - - /* Isoc-out */ - status = usbd_open_pipe(sc->sc_iface1, sc->sc_isoc_out_ep, - USBD_EXCLUSIVE_USE, &sc->sc_isoc_out_pipe); - if (status) { - NG_UBT_ALERT( -"%s: %s - Could not open isoc-out pipe. %s (%d)\n", - __func__, USBDEVNAME(sc->sc_dev), usbd_errstr(status), - status); - goto fail; - } -#endif /* __broken__ */ - return (0); -fail: - ng_ubt_reset(sc); - sc->sc_hook = NULL; - - return (ENXIO); } /* ng_ubt_newhook */ /* @@ -1833,6 +1933,13 @@ ng_ubt_connect(hook_p hook) ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); usbd_status status; + /* Refuse to connect hook if device interface is active */ + if (sc->sc_ctrl_dev != NODEV || sc->sc_intr_dev != NODEV || + sc->sc_bulk_dev != NODEV) + return (EBUSY); + + NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); + /* Start intr transfer */ status = ubt_intr_start(sc); if (status != USBD_NORMAL_COMPLETION) { @@ -1865,11 +1972,9 @@ ng_ubt_connect(hook_p hook) } #endif /* __broken__ */ - NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); - return (0); fail: - ng_ubt_reset(sc); + ubt_reset(sc); sc->sc_hook = NULL; return (ENXIO); @@ -1888,7 +1993,7 @@ ng_ubt_disconnect(hook_p hook) if (hook != sc->sc_hook) return (EINVAL); - ng_ubt_reset(sc); + ubt_reset(sc); sc->sc_hook = NULL; } @@ -1902,10 +2007,10 @@ ng_ubt_disconnect(hook_p hook) Static int ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) { - ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node); - struct ng_mesg *msg = NULL, *rsp = NULL; - int error = 0, queue, qlen; - struct ifqueue *q = NULL; + ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(node); + struct ng_mesg *msg = NULL, *rsp = NULL; + struct ng_bt_mbufq *q = NULL; + int error = 0, queue, qlen; if (sc == NULL) { NG_FREE_ITEM(item); @@ -1928,19 +2033,16 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) "Debug: %d\n" \ "CMD queue: [have:%d,max:%d]\n" \ "ACL queue: [have:%d,max:%d]\n" \ - "SCO queue: [have:%d,max:%d]\n" \ - "Inp queue: [have:%d,max:%d]", + "SCO queue: [have:%d,max:%d]", (sc->sc_hook != NULL)? NG_UBT_HOOK : "", sc->sc_flags, sc->sc_debug, - _IF_QLEN(&sc->sc_cmdq), /* XXX */ - sc->sc_cmdq.ifq_maxlen, /* XXX */ - _IF_QLEN(&sc->sc_aclq), /* XXX */ - sc->sc_aclq.ifq_maxlen, /* XXX */ - _IF_QLEN(&sc->sc_scoq), /* XXX */ - sc->sc_scoq.ifq_maxlen, /* XXX */ - _IF_QLEN(&sc->sc_inq), /* XXX */ - sc->sc_inq.ifq_maxlen /* XXX */ ); + NG_BT_MBUFQ_LEN(&sc->sc_cmdq), + sc->sc_cmdq.maxlen, + NG_BT_MBUFQ_LEN(&sc->sc_aclq), + sc->sc_aclq.maxlen, + NG_BT_MBUFQ_LEN(&sc->sc_scoq), + sc->sc_scoq.maxlen); break; default: @@ -1984,10 +2086,6 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) } switch (queue) { - case NGM_UBT_NODE_QUEUE_IN: - q = &sc->sc_inq; - break; - case NGM_UBT_NODE_QUEUE_CMD: q = &sc->sc_cmdq; break; @@ -2007,7 +2105,7 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) } if (q != NULL) - q->ifq_maxlen = qlen; /* XXX */ + q->maxlen = qlen; } break; @@ -2019,10 +2117,6 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) queue = ((ng_ubt_node_qlen_ep *)(msg->data))->queue; switch (queue) { - case NGM_UBT_NODE_QUEUE_IN: - q = &sc->sc_inq; - break; - case NGM_UBT_NODE_QUEUE_CMD: q = &sc->sc_cmdq; break; @@ -2052,7 +2146,7 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) ((ng_ubt_node_qlen_ep *)(rsp->data))->queue = queue; ((ng_ubt_node_qlen_ep *)(rsp->data))->qlen = - q->ifq_maxlen; /* XXX */ + q->maxlen; } break; @@ -2070,6 +2164,25 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) NG_UBT_STAT_RESET(sc->sc_stat); break; + case NGM_UBT_NODE_DEV_NODES: + if (msg->header.arglen != + sizeof(ng_ubt_node_dev_nodes_ep)) { + error = EMSGSIZE; + break; + } + + if ((sc->sc_flags & UBT_ANY_DEV) || + sc->sc_hook != NULL) { + error = EBUSY; + break; + } + + if (*((ng_ubt_node_dev_nodes_ep *)(msg->data))) + ubt_create_device_nodes(sc); + else + ubt_destroy_device_nodes(sc); + break; + default: error = EINVAL; break; @@ -2094,10 +2207,11 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) Static int ng_ubt_rcvdata(hook_p hook, item_p item) { - ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); - struct mbuf *m = NULL; - usbd_status (*f)(ubt_softc_p, struct mbuf *) = NULL; - int error = 0; + ubt_softc_p sc = (ubt_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct mbuf *m = NULL; + usbd_status (*f)(ubt_softc_p) = NULL; + struct ng_bt_mbufq *q = NULL; + int b, error = 0; if (sc == NULL) { error = EHOSTDOWN; @@ -2116,15 +2230,21 @@ ng_ubt_rcvdata(hook_p hook, item_p item) 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__ */ @@ -2145,58 +2265,485 @@ ng_ubt_rcvdata(hook_p hook, item_p item) if (!(sc->sc_flags & UBT_NEED_FRAME_TYPE)) m_adj(m, sizeof(u_int8_t)); - if ((*f)(sc, m) != USBD_NORMAL_COMPLETION) - error = EIO; + 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: NG_FREE_ITEM(item); return (error); } /* ng_ubt_rcvdata */ +/**************************************************************************** + **************************************************************************** + ** Device specific + **************************************************************************** + ****************************************************************************/ + +/* + * Open endpoint device + * XXX FIXME softc locking + */ + +Static int +ubt_open(dev_t dev, int flag, int mode, usb_proc_ptr p) +{ + ubt_softc_p sc = NULL; + int ep = UBT_ENDPOINT(dev); + + USB_GET_SC_OPEN(ubt, UBT_UNIT(dev), sc); /* check for sc != NULL */ + if (sc->sc_dying) + return (ENXIO); + + if (ep == USB_CONTROL_ENDPOINT) { + if (sc->sc_flags & UBT_CTRL_DEV) + return (EBUSY); + + sc->sc_flags |= UBT_CTRL_DEV; + } else if (ep == UE_GET_ADDR(sc->sc_intr_ep)) { + if (sc->sc_flags & UBT_INTR_DEV) + return (EBUSY); + if (sc->sc_intr_pipe == NULL) + return (ENXIO); + + sc->sc_flags |= UBT_INTR_DEV; + } else if (ep == UE_GET_ADDR(sc->sc_bulk_in_ep)) { + if (sc->sc_flags & UBT_BULK_DEV) + return (EBUSY); + if (sc->sc_bulk_in_pipe == NULL || sc->sc_bulk_out_pipe == NULL) + return (ENXIO); + + sc->sc_flags |= UBT_BULK_DEV; + } else + return (EINVAL); + + return (0); +} /* ubt_open */ + +/* + * Close endpoint device + * XXX FIXME softc locking + */ + +Static int +ubt_close(dev_t dev, int flag, int mode, usb_proc_ptr p) +{ + ubt_softc_p sc = NULL; + int ep = UBT_ENDPOINT(dev); + + USB_GET_SC(ubt, UBT_UNIT(dev), sc); + if (sc == NULL) + return (ENXIO); + + if (ep == USB_CONTROL_ENDPOINT) + sc->sc_flags &= ~UBT_CTRL_DEV; + else if (ep == UE_GET_ADDR(sc->sc_intr_ep)) { + if (sc->sc_intr_pipe != NULL) + usbd_abort_pipe(sc->sc_intr_pipe); + + sc->sc_flags &= ~UBT_INTR_DEV; + } else if (ep == UE_GET_ADDR(sc->sc_bulk_in_ep)) { + /* Close both in and out bulk pipes */ + 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); + + sc->sc_flags &= ~UBT_BULK_DEV; + } else + return (EINVAL); + + return (0); +} /* ubt_close */ + +/* + * Read from the endpoint device + * XXX FIXME softc locking + */ + +Static int +ubt_read(dev_t dev, struct uio *uio, int flag) +{ + ubt_softc_p sc = NULL; + int error = 0, n, tn, ep = UBT_ENDPOINT(dev); + usbd_status status; + usbd_pipe_handle pipe = NULL; + usbd_xfer_handle xfer = NULL; + u_int8_t buf[UBT_BSIZE]; + + USB_GET_SC(ubt, UBT_UNIT(dev), sc); + if (sc == NULL || sc->sc_dying) + return (ENXIO); + + if (ep == USB_CONTROL_ENDPOINT) + return (EOPNOTSUPP); + + if (ep == UE_GET_ADDR(sc->sc_intr_ep)) { + pipe = sc->sc_intr_pipe; + xfer = sc->sc_intr_xfer; + } else if (ep == UE_GET_ADDR(sc->sc_bulk_in_ep)) { + pipe = sc->sc_bulk_in_pipe; + xfer = sc->sc_bulk_in_xfer; + } else + return (EINVAL); + + if (pipe == NULL || xfer == NULL) + return (ENXIO); + + sc->sc_refcnt ++; + + while ((n = min(sizeof(buf), uio->uio_resid)) != 0) { + tn = n; + status = usbd_bulk_transfer(xfer, pipe, USBD_SHORT_XFER_OK, + USBD_DEFAULT_TIMEOUT, buf, &tn, "ubtrd"); + switch (status) { + case USBD_NORMAL_COMPLETION: + error = uiomove(buf, tn, uio); + break; + + case USBD_INTERRUPTED: + error = EINTR; + break; + + case USBD_TIMEOUT: + error = ETIMEDOUT; + break; + + default: + error = EIO; + break; + } + + if (error != 0 || tn < n) + break; + } + + if (-- sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + + return (error); +} /* ubt_read */ + +/* + * Write into the endpoint device + * XXX FIXME softc locking + */ + +Static int +ubt_write(dev_t dev, struct uio *uio, int flag) +{ + ubt_softc_p sc = NULL; + int error = 0, n, ep = UBT_ENDPOINT(dev); + usbd_status status; + u_int8_t buf[UBT_BSIZE]; + + USB_GET_SC(ubt, UBT_UNIT(dev), sc); + if (sc == NULL || sc->sc_dying) + return (ENXIO); + + if (ep == USB_CONTROL_ENDPOINT || ep == UE_GET_ADDR(sc->sc_intr_ep)) + return (EOPNOTSUPP); + if (ep != UE_GET_ADDR(sc->sc_bulk_in_ep)) + return (EINVAL); + + sc->sc_refcnt ++; + + while ((n = min(sizeof(buf), uio->uio_resid)) != 0) { + error = uiomove(buf, n, uio); + if (error != 0) + break; + + status = usbd_bulk_transfer(sc->sc_bulk_out_xfer, + sc->sc_bulk_out_pipe, 0, USBD_DEFAULT_TIMEOUT, + buf, &n,"ubtwr"); + switch (status) { + case USBD_NORMAL_COMPLETION: + break; + + case USBD_INTERRUPTED: + error = EINTR; + break; + + case USBD_TIMEOUT: + error = ETIMEDOUT; + break; + + default: + error = EIO; + break; + } + + if (error != 0) + break; + } + + if (-- sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + + return (error); +} /* ubt_write */ + +/* + * Process ioctl on the endpoint device. Mostly stolen from ugen(4) + * XXX FIXME softc locking + */ + +Static int +ubt_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) +{ + ubt_softc_p sc = NULL; + int len, error = 0, ep = UBT_ENDPOINT(dev); + usbd_status status; + struct usb_string_desc *si = NULL; + struct usb_ctl_request *ur = NULL; + void *ptr = NULL; + struct iovec iov; + struct uio uio; + + USB_GET_SC(ubt, UBT_UNIT(dev), sc); + if (sc == NULL || sc->sc_dying) + return (ENXIO); + + if (ep != USB_CONTROL_ENDPOINT) + return (EOPNOTSUPP); + + sc->sc_refcnt ++; + + switch (cmd) { + case USB_GET_DEVICE_DESC: + *(usb_device_descriptor_t *) data = + *usbd_get_device_descriptor(sc->sc_udev); + break; + + case USB_GET_STRING_DESC: + si = (struct usb_string_desc *) data; + status = usbd_get_string_desc(sc->sc_udev, si->usd_string_index, + si->usd_language_id, &si->usd_desc); + if (status != USBD_NORMAL_COMPLETION) + error = EINVAL; + break; + + case USB_DO_REQUEST: + ur = (void *) data; + len = UGETW(ur->ucr_request.wLength); + + if (!(flag & FWRITE)) { + error = EPERM; + break; + } + + /* Avoid requests that would damage the bus integrity. */ + if ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE && + ur->ucr_request.bRequest == UR_SET_ADDRESS) || + (ur->ucr_request.bmRequestType == UT_WRITE_DEVICE && + ur->ucr_request.bRequest == UR_SET_CONFIG) || + (ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE && + ur->ucr_request.bRequest == UR_SET_INTERFACE) || + len < 0 || len > 32767) { + error = EINVAL; + break; + } + + if (len != 0) { + iov.iov_base = (caddr_t) ur->ucr_data; + iov.iov_len = len; + + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_resid = len; + uio.uio_offset = 0; + uio.uio_segflg = UIO_USERSPACE; + uio.uio_rw = ur->ucr_request.bmRequestType & UT_READ ? + UIO_READ : UIO_WRITE; + uio.uio_procp = p; + + ptr = malloc(len, M_TEMP, M_WAITOK); + if (uio.uio_rw == UIO_WRITE) { + error = uiomove(ptr, len, &uio); + if (error != 0) + goto ret; + } + } + + status = usbd_do_request_flags(sc->sc_udev, &ur->ucr_request, + ptr, ur->ucr_flags, &ur->ucr_actlen, + USBD_DEFAULT_TIMEOUT); + if (status != USBD_NORMAL_COMPLETION) { + error = EIO; + goto ret; + } + + if (len != 0) { + if (uio.uio_rw == UIO_READ) { + error = uiomove(ptr, len, &uio); + if (error != 0) + goto ret; + } + } +ret: + if (ptr != NULL) + free(ptr, M_TEMP); + break; + + case USB_GET_DEVICEINFO: + usbd_fill_deviceinfo(sc->sc_udev, + (struct usb_device_info *) data, 1); + break; + + default: + error = EINVAL; + break; + } + + if (-- sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + + return (error); +} /* ubt_ioctl */ + /* - * Abort transfers and close all USB pipes. + * Poll the endpoint device + * XXX FIXME softc locking + */ + +Static int +ubt_poll(dev_t dev, int events, usb_proc_ptr p) +{ + ubt_softc_p sc = NULL; + int revents = 0, ep = UBT_ENDPOINT(dev); + + USB_GET_SC(ubt, UBT_UNIT(dev), sc); + if (sc == NULL || sc->sc_dying) + return (ENXIO); + + if (ep == USB_CONTROL_ENDPOINT) + return (EOPNOTSUPP); + + if (ep == UE_GET_ADDR(sc->sc_intr_ep)) { + if (sc->sc_intr_pipe != NULL) + revents |= events & (POLLIN | POLLRDNORM); + else + revents = EIO; + } else if (ep == UE_GET_ADDR(sc->sc_bulk_in_ep)) { + if (sc->sc_bulk_in_pipe != NULL) + revents |= events & (POLLIN | POLLRDNORM); + + if (sc->sc_bulk_out_pipe != NULL) + revents |= events & (POLLOUT | POLLWRNORM); + + if (revents == 0) + revents = EIO; /* both pipes closed */ + } else + revents = EINVAL; + + return (revents); +} /* ubt_poll */ + +/* + * Create device nodes for all endpoints. Must be called with node locked. */ Static void -ng_ubt_reset(ubt_softc_p sc) +ubt_create_device_nodes(ubt_softc_p sc) { - /* Abort transfers and close all USB pipes */ + int ep; - /* Interrupt */ - if (sc->sc_intr_pipe != NULL) { - usbd_abort_pipe(sc->sc_intr_pipe); - usbd_close_pipe(sc->sc_intr_pipe); - sc->sc_intr_pipe = NULL; + KASSERT((sc->sc_hook == NULL), ( +"%s: %s - hook != NULL!\n", __func__, USBDEVNAME(sc->sc_dev))); + + /* Control device */ + if (sc->sc_ctrl_dev == NODEV) + sc->sc_ctrl_dev = make_dev(&ubt_cdevsw, + UBT_MINOR(USBDEVUNIT(sc->sc_dev), 0), + UID_ROOT, GID_OPERATOR, 0644, + "%s", USBDEVNAME(sc->sc_dev)); + + /* Interrupt device */ + if (sc->sc_intr_dev == NODEV && sc->sc_intr_ep != -1) { + ep = UE_GET_ADDR(sc->sc_intr_ep); + sc->sc_intr_dev = make_dev(&ubt_cdevsw, + UBT_MINOR(USBDEVUNIT(sc->sc_dev), ep), + UID_ROOT, GID_OPERATOR, 0644, + "%s.%d", USBDEVNAME(sc->sc_dev), ep); } - /* Bulk-in/out */ - if (sc->sc_bulk_in_pipe != NULL) { - usbd_abort_pipe(sc->sc_bulk_in_pipe); - usbd_close_pipe(sc->sc_bulk_in_pipe); - sc->sc_bulk_in_pipe = NULL; + /* + * Bulk-in and bulk-out device + * XXX will create one device for both in and out endpoints. + * XXX note that address of the in and out endpoint should be the same + */ + + if (sc->sc_bulk_dev == NODEV && + sc->sc_bulk_in_ep != -1 && sc->sc_bulk_out_ep != -1 && + UE_GET_ADDR(sc->sc_bulk_in_ep) == UE_GET_ADDR(sc->sc_bulk_out_ep)) { + ep = UE_GET_ADDR(sc->sc_bulk_in_ep); + sc->sc_bulk_dev = make_dev(&ubt_cdevsw, + UBT_MINOR(USBDEVUNIT(sc->sc_dev), ep), + UID_ROOT, GID_OPERATOR, 0644, + "%s.%d", USBDEVNAME(sc->sc_dev), ep); } - if (sc->sc_bulk_out_pipe != NULL) { - usbd_abort_pipe(sc->sc_bulk_out_pipe); - usbd_close_pipe(sc->sc_bulk_out_pipe); - sc->sc_bulk_out_pipe = NULL; +} /* ubt_create_device_nodes */ + +/* + * Destroy device nodes for all endpoints + * XXX FIXME locking + */ + +Static void +ubt_destroy_device_nodes(ubt_softc_p sc) +{ + struct vnode *vp = NULL; + + /* + * Wait for processes to go away. This should be safe as we will not + * call ubt_destroy_device_nodes() from Netgraph unless all devices + * were closed (and thus no active processes). + */ + + if (-- sc->sc_refcnt >= 0) { + ubt_reset(sc); + usb_detach_wait(USBDEV(sc->sc_dev)); } - /* Isoc-in/out */ - if (sc->sc_isoc_in_pipe != NULL) { - usbd_abort_pipe(sc->sc_isoc_in_pipe); - usbd_close_pipe(sc->sc_isoc_in_pipe); - sc->sc_isoc_in_pipe = NULL; + sc->sc_refcnt = 0; + + /* Destroy device nodes */ + if (sc->sc_bulk_dev != NODEV) { + vp = SLIST_FIRST(&sc->sc_bulk_dev->si_hlist); + if (vp != NULL) + VOP_REVOKE(vp, REVOKEALL); + + destroy_dev(sc->sc_bulk_dev); + sc->sc_bulk_dev = NODEV; } - if (sc->sc_isoc_out_pipe != NULL) { - usbd_abort_pipe(sc->sc_isoc_out_pipe); - usbd_close_pipe(sc->sc_isoc_out_pipe); - sc->sc_isoc_out_pipe = NULL; + + if (sc->sc_intr_dev != NODEV) { + vp = SLIST_FIRST(&sc->sc_intr_dev->si_hlist); + if (vp != NULL) + VOP_REVOKE(vp, REVOKEALL); + + destroy_dev(sc->sc_intr_dev); + sc->sc_intr_dev = NODEV; } - /* Cleanup queues */ - IF_DRAIN(&sc->sc_cmdq); - IF_DRAIN(&sc->sc_aclq); - IF_DRAIN(&sc->sc_scoq); - IF_DRAIN(&sc->sc_inq); -} /* ng_ubt_reset */ + if (sc->sc_ctrl_dev != NODEV) { + vp = SLIST_FIRST(&sc->sc_ctrl_dev->si_hlist); + if (vp != NULL) + VOP_REVOKE(vp, REVOKEALL); + + destroy_dev(sc->sc_ctrl_dev); + sc->sc_ctrl_dev = NODEV; + } +} /* ubt_destroy_device_nodes */ |