From dvg at tjc.ru Sat Jan 3 18:14:12 2009 From: dvg at tjc.ru (dvg_lab) Date: Sat Jan 3 18:14:18 2009 Subject: MSI MegaBook PR300 (aka MS1313) usb bluetooth device Message-ID: <21267396.post@talk.nabble.com> I'm on CURRENT built a couple of days ago. I try to use builtin usb device. usbdevs -v shows port 1 addr 2: full speed, self powered, config 1, product 0xa97a(0xa97a), vendor 0x0db0(0x0db0), rev 19.58 I've found that this device 0xa97a described in OpenBSD sys/dev/usb/usbdevs file as product MSI BLUETOOTH_3 0xa97a Bluetooth at line 1941, I've tried to copy this line to the same place in FreeBSD's usbdevs file and recompile the kernel but without success. Is this device in plans to support by FreebSD ? -- View this message in context: http://www.nabble.com/MSI-MegaBook-PR300-%28aka-MS1313%29-usb-bluetooth-device-tp21267396p21267396.html Sent from the freebsd-bluetooth mailing list archive at Nabble.com. From maksim.yevmenkin at gmail.com Sat Jan 3 18:34:59 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Sat Jan 3 18:35:05 2009 Subject: MSI MegaBook PR300 (aka MS1313) usb bluetooth device In-Reply-To: <21267396.post@talk.nabble.com> References: <21267396.post@talk.nabble.com> Message-ID: On Sat, Jan 3, 2009 at 9:55 AM, dvg_lab wrote: > > I'm on CURRENT built a couple of days ago. > I try to use builtin usb device. > usbdevs -v shows > port 1 addr 2: full speed, self powered, config 1, product 0xa97a(0xa97a), > vendor 0x0db0(0x0db0), rev 19.58 > > I've found that this device 0xa97a described in OpenBSD sys/dev/usb/usbdevs > file as > product MSI BLUETOOTH_3 0xa97a Bluetooth > at line 1941, I've tried to copy this line to the same place in FreeBSD's > usbdevs file and recompile the kernel but without success. > > Is this device in plans to support by FreebSD ? 1) did you load bluetooth device driver? ( man ng_ubt(4) ) 2) did you turn the device on? (some laptops have dedicated "bluetooth on/off" button) keep in mind there will NOT be any entries in /dev for bluetooth devices, only netgraph nodes. the easiest way to get this working is to add "ng_ubt_load="YES"" to your loader.conf and reboot. next time bluetooth device is attached devd(8) should automatically load all the required modules and start the stack (see /etc/rc.d/bluetooth for more details). thanks, max From dvg at tjc.ru Sat Jan 3 19:50:15 2009 From: dvg at tjc.ru (Vyacheslav Druzhinin) Date: Sat Jan 3 19:50:22 2009 Subject: MSI MegaBook PR300 (aka MS1313) usb bluetooth device In-Reply-To: References: <21267396.post@talk.nabble.com> Message-ID: <21268561.post@talk.nabble.com> Maksim Yevmenkin-2 wrote: > > On Sat, Jan 3, 2009 at 9:55 AM, dvg_lab wrote: > 1) did you load bluetooth device driver? ( man ng_ubt(4) ) > > 2) did you turn the device on? (some laptops have dedicated "bluetooth > on/off" button) > > keep in mind there will NOT be any entries in /dev for bluetooth > devices, only netgraph nodes. the easiest way to get this working is > to add "ng_ubt_load="YES"" to your loader.conf and reboot. next time > bluetooth device is attached devd(8) should automatically load all the > required modules and start the stack (see /etc/rc.d/bluetooth for more > details). > > thanks, > max > _______________________________________________ > thanx for the hint, after I put the ng_ubt to the loader.conf the device simply works, yes it looks non standard while I searched the /dev/ubt0 thanx a lot, Max. WBR, dvg_lab. -- View this message in context: http://www.nabble.com/MSI-MegaBook-PR300-%28aka-MS1313%29-usb-bluetooth-device-tp21267396p21268561.html Sent from the freebsd-bluetooth mailing list archive at Nabble.com. From maksim.yevmenkin at gmail.com Mon Jan 5 06:00:24 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Mon Jan 5 06:00:30 2009 Subject: new developers wanted Message-ID: dear freebsd bluetooth users, it has become quite obvious to me that i have fallen behind on my freebsd bluetooth work. i have quite a few items in my todo list and its taking me too long to get them done. obviously, its frustrating to freebsd bluetooth users. so, it is time for me to admit that i need help from new developers. if anyone is interested in working on bluetooth in freebsd please contact me directly. the level on expertise is not very important - there are different kinds of project: kernel, user space an even documentation update. i will promise to try my best and work with everyone who wishes to help. most active individual will likely be recommended for commit bit of their own. in the mean time, i will try to get important things done first (such as updating ng_ubt(4) for new usb2 stack, which will become default soon) thanks, max From maksim.yevmenkin at gmail.com Mon Jan 5 16:45:27 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Mon Jan 5 16:45:33 2009 Subject: obexapp 1.4.10 (minor bug fix release) Message-ID: dear freebsd bluetooth users, a new obexapp version 1.4.10 has been released. this version includes minor bug fix to improve compatibility with nokia 6131 phone. i'd like to thank Alex Keda for submitting problem report and help with testing. new version can be downloaded from: http://www.geocities.com/m_evmenkin/obexapp-1.4.10.tar.gz as usual, if there is any problem please let me know. Guido, could you please update port as well? thanks, max From maksim.yevmenkin at gmail.com Wed Jan 14 17:05:54 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Wed Jan 14 17:06:02 2009 Subject: USB2: ng_ubt2 patch Message-ID: dear bluetooth users, please find attached patch for ng_ubt2 (well it is almost a complete rewrite). this is an early preview and likely to have bugs. i briefly kicked the tires to make sure it compiles and did some very quick testing, i.e. run few hci commands, sdp transfers etc. this is my first attempt to write something under usb2, so any review/comments/suggestions/etc. are greatly appreciated. thanks, max -------------- next part -------------- Index: ng_ubt2.c =================================================================== --- ng_ubt2.c (revision 186800) +++ ng_ubt2.c (working copy) @@ -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_0_mtx - 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_1_mtx - 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 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 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 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 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 outgoing queues and task flags is controlled by sc_mbufq_mtx + * lock. It is 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 point 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. + */ + #include #include #include @@ -46,6 +109,7 @@ #include #include +#include #include #include @@ -57,71 +121,56 @@ #include #include -/* - * USB methods - */ +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_probe_t ubt_probe; -static device_attach_t ubt_attach; -static device_detach_t ubt_detach; +static int ubt_task_schedule(ubt_softc_p, uint32_t); +static task_fn_t ubt_task; -static devclass_t ubt_devclass; +/* 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 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; - /* 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,316 +213,280 @@ 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 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 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 *, uint32_t); -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 + * + * The following desribes usb transfers that could be submitted + * on USB device's interface 0 + * + * 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 + */ -/* USB config */ -static const struct usb2_config ubt_config_if_0[UBT_IF_0_N_TRANSFER] = { - +static const struct usb2_config ubt_config_if_0[UBT_IF_0_N_TRANSFER] = +{ + /* Outgoing bulk transfer - ACL packets */ [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, + .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 */ [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, + .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 */ [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, + .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 */ [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 */ + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .mh.bufsize = (sizeof(struct usb2_device_request) + + UBT_CTRL_BUFFER_SIZE), /* bytes */ + .mh.callback = &ubt_ctrl_write_callback, + .mh.timeout = 5000, /* 5 seconds */ }, - + /* Outgoing control transfer to clear stall on outgoing bulk transfer */ [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 */ + .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 */ [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 */ + .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 + */ [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 */ + .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] = { +/* + * USB config + * + * The following desribes usb transfers that could be submitted + * on USB device's interface 1 + * + * Interface 1 on the UBS device must present must 2 endpoints + * 1) Isochronous IN endpoint to receive SCO data + * 2) Isochronous OUT endpoint to send SCO data + * + * Interface 1 (with isochronous endpoints) has several alternate + * configurations with different packet size. + * + * Device is expected to be a high speed device. + */ +static const struct usb2_config ubt_config_if_1[UBT_IF_1_N_TRANSFER] = +{ + /* Incoming isochronous transfer #1 - SCO packets */ [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, + .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 */ [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, + .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 */ [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, + .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 */ [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, + .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, - }, -}; - /* - * 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)); + /* Expect high-speed device */ + if (usb2_get_speed(uaa->device) != USB_SPEED_HIGH) + device_printf(dev, "device is not a high-speed device!\n"); + + /* + * 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 +494,28 @@ /* 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 */ + /* initialize locks */ + mtx_init(&sc->sc_mbufq_mtx, "ubt mbufq", NULL, MTX_DEF); + mtx_init(&sc->sc_if_0_mtx, "ubt if0", NULL, MTX_DEF | MTX_RECURSE); + mtx_init(&sc->sc_if_1_mtx, "ubt if1", NULL, MTX_DEF | MTX_RECURSE); + + /* initialize packet queues */ NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); - - /* bulk-out pipe */ 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)); + /* initialize glue task */ + sc->sc_task_flags = 0; + TASK_INIT(&sc->sc_task, 0, ubt_task, sc->sc_node); - /* 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; - /* * 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 @@ -527,791 +534,995 @@ * 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_if_0, + ubt_config_if_0, UBT_IF_0_N_TRANSFER, + sc->sc_node, &sc->sc_if_0_mtx)) { + 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_if_1, + ubt_config_if_1, UBT_IF_1_N_TRANSFER, + sc->sc_node, &sc->sc_if_1_mtx)) { + 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); - /* destroy Netgraph node */ - + /* Destroy Netgraph node */ if (sc->sc_node != NULL) { NG_NODE_SET_PRIVATE(sc->sc_node, NULL); ng_rmnode_self(sc->sc_node); sc->sc_node = NULL; } - /* free USB transfers, if any */ + /* 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); - mtx_destroy(&sc->sc_mtx); - - /* destroy queues */ - + /* 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_0_mtx); + mtx_destroy(&sc->sc_if_1_mtx); + 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); - 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); + 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); /* transfer completed */ 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); /* transfer 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 */ + } + + sc = NG_NODE_PRIVATE(node); + + 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 */ + } + + m = NULL; + switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: - - /* allocate a new mbuf */ - + /* 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); - if (!(m->m_flags & M_EXT)) { - NG_FREE_M(m); - goto tr_setup; + UBT_STAT_IERROR(sc); + goto submit_next; } - 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; + + /* Add HCI packet type */ + *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; + m->m_pkthdr.len = m->m_len = 1; + + 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; + + UBT_INFO(sc, "got %d bytes from interrupt pipe\n", + xfer->actlen); + + /* 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"); + + UBT_STAT_IERROR(sc); + goto submit_next; } - max_len = (MCLBYTES - m->m_len); + 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); - if (xfer->actlen > max_len) { - xfer->actlen = max_len; + UBT_STAT_IERROR(sc); + goto submit_next; } - ptr = ((uint8_t *)(m->m_data)) + m->m_len; - usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen); + UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ + "length=%d\n", m->m_pkthdr.len, hdr->length); - m->m_pkthdr.len += xfer->actlen; - m->m_len += xfer->actlen; + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); - NG_UBT_INFO(sc, "got %d bytes from interrupt " - "pipe\n", xfer->actlen); + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (error != 0) + UBT_STAT_IERROR(sc); - sc->sc_intr_buffer = m; + /* m == NULL at this point */ + /* FALLTHROUGH */ case USB_ST_SETUP: -tr_setup: +submit_next: + NG_FREE_M(m); /* checks for m != NULL */ - if (sc->sc_intr_buffer) { - ng_send_fn(sc->sc_node, NULL, ubt_intr_read_complete, NULL, 0); - return; - } if (sc->sc_flags & UBT_FLAG_INTR_STALL) { usb2_transfer_start(sc->sc_xfer_if_0[6]); - return; + } else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); } - xfer->frlengths[0] = xfer->max_data_length; + break; - usb2_start_hardware(xfer); - return; - - default: /* Error */ + default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ + /* Try to clear stall first */ sc->sc_flags |= UBT_FLAG_INTR_STALL; usb2_transfer_start(sc->sc_xfer_if_0[6]); - } - return; - + } else + NG_NODE_UNREF(node); /* transfer 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_if_0[2]; + 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); /* XXX 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; + 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 (sc == NULL) { - return; + 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; + sc = NG_NODE_PRIVATE(node); - if (m) { + 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 */ + } - sc->sc_intr_buffer = NULL; + m = 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; + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + /* Allocate new mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + UBT_STAT_IERROR(sc); + goto submit_next; } - 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; + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + UBT_STAT_IERROR(sc); + goto submit_next; } - 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); + /* Add HCI packet type */ + *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT; + m->m_pkthdr.len = m->m_len = 1; - NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (xfer->actlen > MCLBYTES - 1) + xfer->actlen = MCLBYTES - 1; - m = NULL; + usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1, + xfer->actlen); + m->m_pkthdr.len += xfer->actlen; + m->m_len += xfer->actlen; - 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); + UBT_INFO(sc, "got %d bytes from bulk-in pipe\n", + xfer->actlen); - NG_UBT_STAT_IERROR(sc->sc_stat); - } - } -done: - if (m) { - NG_FREE_M(m); - } - /* start USB transfer if not already started */ + /* 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"); - 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; - - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: - - /* 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); - 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; - } + 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); - max_len = (MCLBYTES - m->m_len); - - if (xfer->actlen > max_len) { - xfer->actlen = max_len; + UBT_STAT_IERROR(sc); + goto submit_next; } - ptr = ((uint8_t *)(m->m_data)) + m->m_len; - usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen); + UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \ + "length=%d\n", m->m_pkthdr.len, len); - m->m_pkthdr.len += xfer->actlen; - m->m_len += xfer->actlen; + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); - NG_UBT_INFO(sc, "got %d bytes from bulk-in " - "pipe\n", xfer->actlen); + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (error != 0) + UBT_STAT_IERROR(sc); - sc->sc_bulk_in_buffer = m; + /* m == NULL at this point */ + /* FALLTHOUGH */ 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) { +submit_next: + NG_FREE_M(m); /* checks for m != NULL */ + + if (sc->sc_flags & UBT_FLAG_READ_STALL) usb2_transfer_start(sc->sc_xfer_if_0[5]); - return; + else { + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); } - xfer->frlengths[0] = xfer->max_data_length; + break; - usb2_start_hardware(xfer); - return; - - default: /* Error */ + default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ + /* Try to clear stall first */ sc->sc_flags |= UBT_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer_if_0[5]); - } - return; - + } else + NG_NODE_UNREF(node); /* transfer cancelled */ + break; } -} +} /* ubt_bulk_read_callback */ +/* + * Called when outgoing control transfer initiated to clear stall on + * incoming bulk pipe has completed. + * USB context. + */ + 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]; + 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_if_0[1]; + 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); /* XXX 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_read_complete(node_p node, hook_p hook, void *arg1, int arg2) +ubt_bulk_write_callback(struct usb2_xfer *xfer) { - struct ubt_softc *sc = NG_NODE_PRIVATE(node); - struct mbuf *m; - ng_hci_acldata_pkt_t *hdr; - uint16_t len; - int error; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct mbuf *m; - if (sc == NULL) { - return; + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ } - mtx_lock(&sc->sc_mtx); - m = sc->sc_bulk_in_buffer; + sc = NG_NODE_PRIVATE(node); - if (m) { - - sc->sc_bulk_in_buffer = NULL; - - hdr = mtod(m, ng_hci_acldata_pkt_t *); - - if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) { - NG_UBT_INFO(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; - } - 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); - - NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); - - NG_SEND_DATA_ONLY(error, sc->sc_hook, m); - - m = NULL; - - 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); - - NG_UBT_STAT_IERROR(sc->sc_stat); - } - } -done: - if (m) { - NG_FREE_M(m); - } - /* start USB transfer if not already started */ - - usb2_transfer_start(sc->sc_xfer_if_0[1]); - - mtx_unlock(&sc->sc_mtx); -} - -static void -ubt_bulk_write_callback(struct usb2_xfer *xfer) -{ - struct ubt_softc *sc = xfer->priv_sc; - struct mbuf *m; - 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); + if (xfer->error != 0) + UBT_STAT_OERROR(sc); + else { + UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", + xfer->actlen); - case USB_ST_SETUP: - if (sc->sc_flags & UBT_FLAG_WRITE_STALL) { - usb2_transfer_start(sc->sc_xfer_if_0[4]); - return; + 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); + xfer->frlengths[0] = m->m_pkthdr.len; - NG_UBT_INFO(sc, "bulk-out transfer has been started, " - "len=%d\n", m->m_pkthdr.len); + UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", + m->m_pkthdr.len); - xfer->frlengths[0] = 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)); + UBT_STAT_OERROR(sc); - NG_UBT_STAT_OERROR(sc->sc_stat); - /* try to clear stall first */ sc->sc_flags |= UBT_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer_if_0[4]); - } - return; - + } else + NG_NODE_UNREF(node); /* transfer 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_if_0[0]; + 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); /* XXX 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; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + int n; + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } + + sc = NG_NODE_PRIVATE(node); + + 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 */ + } + switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: -tr_transferred: - offset = 0; + for (n = 0; n < xfer->nframes; n ++) + if (ubt_isoc_read_one_frame(xfer, n) < 0) + break; + /* FALLTHROUGH */ - for (n = 0; n < xfer->nframes; n++) { + case USB_ST_SETUP: +read_next: + for (n = 0; n < xfer->nframes; n ++) + xfer->frlengths[n] = xfer->max_frame_size; - if (xfer->frlengths[n] >= sizeof(hdr)) { + usb2_start_hardware(xfer); + break; - usb2_copy_out(xfer->frbuffers, offset, - &hdr, sizeof(hdr)); + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_STAT_IERROR(sc); + goto read_next; + /* NOT REACHED */ + } - if (hdr.length == (xfer->frlengths[n] - sizeof(hdr))) { + NG_NODE_UNREF(node); /* transfer is cancelled */ + break; + } +} /* ubt_isoc_read_callback */ - NG_UBT_INFO(sc, "got complete SCO data " - "frame, length=%d\n", hdr.length); +/* + * Helper function. Called from ubt_isoc_read_callback() to read + * SCO data from one frame. + * USB context. + */ - if (!NG_BT_MBUFQ_FULL(&sc->sc_sciq)) { +static int +ubt_isoc_read_one_frame(struct usb2_xfer *xfer, uint32_t frame_no) +{ + struct ubt_softc *sc = xfer->priv_sc; + struct mbuf *m; + int len, want, got, error; - /* allocate a new mbuf */ + /* Get existing SCO reassembly buffer */ + m = sc->sc_isoc_in_buffer; + sc->sc_isoc_in_buffer = NULL; - MGETHDR(m, M_DONTWAIT, MT_DATA); + /* 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! */ + } - if (m == NULL) { - goto tr_setup; - } - MCLGET(m, M_DONTWAIT); + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + UBT_STAT_IERROR(sc); + NG_FREE_M(m); + return (-1); /* XXX out of sync! */ + } - if (!(m->m_flags & M_EXT)) { - NG_FREE_M(m); - goto tr_setup; - } - /* - * fix SCO data frame header - * if required - */ + /* 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 (!(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; - } + if (got >= want) + want += mtod(m, ng_hci_scodata_pkt_t *)->length; + } - max_len = (MCLBYTES - m->m_len); + /* Append frame data to the SCO reassembly buffer */ + if (got + len > want) + len = want - got; - if (xfer->frlengths[n] > max_len) { - xfer->frlengths[n] = max_len; - } - ptr = ((uint8_t *)(m->m_data)) + m->m_len; + usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size, + mtod(m, uint8_t *) + m->m_pkthdr.len, len); - usb2_copy_out - (xfer->frbuffers, offset, - ptr, xfer->frlengths[n]); + m->m_pkthdr.len += len; + m->m_len += len; + xfer->frlengths[frame_no] -= len; - m->m_pkthdr.len += xfer->frlengths[n]; - m->m_len += xfer->frlengths[n]; + /* Check if we got everything we wanted, if not - continue */ + if (got != want) + continue; - NG_BT_MBUFQ_ENQUEUE(&sc->sc_sciq, m); - } - } - } - offset += xfer->max_frame_size; + /* 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); + + 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 */ + } + + /* 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) +{ + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct mbuf *m; + uint32_t 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: + 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: -tr_setup: +send_next: + offset = 0; + space = xfer->max_frame_size * xfer->nframes; + m = NULL; - if (NG_BT_MBUFQ_LEN(&sc->sc_sciq) > 0) { - ng_send_fn(sc->sc_node, NULL, ubt_isoc_read_complete, NULL, 0); + while (space > 0) { + if (m == NULL) { + UBT_MBUFQ_LOCK(sc); + NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); + UBT_MBUFQ_UNLOCK(sc); + + if (m == NULL) + break; + } + + n = min(space, m->m_pkthdr.len); + if (n > 0) { + usb2_m_copy_in(xfer->frbuffers,offset, m,0, n); + m_adj(m, n); + + offset += n; + space -= n; + } + + if (m->m_pkthdr.len == 0) + NG_FREE_M(m); } - for (n = 0; n < xfer->nframes; n++) { - xfer->frlengths[n] = xfer->max_frame_size; + + if (offset == 0) { + NG_NODE_UNREF(node); + return; /* nothing to send */ } + /* 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 sized for isoc frames */ + for (n = 0; offset > 0; n ++) { + xfer->frlengths[n] = min(offset, xfer->max_frame_size); + offset -= xfer->max_frame_size; + } + 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); /* transfer cancelled */ + break; } } +/**************************************************************************** + **************************************************************************** + ** Glue + **************************************************************************** + ****************************************************************************/ + +/* + * Schedule glue task. Should be called with sc_mbufq_mtx held. + * Netgraph context. + */ + +static int +ubt_task_schedule(ubt_softc_p sc, uint32_t action) +{ + 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 */ + +/* + * Glue task. Examines sc_task_flags and does things depending on it. + * Taskqueue context. + */ + static void -ubt_isoc_read_complete(node_p node, hook_p hook, void *arg1, int arg2) +ubt_task(void *context, int pending) { - ubt_softc_p sc = NG_NODE_PRIVATE(node); - struct mbuf *m; - int error; + node_p node = context; + ubt_softc_p sc; + uint32_t task_flags; - if (sc == NULL) { - return; + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ } - mtx_lock(&sc->sc_mtx); - while (1) { + sc = NG_NODE_PRIVATE(node); - NG_BT_MBUFQ_DEQUEUE(&sc->sc_sciq, m); + UBT_MBUFQ_LOCK(sc); + task_flags = sc->sc_task_flags; + sc->sc_task_flags = 0; + UBT_MBUFQ_UNLOCK(sc); - 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); + /* Stop all USB transfers */ + if (task_flags & UBT_FLAG_T_STOP_ALL) { + /* + * Interface #0 + */ - NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); - NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len); + mtx_lock(&sc->sc_if_0_mtx); - NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + usb2_transfer_stop(sc->sc_xfer_if_0[0]); /* bulk out */ + usb2_transfer_stop(sc->sc_xfer_if_0[1]); /* bulk in */ + usb2_transfer_stop(sc->sc_xfer_if_0[2]); /* intr in */ + usb2_transfer_stop(sc->sc_xfer_if_0[3]); /* ctrl out */ + usb2_transfer_stop(sc->sc_xfer_if_0[4]); + usb2_transfer_stop(sc->sc_xfer_if_0[5]); + usb2_transfer_stop(sc->sc_xfer_if_0[6]); - m = NULL; + mtx_unlock(&sc->sc_if_0_mtx); - if (error) { - NG_UBT_STAT_IERROR(sc->sc_stat); - } -done: - if (m) { - NG_FREE_M(m); - } + /* + * Interface #1 + */ + + mtx_lock(&sc->sc_if_1_mtx); + + usb2_transfer_stop(sc->sc_xfer_if_1[0]); /* isoc in #1 */ + usb2_transfer_stop(sc->sc_xfer_if_1[1]); /* isoc in #2 */ + usb2_transfer_stop(sc->sc_xfer_if_1[2]); /* isoc out #1 */ + usb2_transfer_stop(sc->sc_xfer_if_1[3]); /* isoc out #2 */ + + mtx_unlock(&sc->sc_if_1_mtx); } - mtx_unlock(&sc->sc_mtx); -} + /* Start all incoming USB transfers */ + if (task_flags & UBT_FLAG_T_START_ALL) { + /* + * Interface #0 + */ -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; + mtx_lock(&sc->sc_if_0_mtx); - 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); - } + NG_NODE_REF(node); + usb2_transfer_start(sc->sc_xfer_if_0[1]); /* bulk in */ - case USB_ST_SETUP: - offset = 0; + NG_NODE_REF(node); + usb2_transfer_start(sc->sc_xfer_if_0[2]); /* intr in */ - for (n = 0; n < xfer->nframes; n++) { + mtx_unlock(&sc->sc_if_0_mtx); - NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m); + /* + * Interface #1 + */ - if (m) { - len = min(xfer->max_frame_size, m->m_pkthdr.len); + mtx_lock(&sc->sc_if_1_mtx); - usb2_m_copy_in(xfer->frbuffers, offset, m, 0, len); + NG_NODE_REF(node); + usb2_transfer_start(sc->sc_xfer_if_1[0]); /* isoc in #1 */ - NG_FREE_M(m); + NG_NODE_REF(node); + usb2_transfer_start(sc->sc_xfer_if_1[1]); /* isoc in #2 */ - xfer->frlengths[n] = len; - offset += len; - } else { - xfer->frlengths[n] = 0; - } - } + mtx_unlock(&sc->sc_if_1_mtx); + } - usb2_start_hardware(xfer); - return; + /* Start outgoing control transfer */ + if (task_flags & UBT_FLAG_T_START_CTRL) { + mtx_lock(&sc->sc_if_0_mtx); + NG_NODE_REF(node); + usb2_transfer_start(sc->sc_xfer_if_0[3]); + mtx_unlock(&sc->sc_if_0_mtx); + } - default: /* Error */ - if (xfer->error == USB_ERR_CANCELLED) { - /* ignore */ - return; - } - goto tr_transferred; + /* Start outgoing bulk transfer */ + if (task_flags & UBT_FLAG_T_START_BULK) { + mtx_lock(&sc->sc_if_0_mtx); + NG_NODE_REF(node); + usb2_transfer_start(sc->sc_xfer_if_0[0]); + mtx_unlock(&sc->sc_if_0_mtx); } -} + /* Start outgoing isoc transfers */ + if (task_flags & UBT_FLAG_T_START_ISOC) { + mtx_lock(&sc->sc_if_1_mtx); + + NG_NODE_REF(node); + usb2_transfer_start(sc->sc_xfer_if_1[2]); /* isoc out #1 */ + + NG_NODE_REF(node); + usb2_transfer_start(sc->sc_xfer_if_1[3]); /* isoc out #2 */ + + mtx_unlock(&sc->sc_if_1_mtx); + } + + NG_NODE_UNREF(node); +} /* ubt_task */ + /**************************************************************************** **************************************************************************** ** Netgraph specific @@ -1319,206 +1530,148 @@ ****************************************************************************/ /* - * Netgraph node constructor. - * Do not allow to create node of this type: + * Netgraph node constructor. Do not allow to create node of this type. + * Netgraph context. */ static int ng_ubt_constructor(node_p node) { return (EINVAL); -} +} /* ng_ubt_constructor */ /* - * Netgraph node destructor. - * Destroy node only when device has been detached: + * Netgraph node destructor. Destroy node only when device has been detached. + * Netgraph context. */ static int ng_ubt_shutdown(node_p node) { - struct ubt_softc *sc = NG_NODE_PRIVATE(node); + struct ubt_softc *sc = NG_NODE_PRIVATE(node); /* Let old node go */ NG_NODE_SET_PRIVATE(node, NULL); NG_NODE_UNREF(node); - if (sc == NULL) { + if (sc == NULL) goto done; - } - 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); + printf("%s: Could not create Netgraph node\n", sc->sc_name); sc->sc_node = NULL; goto done; } + /* Name node */ if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { - printf("%s: Could not name Netgraph node\n", - sc->sc_name); + printf("%s: Could not name Netgraph node\n", sc->sc_name); NG_NODE_UNREF(sc->sc_node); sc->sc_node = NULL; goto done; } + NG_NODE_SET_PRIVATE(sc->sc_node, sc); NG_NODE_FORCE_WRITER(sc->sc_node); + UBT_MBUFQ_LOCK(sc); + sc->sc_task_flags = 0; + UBT_MBUFQ_UNLOCK(sc); done: - if (sc) { - mtx_unlock(&sc->sc_mtx); - } return (0); -} +} /* ng_ubt_shutdown */ /* - * Create new hook. - * There can only be one. + * Create new hook. There can only be one. + * Netgraph context. */ static int ng_ubt_newhook(node_p node, hook_p hook, char const *name) { - struct ubt_softc *sc = NG_NODE_PRIVATE(node); - int error = 0; + struct ubt_softc *sc = NG_NODE_PRIVATE(node); - if (strcmp(name, NG_UBT_HOOK) != 0) { + if (strcmp(name, NG_UBT_HOOK) != 0) return (EINVAL); - } - mtx_lock(&sc->sc_mtx); - if (sc->sc_hook != NULL) { - error = EISCONN; - } else { - sc->sc_hook = hook; - } + if (sc->sc_hook != NULL) + return (EISCONN); - mtx_unlock(&sc->sc_mtx); + sc->sc_hook = hook; - return (error); -} + return (0); +} /* ng_ubt_newhook */ /* - * Connect hook. - * Start incoming USB transfers + * Connect hook. Start incoming USB transfers. + * Netgraph context. */ static int ng_ubt_connect(hook_p hook) { - struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); - mtx_lock(&sc->sc_mtx); + UBT_MBUFQ_LOCK(sc); + ubt_task_schedule(sc, UBT_FLAG_T_START_ALL); + UBT_MBUFQ_UNLOCK(sc); - sc->sc_flags |= (UBT_FLAG_READ_STALL | - UBT_FLAG_WRITE_STALL | - UBT_FLAG_INTR_STALL); - - /* start intr transfer */ - usb2_transfer_start(sc->sc_xfer_if_0[2]); - - /* start bulk-in transfer */ - usb2_transfer_start(sc->sc_xfer_if_0[1]); - - /* start bulk-out transfer */ - usb2_transfer_start(sc->sc_xfer_if_0[0]); - - /* start control-out transfer */ - usb2_transfer_start(sc->sc_xfer_if_0[3]); -#if 0 - XXX can enable this XXX - - /* start isoc-in transfer */ - usb2_transfer_start(sc->sc_xfer_if_1[0]); - - usb2_transfer_start(sc->sc_xfer_if_1[1]); - - /* start isoc-out transfer */ - usb2_transfer_start(sc->sc_xfer_if_1[2]); - usb2_transfer_start(sc->sc_xfer_if_1[3]); -#endif - - mtx_unlock(&sc->sc_mtx); - 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; + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); - if (sc != NULL) { + if (sc == NULL) + return (0); - mtx_lock(&sc->sc_mtx); + if (hook != sc->sc_hook) + return (EINVAL); - if (hook != sc->sc_hook) { - error = EINVAL; - } else { + sc->sc_hook = NULL; - /* stop intr transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[2]); - usb2_transfer_stop(sc->sc_xfer_if_0[6]); + UBT_MBUFQ_LOCK(sc); - /* stop bulk-in transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[1]); - usb2_transfer_stop(sc->sc_xfer_if_0[5]); + /* Drain queues */ + NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); + NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); + NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); - /* stop bulk-out transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[0]); - usb2_transfer_stop(sc->sc_xfer_if_0[4]); + /* Kick off task to stop all USB xfers */ + ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); - /* stop control transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[3]); + UBT_MBUFQ_UNLOCK(sc); - /* stop isoc-in transfer */ - usb2_transfer_stop(sc->sc_xfer_if_1[0]); - usb2_transfer_stop(sc->sc_xfer_if_1[1]); - - /* stop isoc-out transfer */ - usb2_transfer_stop(sc->sc_xfer_if_1[2]); - usb2_transfer_stop(sc->sc_xfer_if_1[3]); - - /* 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); - - sc->sc_hook = NULL; - } - - mtx_unlock(&sc->sc_mtx); - } - return (error); -} - + 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; + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + struct ng_mesg *msg, *rsp = NULL; + struct ng_bt_mbufq *q; + int error = 0, queue, qlen; if (sc == NULL) { NG_FREE_ITEM(item); return (EHOSTDOWN); } - mtx_lock(&sc->sc_mtx); NGI_GET_MSG(item, msg); @@ -1527,25 +1680,29 @@ 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, + "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]", + (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 +1714,54 @@ 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; + break; + } - if (qlen <= 0) { - error = EINVAL; - break; - } - switch (queue) { - case NGM_UBT_NODE_QUEUE_CMD: - q = &sc->sc_cmdq; - break; + queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; + qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; - case NGM_UBT_NODE_QUEUE_ACL: - q = &sc->sc_aclq; - break; + switch (queue) { + case NGM_UBT_NODE_QUEUE_CMD: + q = &sc->sc_cmdq; + break; - case NGM_UBT_NODE_QUEUE_SCO: - q = &sc->sc_scoq; - break; + case NGM_UBT_NODE_QUEUE_ACL: + q = &sc->sc_aclq; + break; - default: - q = NULL; - error = EINVAL; - break; - } + case NGM_UBT_NODE_QUEUE_SCO: + q = &sc->sc_scoq; + break; - if (q != NULL) { - q->maxlen = qlen; - } + default: + error = EINVAL; + goto done; + /* NOT REACHED */ } + + q->maxlen = qlen; break; case NGM_UBT_NODE_GET_QLEN: @@ -1617,7 +1769,9 @@ 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 +1786,36 @@ break; default: - q = NULL; error = EINVAL; + goto done; + /* NOT REACHED */ + } + + NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), + M_NOWAIT); + if (rsp == NULL) { + error = ENOMEM; break; } - 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_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 +1828,172 @@ 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; + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct mbuf *m; + struct ng_bt_mbufq *q; + uint32_t action; + int error = 0; if (sc == NULL) { error = EHOSTDOWN; goto done; } - mtx_lock(&sc->sc_mtx); 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; +#if 0 /* XXX FIXME - not yet */ q = &sc->sc_scoq; + action = UBT_FLAG_T_START_ISOC; break; +#else + NG_FREE_M(m); + goto done; +#endif 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); + Index: ng_ubt2_var.h =================================================================== --- ng_ubt2_var.h (revision 186800) +++ ng_ubt2_var.h (working copy) @@ -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 @@ -32,95 +32,94 @@ */ #ifndef _NG_UBT_VAR_H_ -#define _NG_UBT_VAR_H_ +#define _NG_UBT_VAR_H_ 1 -/* 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) - /* 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 /* 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 */ +#define UBT_IF_0_N_TRANSFER 7 /* units */ +#define UBT_IF_1_N_TRANSFER 4 /* units */ +#define UBT_ISOC_NFRAMES 25 /* units */ /* 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_debug_ep sc_debug; /* debug level */ - 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))) + uint32_t 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 */ - uint8_t sc_name[16]; + 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)) - struct mtx sc_mtx; - /* 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_0_mtx; /* if #0 lock */ + struct usb2_xfer *sc_xfer_if_0[UBT_IF_0_N_TRANSFER]; - /* Interrupt pipe (HCI events) */ - struct mbuf *sc_intr_buffer; /* interrupt buffer */ + struct mtx sc_if_1_mtx; /* if #1 lock */ + struct usb2_xfer *sc_xfer_if_1[UBT_IF_1_N_TRANSFER]; - /* 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) + struct mtx sc_mbufq_mtx; /* lock for all queues */ - /* Bulk in pipe (ACL data) */ - struct mbuf *sc_bulk_in_buffer; /* bulk-in buffer */ + /* 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) +#define UBT_INTR_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */ - /* 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 */ + /* SCO data */ + struct ng_bt_mbufq sc_scoq; /* SCO data queue */ + struct mbuf *sc_isoc_in_buffer; /* SCO reassembly buffer */ - /* Isoc. in pipe (ACL data) */ - struct ng_bt_mbufq sc_sciq; /* SCO data queue */ - /* 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 */ + uint32_t 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 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) */ +#define UBT_FLAG_T_START_ISOC (1 << 5) /* start isoc xfer (write) */ + + struct task sc_task; }; 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_ */ + From hselasky at c2i.net Thu Jan 15 01:51:52 2009 From: hselasky at c2i.net (Hans Petter Selasky) Date: Thu Jan 15 01:51:58 2009 Subject: USB2: ng_ubt2 patch In-Reply-To: References: Message-ID: <200901150954.11454.hselasky@c2i.net> On Thursday 15 January 2009, Maksim Yevmenkin wrote: > dear bluetooth users, > > please find attached patch for ng_ubt2 (well it is almost a complete > rewrite). > > this is an early preview and likely to have bugs. i briefly kicked the > tires to make sure it compiles and did some very quick testing, i.e. > run few hci commands, sdp transfers etc. > > this is my first attempt to write something under usb2, so any > review/comments/suggestions/etc. are greatly appreciated. > > thanks, > max Hi, This looks very interesting. 1) Maybe you just want to merge together all the USB config structures into one? 2) There are some isochronous details for non-Full speed USB. UBT_ISOC_NFRAMES must be a factor of 8 if it should work. How much bluetooth functionality have you tested? BTW: I think your patch is pretty Ok. Is what you attached the final version of your patch? --HPS From maksim.yevmenkin at gmail.com Thu Jan 15 09:55:55 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Thu Jan 15 09:56:01 2009 Subject: USB2: ng_ubt2 patch In-Reply-To: <200901150954.11454.hselasky@c2i.net> References: <200901150954.11454.hselasky@c2i.net> Message-ID: On Thu, Jan 15, 2009 at 12:54 AM, Hans Petter Selasky wrote: > On Thursday 15 January 2009, Maksim Yevmenkin wrote: >> dear bluetooth users, >> >> please find attached patch for ng_ubt2 (well it is almost a complete >> rewrite). >> >> this is an early preview and likely to have bugs. i briefly kicked the >> tires to make sure it compiles and did some very quick testing, i.e. >> run few hci commands, sdp transfers etc. >> >> this is my first attempt to write something under usb2, so any >> review/comments/suggestions/etc. are greatly appreciated. > > This looks very interesting. thanks for taking time and looking at this. > 1) Maybe you just want to merge together all the USB config structures into > one? i guess, i could. since isoc transfers are going over different (from control, interrupt and bulk transfers) interface, i kept original code that had two separate usb2_config structures (i.e. one per each interface). for the same reason, i added separate lock for interface 1 (where isoc transfers are). i figured, due to the nature of isoc transfers, this lock will be grabbed a lot more often. it seemed like a good idea to use different lock so there would be less contention with interface 0 lock. but then again, may be i just full of it :) and it really does not matter. could you please tell me what is the reason for merging usb2_config structures? is it better for usb2? > 2) There are some isochronous details for non-Full speed USB. UBT_ISOC_NFRAMES > must be a factor of 8 if it should work. ok. the bluetooth spec says device should be a high speed device, however, i have a bluetooth usb dongle (made by 3com) that is not a high speed device. so, if i just bump UBT_ISOC_NFRAMES to 32, this should work for both full- and high-speed devices, right? another question is how to submit isoc. writes. recall that ubt driver uses multiple isoc transfers in both directions. older (usb1) code kept track of isoc write transfers and knew which were not active. so when ubt needed to write isoc frame, it simply picked inactive isoc transfer. so, should i simply start all isoc write transfers and let usb2 code to figure it out or what? also, i'm a bit confused about api. what is the difference between usb2_transfer_start() and usb2_start_hardware() and when one should use former or later? > How much bluetooth functionality have you tested? like i said, i kicked the tires :) hci commands/events appear to work. acl/l2cap appears to work as well. i have not tried isoc (read my questions above). of course, i will need to run torture tests etc. > BTW: I think your patch is pretty Ok. Is what you attached the final version > of your patch? probably not. i'm sure there will be some tweaks to it. hopefully nothing too big. i'm just not sure how should i proceed at this point, i.e. do i 1) start committing this to -head and let you pick it up from there or 2) let you commit this into p4 (or whatever) and then let you merge it into -head thanks, max From hselasky at c2i.net Thu Jan 15 13:22:59 2009 From: hselasky at c2i.net (Hans Petter Selasky) Date: Thu Jan 15 13:23:07 2009 Subject: USB2: ng_ubt2 patch In-Reply-To: References: <200901150954.11454.hselasky@c2i.net> Message-ID: <200901152225.19150.hselasky@c2i.net> On Thursday 15 January 2009, Maksim Yevmenkin wrote: > On Thu, Jan 15, 2009 at 12:54 AM, Hans Petter Selasky wrote: > > On Thursday 15 January 2009, Maksim Yevmenkin wrote: > >> dear bluetooth users, > >> > >> please find attached patch for ng_ubt2 (well it is almost a complete > >> rewrite). > >> > >> this is an early preview and likely to have bugs. i briefly kicked the > >> tires to make sure it compiles and did some very quick testing, i.e. > >> run few hci commands, sdp transfers etc. > >> > >> this is my first attempt to write something under usb2, so any > >> review/comments/suggestions/etc. are greatly appreciated. > > > > This looks very interesting. > > thanks for taking time and looking at this. > > > 1) Maybe you just want to merge together all the USB config structures > > into one? > > i guess, i could. since isoc transfers are going over different (from > control, interrupt and bulk transfers) interface, i kept original code > that had two separate usb2_config structures (i.e. one per each > interface). for the same reason, i added separate lock for interface 1 > (where isoc transfers are). i figured, due to the nature of isoc > transfers, this lock will be grabbed a lot more often. it seemed like > a good idea to use different lock so there would be less contention > with interface 0 lock. but then again, may be i just full of it :) and > it really does not matter. > > could you please tell me what is the reason for merging usb2_config > structures? is it better for usb2? It will save some memory allocations. > > > 2) There are some isochronous details for non-Full speed USB. > > UBT_ISOC_NFRAMES must be a factor of 8 if it should work. > > ok. the bluetooth spec says device should be a high speed device, > however, i have a bluetooth usb dongle (made by 3com) that is not a > high speed device. so, if i just bump UBT_ISOC_NFRAMES to 32, this > should work for both full- and high-speed devices, right? Yes, just note that High-Speed USB is 8000 fps while Full-Speed USB is 1000 fps. > > another question is how to submit isoc. writes. recall that ubt driver > uses multiple isoc transfers in both directions. older (usb1) code > kept track of isoc write transfers and knew which were not active. so > when ubt needed to write isoc frame, it simply picked inactive isoc > transfer. so, should i simply start all isoc write transfers and let > usb2 code to figure it out or what? also, i'm a bit confused about > api. what is the difference between usb2_transfer_start() and > usb2_start_hardware() and when one should use former or later? Two ISOC transfers are enough for double buffering. > > > How much bluetooth functionality have you tested? > > like i said, i kicked the tires :) hci commands/events appear to work. > acl/l2cap appears to work as well. i have not tried isoc (read my > questions above). of course, i will need to run torture tests etc. Great! > > > BTW: I think your patch is pretty Ok. Is what you attached the final > > version of your patch? > > probably not. i'm sure there will be some tweaks to it. hopefully > nothing too big. i'm just not sure how should i proceed at this point, > i.e. do i > > 1) start committing this to -head and let you pick it up from there > > or > > 2) let you commit this into p4 (or whatever) and then let you merge it > into -head Just re-send me the latest patch you've got and I'll get it in P4 and my private SVN first. --HPS From maksim.yevmenkin at gmail.com Thu Jan 15 17:08:06 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Thu Jan 15 17:08:12 2009 Subject: USB2: ng_ubt2 patch In-Reply-To: <200901152225.19150.hselasky@c2i.net> References: <200901150954.11454.hselasky@c2i.net> <200901152225.19150.hselasky@c2i.net> Message-ID: On Thu, Jan 15, 2009 at 1:25 PM, Hans Petter Selasky wrote: > On Thursday 15 January 2009, Maksim Yevmenkin wrote: >> On Thu, Jan 15, 2009 at 12:54 AM, Hans Petter Selasky > wrote: >> > On Thursday 15 January 2009, Maksim Yevmenkin wrote: [...] >> thanks for taking time and looking at this. >> >> > 1) Maybe you just want to merge together all the USB config structures >> > into one? >> >> i guess, i could. since isoc transfers are going over different (from >> control, interrupt and bulk transfers) interface, i kept original code >> that had two separate usb2_config structures (i.e. one per each >> interface). for the same reason, i added separate lock for interface 1 >> (where isoc transfers are). i figured, due to the nature of isoc >> transfers, this lock will be grabbed a lot more often. it seemed like >> a good idea to use different lock so there would be less contention >> with interface 0 lock. but then again, may be i just full of it :) and >> it really does not matter. >> >> could you please tell me what is the reason for merging usb2_config >> structures? is it better for usb2? > > It will save some memory allocations. i will see what i can do. [...] >> another question is how to submit isoc. writes. recall that ubt driver >> uses multiple isoc transfers in both directions. older (usb1) code >> kept track of isoc write transfers and knew which were not active. so >> when ubt needed to write isoc frame, it simply picked inactive isoc >> transfer. so, should i simply start all isoc write transfers and let >> usb2 code to figure it out or what? also, i'm a bit confused about >> api. what is the difference between usb2_transfer_start() and >> usb2_start_hardware() and when one should use former or later? > > Two ISOC transfers are enough for double buffering. actually, could you please answer the question: is there an api that would tell me if a given transfer is in progress? i kinda hate to poke into usb2 guts and look at flags_int structure inside usb2_xfer. [...] >> > BTW: I think your patch is pretty Ok. Is what you attached the final >> > version of your patch? >> >> probably not. i'm sure there will be some tweaks to it. hopefully >> nothing too big. i'm just not sure how should i proceed at this point, >> i.e. do i >> >> 1) start committing this to -head and let you pick it up from there >> >> or >> >> 2) let you commit this into p4 (or whatever) and then let you merge it >> into -head > > Just re-send me the latest patch you've got and I'll get it in P4 and my > private SVN first. all right, i will do that. but first i need to iron out a couple of wrinkles :) thanks max From hselasky at c2i.net Fri Jan 16 02:10:04 2009 From: hselasky at c2i.net (Hans Petter Selasky) Date: Fri Jan 16 02:10:10 2009 Subject: USB2: ng_ubt2 patch In-Reply-To: References: <200901152225.19150.hselasky@c2i.net> Message-ID: <200901161012.24544.hselasky@c2i.net> On Friday 16 January 2009, Maksim Yevmenkin wrote: > On Thu, Jan 15, 2009 at 1:25 PM, Hans Petter Selasky wrote: > > On Thursday 15 January 2009, Maksim Yevmenkin wrote: > >> On Thu, Jan 15, 2009 at 12:54 AM, Hans Petter Selasky > > > > wrote: > >> > On Thursday 15 January 2009, Maksim Yevmenkin wrote: > > [...] > > >> thanks for taking time and looking at this. > >> > >> > 1) Maybe you just want to merge together all the USB config structures > >> > into one? > >> > >> i guess, i could. since isoc transfers are going over different (from > >> control, interrupt and bulk transfers) interface, i kept original code > >> that had two separate usb2_config structures (i.e. one per each > >> interface). for the same reason, i added separate lock for interface 1 > >> (where isoc transfers are). i figured, due to the nature of isoc > >> transfers, this lock will be grabbed a lot more often. it seemed like > >> a good idea to use different lock so there would be less contention > >> with interface 0 lock. but then again, may be i just full of it :) and > >> it really does not matter. > >> > >> could you please tell me what is the reason for merging usb2_config > >> structures? is it better for usb2? > > > > It will save some memory allocations. > > i will see what i can do. > > [...] > > >> another question is how to submit isoc. writes. recall that ubt driver > >> uses multiple isoc transfers in both directions. older (usb1) code > >> kept track of isoc write transfers and knew which were not active. so > >> when ubt needed to write isoc frame, it simply picked inactive isoc > >> transfer. so, should i simply start all isoc write transfers and let > >> usb2 code to figure it out or what? also, i'm a bit confused about > >> api. what is the difference between If isochronous is being used, then I recommend you have two transfers running at any time in both directions. If there is nothing to send you send zero length frames. Else you end up with glitches in the isochronous stream when you change from one transfer to the other! Both isochronous transfers should be queued all the time! > >> usb2_transfer_start() and > >> usb2_start_hardware() and when one should use former or later? usb2_transfer_start() kicks the callback. usb2_start_hardware() kicks the hardware. I don't recomment calling usb2_start_hardware() outside the callback, but if you check with "usb2_transfer_pending()" then it is possible. > > > > Two ISOC transfers are enough for double buffering. > > actually, could you please answer the question: is there an api that > would tell me if a given transfer is in progress? i kinda hate to poke > into usb2 guts and look at flags_int structure inside usb2_xfer. Yes, see "usb2_transfer_pending()". --HPS From maksim.yevmenkin at gmail.com Mon Jan 19 14:21:59 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Mon Jan 19 14:22:08 2009 Subject: USB2: ng_ubt2 patch In-Reply-To: <200901161012.24544.hselasky@c2i.net> References: <200901152225.19150.hselasky@c2i.net> <200901161012.24544.hselasky@c2i.net> Message-ID: hello, [...] >> >> > 1) Maybe you just want to merge together all the USB config structures >> >> > into one? >> >> >> >> i guess, i could. since isoc transfers are going over different (from >> >> control, interrupt and bulk transfers) interface, i kept original code >> >> that had two separate usb2_config structures (i.e. one per each >> >> interface). for the same reason, i added separate lock for interface 1 >> >> (where isoc transfers are). i figured, due to the nature of isoc >> >> transfers, this lock will be grabbed a lot more often. it seemed like >> >> a good idea to use different lock so there would be less contention >> >> with interface 0 lock. but then again, may be i just full of it :) and >> >> it really does not matter. >> >> >> >> could you please tell me what is the reason for merging usb2_config >> >> structures? is it better for usb2? >> > >> > It will save some memory allocations. >> >> i will see what i can do. ok, as per your request, i have merged two usb2_config structures into one (see attached patch) [...] > If isochronous is being used, then I recommend you have two transfers running > at any time in both directions. If there is nothing to send you send zero > length frames. Else you end up with glitches in the isochronous stream when > you change from one transfer to the other! Both isochronous transfers should > be queued all the time! ok, i've made this change as well. newer code queues and keeps both read and both write isoc. transfers running all the time. the attached patch seems to work (there was a reference counting problem in the previous version of the patch). i also tortured it a bit, i.e. stop the stack and/or yank the device while stack it active and sending lots of data (i used l2ping -f to create lots of activity). i think, it can be committed to -head now. it should be stable enough for early adopters of usb2 and bluetooth :) so, please let me know how should i proceed at this point? do i 1) commit it to head myself or 2) wait for you commit it into your cvs/scv/p4/etc. branch and then merge it into -head thanks, max -------------- next part -------------- Index: ng_ubt2.c =================================================================== --- ng_ubt2.c (revision 187313) +++ ng_ubt2.c (working copy) @@ -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 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_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); -static devclass_t ubt_devclass; +/* 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 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; - /* 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,316 +216,267 @@ 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 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 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); -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 + * + * 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 + */ -/* USB config */ -static const struct usb2_config ubt_config_if_0[UBT_IF_0_N_TRANSFER] = { +static const struct usb2_config ubt_config[UBT_N_TRANSFER] = +{ + /* + * Interface #0 + */ - [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, + /* 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, }, - - [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, + /* 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, }, - - [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, + /* 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, }, - - [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 */ + /* 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 */ }, - - [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 */ + /* 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 */ }, - - [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 */ + /* 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 */ }, - - [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 */ + /* + * 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_full_speed[UBT_IF_1_N_TRANSFER] = { + /* + * Interface #1 + */ - [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, + /* 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, - .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, - .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, - .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, }, }; -/* 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, - }, -}; - /* - * 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 @@ /* 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 */ + /* 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); + + /* initialize packet queues */ NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN); - - /* bulk-out pipe */ 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)); + /* initialize glue task */ + sc->sc_task_flags = 0; + TASK_INIT(&sc->sc_task, 0, ubt_task, sc->sc_node); - /* 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; - /* * 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,1003 +520,1134 @@ * 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); + struct ubt_softc *sc = device_get_softc(dev); + node_p node = sc->sc_node; - /* destroy Netgraph node */ + /* Destroy Netgraph node */ + if (node != NULL) { + sc->sc_node = NULL; - if (sc->sc_node != NULL) { - NG_NODE_SET_PRIVATE(sc->sc_node, NULL); - ng_rmnode_self(sc->sc_node); - sc->sc_node = NULL; + NG_NODE_SET_PRIVATE(node, NULL); + NG_NODE_REALLY_DIE(node); + NG_NODE_REF(node); + ng_rmnode_self(node); } - /* free USB transfers, if any */ - usb2_transfer_unsetup(sc->sc_xfer_if_0, UBT_IF_0_N_TRANSFER); + /* Free USB transfers, if any */ + usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); - usb2_transfer_unsetup(sc->sc_xfer_if_1, UBT_IF_1_N_TRANSFER); + if (node != NULL) + NG_NODE_UNREF(node); - mtx_destroy(&sc->sc_mtx); - - /* destroy queues */ - + /* 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); - 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); + 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 */ + } + + sc = NG_NODE_PRIVATE(node); + + 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 */ + } + + m = NULL; + switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: - - /* allocate a new mbuf */ - + /* 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); - if (!(m->m_flags & M_EXT)) { - NG_FREE_M(m); - goto tr_setup; + UBT_STAT_IERROR(sc); + goto submit_next; } - 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; + + /* Add HCI packet type */ + *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT; + m->m_pkthdr.len = m->m_len = 1; + + 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; + + UBT_INFO(sc, "got %d bytes from interrupt pipe\n", + xfer->actlen); + + /* 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"); + + UBT_STAT_IERROR(sc); + goto submit_next; } - max_len = (MCLBYTES - m->m_len); + 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); - if (xfer->actlen > max_len) { - xfer->actlen = max_len; + UBT_STAT_IERROR(sc); + goto submit_next; } - ptr = ((uint8_t *)(m->m_data)) + m->m_len; - usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen); + UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \ + "length=%d\n", m->m_pkthdr.len, hdr->length); - m->m_pkthdr.len += xfer->actlen; - m->m_len += xfer->actlen; + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); - NG_UBT_INFO(sc, "got %d bytes from interrupt " - "pipe\n", xfer->actlen); + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (error != 0) + UBT_STAT_IERROR(sc); - sc->sc_intr_buffer = m; + /* m == NULL at this point */ + /* FALLTHROUGH */ case USB_ST_SETUP: -tr_setup: +submit_next: + NG_FREE_M(m); /* checks for m != NULL */ - if (sc->sc_intr_buffer) { - ng_send_fn(sc->sc_node, NULL, ubt_intr_read_complete, NULL, 0); - return; + 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); } - if (sc->sc_flags & UBT_FLAG_INTR_STALL) { - usb2_transfer_start(sc->sc_xfer_if_0[6]); - return; - } - xfer->frlengths[0] = xfer->max_data_length; + break; - usb2_start_hardware(xfer); - return; + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "interrupt transfer failed: %s\n", + usb2_errstr(xfer->error)); - default: /* Error */ - if (xfer->error != USB_ERR_CANCELLED) { - /* try to clear stall first */ + /* Try to clear stall first */ sc->sc_flags |= UBT_FLAG_INTR_STALL; - usb2_transfer_start(sc->sc_xfer_if_0[6]); - } - return; - + 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; + 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 (sc == NULL) { - return; + 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; + sc = NG_NODE_PRIVATE(node); - 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; - - 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]); + m = NULL; - 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; - 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); - if (!(m->m_flags & M_EXT)) { - NG_FREE_M(m); - goto tr_setup; + UBT_STAT_IERROR(sc); + goto submit_next; } - 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; - } - 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; + if (xfer->actlen > MCLBYTES - 1) + xfer->actlen = MCLBYTES - 1; - usb2_copy_out(xfer->frbuffers, 0, ptr, xfer->actlen); - + 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; + UBT_STAT_IERROR(sc); + goto submit_next; } - if (sc->sc_flags & UBT_FLAG_READ_STALL) { - usb2_transfer_start(sc->sc_xfer_if_0[5]); - return; - } - 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); - } -} + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (error != 0) + UBT_STAT_IERROR(sc); -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; + /* m == NULL at this point */ + /* FALLTHOUGH */ - if (sc == NULL) { - return; - } - mtx_lock(&sc->sc_mtx); + case USB_ST_SETUP: +submit_next: + NG_FREE_M(m); /* checks for m != NULL */ - m = sc->sc_bulk_in_buffer; - - if (m) { - - sc->sc_bulk_in_buffer = NULL; - - hdr = mtod(m, ng_hci_acldata_pkt_t *); - - if (sc->sc_hook == NULL || NG_HOOK_NOT_VALID(sc->sc_hook)) { - NG_UBT_INFO(sc, "No upstream hook\n"); - goto done; + 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); } - NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len); + break; - if (m->m_pkthdr.len < sizeof(*hdr)) { - NG_UBT_INFO(sc, "Packet too short\n"); - goto done; - } - len = le16toh(hdr->length); + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "bulk-in transfer failed: %s\n", + usb2_errstr(xfer->error)); - 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); + /* 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 */ - NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); +/* + * Called when outgoing control transfer initiated to clear stall on + * incoming bulk pipe has completed. + * USB context. + */ - NG_SEND_DATA_ONLY(error, sc->sc_hook, m); +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; - m = NULL; + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } - 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); + sc = NG_NODE_PRIVATE(node); + xfer_other = sc->sc_xfer[UBT_IF_0_BULK_DT_RD]; - NG_UBT_STAT_IERROR(sc->sc_stat); - } - } -done: - if (m) { - NG_FREE_M(m); - } - /* start USB transfer if not already started */ + 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 */ - usb2_transfer_start(sc->sc_xfer_if_0[1]); +/* + * Called when outgoing bulk transfer (ACL packet) has completed, i.e. + * ACL packet was sent to the device. + * USB context. + */ - mtx_unlock(&sc->sc_mtx); -} - 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); + if (xfer->error != 0) + UBT_STAT_OERROR(sc); + else { + UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", + xfer->actlen); - case USB_ST_SETUP: - if (sc->sc_flags & UBT_FLAG_WRITE_STALL) { - usb2_transfer_start(sc->sc_xfer_if_0[4]); - return; + 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); + xfer->frlengths[0] = m->m_pkthdr.len; - NG_UBT_INFO(sc, "bulk-out transfer has been started, " - "len=%d\n", m->m_pkthdr.len); + UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n", + m->m_pkthdr.len); - xfer->frlengths[0] = 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)); + UBT_STAT_OERROR(sc); - NG_UBT_STAT_OERROR(sc->sc_stat); - /* 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; + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + int n; - switch (USB_GET_STATE(xfer)) { - case USB_ST_TRANSFERRED: -tr_transferred: - offset = 0; + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } - for (n = 0; n < xfer->nframes; n++) { + sc = NG_NODE_PRIVATE(node); - if (xfer->frlengths[n] >= sizeof(hdr)) { + 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 */ + } - usb2_copy_out(xfer->frbuffers, offset, - &hdr, sizeof(hdr)); + 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 (hdr.length == (xfer->frlengths[n] - sizeof(hdr))) { + case USB_ST_SETUP: +read_next: + for (n = 0; n < xfer->nframes; n ++) + xfer->frlengths[n] = xfer->max_frame_size; - NG_UBT_INFO(sc, "got complete SCO data " - "frame, length=%d\n", hdr.length); + usb2_start_hardware(xfer); + break; - if (!NG_BT_MBUFQ_FULL(&sc->sc_sciq)) { + default: /* Error */ + if (xfer->error != USB_ERR_CANCELLED) { + UBT_STAT_IERROR(sc); + goto read_next; + /* NOT REACHED */ + } - /* allocate a new mbuf */ + NG_NODE_UNREF(node); /* cancelled */ + break; + } +} /* ubt_isoc_read_callback */ - MGETHDR(m, M_DONTWAIT, MT_DATA); +/* + * Helper function. Called from ubt_isoc_read_callback() to read + * SCO data from one frame. + * USB context. + */ - if (m == NULL) { - goto tr_setup; - } - MCLGET(m, M_DONTWAIT); +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; - if (!(m->m_flags & M_EXT)) { - NG_FREE_M(m); - goto tr_setup; - } - /* - * fix SCO data frame header - * if required - */ + /* Get existing SCO reassembly buffer */ + m = sc->sc_isoc_in_buffer; + sc->sc_isoc_in_buffer = NULL; - 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; - } + /* 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! */ + } - max_len = (MCLBYTES - m->m_len); - - if (xfer->frlengths[n] > max_len) { - xfer->frlengths[n] = max_len; - } - ptr = ((uint8_t *)(m->m_data)) + m->m_len; - - usb2_copy_out - (xfer->frbuffers, offset, - ptr, xfer->frlengths[n]); - - m->m_pkthdr.len += xfer->frlengths[n]; - m->m_len += xfer->frlengths[n]; - - NG_BT_MBUFQ_ENQUEUE(&sc->sc_sciq, m); - } - } + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + UBT_STAT_IERROR(sc); + NG_FREE_M(m); + return (-1); /* XXX out of sync! */ } - offset += xfer->max_frame_size; - } - case USB_ST_SETUP: -tr_setup: + /* 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 (NG_BT_MBUFQ_LEN(&sc->sc_sciq) > 0) { - ng_send_fn(sc->sc_node, NULL, ubt_isoc_read_complete, NULL, 0); + if (got >= want) + want += mtod(m, ng_hci_scodata_pkt_t *)->length; } - for (n = 0; n < xfer->nframes; n++) { - xfer->frlengths[n] = xfer->max_frame_size; - } - usb2_start_hardware(xfer); - return; + /* Append frame data to the SCO reassembly buffer */ + if (got + len > want) + len = want - got; - default: /* Error */ - if (xfer->error == USB_ERR_CANCELLED) { - /* ignore */ - return; - } - goto tr_transferred; - } -} + usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size, + mtod(m, uint8_t *) + m->m_pkthdr.len, len); -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; + m->m_pkthdr.len += len; + m->m_len += len; + xfer->frlengths[frame_no] -= len; - if (sc == NULL) { - return; - } - mtx_lock(&sc->sc_mtx); + /* Check if we got everything we wanted, if not - continue */ + if (got != want) + continue; - while (1) { + /* 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_BT_MBUFQ_DEQUEUE(&sc->sc_sciq, m); + UBT_STAT_PCKTS_RECV(sc); + UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len); - 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); + NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + if (error != 0) + UBT_STAT_IERROR(sc); - NG_UBT_STAT_PCKTS_RECV(sc->sc_stat); - NG_UBT_STAT_BYTES_RECV(sc->sc_stat, m->m_pkthdr.len); + /* m == NULL at this point */ + } - NG_SEND_DATA_ONLY(error, sc->sc_hook, m); + /* Put SCO reassembly buffer back */ + sc->sc_isoc_in_buffer = m; - m = NULL; + return (0); +} /* ubt_isoc_read_one_frame */ - if (error) { - NG_UBT_STAT_IERROR(sc->sc_stat); - } -done: - if (m) { - NG_FREE_M(m); - } - } +/* + * Called when outgoing isoc transfer (SCO packet) has completed, i.e. + * SCO packet was sent to the device. + * USB context. + */ - mtx_unlock(&sc->sc_mtx); -} - 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; + node_p node = NG_HOOK_NODE(hook); + struct ubt_softc *sc; - if (sc != NULL) { + if (NG_NODE_NOT_VALID(node)) + return (0); - mtx_lock(&sc->sc_mtx); + sc = NG_NODE_PRIVATE(node); - if (hook != sc->sc_hook) { - error = EINVAL; - } else { + if (hook != sc->sc_hook) + return (EINVAL); - /* stop intr transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[2]); - usb2_transfer_stop(sc->sc_xfer_if_0[6]); + sc->sc_hook = NULL; - /* stop bulk-in transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[1]); - usb2_transfer_stop(sc->sc_xfer_if_0[5]); + UBT_MBUFQ_LOCK(sc); - /* stop bulk-out transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[0]); - usb2_transfer_stop(sc->sc_xfer_if_0[4]); + /* Drain queues */ + NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq); + NG_BT_MBUFQ_DRAIN(&sc->sc_aclq); + NG_BT_MBUFQ_DRAIN(&sc->sc_scoq); - /* stop control transfer */ - usb2_transfer_stop(sc->sc_xfer_if_0[3]); + /* Kick off task to stop all USB xfers */ + ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL); - /* stop isoc-in transfer */ - usb2_transfer_stop(sc->sc_xfer_if_1[0]); - usb2_transfer_stop(sc->sc_xfer_if_1[1]); + UBT_MBUFQ_UNLOCK(sc); - /* stop isoc-out transfer */ - usb2_transfer_stop(sc->sc_xfer_if_1[2]); - usb2_transfer_stop(sc->sc_xfer_if_1[3]); - - /* 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); - - sc->sc_hook = NULL; - } - - mtx_unlock(&sc->sc_mtx); - } - return (error); -} - + 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; + struct ubt_softc *sc = NG_NODE_PRIVATE(node); + struct ng_mesg *msg, *rsp = NULL; + struct ng_bt_mbufq *q; + int error = 0, queue, qlen; - if (sc == NULL) { - NG_FREE_ITEM(item); - return (EHOSTDOWN); - } - mtx_lock(&sc->sc_mtx); - NGI_GET_MSG(item, msg); switch (msg->header.typecookie) { @@ -1527,25 +1655,31 @@ 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 @@ 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; + break; + } - if (qlen <= 0) { - error = EINVAL; - break; - } - switch (queue) { - case NGM_UBT_NODE_QUEUE_CMD: - q = &sc->sc_cmdq; - break; + queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue; + qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen; - case NGM_UBT_NODE_QUEUE_ACL: - q = &sc->sc_aclq; - break; + switch (queue) { + case NGM_UBT_NODE_QUEUE_CMD: + q = &sc->sc_cmdq; + break; - case NGM_UBT_NODE_QUEUE_SCO: - q = &sc->sc_scoq; - break; + case NGM_UBT_NODE_QUEUE_ACL: + q = &sc->sc_aclq; + break; - default: - q = NULL; - error = EINVAL; - break; - } + case NGM_UBT_NODE_QUEUE_SCO: + q = &sc->sc_scoq; + break; - if (q != NULL) { - q->maxlen = qlen; - } + default: + error = EINVAL; + goto done; + /* NOT REACHED */ } + + q->maxlen = qlen; break; case NGM_UBT_NODE_GET_QLEN: @@ -1617,7 +1746,9 @@ 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 @@ break; default: - q = NULL; error = EINVAL; + goto done; + /* NOT REACHED */ + } + + NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep), + M_NOWAIT); + if (rsp == NULL) { + error = ENOMEM; break; } - 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_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 @@ 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; + struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + struct mbuf *m; + struct ng_bt_mbufq *q; + int action, error = 0; - if (sc == NULL) { - error = EHOSTDOWN; - goto done; - } - mtx_lock(&sc->sc_mtx); - 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); + Index: ng_ubt2_var.h =================================================================== --- ng_ubt2_var.h (revision 187313) +++ ng_ubt2_var.h (working copy) @@ -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 @@ -32,95 +32,111 @@ */ #ifndef _NG_UBT_VAR_H_ -#define _NG_UBT_VAR_H_ +#define _NG_UBT_VAR_H_ 1 -/* 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) - /* 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_debug_ep sc_debug; /* debug level */ - 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))) + 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 */ - uint8_t sc_name[16]; + 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)) - struct mtx sc_mtx; - /* 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 */ + /* SCO data */ + struct ng_bt_mbufq sc_scoq; /* SCO data queue */ + struct mbuf *sc_isoc_in_buffer; /* SCO reassembly buffer */ - /* Isoc. in pipe (ACL data) */ - struct ng_bt_mbufq sc_sciq; /* SCO data queue */ + /* Netgraph specific */ + node_p sc_node; /* pointer back to node */ + hook_p sc_hook; /* upstream hook */ - /* Netgraph specific */ - 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_ */ + From imp at bsdimp.com Mon Jan 19 17:24:12 2009 From: imp at bsdimp.com (M. Warner Losh) Date: Mon Jan 19 17:24:19 2009 Subject: USB2: ng_ubt2 patch In-Reply-To: References: <200901161012.24544.hselasky@c2i.net> Message-ID: <20090119.182153.1428097836.imp@bsdimp.com> In message: Maksim Yevmenkin writes: : do i : : 1) commit it to head myself : or : 2) wait for you commit it into your cvs/scv/p4/etc. branch and then : merge it into -head The answer really should be #1. We've got to have the barriers for entry into usb2 be relatively low. Since you've talked to Hans-Peter Selasky about this change, and he's basically signed off on it, you should be good to go... Warner From hselasky at c2i.net Tue Jan 20 00:25:11 2009 From: hselasky at c2i.net (Hans Petter Selasky) Date: Tue Jan 20 00:25:18 2009 Subject: USB2: ng_ubt2 patch In-Reply-To: References: <200901161012.24544.hselasky@c2i.net> Message-ID: <200901200927.34134.hselasky@c2i.net> Hi, > > 1) commit it to head myself You have my blessing to commit it. I will verify the changes next time I integrate my P4 repository! --HPS From bms at incunabulum.net Tue Jan 20 05:41:18 2009 From: bms at incunabulum.net (Bruce Simpson) Date: Tue Jan 20 05:41:25 2009 Subject: Ongoing Bluetooth work; Linux-like APIs? In-Reply-To: References: <200901152225.19150.hselasky@c2i.net> <200901161012.24544.hselasky@c2i.net> Message-ID: <4975CFD0.1000002@incunabulum.net> Hi guys, I am pleased to see all the hard work going in to maintaining the FreeBSD Bluetooth support... I wonder though if anyone has considered implementing the Linux APIs ? It's kinda difficult to build applications with what we currently have in FreeBSD -- a whole host of higher level language bindings have popped up. Whilst they've done work on making sure it works on Windows with all the stacks they have there, I wonder if FreeBSD's being different from Linux is doing us any good here. Just a suggestion... as it is, I've had to side-step FreeBSD for the time being for what I am cooking at the moment :-( cheers BMS From alfred at freebsd.org Tue Jan 20 08:07:29 2009 From: alfred at freebsd.org (Alfred Perlstein) Date: Tue Jan 20 08:07:42 2009 Subject: Ongoing Bluetooth work; Linux-like APIs? In-Reply-To: <4975CFD0.1000002@incunabulum.net> References: <200901152225.19150.hselasky@c2i.net> <200901161012.24544.hselasky@c2i.net> <4975CFD0.1000002@incunabulum.net> Message-ID: <20090120155028.GU60686@elvis.mu.org> * Bruce Simpson [090120 05:21] wrote: > Hi guys, > > I am pleased to see all the hard work going in to maintaining the > FreeBSD Bluetooth support... > > I wonder though if anyone has considered implementing the Linux APIs ? > > It's kinda difficult to build applications with what we currently have > in FreeBSD -- a whole host of higher level language bindings have popped > up. Whilst they've done work on making sure it works on Windows with all > the stacks they have there, I wonder if FreeBSD's being different from > Linux is doing us any good here. > > Just a suggestion... as it is, I've had to side-step FreeBSD for the > time being for what I am cooking at the moment :-( > > cheers > BMS That would be good. Please ping about it if you don't see it within a few weeks. -- - Alfred Perlstein From bms at incunabulum.net Tue Jan 20 09:58:40 2009 From: bms at incunabulum.net (Bruce M Simpson) Date: Tue Jan 20 09:58:47 2009 Subject: Ongoing Bluetooth work; Linux-like APIs? In-Reply-To: <20090120155028.GU60686@elvis.mu.org> References: <200901152225.19150.hselasky@c2i.net> <200901161012.24544.hselasky@c2i.net> <4975CFD0.1000002@incunabulum.net> <20090120155028.GU60686@elvis.mu.org> Message-ID: <49760FF0.7000400@incunabulum.net> Alfred Perlstein wrote: > ... >> I wonder though if anyone has considered implementing the Linux APIs ? >> ... >> > > That would be good. Please ping about it if you don't see it within > a few weeks. > If folk have cycles to look at that, and are interested in following up, that is excellent news... Meanwhile, a good set of pointers would be: http://org.csail.mit.edu/pybluez/ http://lightblue.sourceforge.net/ http://people.csail.mit.edu/albert/bluez-intro/c404.html http://code.google.com/p/bluecove/ Broadly there's a surface of interfaces to underlying system services (e.g. RFCOMM sockets, OBEX libraries, HCI sockets and/or layer access, and SDP in particular) which needs to be considered. The APIs above themselves have limitations with things like working with multiple interfaces, etc. I had a very brief crack at trying to get BlueCove to work with the FreeBSD stack, but I couldn't justify the time involved to get it up and running, given we'd already started building a solution on Linux. The differences between the Linux and FreeBSD stacks are small but subtle enough to render generic code incompatible... sigh. thanks, BMS From maksim.yevmenkin at gmail.com Tue Jan 20 14:22:11 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Tue Jan 20 14:22:17 2009 Subject: ng_ubt2 patch committed Message-ID: dear frebsd-bluetooth@ users with hps & alfred blessing updated version of the ng_ubt2 driver for usb2 was committed to -head. please give it a try and let me know if there are any problems. i'm also interested to hear about your experiences with ng_ubt2 and how it stacks up to usb1 ng_ubt. thanks, max === svn commit: r187494 - head/sys/dev/usb2/bluetooth 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 From olli at lurza.secnetix.de Wed Jan 21 09:47:23 2009 From: olli at lurza.secnetix.de (Oliver Fromme) Date: Wed Jan 21 09:47:29 2009 Subject: Ongoing Bluetooth work; Linux-like APIs? In-Reply-To: <49760FF0.7000400@incunabulum.net> Message-ID: <200901211747.n0LHlJfw039854@lurza.secnetix.de> Bruce M Simpson wrote: > Alfred Perlstein wrote: > > ... > > > I wonder though if anyone has considered implementing the Linux APIs ? > > > ... > > > > That would be good. Please ping about it if you don't see it within > > a few weeks. > > If folk have cycles to look at that, and are interested in following up, > that is excellent news... > > Meanwhile, a good set of pointers would be: > http://org.csail.mit.edu/pybluez/ I'm not exactly sure what the purpose of pybluez is. Python already supports Bluetooth on FreeBSD out of the box. I'm controlling several devices via Bluetooth (RFCOMM/SPP) with Python programs on FreeBSD. It works great. The very same programs work on NetBSD, too. I haven't tried Linux, though. Best regards Oliver -- Oliver Fromme, secnetix GmbH & Co. KG, Marktplatz 29, 85567 Grafing b. M. Handelsregister: Registergericht Muenchen, HRA 74606, Gesch?ftsfuehrung: secnetix Verwaltungsgesellsch. mbH, Handelsregister: Registergericht M?n- chen, HRB 125758, Gesch?ftsf?hrer: Maik Bachmann, Olaf Erb, Ralf Gebhart FreeBSD-Dienstleistungen, -Produkte und mehr: http://www.secnetix.de/bsd "The ITU has offered the IETF formal alignment with its corresponding technology, Penguins, but that won't fly." -- RFC 2549 From maksim.yevmenkin at gmail.com Wed Jan 21 12:07:19 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Wed Jan 21 12:07:25 2009 Subject: Ongoing Bluetooth work; Linux-like APIs? In-Reply-To: <200901211747.n0LHlJfw039854@lurza.secnetix.de> References: <49760FF0.7000400@incunabulum.net> <200901211747.n0LHlJfw039854@lurza.secnetix.de> Message-ID: On Wed, Jan 21, 2009 at 9:47 AM, Oliver Fromme wrote: > Bruce M Simpson wrote: > > Alfred Perlstein wrote: > > > ... > > > > I wonder though if anyone has considered implementing the Linux APIs ? > > > > ... > > > > > > That would be good. Please ping about it if you don't see it within > > > a few weeks. > > > > If folk have cycles to look at that, and are interested in following up, > > that is excellent news... > > > > Meanwhile, a good set of pointers would be: > > http://org.csail.mit.edu/pybluez/ > > I'm not exactly sure what the purpose of pybluez is. > Python already supports Bluetooth on FreeBSD out of the box. > I'm controlling several devices via Bluetooth (RFCOMM/SPP) > with Python programs on FreeBSD. It works great. The very > same programs work on NetBSD, too. I haven't tried Linux, > though. i think Bruce is talking about more high level (than bluetooth sockets support) stuff. for example, performing device inquiry, registering service with sdp server etc. while bluez libhci stuff (device inquiry and other general things such as sending and receiving hci commands and events) can be done fairly quickly, sdp api is quite different. Iain Hibbert (of netbsd) and i talking about sdp and how we can make it better. if anyone is working on libhci for freebsd, please let me know. thanks, max From maksim.yevmenkin at gmail.com Fri Jan 23 11:18:49 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Fri Jan 23 11:18:56 2009 Subject: ng_ubt2 patch for stalled transfers Message-ID: dear freebsd-bluetooth@ users, it has been brought to my attention that recently committed ng_ubt2 (USB2) driver for bluetooth usb dongles may have issues with stalled transfers. if you are experiencing stalled transfers and crashes with USB2/ng_ubt2 please try the attached patch and let me know if it makes it better. the patch probably will not fix stalled transfers, but at least the system should not be crashing anymore (or so i hope :) if you are using USB2/ng_usb2 and do not experience stalled transfers, please try the patch anyway. it should not make any difference, but i'd like to get as much testing as i can. thanks, max -------------- next part -------------- Index: ng_ubt2.c =================================================================== --- ng_ubt2.c (revision 187505) +++ ng_ubt2.c (working copy) @@ -243,13 +243,11 @@ /* 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 usb2_callback_t ubt_clear_stall_callback; static int ubt_isoc_read_one_frame(struct usb2_xfer *, int); @@ -316,7 +314,7 @@ .endpoint = 0x00, /* control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), - .mh.callback = &ubt_bulk_write_clear_stall_callback, + .mh.callback = &ubt_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, @@ -326,7 +324,7 @@ .endpoint = 0x00, /* control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), - .mh.callback = &ubt_bulk_read_clear_stall_callback, + .mh.callback = &ubt_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, @@ -339,7 +337,7 @@ .endpoint = 0x00, /* control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), - .mh.callback = &ubt_intr_read_clear_stall_callback, + .mh.callback = &ubt_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, @@ -623,6 +621,9 @@ ng_rmnode_self(node); } + /* Make sure ubt_task in gone */ + taskqueue_drain(taskqueue_swi, &sc->sc_task); + /* Free USB transfers, if any */ usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER); @@ -843,35 +844,6 @@ } /* 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) -{ - 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. @@ -990,35 +962,6 @@ } /* ubt_bulk_read_callback */ /* - * Called when outgoing control transfer initiated to clear stall on - * incoming bulk pipe has completed. - * USB context. - */ - -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; - - 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_RD]; - - 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. @@ -1096,35 +1039,6 @@ } /* 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) -{ - 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. @@ -1361,6 +1275,80 @@ } } +/* + * Called when an outgoing control transfer to clear stall on another + * pipe has been completed. Generic for all clear stall transfers. + * USB context. + */ + +static void +ubt_clear_stall_callback(struct usb2_xfer *xfer) +{ + node_p node = xfer->priv_sc; + struct ubt_softc *sc; + struct usb2_xfer *xfer_other; + int flag; + + if (NG_NODE_NOT_VALID(node)) { + NG_NODE_UNREF(node); + return; /* netgraph node is gone */ + } + + sc = NG_NODE_PRIVATE(node); + + /* + * Figure out which clear stall transfer has completed + * and set xfer_other and flag accordingly + */ + + if (xfer == sc->sc_xfer[UBT_IF_0_BULK_CS_WR]) { + xfer_other = sc->sc_xfer[UBT_IF_0_BULK_DT_WR]; + flag = UBT_FLAG_WRITE_STALL; + } else if (xfer == sc->sc_xfer[UBT_IF_0_BULK_CS_RD]) { + xfer_other = sc->sc_xfer[UBT_IF_0_BULK_DT_RD]; + flag = UBT_FLAG_READ_STALL; + } else if (xfer == sc->sc_xfer[UBT_IF_0_INTR_CS_RD]) { + xfer_other = sc->sc_xfer[UBT_IF_0_INTR_DT_RD]; + flag = UBT_FLAG_INTR_STALL; + } else + panic("could not set xfer_other! xfer=%p\n", xfer); + + if (xfer_other == NULL) { + UBT_WARN(sc, "other transfer is gone. are we dying?!\n"); + NG_NODE_UNREF(node); + return; /* other transfer is gone. are we dying?! */ + } + + switch (USB_GET_STATE(xfer)) { + case USB_ST_SETUP: + /* + * Ignore return value from usb2_clear_stall_callback() + * as it appears it can not return anything other than + * zero in USB_ST_SETUP case + */ + (void) usb2_clear_stall_callback(xfer, xfer_other); + break; + + case USB_ST_TRANSFERRED: + UBT_INFO(sc, "stall cleared, flag=%#x\n", flag); +submit_other: + sc->sc_flags &= ~flag; + usb2_transfer_start(xfer_other); + break; + + default: + if (xfer->error != USB_ERR_CANCELLED) { + UBT_WARN(sc, "clear stall transfer failed: %s, " \ + "flag=%#x\n", usb2_errstr(xfer->error), flag); + goto submit_other; + /* NOT REACHED */ + } + + NG_NODE_UNREF(node); /* cancelled */ + break; + } +} /* ubt_clear_stall_callback */ + /**************************************************************************** **************************************************************************** ** Glue From maksim.yevmenkin at gmail.com Mon Jan 26 13:10:40 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Mon Jan 26 13:10:46 2009 Subject: ng_ubt2 patch committed In-Reply-To: References: Message-ID: dear frebsd-bluetooth@ users with HPS blessing another updated/cleaned up version of the ng_ubt2 driver for usb2 was committed to -head. this version should properly handle stalled usb transfers (i.e. the system should not panic anymore). also the code is now (hopefully) easier to read/understand/maintain. please give it a try and let me know if there are any problems. i'm also interested to hear about your experiences with ng_ubt2 and how it stacks up to usb1 ng_ubt. i also would like to thank HPS for his patience while working with me on this. thanks, max ==== Author: emax Date: Mon Jan 26 20:59:41 2009 New Revision: 187741 URL: http://svn.freebsd.org/changeset/base/187741 Log: Clean up ng_ubt2. Get rid of excessive use of NG_NODE_REF/UNREF(). Make detach() completely synchronous. Properly handle stalled USB transfers (use internal mechanism instead of submitting own control transfers). Rename/remove a couple of variables and update comments. This work was done in close collaboration with HPS. Reviewed by: HPS Modified: head/sys/dev/usb2/bluetooth/ng_ubt2.c head/sys/dev/usb2/bluetooth/ng_ubt2_var.h From maksim.yevmenkin at gmail.com Fri Jan 30 14:30:07 2009 From: maksim.yevmenkin at gmail.com (Maksim Yevmenkin) Date: Fri Jan 30 14:30:13 2009 Subject: pan profile support in freebsd Message-ID: dear freebsd-bluetooth@ users, i'm pleased to announce that i've just imported btpand(8) daemon from netbsd. this daemon provides support for NAP, GN and PANU profiles. i've briefly tested it with a couple of PAN-enabled devices (i.e. my windows-xp laptop and old hp ipaq 1940 running windows ce 4.20). this is not yet connected to the build, but it is possible to build and run it. please give it a try and let me know if it works for you. ==== Author: emax Date: Fri Jan 30 22:23:21 2009 New Revision: 187938 URL: http://svn.freebsd.org/changeset/base/187938 Log: Add btpand(8) daemon from NetBSD. This daemon provides support for Bluetooth Network Access Point (NAP), Group Ad-hoc Network (GN) and Personal Area Network User (PANU) profiles. Obtained from: NetBSD MFC after: 1 month ==== thanks, max