102ac6454SAndrew Thompson /*- 202ac6454SAndrew Thompson * Copyright (c) 1996-2000 Whistle Communications, Inc. 302ac6454SAndrew Thompson * All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 3. Neither the name of author nor the names of its 1402ac6454SAndrew Thompson * contributors may be used to endorse or promote products derived 1502ac6454SAndrew Thompson * from this software without specific prior written permission. 1602ac6454SAndrew Thompson * 1702ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY NICK HIBMA AND CONTRIBUTORS 1802ac6454SAndrew Thompson * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 1902ac6454SAndrew Thompson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2002ac6454SAndrew Thompson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 2102ac6454SAndrew Thompson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2202ac6454SAndrew Thompson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2302ac6454SAndrew Thompson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2402ac6454SAndrew Thompson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2502ac6454SAndrew Thompson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2602ac6454SAndrew Thompson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2702ac6454SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE. 2802ac6454SAndrew Thompson * 2902ac6454SAndrew Thompson */ 3002ac6454SAndrew Thompson 3102ac6454SAndrew Thompson #include <sys/cdefs.h> 3202ac6454SAndrew Thompson __FBSDID("$FreeBSD$"); 3302ac6454SAndrew Thompson 3402ac6454SAndrew Thompson /* Driver for arbitrary double bulk pipe devices. 3502ac6454SAndrew Thompson * The driver assumes that there will be the same driver on the other side. 3602ac6454SAndrew Thompson * 3702ac6454SAndrew Thompson * XXX Some more information on what the framing of the IP packets looks like. 3802ac6454SAndrew Thompson * 3902ac6454SAndrew Thompson * To take full advantage of bulk transmission, packets should be chosen 4002ac6454SAndrew Thompson * between 1k and 5k in size (1k to make sure the sending side starts 4102ac6454SAndrew Thompson * streaming, and <5k to avoid overflowing the system with small TDs). 4202ac6454SAndrew Thompson */ 4302ac6454SAndrew Thompson 4402ac6454SAndrew Thompson 4502ac6454SAndrew Thompson /* probe/attach/detach: 4602ac6454SAndrew Thompson * Connect the driver to the hardware and netgraph 4702ac6454SAndrew Thompson * 4802ac6454SAndrew Thompson * The reason we submit a bulk in transfer is that USB does not know about 4902ac6454SAndrew Thompson * interrupts. The bulk transfer continuously polls the device for data. 5002ac6454SAndrew Thompson * While the device has no data available, the device NAKs the TDs. As soon 5102ac6454SAndrew Thompson * as there is data, the transfer happens and the data comes flowing in. 5202ac6454SAndrew Thompson * 5302ac6454SAndrew Thompson * In case you were wondering, interrupt transfers happen exactly that way. 5402ac6454SAndrew Thompson * It therefore doesn't make sense to use the interrupt pipe to signal 5502ac6454SAndrew Thompson * 'data ready' and then schedule a bulk transfer to fetch it. That would 5602ac6454SAndrew Thompson * incur a 2ms delay at least, without reducing bandwidth requirements. 5702ac6454SAndrew Thompson * 5802ac6454SAndrew Thompson */ 5902ac6454SAndrew Thompson 60ed6d949aSAndrew Thompson #include <sys/stdint.h> 61ed6d949aSAndrew Thompson #include <sys/stddef.h> 62ed6d949aSAndrew Thompson #include <sys/param.h> 63ed6d949aSAndrew Thompson #include <sys/queue.h> 64ed6d949aSAndrew Thompson #include <sys/types.h> 65ed6d949aSAndrew Thompson #include <sys/systm.h> 66ed6d949aSAndrew Thompson #include <sys/kernel.h> 67ed6d949aSAndrew Thompson #include <sys/bus.h> 68ed6d949aSAndrew Thompson #include <sys/linker_set.h> 69ed6d949aSAndrew Thompson #include <sys/module.h> 70ed6d949aSAndrew Thompson #include <sys/lock.h> 71ed6d949aSAndrew Thompson #include <sys/mutex.h> 72ed6d949aSAndrew Thompson #include <sys/condvar.h> 73ed6d949aSAndrew Thompson #include <sys/sysctl.h> 74ed6d949aSAndrew Thompson #include <sys/sx.h> 75ed6d949aSAndrew Thompson #include <sys/unistd.h> 76ed6d949aSAndrew Thompson #include <sys/callout.h> 77ed6d949aSAndrew Thompson #include <sys/malloc.h> 78ed6d949aSAndrew Thompson #include <sys/priv.h> 79ed6d949aSAndrew Thompson 8002ac6454SAndrew Thompson #include <dev/usb/usb.h> 81ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 82ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 83ed6d949aSAndrew Thompson #include "usbdevs.h" 8402ac6454SAndrew Thompson 8502ac6454SAndrew Thompson #define USB_DEBUG_VAR udbp_debug 8602ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 8702ac6454SAndrew Thompson 8802ac6454SAndrew Thompson #include <sys/mbuf.h> 8902ac6454SAndrew Thompson 9002ac6454SAndrew Thompson #include <netgraph/ng_message.h> 9102ac6454SAndrew Thompson #include <netgraph/netgraph.h> 9202ac6454SAndrew Thompson #include <netgraph/ng_parse.h> 9302ac6454SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h> 9402ac6454SAndrew Thompson 9502ac6454SAndrew Thompson #include <dev/usb/misc/udbp.h> 9602ac6454SAndrew Thompson 9702ac6454SAndrew Thompson #if USB_DEBUG 9802ac6454SAndrew Thompson static int udbp_debug = 0; 9902ac6454SAndrew Thompson 1009360ae40SAndrew Thompson SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp"); 1019360ae40SAndrew Thompson SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RW, 10202ac6454SAndrew Thompson &udbp_debug, 0, "udbp debug level"); 10302ac6454SAndrew Thompson #endif 10402ac6454SAndrew Thompson 10502ac6454SAndrew Thompson #define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in 10602ac6454SAndrew Thompson * msecs */ 10702ac6454SAndrew Thompson #define UDBP_BUFFERSIZE MCLBYTES /* maximum number of bytes in one 10802ac6454SAndrew Thompson * transfer */ 10902ac6454SAndrew Thompson #define UDBP_T_WR 0 11002ac6454SAndrew Thompson #define UDBP_T_RD 1 11102ac6454SAndrew Thompson #define UDBP_T_WR_CS 2 11202ac6454SAndrew Thompson #define UDBP_T_RD_CS 3 11302ac6454SAndrew Thompson #define UDBP_T_MAX 4 11402ac6454SAndrew Thompson #define UDBP_Q_MAXLEN 50 11502ac6454SAndrew Thompson 11602ac6454SAndrew Thompson struct udbp_softc { 11702ac6454SAndrew Thompson 11802ac6454SAndrew Thompson struct mtx sc_mtx; 11902ac6454SAndrew Thompson struct ng_bt_mbufq sc_xmitq_hipri; /* hi-priority transmit queue */ 12002ac6454SAndrew Thompson struct ng_bt_mbufq sc_xmitq; /* low-priority transmit queue */ 12102ac6454SAndrew Thompson 122760bc48eSAndrew Thompson struct usb_xfer *sc_xfer[UDBP_T_MAX]; 12302ac6454SAndrew Thompson node_p sc_node; /* back pointer to node */ 12402ac6454SAndrew Thompson hook_p sc_hook; /* pointer to the hook */ 12502ac6454SAndrew Thompson struct mbuf *sc_bulk_in_buffer; 12602ac6454SAndrew Thompson 12702ac6454SAndrew Thompson uint32_t sc_packets_in; /* packets in from downstream */ 12802ac6454SAndrew Thompson uint32_t sc_packets_out; /* packets out towards downstream */ 12902ac6454SAndrew Thompson 13002ac6454SAndrew Thompson uint8_t sc_flags; 13102ac6454SAndrew Thompson #define UDBP_FLAG_READ_STALL 0x01 /* read transfer stalled */ 13202ac6454SAndrew Thompson #define UDBP_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ 13302ac6454SAndrew Thompson 13402ac6454SAndrew Thompson uint8_t sc_name[16]; 13502ac6454SAndrew Thompson }; 13602ac6454SAndrew Thompson 13702ac6454SAndrew Thompson /* prototypes */ 13802ac6454SAndrew Thompson 13902ac6454SAndrew Thompson static int udbp_modload(module_t mod, int event, void *data); 14002ac6454SAndrew Thompson 14102ac6454SAndrew Thompson static device_probe_t udbp_probe; 14202ac6454SAndrew Thompson static device_attach_t udbp_attach; 14302ac6454SAndrew Thompson static device_detach_t udbp_detach; 14402ac6454SAndrew Thompson 145e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_read_callback; 146e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_read_clear_stall_callback; 147e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_write_callback; 148e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_write_clear_stall_callback; 14902ac6454SAndrew Thompson 15002ac6454SAndrew Thompson static void udbp_bulk_read_complete(node_p, hook_p, void *, int); 15102ac6454SAndrew Thompson 15202ac6454SAndrew Thompson static ng_constructor_t ng_udbp_constructor; 15302ac6454SAndrew Thompson static ng_rcvmsg_t ng_udbp_rcvmsg; 15402ac6454SAndrew Thompson static ng_shutdown_t ng_udbp_rmnode; 15502ac6454SAndrew Thompson static ng_newhook_t ng_udbp_newhook; 15602ac6454SAndrew Thompson static ng_connect_t ng_udbp_connect; 15702ac6454SAndrew Thompson static ng_rcvdata_t ng_udbp_rcvdata; 15802ac6454SAndrew Thompson static ng_disconnect_t ng_udbp_disconnect; 15902ac6454SAndrew Thompson 16002ac6454SAndrew Thompson /* Parse type for struct ngudbpstat */ 16102ac6454SAndrew Thompson static const struct ng_parse_struct_field 16202ac6454SAndrew Thompson ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO; 16302ac6454SAndrew Thompson 16402ac6454SAndrew Thompson static const struct ng_parse_type ng_udbp_stat_type = { 16502ac6454SAndrew Thompson &ng_parse_struct_type, 16602ac6454SAndrew Thompson &ng_udbp_stat_type_fields 16702ac6454SAndrew Thompson }; 16802ac6454SAndrew Thompson 16902ac6454SAndrew Thompson /* List of commands and how to convert arguments to/from ASCII */ 17002ac6454SAndrew Thompson static const struct ng_cmdlist ng_udbp_cmdlist[] = { 17102ac6454SAndrew Thompson { 17202ac6454SAndrew Thompson NGM_UDBP_COOKIE, 17302ac6454SAndrew Thompson NGM_UDBP_GET_STATUS, 17402ac6454SAndrew Thompson "getstatus", 17502ac6454SAndrew Thompson NULL, 17602ac6454SAndrew Thompson &ng_udbp_stat_type, 17702ac6454SAndrew Thompson }, 17802ac6454SAndrew Thompson { 17902ac6454SAndrew Thompson NGM_UDBP_COOKIE, 18002ac6454SAndrew Thompson NGM_UDBP_SET_FLAG, 18102ac6454SAndrew Thompson "setflag", 18202ac6454SAndrew Thompson &ng_parse_int32_type, 18302ac6454SAndrew Thompson NULL 18402ac6454SAndrew Thompson }, 18502ac6454SAndrew Thompson {0} 18602ac6454SAndrew Thompson }; 18702ac6454SAndrew Thompson 18802ac6454SAndrew Thompson /* Netgraph node type descriptor */ 18902ac6454SAndrew Thompson static struct ng_type ng_udbp_typestruct = { 19002ac6454SAndrew Thompson .version = NG_ABI_VERSION, 19102ac6454SAndrew Thompson .name = NG_UDBP_NODE_TYPE, 19202ac6454SAndrew Thompson .constructor = ng_udbp_constructor, 19302ac6454SAndrew Thompson .rcvmsg = ng_udbp_rcvmsg, 19402ac6454SAndrew Thompson .shutdown = ng_udbp_rmnode, 19502ac6454SAndrew Thompson .newhook = ng_udbp_newhook, 19602ac6454SAndrew Thompson .connect = ng_udbp_connect, 19702ac6454SAndrew Thompson .rcvdata = ng_udbp_rcvdata, 19802ac6454SAndrew Thompson .disconnect = ng_udbp_disconnect, 19902ac6454SAndrew Thompson .cmdlist = ng_udbp_cmdlist, 20002ac6454SAndrew Thompson }; 20102ac6454SAndrew Thompson 20202ac6454SAndrew Thompson /* USB config */ 203760bc48eSAndrew Thompson static const struct usb_config udbp_config[UDBP_T_MAX] = { 20402ac6454SAndrew Thompson 20502ac6454SAndrew Thompson [UDBP_T_WR] = { 20602ac6454SAndrew Thompson .type = UE_BULK, 20702ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 20802ac6454SAndrew Thompson .direction = UE_DIR_OUT, 2094eae601eSAndrew Thompson .bufsize = UDBP_BUFFERSIZE, 2104eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 2114eae601eSAndrew Thompson .callback = &udbp_bulk_write_callback, 2124eae601eSAndrew Thompson .timeout = UDBP_TIMEOUT, 21302ac6454SAndrew Thompson }, 21402ac6454SAndrew Thompson 21502ac6454SAndrew Thompson [UDBP_T_RD] = { 21602ac6454SAndrew Thompson .type = UE_BULK, 21702ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 21802ac6454SAndrew Thompson .direction = UE_DIR_IN, 2194eae601eSAndrew Thompson .bufsize = UDBP_BUFFERSIZE, 2204eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 2214eae601eSAndrew Thompson .callback = &udbp_bulk_read_callback, 22202ac6454SAndrew Thompson }, 22302ac6454SAndrew Thompson 22402ac6454SAndrew Thompson [UDBP_T_WR_CS] = { 22502ac6454SAndrew Thompson .type = UE_CONTROL, 22602ac6454SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 22702ac6454SAndrew Thompson .direction = UE_DIR_ANY, 228760bc48eSAndrew Thompson .bufsize = sizeof(struct usb_device_request), 2294eae601eSAndrew Thompson .callback = &udbp_bulk_write_clear_stall_callback, 2304eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 2314eae601eSAndrew Thompson .interval = 50, /* 50ms */ 23202ac6454SAndrew Thompson }, 23302ac6454SAndrew Thompson 23402ac6454SAndrew Thompson [UDBP_T_RD_CS] = { 23502ac6454SAndrew Thompson .type = UE_CONTROL, 23602ac6454SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 23702ac6454SAndrew Thompson .direction = UE_DIR_ANY, 238760bc48eSAndrew Thompson .bufsize = sizeof(struct usb_device_request), 2394eae601eSAndrew Thompson .callback = &udbp_bulk_read_clear_stall_callback, 2404eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 2414eae601eSAndrew Thompson .interval = 50, /* 50ms */ 24202ac6454SAndrew Thompson }, 24302ac6454SAndrew Thompson }; 24402ac6454SAndrew Thompson 24502ac6454SAndrew Thompson static devclass_t udbp_devclass; 24602ac6454SAndrew Thompson 24702ac6454SAndrew Thompson static device_method_t udbp_methods[] = { 24802ac6454SAndrew Thompson /* Device interface */ 24902ac6454SAndrew Thompson DEVMETHOD(device_probe, udbp_probe), 25002ac6454SAndrew Thompson DEVMETHOD(device_attach, udbp_attach), 25102ac6454SAndrew Thompson DEVMETHOD(device_detach, udbp_detach), 25202ac6454SAndrew Thompson {0, 0} 25302ac6454SAndrew Thompson }; 25402ac6454SAndrew Thompson 25502ac6454SAndrew Thompson static driver_t udbp_driver = { 25602ac6454SAndrew Thompson .name = "udbp", 25702ac6454SAndrew Thompson .methods = udbp_methods, 25802ac6454SAndrew Thompson .size = sizeof(struct udbp_softc), 25902ac6454SAndrew Thompson }; 26002ac6454SAndrew Thompson 2619aef556dSAndrew Thompson DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_devclass, udbp_modload, 0); 26202ac6454SAndrew Thompson MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 26302ac6454SAndrew Thompson MODULE_DEPEND(udbp, usb, 1, 1, 1); 26402ac6454SAndrew Thompson 26502ac6454SAndrew Thompson static int 26602ac6454SAndrew Thompson udbp_modload(module_t mod, int event, void *data) 26702ac6454SAndrew Thompson { 26802ac6454SAndrew Thompson int error; 26902ac6454SAndrew Thompson 27002ac6454SAndrew Thompson switch (event) { 27102ac6454SAndrew Thompson case MOD_LOAD: 27202ac6454SAndrew Thompson error = ng_newtype(&ng_udbp_typestruct); 27302ac6454SAndrew Thompson if (error != 0) { 27402ac6454SAndrew Thompson printf("%s: Could not register " 27502ac6454SAndrew Thompson "Netgraph node type, error=%d\n", 27602ac6454SAndrew Thompson NG_UDBP_NODE_TYPE, error); 27702ac6454SAndrew Thompson } 27802ac6454SAndrew Thompson break; 27902ac6454SAndrew Thompson 28002ac6454SAndrew Thompson case MOD_UNLOAD: 28102ac6454SAndrew Thompson error = ng_rmtype(&ng_udbp_typestruct); 28202ac6454SAndrew Thompson break; 28302ac6454SAndrew Thompson 28402ac6454SAndrew Thompson default: 28502ac6454SAndrew Thompson error = EOPNOTSUPP; 28602ac6454SAndrew Thompson break; 28702ac6454SAndrew Thompson } 28802ac6454SAndrew Thompson return (error); 28902ac6454SAndrew Thompson } 29002ac6454SAndrew Thompson 29102ac6454SAndrew Thompson static int 29202ac6454SAndrew Thompson udbp_probe(device_t dev) 29302ac6454SAndrew Thompson { 294760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 29502ac6454SAndrew Thompson 296f29a0724SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST) { 29702ac6454SAndrew Thompson return (ENXIO); 29802ac6454SAndrew Thompson } 29902ac6454SAndrew Thompson /* 30002ac6454SAndrew Thompson * XXX Julian, add the id of the device if you have one to test 30102ac6454SAndrew Thompson * things with. run 'usbdevs -v' and note the 3 ID's that appear. 30202ac6454SAndrew Thompson * The Vendor Id and Product Id are in hex and the Revision Id is in 30302ac6454SAndrew Thompson * bcd. But as usual if the revision is 0x101 then you should 30402ac6454SAndrew Thompson * compare the revision id in the device descriptor with 0x101 Or go 30502ac6454SAndrew Thompson * search the file usbdevs.h. Maybe the device is already in there. 30602ac6454SAndrew Thompson */ 30702ac6454SAndrew Thompson if (((uaa->info.idVendor == USB_VENDOR_NETCHIP) && 30802ac6454SAndrew Thompson (uaa->info.idProduct == USB_PRODUCT_NETCHIP_TURBOCONNECT))) 30902ac6454SAndrew Thompson return (0); 31002ac6454SAndrew Thompson 31102ac6454SAndrew Thompson if (((uaa->info.idVendor == USB_VENDOR_PROLIFIC) && 31202ac6454SAndrew Thompson ((uaa->info.idProduct == USB_PRODUCT_PROLIFIC_PL2301) || 31302ac6454SAndrew Thompson (uaa->info.idProduct == USB_PRODUCT_PROLIFIC_PL2302)))) 31402ac6454SAndrew Thompson return (0); 31502ac6454SAndrew Thompson 31602ac6454SAndrew Thompson if ((uaa->info.idVendor == USB_VENDOR_ANCHOR) && 31702ac6454SAndrew Thompson (uaa->info.idProduct == USB_PRODUCT_ANCHOR_EZLINK)) 31802ac6454SAndrew Thompson return (0); 31902ac6454SAndrew Thompson 32002ac6454SAndrew Thompson if ((uaa->info.idVendor == USB_VENDOR_GENESYS) && 32102ac6454SAndrew Thompson (uaa->info.idProduct == USB_PRODUCT_GENESYS_GL620USB)) 32202ac6454SAndrew Thompson return (0); 32302ac6454SAndrew Thompson 32402ac6454SAndrew Thompson return (ENXIO); 32502ac6454SAndrew Thompson } 32602ac6454SAndrew Thompson 32702ac6454SAndrew Thompson static int 32802ac6454SAndrew Thompson udbp_attach(device_t dev) 32902ac6454SAndrew Thompson { 330760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 33102ac6454SAndrew Thompson struct udbp_softc *sc = device_get_softc(dev); 33202ac6454SAndrew Thompson int error; 33302ac6454SAndrew Thompson 334a593f6b8SAndrew Thompson device_set_usb_desc(dev); 33502ac6454SAndrew Thompson 33602ac6454SAndrew Thompson snprintf(sc->sc_name, sizeof(sc->sc_name), 33702ac6454SAndrew Thompson "%s", device_get_nameunit(dev)); 33802ac6454SAndrew Thompson 33902ac6454SAndrew Thompson mtx_init(&sc->sc_mtx, "udbp lock", NULL, MTX_DEF | MTX_RECURSE); 34002ac6454SAndrew Thompson 341a593f6b8SAndrew Thompson error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 34202ac6454SAndrew Thompson sc->sc_xfer, udbp_config, UDBP_T_MAX, sc, &sc->sc_mtx); 34302ac6454SAndrew Thompson if (error) { 344a593f6b8SAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 34502ac6454SAndrew Thompson goto detach; 34602ac6454SAndrew Thompson } 34702ac6454SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_xmitq, UDBP_Q_MAXLEN); 34802ac6454SAndrew Thompson 34902ac6454SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_xmitq_hipri, UDBP_Q_MAXLEN); 35002ac6454SAndrew Thompson 35102ac6454SAndrew Thompson /* create Netgraph node */ 35202ac6454SAndrew Thompson 35302ac6454SAndrew Thompson if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { 35402ac6454SAndrew Thompson printf("%s: Could not create Netgraph node\n", 35502ac6454SAndrew Thompson sc->sc_name); 35602ac6454SAndrew Thompson sc->sc_node = NULL; 35702ac6454SAndrew Thompson goto detach; 35802ac6454SAndrew Thompson } 35902ac6454SAndrew Thompson /* name node */ 36002ac6454SAndrew Thompson 36102ac6454SAndrew Thompson if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { 36202ac6454SAndrew Thompson printf("%s: Could not name node\n", 36302ac6454SAndrew Thompson sc->sc_name); 36402ac6454SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 36502ac6454SAndrew Thompson sc->sc_node = NULL; 36602ac6454SAndrew Thompson goto detach; 36702ac6454SAndrew Thompson } 36802ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 36902ac6454SAndrew Thompson 37002ac6454SAndrew Thompson /* the device is now operational */ 37102ac6454SAndrew Thompson 37202ac6454SAndrew Thompson return (0); /* success */ 37302ac6454SAndrew Thompson 37402ac6454SAndrew Thompson detach: 37502ac6454SAndrew Thompson udbp_detach(dev); 37602ac6454SAndrew Thompson return (ENOMEM); /* failure */ 37702ac6454SAndrew Thompson } 37802ac6454SAndrew Thompson 37902ac6454SAndrew Thompson static int 38002ac6454SAndrew Thompson udbp_detach(device_t dev) 38102ac6454SAndrew Thompson { 38202ac6454SAndrew Thompson struct udbp_softc *sc = device_get_softc(dev); 38302ac6454SAndrew Thompson 38402ac6454SAndrew Thompson /* destroy Netgraph node */ 38502ac6454SAndrew Thompson 38602ac6454SAndrew Thompson if (sc->sc_node != NULL) { 38702ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, NULL); 38802ac6454SAndrew Thompson ng_rmnode_self(sc->sc_node); 38902ac6454SAndrew Thompson sc->sc_node = NULL; 39002ac6454SAndrew Thompson } 39102ac6454SAndrew Thompson /* free USB transfers, if any */ 39202ac6454SAndrew Thompson 393a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UDBP_T_MAX); 39402ac6454SAndrew Thompson 39502ac6454SAndrew Thompson mtx_destroy(&sc->sc_mtx); 39602ac6454SAndrew Thompson 39702ac6454SAndrew Thompson /* destroy queues */ 39802ac6454SAndrew Thompson 39902ac6454SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq); 40002ac6454SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq_hipri); 40102ac6454SAndrew Thompson 40202ac6454SAndrew Thompson /* extra check */ 40302ac6454SAndrew Thompson 40402ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 40502ac6454SAndrew Thompson m_freem(sc->sc_bulk_in_buffer); 40602ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 40702ac6454SAndrew Thompson } 40802ac6454SAndrew Thompson return (0); /* success */ 40902ac6454SAndrew Thompson } 41002ac6454SAndrew Thompson 41102ac6454SAndrew Thompson static void 412ed6d949aSAndrew Thompson udbp_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 41302ac6454SAndrew Thompson { 414ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 415ed6d949aSAndrew Thompson struct usb_page_cache *pc; 41602ac6454SAndrew Thompson struct mbuf *m; 417ed6d949aSAndrew Thompson int actlen; 418ed6d949aSAndrew Thompson 419ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 42002ac6454SAndrew Thompson 42102ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 42202ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 42302ac6454SAndrew Thompson 42402ac6454SAndrew Thompson /* allocate new mbuf */ 42502ac6454SAndrew Thompson 42602ac6454SAndrew Thompson MGETHDR(m, M_DONTWAIT, MT_DATA); 42702ac6454SAndrew Thompson 42802ac6454SAndrew Thompson if (m == NULL) { 42902ac6454SAndrew Thompson goto tr_setup; 43002ac6454SAndrew Thompson } 43102ac6454SAndrew Thompson MCLGET(m, M_DONTWAIT); 43202ac6454SAndrew Thompson 43302ac6454SAndrew Thompson if (!(m->m_flags & M_EXT)) { 43402ac6454SAndrew Thompson m_freem(m); 43502ac6454SAndrew Thompson goto tr_setup; 43602ac6454SAndrew Thompson } 437ed6d949aSAndrew Thompson m->m_pkthdr.len = m->m_len = actlen; 43802ac6454SAndrew Thompson 439ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 440ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, m->m_data, actlen); 44102ac6454SAndrew Thompson 44202ac6454SAndrew Thompson sc->sc_bulk_in_buffer = m; 44302ac6454SAndrew Thompson 444ed6d949aSAndrew Thompson DPRINTF("received package %d bytes\n", actlen); 44502ac6454SAndrew Thompson 44602ac6454SAndrew Thompson case USB_ST_SETUP: 44702ac6454SAndrew Thompson tr_setup: 44802ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 44902ac6454SAndrew Thompson ng_send_fn(sc->sc_node, NULL, &udbp_bulk_read_complete, NULL, 0); 45002ac6454SAndrew Thompson return; 45102ac6454SAndrew Thompson } 45202ac6454SAndrew Thompson if (sc->sc_flags & UDBP_FLAG_READ_STALL) { 453a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); 45402ac6454SAndrew Thompson return; 45502ac6454SAndrew Thompson } 456ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 457a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 45802ac6454SAndrew Thompson return; 45902ac6454SAndrew Thompson 46002ac6454SAndrew Thompson default: /* Error */ 461ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 46202ac6454SAndrew Thompson /* try to clear stall first */ 46302ac6454SAndrew Thompson sc->sc_flags |= UDBP_FLAG_READ_STALL; 464a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); 46502ac6454SAndrew Thompson } 46602ac6454SAndrew Thompson return; 46702ac6454SAndrew Thompson 46802ac6454SAndrew Thompson } 46902ac6454SAndrew Thompson } 47002ac6454SAndrew Thompson 47102ac6454SAndrew Thompson static void 472ed6d949aSAndrew Thompson udbp_bulk_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 47302ac6454SAndrew Thompson { 474ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 475760bc48eSAndrew Thompson struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_RD]; 47602ac6454SAndrew Thompson 477a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 47802ac6454SAndrew Thompson DPRINTF("stall cleared\n"); 47902ac6454SAndrew Thompson sc->sc_flags &= ~UDBP_FLAG_READ_STALL; 480a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 48102ac6454SAndrew Thompson } 48202ac6454SAndrew Thompson } 48302ac6454SAndrew Thompson 48402ac6454SAndrew Thompson static void 48502ac6454SAndrew Thompson udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2) 48602ac6454SAndrew Thompson { 48702ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 48802ac6454SAndrew Thompson struct mbuf *m; 48902ac6454SAndrew Thompson int error; 49002ac6454SAndrew Thompson 49102ac6454SAndrew Thompson if (sc == NULL) { 49202ac6454SAndrew Thompson return; 49302ac6454SAndrew Thompson } 49402ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 49502ac6454SAndrew Thompson 49602ac6454SAndrew Thompson m = sc->sc_bulk_in_buffer; 49702ac6454SAndrew Thompson 49802ac6454SAndrew Thompson if (m) { 49902ac6454SAndrew Thompson 50002ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 50102ac6454SAndrew Thompson 50202ac6454SAndrew Thompson if ((sc->sc_hook == NULL) || 50302ac6454SAndrew Thompson NG_HOOK_NOT_VALID(sc->sc_hook)) { 50402ac6454SAndrew Thompson DPRINTF("No upstream hook\n"); 50502ac6454SAndrew Thompson goto done; 50602ac6454SAndrew Thompson } 50702ac6454SAndrew Thompson sc->sc_packets_in++; 50802ac6454SAndrew Thompson 50902ac6454SAndrew Thompson NG_SEND_DATA_ONLY(error, sc->sc_hook, m); 51002ac6454SAndrew Thompson 51102ac6454SAndrew Thompson m = NULL; 51202ac6454SAndrew Thompson } 51302ac6454SAndrew Thompson done: 51402ac6454SAndrew Thompson if (m) { 51502ac6454SAndrew Thompson m_freem(m); 51602ac6454SAndrew Thompson } 51702ac6454SAndrew Thompson /* start USB bulk-in transfer, if not already started */ 51802ac6454SAndrew Thompson 519a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); 52002ac6454SAndrew Thompson 52102ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 52202ac6454SAndrew Thompson } 52302ac6454SAndrew Thompson 52402ac6454SAndrew Thompson static void 525ed6d949aSAndrew Thompson udbp_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 52602ac6454SAndrew Thompson { 527ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 528ed6d949aSAndrew Thompson struct usb_page_cache *pc; 52902ac6454SAndrew Thompson struct mbuf *m; 53002ac6454SAndrew Thompson 53102ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 53202ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 53302ac6454SAndrew Thompson 53402ac6454SAndrew Thompson sc->sc_packets_out++; 53502ac6454SAndrew Thompson 53602ac6454SAndrew Thompson case USB_ST_SETUP: 53702ac6454SAndrew Thompson if (sc->sc_flags & UDBP_FLAG_WRITE_STALL) { 538a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); 53902ac6454SAndrew Thompson return; 54002ac6454SAndrew Thompson } 54102ac6454SAndrew Thompson /* get next mbuf, if any */ 54202ac6454SAndrew Thompson 54302ac6454SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq_hipri, m); 54402ac6454SAndrew Thompson if (m == NULL) { 54502ac6454SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq, m); 54602ac6454SAndrew Thompson if (m == NULL) { 54702ac6454SAndrew Thompson DPRINTF("Data queue is empty\n"); 54802ac6454SAndrew Thompson return; 54902ac6454SAndrew Thompson } 55002ac6454SAndrew Thompson } 55102ac6454SAndrew Thompson if (m->m_pkthdr.len > MCLBYTES) { 55202ac6454SAndrew Thompson DPRINTF("truncating large packet " 55302ac6454SAndrew Thompson "from %d to %d bytes\n", m->m_pkthdr.len, 55402ac6454SAndrew Thompson MCLBYTES); 55502ac6454SAndrew Thompson m->m_pkthdr.len = MCLBYTES; 55602ac6454SAndrew Thompson } 557ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 558ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 55902ac6454SAndrew Thompson 560ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 561ed6d949aSAndrew Thompson 562ed6d949aSAndrew Thompson DPRINTF("packet out: %d bytes\n", m->m_pkthdr.len); 56302ac6454SAndrew Thompson 56402ac6454SAndrew Thompson m_freem(m); 56502ac6454SAndrew Thompson 566a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 56702ac6454SAndrew Thompson return; 56802ac6454SAndrew Thompson 56902ac6454SAndrew Thompson default: /* Error */ 570ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 57102ac6454SAndrew Thompson /* try to clear stall first */ 57202ac6454SAndrew Thompson sc->sc_flags |= UDBP_FLAG_WRITE_STALL; 573a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); 57402ac6454SAndrew Thompson } 57502ac6454SAndrew Thompson return; 57602ac6454SAndrew Thompson 57702ac6454SAndrew Thompson } 57802ac6454SAndrew Thompson } 57902ac6454SAndrew Thompson 58002ac6454SAndrew Thompson static void 581ed6d949aSAndrew Thompson udbp_bulk_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 58202ac6454SAndrew Thompson { 583ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 584760bc48eSAndrew Thompson struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_WR]; 58502ac6454SAndrew Thompson 586a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 58702ac6454SAndrew Thompson DPRINTF("stall cleared\n"); 58802ac6454SAndrew Thompson sc->sc_flags &= ~UDBP_FLAG_WRITE_STALL; 589a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 59002ac6454SAndrew Thompson } 59102ac6454SAndrew Thompson } 59202ac6454SAndrew Thompson 59302ac6454SAndrew Thompson /*********************************************************************** 59402ac6454SAndrew Thompson * Start of Netgraph methods 59502ac6454SAndrew Thompson **********************************************************************/ 59602ac6454SAndrew Thompson 59702ac6454SAndrew Thompson /* 59802ac6454SAndrew Thompson * If this is a device node so this work is done in the attach() 59902ac6454SAndrew Thompson * routine and the constructor will return EINVAL as you should not be able 60002ac6454SAndrew Thompson * to create nodes that depend on hardware (unless you can add the hardware :) 60102ac6454SAndrew Thompson */ 60202ac6454SAndrew Thompson static int 60302ac6454SAndrew Thompson ng_udbp_constructor(node_p node) 60402ac6454SAndrew Thompson { 60502ac6454SAndrew Thompson return (EINVAL); 60602ac6454SAndrew Thompson } 60702ac6454SAndrew Thompson 60802ac6454SAndrew Thompson /* 60902ac6454SAndrew Thompson * Give our ok for a hook to be added... 61002ac6454SAndrew Thompson * If we are not running this might kick a device into life. 61102ac6454SAndrew Thompson * Possibly decode information out of the hook name. 61202ac6454SAndrew Thompson * Add the hook's private info to the hook structure. 61302ac6454SAndrew Thompson * (if we had some). In this example, we assume that there is a 61402ac6454SAndrew Thompson * an array of structs, called 'channel' in the private info, 61502ac6454SAndrew Thompson * one for each active channel. The private 61602ac6454SAndrew Thompson * pointer of each hook points to the appropriate UDBP_hookinfo struct 61702ac6454SAndrew Thompson * so that the source of an input packet is easily identified. 61802ac6454SAndrew Thompson */ 61902ac6454SAndrew Thompson static int 62002ac6454SAndrew Thompson ng_udbp_newhook(node_p node, hook_p hook, const char *name) 62102ac6454SAndrew Thompson { 62202ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 62302ac6454SAndrew Thompson int32_t error = 0; 62402ac6454SAndrew Thompson 62502ac6454SAndrew Thompson if (strcmp(name, NG_UDBP_HOOK_NAME)) { 62602ac6454SAndrew Thompson return (EINVAL); 62702ac6454SAndrew Thompson } 62802ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 62902ac6454SAndrew Thompson 63002ac6454SAndrew Thompson if (sc->sc_hook != NULL) { 63102ac6454SAndrew Thompson error = EISCONN; 63202ac6454SAndrew Thompson } else { 63302ac6454SAndrew Thompson sc->sc_hook = hook; 63402ac6454SAndrew Thompson NG_HOOK_SET_PRIVATE(hook, NULL); 63502ac6454SAndrew Thompson } 63602ac6454SAndrew Thompson 63702ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 63802ac6454SAndrew Thompson 63902ac6454SAndrew Thompson return (error); 64002ac6454SAndrew Thompson } 64102ac6454SAndrew Thompson 64202ac6454SAndrew Thompson /* 64302ac6454SAndrew Thompson * Get a netgraph control message. 64402ac6454SAndrew Thompson * Check it is one we understand. If needed, send a response. 64502ac6454SAndrew Thompson * We could save the address for an async action later, but don't here. 64602ac6454SAndrew Thompson * Always free the message. 64702ac6454SAndrew Thompson * The response should be in a malloc'd region that the caller can 'free'. 64802ac6454SAndrew Thompson * A response is not required. 64902ac6454SAndrew Thompson * Theoretically you could respond defferently to old message types if 65002ac6454SAndrew Thompson * the cookie in the header didn't match what we consider to be current 65102ac6454SAndrew Thompson * (so that old userland programs could continue to work). 65202ac6454SAndrew Thompson */ 65302ac6454SAndrew Thompson static int 65402ac6454SAndrew Thompson ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook) 65502ac6454SAndrew Thompson { 65602ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 65702ac6454SAndrew Thompson struct ng_mesg *resp = NULL; 65802ac6454SAndrew Thompson int error = 0; 65902ac6454SAndrew Thompson struct ng_mesg *msg; 66002ac6454SAndrew Thompson 66102ac6454SAndrew Thompson NGI_GET_MSG(item, msg); 66202ac6454SAndrew Thompson /* Deal with message according to cookie and command */ 66302ac6454SAndrew Thompson switch (msg->header.typecookie) { 66402ac6454SAndrew Thompson case NGM_UDBP_COOKIE: 66502ac6454SAndrew Thompson switch (msg->header.cmd) { 66602ac6454SAndrew Thompson case NGM_UDBP_GET_STATUS: 66702ac6454SAndrew Thompson { 66802ac6454SAndrew Thompson struct ngudbpstat *stats; 66902ac6454SAndrew Thompson 67002ac6454SAndrew Thompson NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 67102ac6454SAndrew Thompson if (!resp) { 67202ac6454SAndrew Thompson error = ENOMEM; 67302ac6454SAndrew Thompson break; 67402ac6454SAndrew Thompson } 67502ac6454SAndrew Thompson stats = (struct ngudbpstat *)resp->data; 67602ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 67702ac6454SAndrew Thompson stats->packets_in = sc->sc_packets_in; 67802ac6454SAndrew Thompson stats->packets_out = sc->sc_packets_out; 67902ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 68002ac6454SAndrew Thompson break; 68102ac6454SAndrew Thompson } 68202ac6454SAndrew Thompson case NGM_UDBP_SET_FLAG: 68302ac6454SAndrew Thompson if (msg->header.arglen != sizeof(uint32_t)) { 68402ac6454SAndrew Thompson error = EINVAL; 68502ac6454SAndrew Thompson break; 68602ac6454SAndrew Thompson } 68702ac6454SAndrew Thompson DPRINTF("flags = 0x%08x\n", 68802ac6454SAndrew Thompson *((uint32_t *)msg->data)); 68902ac6454SAndrew Thompson break; 69002ac6454SAndrew Thompson default: 69102ac6454SAndrew Thompson error = EINVAL; /* unknown command */ 69202ac6454SAndrew Thompson break; 69302ac6454SAndrew Thompson } 69402ac6454SAndrew Thompson break; 69502ac6454SAndrew Thompson default: 69602ac6454SAndrew Thompson error = EINVAL; /* unknown cookie type */ 69702ac6454SAndrew Thompson break; 69802ac6454SAndrew Thompson } 69902ac6454SAndrew Thompson 70002ac6454SAndrew Thompson /* Take care of synchronous response, if any */ 70102ac6454SAndrew Thompson NG_RESPOND_MSG(error, node, item, resp); 70202ac6454SAndrew Thompson NG_FREE_MSG(msg); 70302ac6454SAndrew Thompson return (error); 70402ac6454SAndrew Thompson } 70502ac6454SAndrew Thompson 70602ac6454SAndrew Thompson /* 70702ac6454SAndrew Thompson * Accept data from the hook and queue it for output. 70802ac6454SAndrew Thompson */ 70902ac6454SAndrew Thompson static int 71002ac6454SAndrew Thompson ng_udbp_rcvdata(hook_p hook, item_p item) 71102ac6454SAndrew Thompson { 71202ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 71302ac6454SAndrew Thompson struct ng_bt_mbufq *queue_ptr; 71402ac6454SAndrew Thompson struct mbuf *m; 71502ac6454SAndrew Thompson struct ng_tag_prio *ptag; 71602ac6454SAndrew Thompson int error; 71702ac6454SAndrew Thompson 71802ac6454SAndrew Thompson if (sc == NULL) { 71902ac6454SAndrew Thompson NG_FREE_ITEM(item); 72002ac6454SAndrew Thompson return (EHOSTDOWN); 72102ac6454SAndrew Thompson } 72202ac6454SAndrew Thompson NGI_GET_M(item, m); 72302ac6454SAndrew Thompson NG_FREE_ITEM(item); 72402ac6454SAndrew Thompson 72502ac6454SAndrew Thompson /* 72602ac6454SAndrew Thompson * Now queue the data for when it can be sent 72702ac6454SAndrew Thompson */ 72802ac6454SAndrew Thompson ptag = (void *)m_tag_locate(m, NGM_GENERIC_COOKIE, 72902ac6454SAndrew Thompson NG_TAG_PRIO, NULL); 73002ac6454SAndrew Thompson 73102ac6454SAndrew Thompson if (ptag && (ptag->priority > NG_PRIO_CUTOFF)) 73202ac6454SAndrew Thompson queue_ptr = &sc->sc_xmitq_hipri; 73302ac6454SAndrew Thompson else 73402ac6454SAndrew Thompson queue_ptr = &sc->sc_xmitq; 73502ac6454SAndrew Thompson 73602ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 73702ac6454SAndrew Thompson 73802ac6454SAndrew Thompson if (NG_BT_MBUFQ_FULL(queue_ptr)) { 73902ac6454SAndrew Thompson NG_BT_MBUFQ_DROP(queue_ptr); 74002ac6454SAndrew Thompson NG_FREE_M(m); 74102ac6454SAndrew Thompson error = ENOBUFS; 74202ac6454SAndrew Thompson } else { 74302ac6454SAndrew Thompson NG_BT_MBUFQ_ENQUEUE(queue_ptr, m); 74402ac6454SAndrew Thompson /* 74502ac6454SAndrew Thompson * start bulk-out transfer, if not already started: 74602ac6454SAndrew Thompson */ 747a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); 74802ac6454SAndrew Thompson error = 0; 74902ac6454SAndrew Thompson } 75002ac6454SAndrew Thompson 75102ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 75202ac6454SAndrew Thompson 75302ac6454SAndrew Thompson return (error); 75402ac6454SAndrew Thompson } 75502ac6454SAndrew Thompson 75602ac6454SAndrew Thompson /* 75702ac6454SAndrew Thompson * Do local shutdown processing.. 75802ac6454SAndrew Thompson * We are a persistant device, we refuse to go away, and 75902ac6454SAndrew Thompson * only remove our links and reset ourself. 76002ac6454SAndrew Thompson */ 76102ac6454SAndrew Thompson static int 76202ac6454SAndrew Thompson ng_udbp_rmnode(node_p node) 76302ac6454SAndrew Thompson { 76402ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 76502ac6454SAndrew Thompson 76602ac6454SAndrew Thompson /* Let old node go */ 76702ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(node, NULL); 76802ac6454SAndrew Thompson NG_NODE_UNREF(node); /* forget it ever existed */ 76902ac6454SAndrew Thompson 77002ac6454SAndrew Thompson if (sc == NULL) { 77102ac6454SAndrew Thompson goto done; 77202ac6454SAndrew Thompson } 77302ac6454SAndrew Thompson /* Create Netgraph node */ 77402ac6454SAndrew Thompson if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { 77502ac6454SAndrew Thompson printf("%s: Could not create Netgraph node\n", 77602ac6454SAndrew Thompson sc->sc_name); 77702ac6454SAndrew Thompson sc->sc_node = NULL; 77802ac6454SAndrew Thompson goto done; 77902ac6454SAndrew Thompson } 78002ac6454SAndrew Thompson /* Name node */ 78102ac6454SAndrew Thompson if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { 78202ac6454SAndrew Thompson printf("%s: Could not name Netgraph node\n", 78302ac6454SAndrew Thompson sc->sc_name); 78402ac6454SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 78502ac6454SAndrew Thompson sc->sc_node = NULL; 78602ac6454SAndrew Thompson goto done; 78702ac6454SAndrew Thompson } 78802ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 78902ac6454SAndrew Thompson 79002ac6454SAndrew Thompson done: 79102ac6454SAndrew Thompson if (sc) { 79202ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 79302ac6454SAndrew Thompson } 79402ac6454SAndrew Thompson return (0); 79502ac6454SAndrew Thompson } 79602ac6454SAndrew Thompson 79702ac6454SAndrew Thompson /* 79802ac6454SAndrew Thompson * This is called once we've already connected a new hook to the other node. 79902ac6454SAndrew Thompson * It gives us a chance to balk at the last minute. 80002ac6454SAndrew Thompson */ 80102ac6454SAndrew Thompson static int 80202ac6454SAndrew Thompson ng_udbp_connect(hook_p hook) 80302ac6454SAndrew Thompson { 80402ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 80502ac6454SAndrew Thompson 80602ac6454SAndrew Thompson /* probably not at splnet, force outward queueing */ 80702ac6454SAndrew Thompson NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 80802ac6454SAndrew Thompson 80902ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 81002ac6454SAndrew Thompson 81102ac6454SAndrew Thompson sc->sc_flags |= (UDBP_FLAG_READ_STALL | 81202ac6454SAndrew Thompson UDBP_FLAG_WRITE_STALL); 81302ac6454SAndrew Thompson 81402ac6454SAndrew Thompson /* start bulk-in transfer */ 815a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); 81602ac6454SAndrew Thompson 81702ac6454SAndrew Thompson /* start bulk-out transfer */ 818a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); 81902ac6454SAndrew Thompson 82002ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 82102ac6454SAndrew Thompson 82202ac6454SAndrew Thompson return (0); 82302ac6454SAndrew Thompson } 82402ac6454SAndrew Thompson 82502ac6454SAndrew Thompson /* 82602ac6454SAndrew Thompson * Dook disconnection 82702ac6454SAndrew Thompson * 82802ac6454SAndrew Thompson * For this type, removal of the last link destroys the node 82902ac6454SAndrew Thompson */ 83002ac6454SAndrew Thompson static int 83102ac6454SAndrew Thompson ng_udbp_disconnect(hook_p hook) 83202ac6454SAndrew Thompson { 83302ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 83402ac6454SAndrew Thompson int error = 0; 83502ac6454SAndrew Thompson 83602ac6454SAndrew Thompson if (sc != NULL) { 83702ac6454SAndrew Thompson 83802ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 83902ac6454SAndrew Thompson 84002ac6454SAndrew Thompson if (hook != sc->sc_hook) { 84102ac6454SAndrew Thompson error = EINVAL; 84202ac6454SAndrew Thompson } else { 84302ac6454SAndrew Thompson 84402ac6454SAndrew Thompson /* stop bulk-in transfer */ 845a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD_CS]); 846a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD]); 84702ac6454SAndrew Thompson 84802ac6454SAndrew Thompson /* stop bulk-out transfer */ 849a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR_CS]); 850a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR]); 85102ac6454SAndrew Thompson 85202ac6454SAndrew Thompson /* cleanup queues */ 85302ac6454SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq); 85402ac6454SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq_hipri); 85502ac6454SAndrew Thompson 85602ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 85702ac6454SAndrew Thompson m_freem(sc->sc_bulk_in_buffer); 85802ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 85902ac6454SAndrew Thompson } 86002ac6454SAndrew Thompson sc->sc_hook = NULL; 86102ac6454SAndrew Thompson } 86202ac6454SAndrew Thompson 86302ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 86402ac6454SAndrew Thompson } 86502ac6454SAndrew Thompson if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 86602ac6454SAndrew Thompson && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 86702ac6454SAndrew Thompson ng_rmnode_self(NG_HOOK_NODE(hook)); 86802ac6454SAndrew Thompson 86902ac6454SAndrew Thompson return (error); 87002ac6454SAndrew Thompson } 871