From owner-svn-src-all@FreeBSD.ORG Tue Jan 20 22:17:06 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 023A9106566B; Tue, 20 Jan 2009 22:17:06 +0000 (UTC) (envelope-from emax@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id E3C0E8FC08; Tue, 20 Jan 2009 22:17:05 +0000 (UTC) (envelope-from emax@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n0KMH5SV058354; Tue, 20 Jan 2009 22:17:05 GMT (envelope-from emax@svn.freebsd.org) Received: (from emax@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n0KMH5U8058352; Tue, 20 Jan 2009 22:17:05 GMT (envelope-from emax@svn.freebsd.org) Message-Id: <200901202217.n0KMH5U8058352@svn.freebsd.org> From: Maksim Yevmenkin Date: Tue, 20 Jan 2009 22:17:05 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r187494 - head/sys/dev/usb2/bluetooth X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 20 Jan 2009 22:17:06 -0000 Author: emax Date: Tue Jan 20 22:17:05 2009 New Revision: 187494 URL: http://svn.freebsd.org/changeset/base/187494 Log: Update (well, actually rewrite mostly) ng_ubt2 driver for USB2. Reviewed by: HPS, alfred Blessed by: HPS Modified: head/sys/dev/usb2/bluetooth/ng_ubt2.c head/sys/dev/usb2/bluetooth/ng_ubt2_var.h Modified: head/sys/dev/usb2/bluetooth/ng_ubt2.c ============================================================================== --- head/sys/dev/usb2/bluetooth/ng_ubt2.c Tue Jan 20 22:06:07 2009 (r187493) +++ head/sys/dev/usb2/bluetooth/ng_ubt2.c Tue Jan 20 22:17:05 2009 (r187494) @@ -3,7 +3,7 @@ */ /*- - * Copyright (c) 2001-2002 Maksim Yevmenkin + * Copyright (c) 2001-2009 Maksim Yevmenkin * 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 #include #include @@ -44,8 +107,11 @@ #include #include #include +#include +#include #include +#include #include #include @@ -57,71 +123,57 @@ #include #include -/* - * 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 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), -}; - -/* - * 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; +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 int ubt_task_schedule(ubt_softc_p, int); +static task_fn_t ubt_task; +static void ubt_xfer_start(ubt_softc_p, int); + +/* 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_cm 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 -}; - -/* 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, - }, - - [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, - }, - - [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 */ - }, - - [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 */ - }, - - [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 */ - }, - - [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 */ - }, -}; - -/* 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, - }, - - [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, - }, - - [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, - }, - - [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, - }, -}; - -/* 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, - }, - - [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, - }, - - [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, - }, - - [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, - }, +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 }; -/* - * 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 **************************************************************************** ****************************************************************************/ +/* 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); + /* - * Load/Unload the driver module + * 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 */ -static int -ubt_modevent(module_t mod, int event, void *data) +static const struct usb2_config ubt_config[UBT_N_TRANSFER] = { - int error; + /* + * Interface #0 + */ - 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; + /* 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, + }, + /* 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, + }, + /* 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, + }, + /* 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 */ + }, + /* 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 */ + }, + /* 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 */ + }, + /* + * 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 */ + }, - case MOD_UNLOAD: - error = ng_rmtype(&typestruct); - break; + /* + * Interface #1 + */ - default: - error = EOPNOTSUPP; - break; - } - return (error); -} /* ubt_modevent */ + /* 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, + }, + /* 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, + }, + /* 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, + }, + /* 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, + }, +}; /* * 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,794 +520,1006 @@ 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; - } - 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; + j ++; } - /* 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); + struct ubt_softc *sc = device_get_softc(dev); + node_p node = sc->sc_node; - /* destroy Netgraph 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 != 0) + UBT_STAT_OERROR(sc); + else { + UBT_INFO(sc, "sent %d bytes to control pipe\n", + xfer->actlen); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***