Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 20 Jan 2009 22:17:05 +0000 (UTC)
From:      Maksim Yevmenkin <emax@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r187494 - head/sys/dev/usb2/bluetooth
Message-ID:  <200901202217.n0KMH5U8058352@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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@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 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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200901202217.n0KMH5U8058352>