diff options
author | emax <emax@FreeBSD.org> | 2009-01-20 22:17:05 +0000 |
---|---|---|
committer | emax <emax@FreeBSD.org> | 2009-01-20 22:17:05 +0000 |
commit | 3fd09aadc79eb9fe325206d1a974db2562d32578 (patch) | |
tree | bfe3190b140fde7936ef08fcae170963b7a4319b | |
parent | 1a9032d3393992926a15a517a335cdb0e656baf9 (diff) | |
download | FreeBSD-src-3fd09aadc79eb9fe325206d1a974db2562d32578.zip FreeBSD-src-3fd09aadc79eb9fe325206d1a974db2562d32578.tar.gz |
Update (well, actually rewrite mostly) ng_ubt2 driver for USB2.
Reviewed by: HPS, alfred
Blessed by: HPS
-rw-r--r-- | sys/dev/usb2/bluetooth/ng_ubt2.c | 2355 | ||||
-rw-r--r-- | sys/dev/usb2/bluetooth/ng_ubt2_var.h | 154 |
2 files changed, 1362 insertions, 1147 deletions
diff --git a/sys/dev/usb2/bluetooth/ng_ubt2.c b/sys/dev/usb2/bluetooth/ng_ubt2.c index 1751312..668b3ce 100644 --- a/sys/dev/usb2/bluetooth/ng_ubt2.c +++ b/sys/dev/usb2/bluetooth/ng_ubt2.c @@ -3,7 +3,7 @@ */ /*- - * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,6 +31,69 @@ * $FreeBSD$ */ +/* + * NOTE: ng_ubt2 driver has a split personality. On one side it is + * a USB2 device driver and on the other it is a Netgraph node. This + * driver will *NOT* create traditional /dev/ enties, only Netgraph + * node. + * + * NOTE ON LOCKS USED: ng_ubt2 drives uses 3 locks (mutexes) + * + * 1) sc_if_mtx[0] - lock for device's interface #0. This lock is used + * by USB2 for any USB request going over device's interface #0, i.e. + * interrupt, control and bulk transfers. + * + * 2) sc_if_mtx[1] - lock for device's interface #1. This lock is used + * by USB2 for any USB request going over device's interface #1, i.e + * isoc. transfers. + * + * 3) sc_mbufq_mtx - lock for mbufq and task flags. This lock is used + * to protect device's outgoing mbuf queues and task flags. This lock + * *SHOULD NOT* be grabbed for a long time. In fact, think of it as a + * spin lock. + * + * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts. + * + * 1) USB context. This is where all the USB related stuff happens. All + * callbacks run in this context. All callbacks are called (by USB2) with + * appropriate interface lock held. It is (generally) allowed to grab + * any additional locks. + * + * 2) Netgraph context. This is where all the Netgraph related stuff happens. + * Since we mark node as WRITER, the Netgraph node will be "locked" (from + * Netgraph point of view). Any variable that is only modified from the + * Netgraph context does not require any additonal locking. It is generally + * *NOT* allowed to grab *ANY* additional lock. Whatever you do, *DO NOT* + * not grab any long-sleep lock in the Netgraph context. In fact, the only + * lock that is allowed in the Netgraph context is the sc_mbufq_mtx lock. + * + * 3) Taskqueue context. This is where ubt_task runs. Since we are NOT allowed + * to grab any locks in the Netgraph context, and, USB2 requires us to + * grab interface lock before doing things with transfers, we need to + * transition from the Netgraph context to the Taskqueue context before + * we can call into USB2 subsystem. + * + * So, to put everything together, the rules are as follows. + * It is OK to call from the USB context or the Taskqueue context into + * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words + * it is allowed to call into the Netgraph context with locks held. + * Is it *NOT* OK to call from the Netgraph context into the USB context, + * because USB2 requires us to grab interface locks and we can not do that. + * To avoid this, we set task flags to indicate which actions we want to + * perform and schedule ubt_task which would run in the Taskqueue context. + * Is is OK to call from the Taskqueue context into the USB context, + * and, ubt_task does just that (i.e. grabs appropriate interface locks + * before calling into USB2). + * Access to the outgoing queues and task flags is controlled by the + * sc_mbufq_mtx lock. It is an unavoidable evil. Again, sc_mbufq_mtx should + * really be a spin lock. + * All USB callbacks accept Netgraph node pointer as private data. To + * ensure that Netgraph node pointer remains valid for the duration of the + * transfer, we grab a referrence to the node. In other words, if transfer is + * pending, then we should have a referrence on the node. NG_NODE_[NOT_]VALID + * macro is used to check if node is still present and pointer is valid. + */ + #include <dev/usb2/include/usb2_devid.h> #include <dev/usb2/include/usb2_standard.h> #include <dev/usb2/include/usb2_mfunc.h> @@ -44,8 +107,11 @@ #include <dev/usb2/core/usb2_lookup.h> #include <dev/usb2/core/usb2_util.h> #include <dev/usb2/core/usb2_busdma.h> +#include <dev/usb2/core/usb2_process.h> +#include <dev/usb2/core/usb2_transfer.h> #include <sys/mbuf.h> +#include <sys/taskqueue.h> #include <netgraph/ng_message.h> #include <netgraph/netgraph.h> @@ -57,71 +123,57 @@ #include <dev/usb2/bluetooth/usb2_bluetooth.h> #include <dev/usb2/bluetooth/ng_ubt2_var.h> -/* - * USB methods - */ - -static device_probe_t ubt_probe; -static device_attach_t ubt_attach; -static device_detach_t ubt_detach; - -static devclass_t ubt_devclass; +static int ubt_modevent(module_t, int, void *); +static device_probe_t ubt_probe; +static device_attach_t ubt_attach; +static device_detach_t ubt_detach; -static device_method_t ubt_methods[] = { - DEVMETHOD(device_probe, ubt_probe), - DEVMETHOD(device_attach, ubt_attach), - DEVMETHOD(device_detach, ubt_detach), - {0, 0} -}; +static int ubt_task_schedule(ubt_softc_p, int); +static task_fn_t ubt_task; +static void ubt_xfer_start(ubt_softc_p, int); -static driver_t ubt_driver = { - .name = "ubt", - .methods = ubt_methods, - .size = sizeof(struct ubt_softc), -}; - -/* - * Netgraph methods - */ - -static ng_constructor_t ng_ubt_constructor; -static ng_shutdown_t ng_ubt_shutdown; -static ng_newhook_t ng_ubt_newhook; -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; +/* Netgraph methods */ +static ng_constructor_t ng_ubt_constructor; +static ng_shutdown_t ng_ubt_shutdown; +static ng_newhook_t ng_ubt_newhook; +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; /* Queue length */ -static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = +static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] = { - {"queue", &ng_parse_int32_type,}, - {"qlen", &ng_parse_int32_type,}, - {NULL,} + { "queue", &ng_parse_int32_type, }, + { "qlen", &ng_parse_int32_type, }, + { NULL, } }; -static const struct ng_parse_type ng_ubt_node_qlen_type = { +static const struct ng_parse_type ng_ubt_node_qlen_type = +{ &ng_parse_struct_type, &ng_ubt_node_qlen_type_fields }; /* Stat info */ -static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] = +static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] = { - {"pckts_recv", &ng_parse_uint32_type,}, - {"bytes_recv", &ng_parse_uint32_type,}, - {"pckts_sent", &ng_parse_uint32_type,}, - {"bytes_sent", &ng_parse_uint32_type,}, - {"oerrors", &ng_parse_uint32_type,}, - {"ierrors", &ng_parse_uint32_type,}, - {NULL,} + { "pckts_recv", &ng_parse_uint32_type, }, + { "bytes_recv", &ng_parse_uint32_type, }, + { "pckts_sent", &ng_parse_uint32_type, }, + { "bytes_sent", &ng_parse_uint32_type, }, + { "oerrors", &ng_parse_uint32_type, }, + { "ierrors", &ng_parse_uint32_type, }, + { NULL, } }; -static const struct ng_parse_type ng_ubt_node_stat_type = { +static const struct ng_parse_type ng_ubt_node_stat_type = +{ &ng_parse_struct_type, &ng_ubt_node_stat_type_fields }; /* Netgraph node command list */ -static const struct ng_cmdlist ng_ubt_cmdlist[] = { +static const struct ng_cmdlist ng_ubt_cmdlist[] = +{ { NGM_UBT_COOKIE, NGM_UBT_NODE_SET_DEBUG, @@ -164,315 +216,266 @@ static const struct ng_cmdlist ng_ubt_cmdlist[] = { NULL, NULL }, - {0,} + { 0, } }; /* Netgraph node type */ -static struct ng_type typestruct = { - .version = NG_ABI_VERSION, - .name = NG_UBT_NODE_TYPE, - .constructor = ng_ubt_constructor, - .rcvmsg = ng_ubt_rcvmsg, - .shutdown = ng_ubt_shutdown, - .newhook = ng_ubt_newhook, - .connect = ng_ubt_connect, - .rcvdata = ng_ubt_rcvdata, - .disconnect = ng_ubt_disconnect, - .cmdlist = ng_ubt_cmdlist +static struct ng_type typestruct = +{ + .version = NG_ABI_VERSION, + .name = NG_UBT_NODE_TYPE, + .constructor = ng_ubt_constructor, + .rcvmsg = ng_ubt_rcvmsg, + .shutdown = ng_ubt_shutdown, + .newhook = ng_ubt_newhook, + .connect = ng_ubt_connect, + .rcvdata = ng_ubt_rcvdata, + .disconnect = ng_ubt_disconnect, + .cmdlist = ng_ubt_cmdlist }; -/* USB methods */ - -static usb2_callback_t ubt_ctrl_write_callback; -static usb2_callback_t ubt_intr_read_callback; -static usb2_callback_t ubt_intr_read_clear_stall_callback; -static usb2_callback_t ubt_bulk_read_callback; -static usb2_callback_t ubt_bulk_read_clear_stall_callback; -static usb2_callback_t ubt_bulk_write_callback; -static usb2_callback_t ubt_bulk_write_clear_stall_callback; -static usb2_callback_t ubt_isoc_read_callback; -static usb2_callback_t ubt_isoc_write_callback; - -static int ubt_modevent(module_t, int, void *); -static void ubt_intr_read_complete(node_p, hook_p, void *, int); -static void ubt_bulk_read_complete(node_p, hook_p, void *, int); -static void ubt_isoc_read_complete(node_p, hook_p, void *, int); - -/* USB config */ -static const struct usb2_config ubt_config_if_0[UBT_IF_0_N_TRANSFER] = { - - [0] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = UBT_BULK_WRITE_BUFFER_SIZE, - .mh.flags = {.pipe_bof = 1,}, - .mh.callback = &ubt_bulk_write_callback, - }, - - [1] = { - .type = UE_BULK, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = UBT_BULK_READ_BUFFER_SIZE, - .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, - .mh.callback = &ubt_bulk_read_callback, - }, +/**************************************************************************** + **************************************************************************** + ** USB specific + **************************************************************************** + ****************************************************************************/ - [2] = { - .type = UE_INTERRUPT, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, - .mh.bufsize = 0x110, /* bytes */ - .mh.callback = &ubt_intr_read_callback, - }, +/* USB methods */ +static usb2_callback_t ubt_ctrl_write_callback; +static usb2_callback_t ubt_intr_read_callback; +static usb2_callback_t ubt_intr_read_clear_stall_callback; +static usb2_callback_t ubt_bulk_read_callback; +static usb2_callback_t ubt_bulk_read_clear_stall_callback; +static usb2_callback_t ubt_bulk_write_callback; +static usb2_callback_t ubt_bulk_write_clear_stall_callback; +static usb2_callback_t ubt_isoc_read_callback; +static usb2_callback_t ubt_isoc_write_callback; + +static int ubt_isoc_read_one_frame(struct usb2_xfer *, int); - [3] = { - .type = UE_CONTROL, - .endpoint = 0x00, /* Control pipe */ - .direction = UE_DIR_ANY, - .mh.bufsize = (sizeof(struct usb2_device_request) + UBT_CTRL_BUFFER_SIZE), - .mh.callback = &ubt_ctrl_write_callback, - .mh.timeout = 5000, /* 5 seconds */ - }, +/* + * USB config + * + * The following desribes usb transfers that could be submitted on USB device. + * + * Interface 0 on the USB device must present the following endpoints + * 1) Interrupt endpoint to receive HCI events + * 2) Bulk IN endpoint to receive ACL data + * 3) Bulk OUT endpoint to send ACL data + * + * Interface 1 on the USB device must present the following endpoints + * 1) Isochronous IN endpoint to receive SCO data + * 2) Isochronous OUT endpoint to send SCO data + */ - [4] = { - .type = UE_CONTROL, - .endpoint = 0x00, /* Control pipe */ - .direction = UE_DIR_ANY, - .mh.bufsize = sizeof(struct usb2_device_request), - .mh.callback = &ubt_bulk_write_clear_stall_callback, - .mh.timeout = 1000, /* 1 second */ - .mh.interval = 50, /* 50ms */ +static const struct usb2_config ubt_config[UBT_N_TRANSFER] = +{ + /* + * Interface #0 + */ + + /* Outgoing bulk transfer - ACL packets */ + [UBT_IF_0_BULK_DT_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = UBT_BULK_WRITE_BUFFER_SIZE, + .mh.flags = { .pipe_bof = 1, }, + .mh.callback = &ubt_bulk_write_callback, }, - - [5] = { - .type = UE_CONTROL, - .endpoint = 0x00, /* Control pipe */ - .direction = UE_DIR_ANY, - .mh.bufsize = sizeof(struct usb2_device_request), - .mh.callback = &ubt_bulk_read_clear_stall_callback, - .mh.timeout = 1000, /* 1 second */ - .mh.interval = 50, /* 50ms */ + /* Incoming bulk transfer - ACL packets */ + [UBT_IF_0_BULK_DT_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = UBT_BULK_READ_BUFFER_SIZE, + .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, + .mh.callback = &ubt_bulk_read_callback, }, - - [6] = { - .type = UE_CONTROL, - .endpoint = 0x00, /* Control pipe */ - .direction = UE_DIR_ANY, - .mh.bufsize = sizeof(struct usb2_device_request), - .mh.callback = &ubt_intr_read_clear_stall_callback, - .mh.timeout = 1000, /* 1 second */ - .mh.interval = 50, /* 50ms */ + /* Incoming interrupt transfer - HCI events */ + [UBT_IF_0_INTR_DT_RD] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, }, + .mh.bufsize = UBT_INTR_BUFFER_SIZE, + .mh.callback = &ubt_intr_read_callback, }, -}; - -/* USB config */ -static const struct usb2_config - ubt_config_if_1_full_speed[UBT_IF_1_N_TRANSFER] = { - - [0] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &ubt_isoc_read_callback, + /* Outgoing control transfer - HCI commands */ + [UBT_IF_0_CTRL_DT_WR] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = UBT_CTRL_BUFFER_SIZE, + .mh.callback = &ubt_ctrl_write_callback, + .mh.timeout = 5000, /* 5 seconds */ }, - - [1] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &ubt_isoc_read_callback, + /* Outgoing control transfer to clear stall on outgoing bulk transfer */ + [UBT_IF_0_BULK_CS_WR] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ubt_bulk_write_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ }, - - [2] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &ubt_isoc_write_callback, + /* Outgoing control transfer to clear stall on incoming bulk transfer */ + [UBT_IF_0_BULK_CS_RD] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ubt_bulk_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ }, - - [3] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &ubt_isoc_write_callback, + /* + * Outgoing control transfer to clear stall on incoming + * interrupt transfer + */ + [UBT_IF_0_INTR_CS_RD] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = sizeof(struct usb2_device_request), + .mh.callback = &ubt_intr_read_clear_stall_callback, + .mh.timeout = 1000, /* 1 second */ + .mh.interval = 50, /* 50ms */ }, -}; -/* USB config */ -static const struct usb2_config - ubt_config_if_1_high_speed[UBT_IF_1_N_TRANSFER] = { - - [0] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES * 8, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &ubt_isoc_read_callback, + /* + * Interface #1 + */ + + /* Incoming isochronous transfer #1 - SCO packets */ + [UBT_IF_1_ISOC_DT_RD1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = { .short_xfer_ok = 1, }, + .mh.callback = &ubt_isoc_read_callback, }, - - [1] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES * 8, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &ubt_isoc_read_callback, + /* Incoming isochronous transfer #2 - SCO packets */ + [UBT_IF_1_ISOC_DT_RD2] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = { .short_xfer_ok = 1, }, + .mh.callback = &ubt_isoc_read_callback, }, - - [2] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES * 8, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &ubt_isoc_write_callback, + /* Outgoing isochronous transfer #1 - SCO packets */ + [UBT_IF_1_ISOC_DT_WR1] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = { .short_xfer_ok = 1, }, + .mh.callback = &ubt_isoc_write_callback, }, - - [3] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UBT_ISOC_NFRAMES * 8, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &ubt_isoc_write_callback, + /* Outgoing isochronous transfer #2 - SCO packets */ + [UBT_IF_1_ISOC_DT_WR2] = { + .type = UE_ISOCHRONOUS, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ + .mh.frames = UBT_ISOC_NFRAMES, + .mh.flags = { .short_xfer_ok = 1, }, + .mh.callback = &ubt_isoc_write_callback, }, }; /* - * Module - */ - -DRIVER_MODULE(ng_ubt, ushub, ubt_driver, ubt_devclass, ubt_modevent, 0); -MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); -MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); -MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); -MODULE_DEPEND(ng_ubt, usb2_bluetooth, 1, 1, 1); -MODULE_DEPEND(ng_ubt, usb2_core, 1, 1, 1); - -/**************************************************************************** - **************************************************************************** - ** USB specific - **************************************************************************** - ****************************************************************************/ - -/* - * Load/Unload the driver module - */ - -static int -ubt_modevent(module_t mod, int event, void *data) -{ - int error; - - switch (event) { - 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); - } - break; - - case MOD_UNLOAD: - error = ng_rmtype(&typestruct); - break; - - default: - error = EOPNOTSUPP; - break; - } - return (error); -} /* ubt_modevent */ - -/* * If for some reason device should not be attached then put * VendorID/ProductID pair into the list below. The format is * as follows: * - * { VENDOR_ID, PRODUCT_ID }, + * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) }, * * where VENDOR_ID and PRODUCT_ID are hex numbers. */ static const struct usb2_device_id ubt_ignore_devs[] = { /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */ - {USB_VPI(USB_VENDOR_AVM, 0x2200, 0)}, + { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) }, }; /* List of supported bluetooth devices */ static const struct usb2_device_id ubt_devs[] = { - /* Generic Bluetooth class devices. */ - {USB_IFACE_CLASS(UDCLASS_WIRELESS), - USB_IFACE_SUBCLASS(UDSUBCLASS_RF), - USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH)}, + /* Generic Bluetooth class devices */ + { USB_IFACE_CLASS(UDCLASS_WIRELESS), + USB_IFACE_SUBCLASS(UDSUBCLASS_RF), + USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */ - {USB_VPI(USB_VENDOR_AVM, 0x3800, 0)}, + { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) }, }; /* - * Probe for a USB Bluetooth device + * Probe for a USB Bluetooth device. + * USB context. */ static int ubt_probe(device_t dev) { - struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); - if (uaa->usb2_mode != USB_MODE_HOST) { + if (uaa->usb2_mode != USB_MODE_HOST) return (ENXIO); - } - if (uaa->info.bIfaceIndex != 0) { + + if (uaa->info.bIfaceIndex != 0) return (ENXIO); - } + if (usb2_lookup_id_by_uaa(ubt_ignore_devs, - sizeof(ubt_ignore_devs), uaa) == 0) { + sizeof(ubt_ignore_devs), uaa) == 0) return (ENXIO); - } + return (usb2_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa)); -} +} /* ubt_probe */ /* - * Attach the device + * Attach the device. + * USB context. */ static int ubt_attach(device_t dev) { - struct usb2_attach_arg *uaa = device_get_ivars(dev); - struct ubt_softc *sc = device_get_softc(dev); - const struct usb2_config *isoc_setup; - struct usb2_endpoint_descriptor *ed; - uint16_t wMaxPacketSize; - uint8_t alt_index; - uint8_t iface_index; - uint8_t i; - uint8_t j; + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ubt_softc *sc = device_get_softc(dev); + struct usb2_endpoint_descriptor *ed; + uint16_t wMaxPacketSize; + uint8_t alt_index, iface_index, i, j; device_set_usb2_desc(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), - "%s", device_get_nameunit(dev)); + "%s", device_get_nameunit(dev)); + + /* + * Create Netgraph node + */ + + sc->sc_hook = NULL; + + if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { + device_printf(dev, "could not create Netgraph node\n"); + return (ENXIO); + } + + /* Name Netgraph node */ + if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { + device_printf(dev, "could not name Netgraph node\n"); + NG_NODE_UNREF(sc->sc_node); + return (ENXIO); + } + NG_NODE_SET_PRIVATE(sc->sc_node, sc); + NG_NODE_FORCE_WRITER(sc->sc_node); /* * Initialize device softc structure @@ -481,34 +484,28 @@ ubt_attach(device_t dev) /* state */ sc->sc_debug = NG_UBT_WARN_LEVEL; sc->sc_flags = 0; - NG_UBT_STAT_RESET(sc->sc_stat); + UBT_STAT_RESET(sc); - /* control pipe */ - NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); + /* initialize locks */ + mtx_init(&sc->sc_mbufq_mtx, "ubt mbufq", NULL, MTX_DEF); + mtx_init(&sc->sc_if_mtx[0], "ubt if0", NULL, MTX_DEF | MTX_RECURSE); + mtx_init(&sc->sc_if_mtx[1], "ubt if1", NULL, MTX_DEF | MTX_RECURSE); - /* bulk-out pipe */ + /* initialize packet queues */ + NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN); + NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN); - /* isoc-out pipe */ - NG_BT_MBUFQ_INIT(&sc->sc_scoq, - (usb2_get_speed(uaa->device) == USB_SPEED_HIGH) ? - (2 * UBT_ISOC_NFRAMES * 8) : - (2 * UBT_ISOC_NFRAMES)); - - /* isoc-in pipe */ - NG_BT_MBUFQ_INIT(&sc->sc_sciq, - (usb2_get_speed(uaa->device) == USB_SPEED_HIGH) ? - (2 * UBT_ISOC_NFRAMES * 8) : - (2 * UBT_ISOC_NFRAMES)); - - /* netgraph part */ - sc->sc_node = NULL; - sc->sc_hook = NULL; + /* initialize glue task */ + sc->sc_task_flags = 0; + TASK_INIT(&sc->sc_task, 0, ubt_task, sc->sc_node); /* * Configure Bluetooth USB device. Discover all required USB * interfaces and endpoints. * + * Device is expected to be a high-speed device. + * * USB device must present two interfaces: * 1) Interface 0 that has 3 endpoints * 1) Interrupt endpoint to receive HCI events @@ -523,1002 +520,1133 @@ ubt_attach(device_t dev) * configurations with different packet size. */ + bzero(&sc->sc_xfer, sizeof(sc->sc_xfer)); + /* * Interface 0 */ - mtx_init(&sc->sc_mtx, "ubt lock", NULL, MTX_DEF | MTX_RECURSE); - iface_index = 0; - if (usb2_transfer_setup - (uaa->device, &iface_index, sc->sc_xfer_if_0, ubt_config_if_0, - UBT_IF_0_N_TRANSFER, sc, &sc->sc_mtx)) { - device_printf(dev, "Could not allocate transfers " - "for interface 0!\n"); + if (usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + ubt_config, UBT_IF_0_N_TRANSFER, + sc->sc_node, &sc->sc_if_mtx[0])) { + device_printf(dev, "could not allocate transfers for " \ + "interface 0!\n"); goto detach; } + /* * Interface 1 - * (search alternate settings, and find - * the descriptor with the largest + * (search alternate settings, and find the descriptor with the largest * wMaxPacketSize) */ - isoc_setup = - ((usb2_get_speed(uaa->device) == USB_SPEED_HIGH) ? - ubt_config_if_1_high_speed : - ubt_config_if_1_full_speed); wMaxPacketSize = 0; - - /* search through all the descriptors looking for bidir mode */ - - alt_index = 0 - 1; + alt_index = 0; i = 0; j = 0; + + /* Search through all the descriptors looking for bidir mode */ while (1) { uint16_t temp; - ed = usb2_find_edesc( - usb2_get_config_descriptor(uaa->device), 1, i, j); + ed = usb2_find_edesc(usb2_get_config_descriptor(uaa->device), + 1, i, j); if (ed == NULL) { - if (j == 0) { - /* end of interfaces */ - break; - } else { + if (j != 0) { /* next interface */ j = 0; - i++; + i ++; continue; } + + break; /* end of interfaces */ } + temp = UGETW(ed->wMaxPacketSize); if (temp > wMaxPacketSize) { wMaxPacketSize = temp; alt_index = i; } - j++; - } - if (usb2_set_alt_interface_index(uaa->device, 1, alt_index)) { - device_printf(dev, "Could not set alternate " - "setting %d for interface 1!\n", alt_index); - goto detach; + j ++; } - iface_index = 1; - if (usb2_transfer_setup - (uaa->device, &iface_index, sc->sc_xfer_if_1, - isoc_setup, UBT_IF_1_N_TRANSFER, sc, &sc->sc_mtx)) { - device_printf(dev, "Could not allocate transfers " - "for interface 1!\n"); - goto detach; - } - /* create Netgraph node */ - if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { - printf("%s: Could not create Netgraph node\n", - sc->sc_name); - sc->sc_node = NULL; + /* Set alt configuration only if we found it */ + if (wMaxPacketSize > 0 && + usb2_set_alt_interface_index(uaa->device, 1, alt_index)) { + device_printf(dev, "could not set alternate setting %d " \ + "for interface 1!\n", alt_index); goto detach; } - /* name node */ - if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { - printf("%s: Could not name Netgraph node\n", - sc->sc_name); - NG_NODE_UNREF(sc->sc_node); - sc->sc_node = NULL; + iface_index = 1; + if (usb2_transfer_setup(uaa->device, &iface_index, + &sc->sc_xfer[UBT_IF_0_N_TRANSFER], + &ubt_config[UBT_IF_0_N_TRANSFER], UBT_IF_1_N_TRANSFER, + sc->sc_node, &sc->sc_if_mtx[1])) { + device_printf(dev, "could not allocate transfers for " \ + "interface 1!\n"); goto detach; } - NG_NODE_SET_PRIVATE(sc->sc_node, sc); - NG_NODE_FORCE_WRITER(sc->sc_node); - - /* claim all interfaces on the device */ - - for (i = 1;; i++) { - if (usb2_get_iface(uaa->device, i) == NULL) { - break; - } + /* Claim all interfaces on the device */ + for (i = 1; usb2_get_iface(uaa->device, i) != NULL; i ++) usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); - } - return (0); /* success */ + return (0); /* success */ detach: ubt_detach(dev); return (ENXIO); -} +} /* ubt_attach */ /* - * Detach the device + * Detach the device. + * USB context. */ int ubt_detach(device_t dev) { - struct ubt_softc *sc = device_get_softc(dev); - - /* destroy Netgraph node */ + struct ubt_softc *sc = device_get_softc(dev); + node_p node = sc->sc_node; - if (sc->sc_node != NULL) { - NG_NODE_SET_PRIVATE(sc->sc_node, NULL); - ng_rmnode_self(sc->sc_node); + /* Destroy Netgraph node */ + if (node != NULL) { sc->sc_node = NULL; - } - /* free USB transfers, if any */ - - usb2_transfer_unsetup(sc->sc_xfer_if_0, UBT_IF_0_N_TRANSFER); - usb2_transfer_unsetup(sc->sc_xfer_if_1, UBT_IF_1_N_TRANSFER); + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_REALLY_DIE(node); + NG_NODE_REF(node); + ng_rmnode_self(node); + } - mtx_destroy(&sc->sc_mtx); + /* Free USB transfers, if any */ + usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); - /* destroy queues */ + if (node != NULL) + NG_NODE_UNREF(node); + /* Destroy queues */ + UBT_MBUFQ_LOCK(sc); NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq); NG_BT_MBUFQ_DESTROY(&sc->sc_aclq); NG_BT_MBUFQ_DESTROY(&sc->sc_scoq); - NG_BT_MBUFQ_DESTROY(&sc->sc_sciq); + UBT_MBUFQ_UNLOCK(sc); + + mtx_destroy(&sc->sc_if_mtx[0]); + mtx_destroy(&sc->sc_if_mtx[1]); + mtx_destroy(&sc->sc_mbufq_mtx); return (0); -} +} /* ubt_detach */ + +/* + * Called when outgoing control request (HCI command) has completed, i.e. + * HCI command was sent to the device. + * USB context. + */ static void ubt_ctrl_write_callback(struct usb2_xfer *xfer) { - struct ubt_softc *sc = xfer->priv_sc; - struct usb2_device_request req; - struct mbuf *m; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct usb2_device_request req; + struct mbuf *m; + + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } + + sc = NG_NODE_PRIVATE(node); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: -tr_transferred: - - if (xfer->error) { - NG_UBT_STAT_OERROR(sc->sc_stat); - } else { - NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen); - NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); + if (xfer->error != 0) + UBT_STAT_OERROR(sc); + else { + UBT_INFO(sc, "sent %d bytes to control pipe\n", + xfer->actlen); + + UBT_STAT_BYTES_SENT(sc, xfer->actlen); + UBT_STAT_PCKTS_SENT(sc); } + /* FALLTHROUGH */ case USB_ST_SETUP: - - /* get next mbuf, if any */ - +send_next: + /* Get next command mbuf, if any */ + UBT_MBUFQ_LOCK(sc); NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m); + UBT_MBUFQ_UNLOCK(sc); if (m == NULL) { - NG_UBT_INFO(sc, "HCI command queue is empty\n"); + UBT_INFO(sc, "HCI command queue is empty\n"); + NG_NODE_UNREF(node); return; } - /* - * check HCI command frame size and - * copy it to USB transfer buffer: - */ - - if (m->m_pkthdr.len > UBT_CTRL_BUFFER_SIZE) { - panic("HCI command frame too big, size=%zd, len=%d\n", - UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); - } - /* initialize a USB control request and then schedule it */ + /* 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(sc, "Sending control request, bmRequestType=0x%02x, " - "wLength=%d\n", req.bmRequestType, UGETW(req.wLength)); + UBT_INFO(sc, "Sending control request, " \ + "bmRequestType=0x%02x, wLength=%d\n", + req.bmRequestType, UGETW(req.wLength)); usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); usb2_m_copy_in(xfer->frbuffers + 1, 0, m, 0, m->m_pkthdr.len); xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = m->m_pkthdr.len; - xfer->nframes = xfer->frlengths[1] ? 2 : 1; + xfer->nframes = 2; NG_FREE_M(m); usb2_start_hardware(xfer); - return; + break; - default: /* Error */ - if (xfer->error == USB_ERR_CANCELLED) { - /* ignore */ - return; + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "control transfer failed: %s\n", + usb2_errstr(xfer->error)); + + UBT_STAT_OERROR(sc); + goto send_next; } - goto tr_transferred; + + NG_NODE_UNREF(node); /* cancelled */ + break; } -} +} /* ubt_ctrl_write_callback */ + +/* + * Called when incoming interrupt transfer (HCI event) has completed, i.e. + * HCI event was received from the device. + * USB context. + */ static void ubt_intr_read_callback(struct usb2_xfer *xfer) { - struct ubt_softc *sc = xfer->priv_sc; - struct mbuf *m; - uint32_t max_len; - uint8_t *ptr; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct mbuf *m; + ng_hci_event_pkt_t *hdr; + int error; + + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: + sc = NG_NODE_PRIVATE(node); - /* allocate a new mbuf */ + if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) { + UBT_INFO(sc, "no upstream hook\n"); + NG_NODE_UNREF(node); + return; /* upstream hook is gone */ + } - MGETHDR(m, M_DONTWAIT, MT_DATA); + m = NULL; + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + /* Allocate a new mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { - goto tr_setup; + UBT_STAT_IERROR(sc); + goto submit_next; } - MCLGET(m, M_DONTWAIT); + MCLGET(m, M_DONTWAIT); if (!(m->m_flags & M_EXT)) { - NG_FREE_M(m); - goto tr_setup; - } - if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) { - *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; - m->m_pkthdr.len = m->m_len = 1; - } else { - m->m_pkthdr.len = m->m_len = 0; + UBT_STAT_IERROR(sc); + goto submit_next; } - max_len = (MCLBYTES - m->m_len); - - if (xfer->actlen > max_len) { - xfer->actlen = max_len; - } - ptr = ((uint8_t *)(m->m_data)) + m->m_len; + /* Add HCI packet type */ + *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; + m->m_pkthdr.len = m->m_len = 1; - usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen); + if (xfer->actlen > MCLBYTES - 1) + xfer->actlen = MCLBYTES - 1; + usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1, + xfer->actlen); m->m_pkthdr.len += xfer->actlen; m->m_len += xfer->actlen; - NG_UBT_INFO(sc, "got %d bytes from interrupt " - "pipe\n", xfer->actlen); + UBT_INFO(sc, "got %d bytes from interrupt pipe\n", + xfer->actlen); - sc->sc_intr_buffer = m; - - case USB_ST_SETUP: -tr_setup: + /* Validate packet and send it up the stack */ + if (m->m_pkthdr.len < sizeof(*hdr)) { + UBT_INFO(sc, "HCI event packet is too short\n"); - if (sc->sc_intr_buffer) { - ng_send_fn(sc->sc_node, NULL, ubt_intr_read_complete, NULL, 0); - return; + UBT_STAT_IERROR(sc); + goto submit_next; } - if (sc->sc_flags & UBT_FLAG_INTR_STALL) { - usb2_transfer_start(sc->sc_xfer_if_0[6]); - return; + + hdr = mtod(m, ng_hci_event_pkt_t *); + if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) { + UBT_ERR(sc, "Invalid HCI event packet size, " \ + "length=%d, pktlen=%d\n", + hdr->length, m->m_pkthdr.len); + + UBT_STAT_IERROR(sc); + goto submit_next; } - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - return; + UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ + "length=%d\n", m->m_pkthdr.len, hdr->length); - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - sc->sc_flags |= UBT_FLAG_INTR_STALL; - usb2_transfer_start(sc->sc_xfer_if_0[6]); + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); + + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (error != 0) + UBT_STAT_IERROR(sc); + + /* m == NULL at this point */ + /* FALLTHROUGH */ + + case USB_ST_SETUP: +submit_next: + NG_FREE_M(m); /* checks for m != NULL */ + + if (sc->sc_flags & UBT_FLAG_INTR_STALL) + usb2_transfer_start(sc->sc_xfer[UBT_IF_0_INTR_CS_RD]); + else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); } - return; + break; + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "interrupt transfer failed: %s\n", + usb2_errstr(xfer->error)); + + /* Try to clear stall first */ + sc->sc_flags |= UBT_FLAG_INTR_STALL; + usb2_transfer_start(sc->sc_xfer[UBT_IF_0_INTR_CS_RD]); + } else + NG_NODE_UNREF(node); /* cancelled */ + break; } -} +} /* ubt_intr_read_callback */ + +/* + * Called when outgoing control transfer initiated to clear stall on + * interrupt pipe has completed. + * USB context. + */ static void ubt_intr_read_clear_stall_callback(struct usb2_xfer *xfer) { - struct ubt_softc *sc = xfer->priv_sc; - struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[2]; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct usb2_xfer *xfer_other; + + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } + + sc = NG_NODE_PRIVATE(node); + xfer_other = sc->sc_xfer[UBT_IF_0_INTR_DT_RD]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~UBT_FLAG_INTR_STALL; usb2_transfer_start(xfer_other); - } -} + } else + NG_NODE_UNREF(node); /* cant clear stall */ +} /* ubt_intr_read_clear_stall_callback */ + +/* + * Called when incoming bulk transfer (ACL packet) has completed, i.e. + * ACL packet was received from the device. + * USB context. + */ static void -ubt_intr_read_complete(node_p node, hook_p hook, void *arg1, int arg2) +ubt_bulk_read_callback(struct usb2_xfer *xfer) { - struct ubt_softc *sc = NG_NODE_PRIVATE(node); - struct mbuf *m; - ng_hci_event_pkt_t *hdr; - int error; - - if (sc == NULL) { - return; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct mbuf *m; + ng_hci_acldata_pkt_t *hdr; + uint16_t len; + int error; + + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ } - mtx_lock(&sc->sc_mtx); - - m = sc->sc_intr_buffer; - - if (m) { - - 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(sc, "No upstream hook\n"); - goto done; - } - NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len); - - if (m->m_pkthdr.len < sizeof(*hdr)) { - NG_UBT_INFO(sc, "Packet too short\n"); - goto done; - } - if (hdr->length == (m->m_pkthdr.len - sizeof(*hdr))) { - NG_UBT_INFO(sc, "Got complete HCI event frame, " - "pktlen=%d, length=%d\n", - m->m_pkthdr.len, hdr->length); - - NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); - - NG_SEND_DATA_ONLY(error, sc->sc_hook, m); - m = NULL; + sc = NG_NODE_PRIVATE(node); - if (error != 0) { - NG_UBT_STAT_IERROR(sc->sc_stat); - } - } else { - NG_UBT_ERR(sc, "Invalid HCI event frame size, " - "length=%d, pktlen=%d\n", - hdr->length, m->m_pkthdr.len); - - NG_UBT_STAT_IERROR(sc->sc_stat); - } + if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) { + UBT_INFO(sc, "no upstream hook\n"); + NG_NODE_UNREF(node); + return; /* upstream hook is gone */ } -done: - if (m) { - NG_FREE_M(m); - } - /* start USB transfer if not already started */ - - usb2_transfer_start(sc->sc_xfer_if_0[2]); - mtx_unlock(&sc->sc_mtx); -} - -static void -ubt_bulk_read_callback(struct usb2_xfer *xfer) -{ - struct ubt_softc *sc = xfer->priv_sc; - struct mbuf *m; - uint32_t max_len; - uint8_t *ptr; + m = NULL; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: - - /* allocate new mbuf */ - + /* Allocate new mbuf */ MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) { - goto tr_setup; + UBT_STAT_IERROR(sc); + goto submit_next; } - MCLGET(m, M_DONTWAIT); + MCLGET(m, M_DONTWAIT); if (!(m->m_flags & M_EXT)) { - NG_FREE_M(m); - goto tr_setup; - } - if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) { - *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; - m->m_pkthdr.len = m->m_len = 1; - } else { - m->m_pkthdr.len = m->m_len = 0; + UBT_STAT_IERROR(sc); + goto submit_next; } - max_len = (MCLBYTES - m->m_len); + /* Add HCI packet type */ + *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; + m->m_pkthdr.len = m->m_len = 1; - if (xfer->actlen > max_len) { - xfer->actlen = max_len; - } - ptr = ((uint8_t *)(m->m_data)) + m->m_len; - - usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen); + if (xfer->actlen > MCLBYTES - 1) + xfer->actlen = MCLBYTES - 1; + usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1, + xfer->actlen); m->m_pkthdr.len += xfer->actlen; m->m_len += xfer->actlen; - NG_UBT_INFO(sc, "got %d bytes from bulk-in " - "pipe\n", xfer->actlen); + UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", + xfer->actlen); - sc->sc_bulk_in_buffer = m; + /* Validate packet and send it up the stack */ + if (m->m_pkthdr.len < sizeof(*hdr)) { + UBT_INFO(sc, "HCI ACL packet is too short\n"); - case USB_ST_SETUP: -tr_setup: - if (sc->sc_bulk_in_buffer) { - ng_send_fn(sc->sc_node, NULL, ubt_bulk_read_complete, NULL, 0); - return; - } - if (sc->sc_flags & UBT_FLAG_READ_STALL) { - usb2_transfer_start(sc->sc_xfer_if_0[5]); - return; + UBT_STAT_IERROR(sc); + goto submit_next; } - xfer->frlengths[0] = xfer->max_data_length; - usb2_start_hardware(xfer); - return; + hdr = mtod(m, ng_hci_acldata_pkt_t *); + len = le16toh(hdr->length); + if (len != (m->m_pkthdr.len - sizeof(*hdr))) { + UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \ + "pktlen=%d\n", len, m->m_pkthdr.len); - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ - sc->sc_flags |= UBT_FLAG_READ_STALL; - usb2_transfer_start(sc->sc_xfer_if_0[5]); + UBT_STAT_IERROR(sc); + goto submit_next; } - return; - } -} + UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ + "length=%d\n", m->m_pkthdr.len, len); -static void -ubt_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) -{ - struct ubt_softc *sc = xfer->priv_sc; - struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[1]; + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); - if (usb2_clear_stall_callback(xfer, xfer_other)) { - DPRINTF("stall cleared\n"); - sc->sc_flags &= ~UBT_FLAG_READ_STALL; - usb2_transfer_start(xfer_other); - } -} - -static void -ubt_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2) -{ - struct ubt_softc *sc = NG_NODE_PRIVATE(node); - struct mbuf *m; - ng_hci_acldata_pkt_t *hdr; - uint16_t len; - int error; - - if (sc == NULL) { - return; - } - mtx_lock(&sc->sc_mtx); - - m = sc->sc_bulk_in_buffer; - - if (m) { - - sc->sc_bulk_in_buffer = NULL; + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (error != 0) + UBT_STAT_IERROR(sc); - hdr = mtod(m, ng_hci_acldata_pkt_t *); + /* m == NULL at this point */ + /* FALLTHOUGH */ - if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) { - NG_UBT_INFO(sc, "No upstream hook\n"); - goto done; - } - NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len); - - if (m->m_pkthdr.len < sizeof(*hdr)) { - NG_UBT_INFO(sc, "Packet too short\n"); - goto done; + case USB_ST_SETUP: +submit_next: + NG_FREE_M(m); /* checks for m != NULL */ + + if (sc->sc_flags & UBT_FLAG_READ_STALL) + usb2_transfer_start(sc->sc_xfer[UBT_IF_0_BULK_CS_RD]); + else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); } - len = le16toh(hdr->length); - - if (len == (m->m_pkthdr.len - sizeof(*hdr))) { - NG_UBT_INFO(sc, "Got complete ACL data frame, " - "pktlen=%d, length=%d\n", - m->m_pkthdr.len, len); + break; - NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "bulk-in transfer failed: %s\n", + usb2_errstr(xfer->error)); - NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + /* Try to clear stall first */ + sc->sc_flags |= UBT_FLAG_READ_STALL; + usb2_transfer_start(sc->sc_xfer[UBT_IF_0_BULK_CS_RD]); + } else + NG_NODE_UNREF(node); /* cancelled */ + break; + } +} /* ubt_bulk_read_callback */ - m = NULL; +/* + * Called when outgoing control transfer initiated to clear stall on + * incoming bulk pipe has completed. + * USB context. + */ - if (error != 0) { - NG_UBT_STAT_IERROR(sc->sc_stat); - } - } else { - NG_UBT_ERR(sc, "Invalid ACL frame size, " - "length=%d, pktlen=%d\n", - len, m->m_pkthdr.len); +static void +ubt_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) +{ + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct usb2_xfer *xfer_other; - NG_UBT_STAT_IERROR(sc->sc_stat); - } + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ } -done: - if (m) { - NG_FREE_M(m); - } - /* start USB transfer if not already started */ - usb2_transfer_start(sc->sc_xfer_if_0[1]); + sc = NG_NODE_PRIVATE(node); + xfer_other = sc->sc_xfer[UBT_IF_0_BULK_DT_RD]; - mtx_unlock(&sc->sc_mtx); -} + if (usb2_clear_stall_callback(xfer, xfer_other)) { + DPRINTF("stall cleared\n"); + sc->sc_flags &= ~UBT_FLAG_READ_STALL; + usb2_transfer_start(xfer_other); + } else + NG_NODE_UNREF(node); /* cant clear stall */ +} /* ubt_bulk_read_clear_stall_callback */ + +/* + * Called when outgoing bulk transfer (ACL packet) has completed, i.e. + * ACL packet was sent to the device. + * USB context. + */ static void ubt_bulk_write_callback(struct usb2_xfer *xfer) { - struct ubt_softc *sc = xfer->priv_sc; - struct mbuf *m; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct mbuf *m; + + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } + + sc = NG_NODE_PRIVATE(node); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: - NG_UBT_INFO(sc, "sent %d bytes to bulk-out " - "pipe\n", xfer->actlen); - NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen); - NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); - - case USB_ST_SETUP: - if (sc->sc_flags & UBT_FLAG_WRITE_STALL) { - usb2_transfer_start(sc->sc_xfer_if_0[4]); - return; + if (xfer->error != 0) + UBT_STAT_OERROR(sc); + else { + UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", + xfer->actlen); + + UBT_STAT_BYTES_SENT(sc, xfer->actlen); + UBT_STAT_PCKTS_SENT(sc); } - /* get next mbuf, if any */ + /* FALLTHROUGH */ + case USB_ST_SETUP: + /* Get next mbuf, if any */ + UBT_MBUFQ_LOCK(sc); NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m); + UBT_MBUFQ_UNLOCK(sc); if (m == NULL) { - NG_UBT_INFO(sc, "ACL data queue is empty\n"); - return; + UBT_INFO(sc, "ACL data queue is empty\n"); + NG_NODE_UNREF(node); + return; /* transfer completed */ } + /* - * check ACL data frame size and - * copy it back to a linear USB - * transfer buffer: + * Copy ACL data frame back to a linear USB transfer buffer + * and schedule transfer */ - if (m->m_pkthdr.len > UBT_BULK_WRITE_BUFFER_SIZE) { - panic("ACL data frame too big, size=%d, len=%d\n", - UBT_BULK_WRITE_BUFFER_SIZE, - m->m_pkthdr.len); - } usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); - - NG_UBT_INFO(sc, "bulk-out transfer has been started, " - "len=%d\n", m->m_pkthdr.len); - xfer->frlengths[0] = m->m_pkthdr.len; + UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", + m->m_pkthdr.len); + NG_FREE_M(m); usb2_start_hardware(xfer); - return; + break; - default: /* Error */ + default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "bulk-out transfer failed: %s\n", + usb2_errstr(xfer->error)); - NG_UBT_WARN(sc, "bulk-out transfer failed: %s\n", - usb2_errstr(xfer->error)); - - NG_UBT_STAT_OERROR(sc->sc_stat); + UBT_STAT_OERROR(sc); /* try to clear stall first */ sc->sc_flags |= UBT_FLAG_WRITE_STALL; - usb2_transfer_start(sc->sc_xfer_if_0[4]); - } - return; - + usb2_transfer_start(sc->sc_xfer[UBT_IF_0_BULK_CS_WR]); + } else + NG_NODE_UNREF(node); /* cancelled */ + break; } -} +} /* ubt_bulk_write_callback */ + +/* + * Called when outgoing control transfer initiated to clear stall on + * outgoing bulk pipe has completed. + * USB context. + */ static void ubt_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { - struct ubt_softc *sc = xfer->priv_sc; - struct usb2_xfer *xfer_other = sc->sc_xfer_if_0[0]; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct usb2_xfer *xfer_other; + + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } + + sc = NG_NODE_PRIVATE(node); + xfer_other = sc->sc_xfer[UBT_IF_0_BULK_DT_WR]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~UBT_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); - } -} + } else + NG_NODE_UNREF(node); /* cant clear stall */ +} /* ubt_bulk_write_clear_stall_callback */ + +/* + * Called when incoming isoc transfer (SCO packet) has completed, i.e. + * SCO packet was received from the device. + * USB context. + */ static void ubt_isoc_read_callback(struct usb2_xfer *xfer) { - struct ubt_softc *sc = xfer->priv_sc; - ng_hci_scodata_pkt_t hdr; - struct mbuf *m; - uint8_t *ptr; - uint32_t max_len; - uint32_t n; - uint32_t offset; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: -tr_transferred: - offset = 0; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + int n; - for (n = 0; n < xfer->nframes; n++) { - - if (xfer->frlengths[n] >= sizeof(hdr)) { - - usb2_copy_out(xfer->frbuffers, offset, - &hdr, sizeof(hdr)); - - if (hdr.length == (xfer->frlengths[n] - sizeof(hdr))) { - - NG_UBT_INFO(sc, "got complete SCO data " - "frame, length=%d\n", hdr.length); + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } - if (!NG_BT_MBUFQ_FULL(&sc->sc_sciq)) { + sc = NG_NODE_PRIVATE(node); - /* allocate a new mbuf */ + if ((sc->sc_hook == NULL) || NG_HOOK_NOT_VALID(sc->sc_hook)) { + UBT_INFO(sc, "no upstream hook\n"); + NG_NODE_UNREF(node); + return; /* upstream hook is gone */ + } - MGETHDR(m, M_DONTWAIT, MT_DATA); + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + for (n = 0; n < xfer->nframes; n ++) + if (ubt_isoc_read_one_frame(xfer, n) < 0) + break; + /* FALLTHROUGH */ - if (m == NULL) { - goto tr_setup; - } - MCLGET(m, M_DONTWAIT); + case USB_ST_SETUP: +read_next: + for (n = 0; n < xfer->nframes; n ++) + xfer->frlengths[n] = xfer->max_frame_size; - if (!(m->m_flags & M_EXT)) { - NG_FREE_M(m); - goto tr_setup; - } - /* - * fix SCO data frame header - * if required - */ + usb2_start_hardware(xfer); + break; - if (!(sc->sc_flags & UBT_HAVE_FRAME_TYPE)) { - *mtod(m, uint8_t *)= NG_HCI_SCO_DATA_PKT; - m->m_pkthdr.len = m->m_len = 1; - } else { - m->m_pkthdr.len = m->m_len = 0; - } + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_STAT_IERROR(sc); + goto read_next; + /* NOT REACHED */ + } - max_len = (MCLBYTES - m->m_len); + NG_NODE_UNREF(node); /* cancelled */ + break; + } +} /* ubt_isoc_read_callback */ - if (xfer->frlengths[n] > max_len) { - xfer->frlengths[n] = max_len; - } - ptr = ((uint8_t *)(m->m_data)) + m->m_len; +/* + * Helper function. Called from ubt_isoc_read_callback() to read + * SCO data from one frame. + * USB context. + */ - usb2_copy_out - (xfer->frbuffers, offset, - ptr, xfer->frlengths[n]); +static int +ubt_isoc_read_one_frame(struct usb2_xfer *xfer, int frame_no) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + int len, want, got, error; - m->m_pkthdr.len += xfer->frlengths[n]; - m->m_len += xfer->frlengths[n]; + /* Get existing SCO reassembly buffer */ + m = sc->sc_isoc_in_buffer; + sc->sc_isoc_in_buffer = NULL; - NG_BT_MBUFQ_ENQUEUE(&sc->sc_sciq, m); - } - } + /* While we have data in the frame */ + while ((len = xfer->frlengths[frame_no]) > 0) { + if (m == NULL) { + /* Start new reassembly buffer */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + UBT_STAT_IERROR(sc); + return (-1); /* XXX out of sync! */ } - offset += xfer->max_frame_size; - } - case USB_ST_SETUP: -tr_setup: - - if (NG_BT_MBUFQ_LEN(&sc->sc_sciq) > 0) { - ng_send_fn(sc->sc_node, NULL, ubt_isoc_read_complete, NULL, 0); - } - for (n = 0; n < xfer->nframes; n++) { - xfer->frlengths[n] = xfer->max_frame_size; - } - - usb2_start_hardware(xfer); - return; + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + UBT_STAT_IERROR(sc); + NG_FREE_M(m); + return (-1); /* XXX out of sync! */ + } - default: /* Error */ - if (xfer->error == USB_ERR_CANCELLED) { - /* ignore */ - return; + /* Expect SCO header */ + *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT; + m->m_pkthdr.len = m->m_len = got = 1; + want = sizeof(ng_hci_scodata_pkt_t); + } else { + /* + * Check if we have SCO header and if so + * adjust amount of data we want + */ + got = m->m_pkthdr.len; + want = sizeof(ng_hci_scodata_pkt_t); + + if (got >= want) + want += mtod(m, ng_hci_scodata_pkt_t *)->length; } - goto tr_transferred; - } -} -static void -ubt_isoc_read_complete(node_p node, hook_p hook, void *arg1, int arg2) -{ - ubt_softc_p sc = NG_NODE_PRIVATE(node); - struct mbuf *m; - int error; + /* Append frame data to the SCO reassembly buffer */ + if (got + len > want) + len = want - got; - if (sc == NULL) { - return; - } - mtx_lock(&sc->sc_mtx); + usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size, + mtod(m, uint8_t *) + m->m_pkthdr.len, len); - while (1) { + m->m_pkthdr.len += len; + m->m_len += len; + xfer->frlengths[frame_no] -= len; - NG_BT_MBUFQ_DEQUEUE(&sc->sc_sciq, m); + /* Check if we got everything we wanted, if not - continue */ + if (got != want) + continue; - if (m == NULL) { - break; - } - if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) { - NG_UBT_INFO(sc, "No upstream hook\n"); - goto done; - } - NG_UBT_INFO(sc, "Got complete SCO data frame, " - "pktlen=%d bytes\n", m->m_pkthdr.len); + /* If we got here then we got complete SCO frame */ + UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \ + "length=%d\n", m->m_pkthdr.len, + mtod(m, ng_hci_scodata_pkt_t *)->length); - NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); - NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len); + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (error != 0) + UBT_STAT_IERROR(sc); - m = NULL; - - if (error) { - NG_UBT_STAT_IERROR(sc->sc_stat); - } -done: - if (m) { - NG_FREE_M(m); - } + /* m == NULL at this point */ } - mtx_unlock(&sc->sc_mtx); -} + /* Put SCO reassembly buffer back */ + sc->sc_isoc_in_buffer = m; + + return (0); +} /* ubt_isoc_read_one_frame */ + +/* + * Called when outgoing isoc transfer (SCO packet) has completed, i.e. + * SCO packet was sent to the device. + * USB context. + */ static void ubt_isoc_write_callback(struct usb2_xfer *xfer) { - struct ubt_softc *sc = xfer->priv_sc; - struct mbuf *m; - uint32_t n; - uint32_t len; - uint32_t offset; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct mbuf *m; + int n, space, offset; + + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } + + sc = NG_NODE_PRIVATE(node); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: -tr_transferred: - if (xfer->error) { - NG_UBT_STAT_OERROR(sc->sc_stat); - } else { - NG_UBT_STAT_BYTES_SENT(sc->sc_stat, xfer->actlen); - NG_UBT_STAT_PCKTS_SENT(sc->sc_stat); + if (xfer->error) + UBT_STAT_OERROR(sc); + else { + UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", + xfer->actlen); + + UBT_STAT_BYTES_SENT(sc, xfer->actlen); + UBT_STAT_PCKTS_SENT(sc); } + /* FALLTHROUGH */ case USB_ST_SETUP: +send_next: offset = 0; + space = xfer->max_frame_size * xfer->nframes; + m = NULL; - for (n = 0; n < xfer->nframes; n++) { + while (space > 0) { + if (m == NULL) { + UBT_MBUFQ_LOCK(sc); + NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); + UBT_MBUFQ_UNLOCK(sc); - NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); + if (m == NULL) + break; + } - if (m) { - len = min(xfer->max_frame_size, m->m_pkthdr.len); + n = min(space, m->m_pkthdr.len); + if (n > 0) { + usb2_m_copy_in(xfer->frbuffers, offset, m,0, n); + m_adj(m, n); - usb2_m_copy_in(xfer->frbuffers, offset, m, 0, len); + offset += n; + space -= n; + } - NG_FREE_M(m); + if (m->m_pkthdr.len == 0) + NG_FREE_M(m); /* sets m = NULL */ + } - xfer->frlengths[n] = len; - offset += len; - } else { - xfer->frlengths[n] = 0; - } + /* Put whatever is left from mbuf back on queue */ + if (m != NULL) { + UBT_MBUFQ_LOCK(sc); + NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m); + UBT_MBUFQ_UNLOCK(sc); + } + + /* + * Calculate sizes for isoc frames. + * Note that offset could be 0 at this point (i.e. we have + * nothing to send). That is fine, as we have isoc. transfers + * going in both directions all the time. In this case it + * would be just empty isoc. transfer. + */ + + for (n = 0; n < xfer->nframes; n ++) { + xfer->frlengths[n] = min(offset, xfer->max_frame_size); + offset -= xfer->frlengths[n]; } usb2_start_hardware(xfer); - return; + break; - default: /* Error */ - if (xfer->error == USB_ERR_CANCELLED) { - /* ignore */ - return; + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_STAT_OERROR(sc); + goto send_next; + /* NOT REACHED */ } - goto tr_transferred; + + NG_NODE_UNREF(node); /* cancelled */ + break; } } /**************************************************************************** **************************************************************************** - ** Netgraph specific + ** Glue **************************************************************************** ****************************************************************************/ /* - * Netgraph node constructor. - * Do not allow to create node of this type: + * Schedule glue task. Should be called with sc_mbufq_mtx held. + * Netgraph context. */ static int -ng_ubt_constructor(node_p node) +ubt_task_schedule(ubt_softc_p sc, int action) { - return (EINVAL); -} + mtx_assert(&sc->sc_mbufq_mtx, MA_OWNED); + + if ((sc->sc_task_flags & action) == 0) { + /* + * Try to handle corner case when "start all" and "stop all" + * actions can both be set before task is executed. + * + * Assume the following: + * 1) "stop all" after "start all" cancels "start all", and, + * keeps "stop all" + * + * 2) "start all" after "stop all" is fine because task is + * executing "stop all" first + */ + + if (action == UBT_FLAG_T_STOP_ALL && + (sc->sc_task_flags & UBT_FLAG_T_START_ALL) != 0) + sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL; + + sc->sc_task_flags |= action; + } + + if (sc->sc_task_flags & UBT_FLAG_T_PENDING) + return (1); + + if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) { + NG_NODE_REF(sc->sc_node); + sc->sc_task_flags |= UBT_FLAG_T_PENDING; + return (1); + } + + /* XXX: i think this should never happen */ + + return (0); +} /* ubt_task_schedule */ /* - * Netgraph node destructor. - * Destroy node only when device has been detached: + * Glue task. Examines sc_task_flags and does things depending on it. + * Taskqueue context. */ -static int -ng_ubt_shutdown(node_p node) +static void +ubt_task(void *context, int pending) { - struct ubt_softc *sc = NG_NODE_PRIVATE(node); + node_p node = context; + ubt_softc_p sc; + int task_flags; - /* Let old node go */ - NG_NODE_SET_PRIVATE(node, NULL); - NG_NODE_UNREF(node); + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } - if (sc == NULL) { - goto done; + sc = NG_NODE_PRIVATE(node); + + UBT_MBUFQ_LOCK(sc); + task_flags = sc->sc_task_flags; + sc->sc_task_flags = 0; + UBT_MBUFQ_UNLOCK(sc); + + /* Stop all USB transfers */ + if (task_flags & UBT_FLAG_T_STOP_ALL) { + int i; + + /* + * Interface #0 + */ + + mtx_lock(&sc->sc_if_mtx[0]); + + for (i = UBT_IF_0_BULK_DT_WR; i < UBT_IF_0_N_TRANSFER; i ++) + usb2_transfer_stop(sc->sc_xfer[i]); + + mtx_unlock(&sc->sc_if_mtx[0]); + + /* + * Interface #1 + */ + + mtx_lock(&sc->sc_if_mtx[1]); + + for (i = UBT_IF_1_ISOC_DT_RD1; i < UBT_N_TRANSFER; i ++) + usb2_transfer_stop(sc->sc_xfer[i]); + + mtx_unlock(&sc->sc_if_mtx[1]); } - mtx_lock(&sc->sc_mtx); - /* Create Netgraph node */ - if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) { - printf("%s: Could not create Netgraph node\n", - sc->sc_name); - sc->sc_node = NULL; - goto done; + /* Start all incoming USB transfers */ + if (task_flags & UBT_FLAG_T_START_ALL) { + /* + * Interface #0 + */ + + mtx_lock(&sc->sc_if_mtx[0]); + ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD); + ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD); + mtx_unlock(&sc->sc_if_mtx[0]); + + /* + * Interface #1 + * Start both read and write isoc. transfers by default. + * Get them going all the time even if we have nothing + * to send to avoid any delays. + */ + + mtx_lock(&sc->sc_if_mtx[1]); + ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1); + ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2); + ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1); + ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2); + mtx_unlock(&sc->sc_if_mtx[1]); } - /* Name node */ - if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { - printf("%s: Could not name Netgraph node\n", - sc->sc_name); - NG_NODE_UNREF(sc->sc_node); - sc->sc_node = NULL; - goto done; + + /* Start outgoing control transfer */ + if (task_flags & UBT_FLAG_T_START_CTRL) { + mtx_lock(&sc->sc_if_mtx[0]); + ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR); + mtx_unlock(&sc->sc_if_mtx[0]); } - NG_NODE_SET_PRIVATE(sc->sc_node, sc); - NG_NODE_FORCE_WRITER(sc->sc_node); -done: - if (sc) { - mtx_unlock(&sc->sc_mtx); + /* Start outgoing bulk transfer */ + if (task_flags & UBT_FLAG_T_START_BULK) { + mtx_lock(&sc->sc_if_mtx[0]); + ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR); + mtx_unlock(&sc->sc_if_mtx[0]); } - return (0); -} + + NG_NODE_UNREF(node); +} /* ubt_task */ /* - * Create new hook. - * There can only be one. + * Start USB transfer. + * Helper function called from ubt_task. Must be called with appropriate + * interface lock held. + * Taskqueue context. */ -static int -ng_ubt_newhook(node_p node, hook_p hook, char const *name) +static void +ubt_xfer_start(ubt_softc_p sc, int transfer) { - struct ubt_softc *sc = NG_NODE_PRIVATE(node); - int error = 0; - - if (strcmp(name, NG_UBT_HOOK) != 0) { - return (EINVAL); + if (!usb2_transfer_pending(sc->sc_xfer[transfer])) { + NG_NODE_REF(sc->sc_node); + usb2_transfer_start(sc->sc_xfer[transfer]); } - mtx_lock(&sc->sc_mtx); +} /* ubt_xfer_start */ - if (sc->sc_hook != NULL) { - error = EISCONN; - } else { - sc->sc_hook = hook; - } +/**************************************************************************** + **************************************************************************** + ** Netgraph specific + **************************************************************************** + ****************************************************************************/ - mtx_unlock(&sc->sc_mtx); +/* + * Netgraph node constructor. Do not allow to create node of this type. + * Netgraph context. + */ - return (error); -} +static int +ng_ubt_constructor(node_p node) +{ + return (EINVAL); +} /* ng_ubt_constructor */ /* - * Connect hook. - * Start incoming USB transfers + * Netgraph node destructor. Destroy node only when device has been detached. + * Netgraph context. */ static int -ng_ubt_connect(hook_p hook) +ng_ubt_shutdown(node_p node) { - struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + if (node->nd_flags & NGF_REALLY_DIE) { + /* + * We came here because the USB device is being + * detached, so stop being persistant. + */ + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_UNREF(node); + } else + NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */ - NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); + return (0); +} /* ng_ubt_shutdown */ - mtx_lock(&sc->sc_mtx); +/* + * Create new hook. There can only be one. + * Netgraph context. + */ - sc->sc_flags |= (UBT_FLAG_READ_STALL | - UBT_FLAG_WRITE_STALL | - UBT_FLAG_INTR_STALL); +static int +ng_ubt_newhook(node_p node, hook_p hook, char const *name) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(node); - /* start intr transfer */ - usb2_transfer_start(sc->sc_xfer_if_0[2]); + if (strcmp(name, NG_UBT_HOOK) != 0) + return (EINVAL); - /* start bulk-in transfer */ - usb2_transfer_start(sc->sc_xfer_if_0[1]); + if (sc->sc_hook != NULL) + return (EISCONN); - /* start bulk-out transfer */ - usb2_transfer_start(sc->sc_xfer_if_0[0]); + sc->sc_hook = hook; - /* start control-out transfer */ - usb2_transfer_start(sc->sc_xfer_if_0[3]); -#if 0 - XXX can enable this XXX + return (0); +} /* ng_ubt_newhook */ - /* start isoc-in transfer */ - usb2_transfer_start(sc->sc_xfer_if_1[0]); +/* + * Connect hook. Start incoming USB transfers. + * Netgraph context. + */ - usb2_transfer_start(sc->sc_xfer_if_1[1]); +static int +ng_ubt_connect(hook_p hook) +{ + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); - /* start isoc-out transfer */ - usb2_transfer_start(sc->sc_xfer_if_1[2]); - usb2_transfer_start(sc->sc_xfer_if_1[3]); -#endif + NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); - mtx_unlock(&sc->sc_mtx); + UBT_MBUFQ_LOCK(sc); + ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); + UBT_MBUFQ_UNLOCK(sc); return (0); -} +} /* ng_ubt_connect */ /* - * Disconnect hook + * Disconnect hook. + * Netgraph context. */ static int ng_ubt_disconnect(hook_p hook) { - struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); - int error = 0; - - if (sc != NULL) { - - mtx_lock(&sc->sc_mtx); - - if (hook != sc->sc_hook) { - error = EINVAL; - } else { + node_p node = NG_HOOK_NODE(hook); + struct ubt_softc *sc; - /* stop intr transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[2]); - usb2_transfer_stop(sc->sc_xfer_if_0[6]); + if (NG_NODE_NOT_VALID(node)) + return (0); - /* stop bulk-in transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[1]); - usb2_transfer_stop(sc->sc_xfer_if_0[5]); + sc = NG_NODE_PRIVATE(node); - /* stop bulk-out transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[0]); - usb2_transfer_stop(sc->sc_xfer_if_0[4]); + if (hook != sc->sc_hook) + return (EINVAL); - /* stop control transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[3]); + sc->sc_hook = NULL; - /* stop isoc-in transfer */ - usb2_transfer_stop(sc->sc_xfer_if_1[0]); - usb2_transfer_stop(sc->sc_xfer_if_1[1]); + UBT_MBUFQ_LOCK(sc); - /* stop isoc-out transfer */ - usb2_transfer_stop(sc->sc_xfer_if_1[2]); - usb2_transfer_stop(sc->sc_xfer_if_1[3]); + /* Drain queues */ + NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); + NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); + NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); - /* cleanup queues */ - NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); - NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); - NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); - NG_BT_MBUFQ_DRAIN(&sc->sc_sciq); + /* Kick off task to stop all USB xfers */ + ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); - sc->sc_hook = NULL; - } - - mtx_unlock(&sc->sc_mtx); - } - return (error); -} + UBT_MBUFQ_UNLOCK(sc); + return (0); +} /* ng_ubt_disconnect */ + /* - * Process control message + * Process control message. + * Netgraph context. */ static int ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) { - struct ubt_softc *sc = 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); - return (EHOSTDOWN); - } - mtx_lock(&sc->sc_mtx); + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + struct ng_mesg *msg, *rsp = NULL; + struct ng_bt_mbufq *q; + int error = 0, queue, qlen; NGI_GET_MSG(item, msg); @@ -1527,25 +1655,31 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) switch (msg->header.cmd) { case NGM_TEXT_STATUS: NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT); - if (rsp == NULL) + if (rsp == NULL) { error = ENOMEM; - else - snprintf(rsp->data, NG_TEXTRESPONSE, - "Hook: %s\n" \ - "Flags: %#x\n" \ - "Debug: %d\n" \ - "CMD queue: [have:%d,max:%d]\n" \ - "ACL queue: [have:%d,max:%d]\n" \ - "SCO queue: [have:%d,max:%d]", - (sc->sc_hook != NULL) ? NG_UBT_HOOK : "", - sc->sc_flags, - sc->sc_debug, - 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; + } + + snprintf(rsp->data, NG_TEXTRESPONSE, + "Refs: %d\n" \ + "Hook: %s\n" \ + "Flags: %#x\n" \ + "Task flags: %#x\n" \ + "Debug: %d\n" \ + "CMD queue: [have:%d,max:%d]\n" \ + "ACL queue: [have:%d,max:%d]\n" \ + "SCO queue: [have:%d,max:%d]", + node->nd_refs, + (sc->sc_hook != NULL) ? NG_UBT_HOOK:"", + sc->sc_flags, + sc->sc_task_flags, + sc->sc_debug, + sc->sc_cmdq.len, + sc->sc_cmdq.maxlen, + sc->sc_aclq.len, + sc->sc_aclq.maxlen, + sc->sc_scoq.len, + sc->sc_scoq.maxlen); break; default: @@ -1557,59 +1691,54 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) case NGM_UBT_COOKIE: switch (msg->header.cmd) { case NGM_UBT_NODE_SET_DEBUG: - if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)) + if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){ error = EMSGSIZE; - else - sc->sc_debug = - *((ng_ubt_node_debug_ep *) (msg->data)); + break; + } + + sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data)); break; case NGM_UBT_NODE_GET_DEBUG: NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep), M_NOWAIT); - if (rsp == NULL) + if (rsp == NULL) { error = ENOMEM; - else - *((ng_ubt_node_debug_ep *) (rsp->data)) = - sc->sc_debug; + break; + } + + *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug; break; case NGM_UBT_NODE_SET_QLEN: - if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) + if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) { error = EMSGSIZE; - else { - queue = ((ng_ubt_node_qlen_ep *) - (msg->data))->queue; - qlen = ((ng_ubt_node_qlen_ep *) - (msg->data))->qlen; - - if (qlen <= 0) { - error = EINVAL; - break; - } - switch (queue) { - case NGM_UBT_NODE_QUEUE_CMD: - q = &sc->sc_cmdq; - break; + break; + } - case NGM_UBT_NODE_QUEUE_ACL: - q = &sc->sc_aclq; - break; + queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; + qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; - case NGM_UBT_NODE_QUEUE_SCO: - q = &sc->sc_scoq; - break; + switch (queue) { + case NGM_UBT_NODE_QUEUE_CMD: + q = &sc->sc_cmdq; + break; - default: - q = NULL; - error = EINVAL; - break; - } + case NGM_UBT_NODE_QUEUE_ACL: + q = &sc->sc_aclq; + break; - if (q != NULL) { - q->maxlen = qlen; - } + case NGM_UBT_NODE_QUEUE_SCO: + q = &sc->sc_scoq; + break; + + default: + error = EINVAL; + goto done; + /* NOT REACHED */ } + + q->maxlen = qlen; break; case NGM_UBT_NODE_GET_QLEN: @@ -1617,7 +1746,9 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) error = EMSGSIZE; break; } + queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; + switch (queue) { case NGM_UBT_NODE_QUEUE_CMD: q = &sc->sc_cmdq; @@ -1632,39 +1763,36 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) break; default: - q = NULL; error = EINVAL; - break; + goto done; + /* NOT REACHED */ } - if (q != NULL) { - NG_MKRESPONSE(rsp, msg, - sizeof(ng_ubt_node_qlen_ep), M_NOWAIT); - if (rsp == NULL) { - error = ENOMEM; - break; - } - ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = - queue; - ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = - q->maxlen; + NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), + M_NOWAIT); + if (rsp == NULL) { + error = ENOMEM; + break; } + + ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue; + ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen; break; case NGM_UBT_NODE_GET_STAT: NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep), M_NOWAIT); - if (rsp == NULL) + if (rsp == NULL) { error = ENOMEM; - else { - bcopy(&sc->sc_stat, rsp->data, - sizeof(ng_ubt_node_stat_ep)); + break; } + + bcopy(&sc->sc_stat, rsp->data, + sizeof(ng_ubt_node_stat_ep)); break; case NGM_UBT_NODE_RESET_STAT: - - NG_UBT_STAT_RESET(sc->sc_stat); + UBT_STAT_RESET(sc); break; default: @@ -1677,90 +1805,161 @@ ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook) error = EINVAL; break; } - +done: NG_RESPOND_MSG(error, node, item, rsp); NG_FREE_MSG(msg); - mtx_unlock(&sc->sc_mtx); - return (error); -} +} /* ng_ubt_rcvmsg */ /* - * Process data + * Process data. + * Netgraph context. */ static int ng_ubt_rcvdata(hook_p hook, item_p item) { - struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); - struct mbuf *m; - struct ng_bt_mbufq *q; - struct usb2_xfer *xfer; - int error = 0; - - if (sc == NULL) { - error = EHOSTDOWN; - goto done; - } - mtx_lock(&sc->sc_mtx); + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct mbuf *m; + struct ng_bt_mbufq *q; + int action, error = 0; if (hook != sc->sc_hook) { error = EINVAL; goto done; } - /* deatch mbuf and get HCI frame type */ + + /* Deatch mbuf and get HCI frame type */ NGI_GET_M(item, m); - /* process HCI frame */ + /* + * Minimal size of the HCI frame is 4 bytes: 1 byte frame type, + * 2 bytes connection handle and at least 1 byte of length. + * Panic on data frame that has size smaller than 4 bytes (it + * should not happen) + */ + + if (m->m_pkthdr.len < 4) + panic("HCI frame size is too small! pktlen=%d\n", + m->m_pkthdr.len); + + /* Process HCI frame */ switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */ case NG_HCI_CMD_PKT: - xfer = sc->sc_xfer_if_0[3]; + if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE) + panic("HCI command frame size is too big! " \ + "buffer size=%zd, packet len=%d\n", + UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len); + q = &sc->sc_cmdq; + action = UBT_FLAG_T_START_CTRL; break; case NG_HCI_ACL_DATA_PKT: - xfer = sc->sc_xfer_if_0[0]; + if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE) + panic("ACL data frame size is too big! " \ + "buffer size=%d, packet len=%d\n", + UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len); + q = &sc->sc_aclq; + action = UBT_FLAG_T_START_BULK; break; case NG_HCI_SCO_DATA_PKT: - xfer = NULL; q = &sc->sc_scoq; + action = 0; break; default: - NG_UBT_ERR(sc, "Dropping unsupported HCI frame, " - "type=0x%02x, pktlen=%d\n", - *mtod(m, uint8_t *), - m->m_pkthdr.len); + UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \ + "pktlen=%d\n", *mtod(m, uint8_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(uint8_t)); - } + UBT_MBUFQ_LOCK(sc); if (NG_BT_MBUFQ_FULL(q)) { - NG_UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. " - "Queue full\n", *mtod(m, uint8_t *), - m->m_pkthdr.len); + NG_BT_MBUFQ_DROP(q); + UBT_MBUFQ_UNLOCK(sc); + + UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n", + *mtod(m, uint8_t *), m->m_pkthdr.len); + NG_FREE_M(m); } else { + /* Loose HCI packet type, enqueue mbuf and kick off task */ + m_adj(m, sizeof(uint8_t)); NG_BT_MBUFQ_ENQUEUE(q, m); - } + ubt_task_schedule(sc, action); - if (xfer) { - usb2_transfer_start(xfer); + UBT_MBUFQ_UNLOCK(sc); } done: NG_FREE_ITEM(item); - if (sc) { - mtx_unlock(&sc->sc_mtx); + return (error); +} /* ng_ubt_rcvdata */ + +/**************************************************************************** + **************************************************************************** + ** Module + **************************************************************************** + ****************************************************************************/ + +/* + * Load/Unload the driver module + */ + +static int +ubt_modevent(module_t mod, int event, void *data) +{ + int error; + + switch (event) { + 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); + break; + + case MOD_UNLOAD: + error = ng_rmtype(&typestruct); + break; + + default: + error = EOPNOTSUPP; + break; } + return (error); -} +} /* ubt_modevent */ + +static devclass_t ubt_devclass; + +static device_method_t ubt_methods[] = +{ + DEVMETHOD(device_probe, ubt_probe), + DEVMETHOD(device_attach, ubt_attach), + DEVMETHOD(device_detach, ubt_detach), + { 0, 0 } +}; + +static driver_t ubt_driver = +{ + .name = "ubt", + .methods = ubt_methods, + .size = sizeof(struct ubt_softc), +}; + +DRIVER_MODULE(ng_ubt, ushub, ubt_driver, ubt_devclass, ubt_modevent, 0); +MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION); +MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); +MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); +MODULE_DEPEND(ng_ubt, usb2_bluetooth, 1, 1, 1); +MODULE_DEPEND(ng_ubt, usb2_core, 1, 1, 1); + diff --git a/sys/dev/usb2/bluetooth/ng_ubt2_var.h b/sys/dev/usb2/bluetooth/ng_ubt2_var.h index 8fecc41..82be700 100644 --- a/sys/dev/usb2/bluetooth/ng_ubt2_var.h +++ b/sys/dev/usb2/bluetooth/ng_ubt2_var.h @@ -3,7 +3,7 @@ */ /*- - * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,95 +32,111 @@ */ #ifndef _NG_UBT_VAR_H_ -#define _NG_UBT_VAR_H_ - -/* 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__, sc->sc_name, (s)); \ - } \ - } while (0) +#define _NG_UBT_VAR_H_ 1 /* Debug printf's */ -#define NG_UBT_DEBUG(level, sc, fmt, ...) do { \ - if ((sc)->sc_debug >= (level)) { \ - printf("%s:%s:%d: " fmt, (sc)->sc_name, \ - __FUNCTION__, __LINE__,## __VA_ARGS__); \ - } \ +#define UBT_DEBUG(level, sc, fmt, ...) \ +do { \ + if ((sc)->sc_debug >= (level)) \ + printf("%s:%s:%d: " fmt, (sc)->sc_name, \ + __FUNCTION__, __LINE__,## __VA_ARGS__); \ } while (0) -#define NG_UBT_ALERT(...) NG_UBT_DEBUG(NG_UBT_ALERT_LEVEL, __VA_ARGS__) -#define NG_UBT_ERR(...) NG_UBT_DEBUG(NG_UBT_ERR_LEVEL, __VA_ARGS__) -#define NG_UBT_WARN(...) NG_UBT_DEBUG(NG_UBT_WARN_LEVEL, __VA_ARGS__) -#define NG_UBT_INFO(...) NG_UBT_DEBUG(NG_UBT_INFO_LEVEL, __VA_ARGS__) +#define UBT_ALERT(...) UBT_DEBUG(NG_UBT_ALERT_LEVEL, __VA_ARGS__) +#define UBT_ERR(...) UBT_DEBUG(NG_UBT_ERR_LEVEL, __VA_ARGS__) +#define UBT_WARN(...) UBT_DEBUG(NG_UBT_WARN_LEVEL, __VA_ARGS__) +#define UBT_INFO(...) UBT_DEBUG(NG_UBT_INFO_LEVEL, __VA_ARGS__) + +#define UBT_MBUFQ_LOCK(sc) mtx_lock(&(sc)->sc_mbufq_mtx) +#define UBT_MBUFQ_UNLOCK(sc) mtx_unlock(&(sc)->sc_mbufq_mtx) /* Bluetooth USB control request type */ #define UBT_HCI_REQUEST 0x20 -#define UBT_DEFAULT_QLEN 12 +#define UBT_DEFAULT_QLEN 64 +#define UBT_ISOC_NFRAMES 32 /* should be factor of 8 */ /* Bluetooth USB defines */ -#define UBT_IF_0_N_TRANSFER 7 /* units */ -#define UBT_IF_1_N_TRANSFER 4 /* units */ -#define UBT_ISOC_NFRAMES 25 /* units */ +enum { + /* Interface #0 transfers */ + UBT_IF_0_BULK_DT_WR = 0, + UBT_IF_0_BULK_DT_RD, + UBT_IF_0_INTR_DT_RD, + UBT_IF_0_CTRL_DT_WR, + UBT_IF_0_BULK_CS_WR, + UBT_IF_0_BULK_CS_RD, + UBT_IF_0_INTR_CS_RD, + UBT_IF_0_N_TRANSFER, /* number of interface 0's transfers */ + + /* Interface #1 transfers */ + UBT_IF_1_ISOC_DT_RD1 = UBT_IF_0_N_TRANSFER, + UBT_IF_1_ISOC_DT_RD2, + UBT_IF_1_ISOC_DT_WR1, + UBT_IF_1_ISOC_DT_WR2, + + UBT_N_TRANSFER, /* total number of transfers */ + + UBT_IF_1_N_TRANSFER = UBT_N_TRANSFER - UBT_IF_1_ISOC_DT_RD1, +}; /* USB device softc structure */ struct ubt_softc { + uint8_t sc_name[16]; + /* State */ - ng_ubt_node_debug_ep sc_debug; /* debug level */ - uint32_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_FLAG_READ_STALL (1 << 1)/* read transfer has stalled */ -#define UBT_FLAG_WRITE_STALL (1 << 2)/* write transfer has stalled */ -#define UBT_FLAG_INTR_STALL (1 << 3)/* interrupt transfer has stalled */ - - 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))) - - uint8_t sc_name[16]; - - struct mtx sc_mtx; + ng_ubt_node_debug_ep sc_debug; /* debug level */ + + int sc_flags; /* device flags */ +#define UBT_FLAG_READ_STALL (1 << 0) /* read transfer has stalled */ +#define UBT_FLAG_WRITE_STALL (1 << 1) /* write transfer has stalled */ +#define UBT_FLAG_INTR_STALL (1 << 2) /* inter transfer has stalled */ + + ng_ubt_node_stat_ep sc_stat; /* statistic */ +#define UBT_STAT_PCKTS_SENT(sc) (sc)->sc_stat.pckts_sent ++ +#define UBT_STAT_BYTES_SENT(sc, n) (sc)->sc_stat.bytes_sent += (n) +#define UBT_STAT_PCKTS_RECV(sc) (sc)->sc_stat.pckts_recv ++ +#define UBT_STAT_BYTES_RECV(sc, n) (sc)->sc_stat.bytes_recv += (n) +#define UBT_STAT_OERROR(sc) (sc)->sc_stat.oerrors ++ +#define UBT_STAT_IERROR(sc) (sc)->sc_stat.ierrors ++ +#define UBT_STAT_RESET(sc) bzero(&(sc)->sc_stat, sizeof((sc)->sc_stat)) /* USB device specific */ - struct usb2_xfer *sc_xfer_if_0[UBT_IF_0_N_TRANSFER]; - struct usb2_xfer *sc_xfer_if_1[UBT_IF_1_N_TRANSFER]; + struct mtx sc_if_mtx[2]; /* interface locks */ + struct usb2_xfer *sc_xfer[UBT_N_TRANSFER]; - /* Interrupt pipe (HCI events) */ - struct mbuf *sc_intr_buffer; /* interrupt buffer */ + struct mtx sc_mbufq_mtx; /* lock for all queues */ - /* Control pipe (HCI commands) */ - 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) + /* HCI commands */ + struct ng_bt_mbufq sc_cmdq; /* HCI command queue */ +#define UBT_CTRL_BUFFER_SIZE (sizeof(struct usb2_device_request) + \ + sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE) +#define UBT_INTR_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */ - /* Bulk in pipe (ACL data) */ - struct mbuf *sc_bulk_in_buffer; /* bulk-in buffer */ - - /* Bulk out pipe (ACL data) */ - struct ng_bt_mbufq sc_aclq; /* ACL data queue */ -#define UBT_BULK_READ_BUFFER_SIZE (MCLBYTES-1) /* reserve one byte for ID-tag */ + /* ACL data */ + struct ng_bt_mbufq sc_aclq; /* ACL data queue */ +#define UBT_BULK_READ_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */ #define UBT_BULK_WRITE_BUFFER_SIZE (MCLBYTES) - /* Isoc. out pipe (ACL data) */ - struct ng_bt_mbufq sc_scoq; /* SCO data queue */ - - /* Isoc. in pipe (ACL data) */ - struct ng_bt_mbufq sc_sciq; /* SCO data queue */ + /* SCO data */ + struct ng_bt_mbufq sc_scoq; /* SCO data queue */ + struct mbuf *sc_isoc_in_buffer; /* SCO reassembly buffer */ /* Netgraph specific */ - node_p sc_node; /* pointer back to node */ - hook_p sc_hook; /* upstream hook */ + node_p sc_node; /* pointer back to node */ + hook_p sc_hook; /* upstream hook */ + + /* Glue */ + int sc_task_flags; /* task flags */ +#define UBT_FLAG_T_PENDING (1 << 0) /* task pending */ +#define UBT_FLAG_T_STOP_ALL (1 << 1) /* stop all xfers */ +#define UBT_FLAG_T_START_ALL (1 << 2) /* start all read and isoc + write xfers */ +#define UBT_FLAG_T_START_CTRL (1 << 3) /* start control xfer (write) */ +#define UBT_FLAG_T_START_BULK (1 << 4) /* start bulk xfer (write) */ + + struct task sc_task; }; -typedef struct ubt_softc ubt_softc_t; -typedef struct ubt_softc *ubt_softc_p; +typedef struct ubt_softc ubt_softc_t; +typedef struct ubt_softc * ubt_softc_p; + +#endif /* ndef _NG_UBT_VAR_H_ */ -#endif /* ndef _NG_UBT_VAR_H_ */ |