svn commit: r187494 - head/sys/dev/usb2/bluetooth
Maksim Yevmenkin
emax at FreeBSD.org
Tue Jan 20 14:17:06 PST 2009
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 <m_evmenkin at yahoo.com>
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin at 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 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 ***
More information about the svn-src-head
mailing list