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/module.h> 69ed6d949aSAndrew Thompson #include <sys/lock.h> 70ed6d949aSAndrew Thompson #include <sys/mutex.h> 71ed6d949aSAndrew Thompson #include <sys/condvar.h> 72ed6d949aSAndrew Thompson #include <sys/sysctl.h> 73ed6d949aSAndrew Thompson #include <sys/sx.h> 74ed6d949aSAndrew Thompson #include <sys/unistd.h> 75ed6d949aSAndrew Thompson #include <sys/callout.h> 76ed6d949aSAndrew Thompson #include <sys/malloc.h> 77ed6d949aSAndrew Thompson #include <sys/priv.h> 78ed6d949aSAndrew Thompson 7902ac6454SAndrew Thompson #include <dev/usb/usb.h> 80ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 81ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 82ed6d949aSAndrew Thompson #include "usbdevs.h" 8302ac6454SAndrew Thompson 8402ac6454SAndrew Thompson #define USB_DEBUG_VAR udbp_debug 8502ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 8602ac6454SAndrew Thompson 8702ac6454SAndrew Thompson #include <sys/mbuf.h> 8802ac6454SAndrew Thompson 8902ac6454SAndrew Thompson #include <netgraph/ng_message.h> 9002ac6454SAndrew Thompson #include <netgraph/netgraph.h> 9102ac6454SAndrew Thompson #include <netgraph/ng_parse.h> 9202ac6454SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h> 9302ac6454SAndrew Thompson 9402ac6454SAndrew Thompson #include <dev/usb/misc/udbp.h> 9502ac6454SAndrew Thompson 96b850ecc1SAndrew Thompson #ifdef USB_DEBUG 9702ac6454SAndrew Thompson static int udbp_debug = 0; 9802ac6454SAndrew Thompson 999360ae40SAndrew Thompson SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp"); 1009360ae40SAndrew Thompson SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RW, 10102ac6454SAndrew Thompson &udbp_debug, 0, "udbp debug level"); 10202ac6454SAndrew Thompson #endif 10302ac6454SAndrew Thompson 10402ac6454SAndrew Thompson #define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in 10502ac6454SAndrew Thompson * msecs */ 10602ac6454SAndrew Thompson #define UDBP_BUFFERSIZE MCLBYTES /* maximum number of bytes in one 10702ac6454SAndrew Thompson * transfer */ 10802ac6454SAndrew Thompson #define UDBP_T_WR 0 10902ac6454SAndrew Thompson #define UDBP_T_RD 1 11002ac6454SAndrew Thompson #define UDBP_T_WR_CS 2 11102ac6454SAndrew Thompson #define UDBP_T_RD_CS 3 11202ac6454SAndrew Thompson #define UDBP_T_MAX 4 11302ac6454SAndrew Thompson #define UDBP_Q_MAXLEN 50 11402ac6454SAndrew Thompson 11502ac6454SAndrew Thompson struct udbp_softc { 11602ac6454SAndrew Thompson 11702ac6454SAndrew Thompson struct mtx sc_mtx; 11802ac6454SAndrew Thompson struct ng_bt_mbufq sc_xmitq_hipri; /* hi-priority transmit queue */ 11902ac6454SAndrew Thompson struct ng_bt_mbufq sc_xmitq; /* low-priority transmit queue */ 12002ac6454SAndrew Thompson 121760bc48eSAndrew Thompson struct usb_xfer *sc_xfer[UDBP_T_MAX]; 12202ac6454SAndrew Thompson node_p sc_node; /* back pointer to node */ 12302ac6454SAndrew Thompson hook_p sc_hook; /* pointer to the hook */ 12402ac6454SAndrew Thompson struct mbuf *sc_bulk_in_buffer; 12502ac6454SAndrew Thompson 12602ac6454SAndrew Thompson uint32_t sc_packets_in; /* packets in from downstream */ 12702ac6454SAndrew Thompson uint32_t sc_packets_out; /* packets out towards downstream */ 12802ac6454SAndrew Thompson 12902ac6454SAndrew Thompson uint8_t sc_flags; 13002ac6454SAndrew Thompson #define UDBP_FLAG_READ_STALL 0x01 /* read transfer stalled */ 13102ac6454SAndrew Thompson #define UDBP_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ 13202ac6454SAndrew Thompson 13302ac6454SAndrew Thompson uint8_t sc_name[16]; 13402ac6454SAndrew Thompson }; 13502ac6454SAndrew Thompson 13602ac6454SAndrew Thompson /* prototypes */ 13702ac6454SAndrew Thompson 13802ac6454SAndrew Thompson static int udbp_modload(module_t mod, int event, void *data); 13902ac6454SAndrew Thompson 14002ac6454SAndrew Thompson static device_probe_t udbp_probe; 14102ac6454SAndrew Thompson static device_attach_t udbp_attach; 14202ac6454SAndrew Thompson static device_detach_t udbp_detach; 14302ac6454SAndrew Thompson 144e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_read_callback; 145e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_read_clear_stall_callback; 146e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_write_callback; 147e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_write_clear_stall_callback; 14802ac6454SAndrew Thompson 14902ac6454SAndrew Thompson static void udbp_bulk_read_complete(node_p, hook_p, void *, int); 15002ac6454SAndrew Thompson 15102ac6454SAndrew Thompson static ng_constructor_t ng_udbp_constructor; 15202ac6454SAndrew Thompson static ng_rcvmsg_t ng_udbp_rcvmsg; 15302ac6454SAndrew Thompson static ng_shutdown_t ng_udbp_rmnode; 15402ac6454SAndrew Thompson static ng_newhook_t ng_udbp_newhook; 15502ac6454SAndrew Thompson static ng_connect_t ng_udbp_connect; 15602ac6454SAndrew Thompson static ng_rcvdata_t ng_udbp_rcvdata; 15702ac6454SAndrew Thompson static ng_disconnect_t ng_udbp_disconnect; 15802ac6454SAndrew Thompson 15902ac6454SAndrew Thompson /* Parse type for struct ngudbpstat */ 16002ac6454SAndrew Thompson static const struct ng_parse_struct_field 16102ac6454SAndrew Thompson ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO; 16202ac6454SAndrew Thompson 16302ac6454SAndrew Thompson static const struct ng_parse_type ng_udbp_stat_type = { 16402ac6454SAndrew Thompson &ng_parse_struct_type, 16502ac6454SAndrew Thompson &ng_udbp_stat_type_fields 16602ac6454SAndrew Thompson }; 16702ac6454SAndrew Thompson 16802ac6454SAndrew Thompson /* List of commands and how to convert arguments to/from ASCII */ 16902ac6454SAndrew Thompson static const struct ng_cmdlist ng_udbp_cmdlist[] = { 17002ac6454SAndrew Thompson { 17102ac6454SAndrew Thompson NGM_UDBP_COOKIE, 17202ac6454SAndrew Thompson NGM_UDBP_GET_STATUS, 17302ac6454SAndrew Thompson "getstatus", 17402ac6454SAndrew Thompson NULL, 17502ac6454SAndrew Thompson &ng_udbp_stat_type, 17602ac6454SAndrew Thompson }, 17702ac6454SAndrew Thompson { 17802ac6454SAndrew Thompson NGM_UDBP_COOKIE, 17902ac6454SAndrew Thompson NGM_UDBP_SET_FLAG, 18002ac6454SAndrew Thompson "setflag", 18102ac6454SAndrew Thompson &ng_parse_int32_type, 18202ac6454SAndrew Thompson NULL 18302ac6454SAndrew Thompson }, 18402ac6454SAndrew Thompson {0} 18502ac6454SAndrew Thompson }; 18602ac6454SAndrew Thompson 18702ac6454SAndrew Thompson /* Netgraph node type descriptor */ 18802ac6454SAndrew Thompson static struct ng_type ng_udbp_typestruct = { 18902ac6454SAndrew Thompson .version = NG_ABI_VERSION, 19002ac6454SAndrew Thompson .name = NG_UDBP_NODE_TYPE, 19102ac6454SAndrew Thompson .constructor = ng_udbp_constructor, 19202ac6454SAndrew Thompson .rcvmsg = ng_udbp_rcvmsg, 19302ac6454SAndrew Thompson .shutdown = ng_udbp_rmnode, 19402ac6454SAndrew Thompson .newhook = ng_udbp_newhook, 19502ac6454SAndrew Thompson .connect = ng_udbp_connect, 19602ac6454SAndrew Thompson .rcvdata = ng_udbp_rcvdata, 19702ac6454SAndrew Thompson .disconnect = ng_udbp_disconnect, 19802ac6454SAndrew Thompson .cmdlist = ng_udbp_cmdlist, 19902ac6454SAndrew Thompson }; 20002ac6454SAndrew Thompson 20102ac6454SAndrew Thompson /* USB config */ 202760bc48eSAndrew Thompson static const struct usb_config udbp_config[UDBP_T_MAX] = { 20302ac6454SAndrew Thompson 20402ac6454SAndrew Thompson [UDBP_T_WR] = { 20502ac6454SAndrew Thompson .type = UE_BULK, 20602ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 20702ac6454SAndrew Thompson .direction = UE_DIR_OUT, 2084eae601eSAndrew Thompson .bufsize = UDBP_BUFFERSIZE, 2094eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 2104eae601eSAndrew Thompson .callback = &udbp_bulk_write_callback, 2114eae601eSAndrew Thompson .timeout = UDBP_TIMEOUT, 21202ac6454SAndrew Thompson }, 21302ac6454SAndrew Thompson 21402ac6454SAndrew Thompson [UDBP_T_RD] = { 21502ac6454SAndrew Thompson .type = UE_BULK, 21602ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 21702ac6454SAndrew Thompson .direction = UE_DIR_IN, 2184eae601eSAndrew Thompson .bufsize = UDBP_BUFFERSIZE, 2194eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 2204eae601eSAndrew Thompson .callback = &udbp_bulk_read_callback, 22102ac6454SAndrew Thompson }, 22202ac6454SAndrew Thompson 22302ac6454SAndrew Thompson [UDBP_T_WR_CS] = { 22402ac6454SAndrew Thompson .type = UE_CONTROL, 22502ac6454SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 22602ac6454SAndrew Thompson .direction = UE_DIR_ANY, 227760bc48eSAndrew Thompson .bufsize = sizeof(struct usb_device_request), 2284eae601eSAndrew Thompson .callback = &udbp_bulk_write_clear_stall_callback, 2294eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 2304eae601eSAndrew Thompson .interval = 50, /* 50ms */ 23102ac6454SAndrew Thompson }, 23202ac6454SAndrew Thompson 23302ac6454SAndrew Thompson [UDBP_T_RD_CS] = { 23402ac6454SAndrew Thompson .type = UE_CONTROL, 23502ac6454SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 23602ac6454SAndrew Thompson .direction = UE_DIR_ANY, 237760bc48eSAndrew Thompson .bufsize = sizeof(struct usb_device_request), 2384eae601eSAndrew Thompson .callback = &udbp_bulk_read_clear_stall_callback, 2394eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 2404eae601eSAndrew Thompson .interval = 50, /* 50ms */ 24102ac6454SAndrew Thompson }, 24202ac6454SAndrew Thompson }; 24302ac6454SAndrew Thompson 24402ac6454SAndrew Thompson static devclass_t udbp_devclass; 24502ac6454SAndrew Thompson 24602ac6454SAndrew Thompson static device_method_t udbp_methods[] = { 24702ac6454SAndrew Thompson /* Device interface */ 24802ac6454SAndrew Thompson DEVMETHOD(device_probe, udbp_probe), 24902ac6454SAndrew Thompson DEVMETHOD(device_attach, udbp_attach), 25002ac6454SAndrew Thompson DEVMETHOD(device_detach, udbp_detach), 25102ac6454SAndrew Thompson {0, 0} 25202ac6454SAndrew Thompson }; 25302ac6454SAndrew Thompson 25402ac6454SAndrew Thompson static driver_t udbp_driver = { 25502ac6454SAndrew Thompson .name = "udbp", 25602ac6454SAndrew Thompson .methods = udbp_methods, 25702ac6454SAndrew Thompson .size = sizeof(struct udbp_softc), 25802ac6454SAndrew Thompson }; 25902ac6454SAndrew Thompson 2609aef556dSAndrew Thompson DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_devclass, udbp_modload, 0); 26102ac6454SAndrew Thompson MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 26202ac6454SAndrew Thompson MODULE_DEPEND(udbp, usb, 1, 1, 1); 263910cb8feSAndrew Thompson MODULE_VERSION(udbp, 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 291*9cfbe3e7SHans Petter Selasky static const STRUCT_USB_HOST_ID udbp_devs[] = { 292*9cfbe3e7SHans Petter Selasky {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_TURBOCONNECT, 0)}, 293*9cfbe3e7SHans Petter Selasky {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2301, 0)}, 294*9cfbe3e7SHans Petter Selasky {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2302, 0)}, 295*9cfbe3e7SHans Petter Selasky {USB_VPI(USB_VENDOR_ANCHOR, USB_PRODUCT_ANCHOR_EZLINK, 0)}, 296*9cfbe3e7SHans Petter Selasky {USB_VPI(USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL620USB, 0)}, 297*9cfbe3e7SHans Petter Selasky }; 298*9cfbe3e7SHans Petter Selasky 29902ac6454SAndrew Thompson static int 30002ac6454SAndrew Thompson udbp_probe(device_t dev) 30102ac6454SAndrew Thompson { 302760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 30302ac6454SAndrew Thompson 304*9cfbe3e7SHans Petter Selasky if (uaa->usb_mode != USB_MODE_HOST) 30502ac6454SAndrew Thompson return (ENXIO); 306*9cfbe3e7SHans Petter Selasky if (uaa->info.bConfigIndex != 0) 30702ac6454SAndrew Thompson return (ENXIO); 308*9cfbe3e7SHans Petter Selasky if (uaa->info.bIfaceIndex != 0) 309*9cfbe3e7SHans Petter Selasky return (ENXIO); 310*9cfbe3e7SHans Petter Selasky 311*9cfbe3e7SHans Petter Selasky return (usbd_lookup_id_by_uaa(udbp_devs, sizeof(udbp_devs), uaa)); 31202ac6454SAndrew Thompson } 31302ac6454SAndrew Thompson 31402ac6454SAndrew Thompson static int 31502ac6454SAndrew Thompson udbp_attach(device_t dev) 31602ac6454SAndrew Thompson { 317760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 31802ac6454SAndrew Thompson struct udbp_softc *sc = device_get_softc(dev); 31902ac6454SAndrew Thompson int error; 32002ac6454SAndrew Thompson 321a593f6b8SAndrew Thompson device_set_usb_desc(dev); 32202ac6454SAndrew Thompson 32302ac6454SAndrew Thompson snprintf(sc->sc_name, sizeof(sc->sc_name), 32402ac6454SAndrew Thompson "%s", device_get_nameunit(dev)); 32502ac6454SAndrew Thompson 32602ac6454SAndrew Thompson mtx_init(&sc->sc_mtx, "udbp lock", NULL, MTX_DEF | MTX_RECURSE); 32702ac6454SAndrew Thompson 328a593f6b8SAndrew Thompson error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 32902ac6454SAndrew Thompson sc->sc_xfer, udbp_config, UDBP_T_MAX, sc, &sc->sc_mtx); 33002ac6454SAndrew Thompson if (error) { 331a593f6b8SAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 33202ac6454SAndrew Thompson goto detach; 33302ac6454SAndrew Thompson } 33402ac6454SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_xmitq, UDBP_Q_MAXLEN); 33502ac6454SAndrew Thompson 33602ac6454SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_xmitq_hipri, UDBP_Q_MAXLEN); 33702ac6454SAndrew Thompson 33802ac6454SAndrew Thompson /* create Netgraph node */ 33902ac6454SAndrew Thompson 34002ac6454SAndrew Thompson if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { 34102ac6454SAndrew Thompson printf("%s: Could not create Netgraph node\n", 34202ac6454SAndrew Thompson sc->sc_name); 34302ac6454SAndrew Thompson sc->sc_node = NULL; 34402ac6454SAndrew Thompson goto detach; 34502ac6454SAndrew Thompson } 34602ac6454SAndrew Thompson /* name node */ 34702ac6454SAndrew Thompson 34802ac6454SAndrew Thompson if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { 34902ac6454SAndrew Thompson printf("%s: Could not name node\n", 35002ac6454SAndrew Thompson sc->sc_name); 35102ac6454SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 35202ac6454SAndrew Thompson sc->sc_node = NULL; 35302ac6454SAndrew Thompson goto detach; 35402ac6454SAndrew Thompson } 35502ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 35602ac6454SAndrew Thompson 35702ac6454SAndrew Thompson /* the device is now operational */ 35802ac6454SAndrew Thompson 35902ac6454SAndrew Thompson return (0); /* success */ 36002ac6454SAndrew Thompson 36102ac6454SAndrew Thompson detach: 36202ac6454SAndrew Thompson udbp_detach(dev); 36302ac6454SAndrew Thompson return (ENOMEM); /* failure */ 36402ac6454SAndrew Thompson } 36502ac6454SAndrew Thompson 36602ac6454SAndrew Thompson static int 36702ac6454SAndrew Thompson udbp_detach(device_t dev) 36802ac6454SAndrew Thompson { 36902ac6454SAndrew Thompson struct udbp_softc *sc = device_get_softc(dev); 37002ac6454SAndrew Thompson 37102ac6454SAndrew Thompson /* destroy Netgraph node */ 37202ac6454SAndrew Thompson 37302ac6454SAndrew Thompson if (sc->sc_node != NULL) { 37402ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, NULL); 37502ac6454SAndrew Thompson ng_rmnode_self(sc->sc_node); 37602ac6454SAndrew Thompson sc->sc_node = NULL; 37702ac6454SAndrew Thompson } 37802ac6454SAndrew Thompson /* free USB transfers, if any */ 37902ac6454SAndrew Thompson 380a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UDBP_T_MAX); 38102ac6454SAndrew Thompson 38202ac6454SAndrew Thompson mtx_destroy(&sc->sc_mtx); 38302ac6454SAndrew Thompson 38402ac6454SAndrew Thompson /* destroy queues */ 38502ac6454SAndrew Thompson 38602ac6454SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq); 38702ac6454SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq_hipri); 38802ac6454SAndrew Thompson 38902ac6454SAndrew Thompson /* extra check */ 39002ac6454SAndrew Thompson 39102ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 39202ac6454SAndrew Thompson m_freem(sc->sc_bulk_in_buffer); 39302ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 39402ac6454SAndrew Thompson } 39502ac6454SAndrew Thompson return (0); /* success */ 39602ac6454SAndrew Thompson } 39702ac6454SAndrew Thompson 39802ac6454SAndrew Thompson static void 399ed6d949aSAndrew Thompson udbp_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 40002ac6454SAndrew Thompson { 401ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 402ed6d949aSAndrew Thompson struct usb_page_cache *pc; 40302ac6454SAndrew Thompson struct mbuf *m; 404ed6d949aSAndrew Thompson int actlen; 405ed6d949aSAndrew Thompson 406ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 40702ac6454SAndrew Thompson 40802ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 40902ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 41002ac6454SAndrew Thompson 41102ac6454SAndrew Thompson /* allocate new mbuf */ 41202ac6454SAndrew Thompson 41302ac6454SAndrew Thompson MGETHDR(m, M_DONTWAIT, MT_DATA); 41402ac6454SAndrew Thompson 41502ac6454SAndrew Thompson if (m == NULL) { 41602ac6454SAndrew Thompson goto tr_setup; 41702ac6454SAndrew Thompson } 41802ac6454SAndrew Thompson MCLGET(m, M_DONTWAIT); 41902ac6454SAndrew Thompson 42002ac6454SAndrew Thompson if (!(m->m_flags & M_EXT)) { 42102ac6454SAndrew Thompson m_freem(m); 42202ac6454SAndrew Thompson goto tr_setup; 42302ac6454SAndrew Thompson } 424ed6d949aSAndrew Thompson m->m_pkthdr.len = m->m_len = actlen; 42502ac6454SAndrew Thompson 426ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 427ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, m->m_data, actlen); 42802ac6454SAndrew Thompson 42902ac6454SAndrew Thompson sc->sc_bulk_in_buffer = m; 43002ac6454SAndrew Thompson 431ed6d949aSAndrew Thompson DPRINTF("received package %d bytes\n", actlen); 43202ac6454SAndrew Thompson 43302ac6454SAndrew Thompson case USB_ST_SETUP: 43402ac6454SAndrew Thompson tr_setup: 43502ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 43602ac6454SAndrew Thompson ng_send_fn(sc->sc_node, NULL, &udbp_bulk_read_complete, NULL, 0); 43702ac6454SAndrew Thompson return; 43802ac6454SAndrew Thompson } 43902ac6454SAndrew Thompson if (sc->sc_flags & UDBP_FLAG_READ_STALL) { 440a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); 44102ac6454SAndrew Thompson return; 44202ac6454SAndrew Thompson } 443ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 444a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 44502ac6454SAndrew Thompson return; 44602ac6454SAndrew Thompson 44702ac6454SAndrew Thompson default: /* Error */ 448ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 44902ac6454SAndrew Thompson /* try to clear stall first */ 45002ac6454SAndrew Thompson sc->sc_flags |= UDBP_FLAG_READ_STALL; 451a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); 45202ac6454SAndrew Thompson } 45302ac6454SAndrew Thompson return; 45402ac6454SAndrew Thompson 45502ac6454SAndrew Thompson } 45602ac6454SAndrew Thompson } 45702ac6454SAndrew Thompson 45802ac6454SAndrew Thompson static void 459ed6d949aSAndrew Thompson udbp_bulk_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 46002ac6454SAndrew Thompson { 461ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 462760bc48eSAndrew Thompson struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_RD]; 46302ac6454SAndrew Thompson 464a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 46502ac6454SAndrew Thompson DPRINTF("stall cleared\n"); 46602ac6454SAndrew Thompson sc->sc_flags &= ~UDBP_FLAG_READ_STALL; 467a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 46802ac6454SAndrew Thompson } 46902ac6454SAndrew Thompson } 47002ac6454SAndrew Thompson 47102ac6454SAndrew Thompson static void 47202ac6454SAndrew Thompson udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2) 47302ac6454SAndrew Thompson { 47402ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 47502ac6454SAndrew Thompson struct mbuf *m; 47602ac6454SAndrew Thompson int error; 47702ac6454SAndrew Thompson 47802ac6454SAndrew Thompson if (sc == NULL) { 47902ac6454SAndrew Thompson return; 48002ac6454SAndrew Thompson } 48102ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 48202ac6454SAndrew Thompson 48302ac6454SAndrew Thompson m = sc->sc_bulk_in_buffer; 48402ac6454SAndrew Thompson 48502ac6454SAndrew Thompson if (m) { 48602ac6454SAndrew Thompson 48702ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 48802ac6454SAndrew Thompson 48902ac6454SAndrew Thompson if ((sc->sc_hook == NULL) || 49002ac6454SAndrew Thompson NG_HOOK_NOT_VALID(sc->sc_hook)) { 49102ac6454SAndrew Thompson DPRINTF("No upstream hook\n"); 49202ac6454SAndrew Thompson goto done; 49302ac6454SAndrew Thompson } 49402ac6454SAndrew Thompson sc->sc_packets_in++; 49502ac6454SAndrew Thompson 49602ac6454SAndrew Thompson NG_SEND_DATA_ONLY(error, sc->sc_hook, m); 49702ac6454SAndrew Thompson 49802ac6454SAndrew Thompson m = NULL; 49902ac6454SAndrew Thompson } 50002ac6454SAndrew Thompson done: 50102ac6454SAndrew Thompson if (m) { 50202ac6454SAndrew Thompson m_freem(m); 50302ac6454SAndrew Thompson } 50402ac6454SAndrew Thompson /* start USB bulk-in transfer, if not already started */ 50502ac6454SAndrew Thompson 506a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); 50702ac6454SAndrew Thompson 50802ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 50902ac6454SAndrew Thompson } 51002ac6454SAndrew Thompson 51102ac6454SAndrew Thompson static void 512ed6d949aSAndrew Thompson udbp_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 51302ac6454SAndrew Thompson { 514ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 515ed6d949aSAndrew Thompson struct usb_page_cache *pc; 51602ac6454SAndrew Thompson struct mbuf *m; 51702ac6454SAndrew Thompson 51802ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 51902ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 52002ac6454SAndrew Thompson 52102ac6454SAndrew Thompson sc->sc_packets_out++; 52202ac6454SAndrew Thompson 52302ac6454SAndrew Thompson case USB_ST_SETUP: 52402ac6454SAndrew Thompson if (sc->sc_flags & UDBP_FLAG_WRITE_STALL) { 525a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); 52602ac6454SAndrew Thompson return; 52702ac6454SAndrew Thompson } 52802ac6454SAndrew Thompson /* get next mbuf, if any */ 52902ac6454SAndrew Thompson 53002ac6454SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq_hipri, m); 53102ac6454SAndrew Thompson if (m == NULL) { 53202ac6454SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq, m); 53302ac6454SAndrew Thompson if (m == NULL) { 53402ac6454SAndrew Thompson DPRINTF("Data queue is empty\n"); 53502ac6454SAndrew Thompson return; 53602ac6454SAndrew Thompson } 53702ac6454SAndrew Thompson } 53802ac6454SAndrew Thompson if (m->m_pkthdr.len > MCLBYTES) { 53902ac6454SAndrew Thompson DPRINTF("truncating large packet " 54002ac6454SAndrew Thompson "from %d to %d bytes\n", m->m_pkthdr.len, 54102ac6454SAndrew Thompson MCLBYTES); 54202ac6454SAndrew Thompson m->m_pkthdr.len = MCLBYTES; 54302ac6454SAndrew Thompson } 544ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 545ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 54602ac6454SAndrew Thompson 547ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 548ed6d949aSAndrew Thompson 549ed6d949aSAndrew Thompson DPRINTF("packet out: %d bytes\n", m->m_pkthdr.len); 55002ac6454SAndrew Thompson 55102ac6454SAndrew Thompson m_freem(m); 55202ac6454SAndrew Thompson 553a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 55402ac6454SAndrew Thompson return; 55502ac6454SAndrew Thompson 55602ac6454SAndrew Thompson default: /* Error */ 557ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 55802ac6454SAndrew Thompson /* try to clear stall first */ 55902ac6454SAndrew Thompson sc->sc_flags |= UDBP_FLAG_WRITE_STALL; 560a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); 56102ac6454SAndrew Thompson } 56202ac6454SAndrew Thompson return; 56302ac6454SAndrew Thompson 56402ac6454SAndrew Thompson } 56502ac6454SAndrew Thompson } 56602ac6454SAndrew Thompson 56702ac6454SAndrew Thompson static void 568ed6d949aSAndrew Thompson udbp_bulk_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 56902ac6454SAndrew Thompson { 570ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 571760bc48eSAndrew Thompson struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_WR]; 57202ac6454SAndrew Thompson 573a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 57402ac6454SAndrew Thompson DPRINTF("stall cleared\n"); 57502ac6454SAndrew Thompson sc->sc_flags &= ~UDBP_FLAG_WRITE_STALL; 576a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 57702ac6454SAndrew Thompson } 57802ac6454SAndrew Thompson } 57902ac6454SAndrew Thompson 58002ac6454SAndrew Thompson /*********************************************************************** 58102ac6454SAndrew Thompson * Start of Netgraph methods 58202ac6454SAndrew Thompson **********************************************************************/ 58302ac6454SAndrew Thompson 58402ac6454SAndrew Thompson /* 58502ac6454SAndrew Thompson * If this is a device node so this work is done in the attach() 58602ac6454SAndrew Thompson * routine and the constructor will return EINVAL as you should not be able 58702ac6454SAndrew Thompson * to create nodes that depend on hardware (unless you can add the hardware :) 58802ac6454SAndrew Thompson */ 58902ac6454SAndrew Thompson static int 59002ac6454SAndrew Thompson ng_udbp_constructor(node_p node) 59102ac6454SAndrew Thompson { 59202ac6454SAndrew Thompson return (EINVAL); 59302ac6454SAndrew Thompson } 59402ac6454SAndrew Thompson 59502ac6454SAndrew Thompson /* 59602ac6454SAndrew Thompson * Give our ok for a hook to be added... 59702ac6454SAndrew Thompson * If we are not running this might kick a device into life. 59802ac6454SAndrew Thompson * Possibly decode information out of the hook name. 59902ac6454SAndrew Thompson * Add the hook's private info to the hook structure. 60002ac6454SAndrew Thompson * (if we had some). In this example, we assume that there is a 60102ac6454SAndrew Thompson * an array of structs, called 'channel' in the private info, 60202ac6454SAndrew Thompson * one for each active channel. The private 60302ac6454SAndrew Thompson * pointer of each hook points to the appropriate UDBP_hookinfo struct 60402ac6454SAndrew Thompson * so that the source of an input packet is easily identified. 60502ac6454SAndrew Thompson */ 60602ac6454SAndrew Thompson static int 60702ac6454SAndrew Thompson ng_udbp_newhook(node_p node, hook_p hook, const char *name) 60802ac6454SAndrew Thompson { 60902ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 61002ac6454SAndrew Thompson int32_t error = 0; 61102ac6454SAndrew Thompson 61202ac6454SAndrew Thompson if (strcmp(name, NG_UDBP_HOOK_NAME)) { 61302ac6454SAndrew Thompson return (EINVAL); 61402ac6454SAndrew Thompson } 61502ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 61602ac6454SAndrew Thompson 61702ac6454SAndrew Thompson if (sc->sc_hook != NULL) { 61802ac6454SAndrew Thompson error = EISCONN; 61902ac6454SAndrew Thompson } else { 62002ac6454SAndrew Thompson sc->sc_hook = hook; 62102ac6454SAndrew Thompson NG_HOOK_SET_PRIVATE(hook, NULL); 62202ac6454SAndrew Thompson } 62302ac6454SAndrew Thompson 62402ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 62502ac6454SAndrew Thompson 62602ac6454SAndrew Thompson return (error); 62702ac6454SAndrew Thompson } 62802ac6454SAndrew Thompson 62902ac6454SAndrew Thompson /* 63002ac6454SAndrew Thompson * Get a netgraph control message. 63102ac6454SAndrew Thompson * Check it is one we understand. If needed, send a response. 63202ac6454SAndrew Thompson * We could save the address for an async action later, but don't here. 63302ac6454SAndrew Thompson * Always free the message. 63402ac6454SAndrew Thompson * The response should be in a malloc'd region that the caller can 'free'. 63502ac6454SAndrew Thompson * A response is not required. 63602ac6454SAndrew Thompson * Theoretically you could respond defferently to old message types if 63702ac6454SAndrew Thompson * the cookie in the header didn't match what we consider to be current 63802ac6454SAndrew Thompson * (so that old userland programs could continue to work). 63902ac6454SAndrew Thompson */ 64002ac6454SAndrew Thompson static int 64102ac6454SAndrew Thompson ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook) 64202ac6454SAndrew Thompson { 64302ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 64402ac6454SAndrew Thompson struct ng_mesg *resp = NULL; 64502ac6454SAndrew Thompson int error = 0; 64602ac6454SAndrew Thompson struct ng_mesg *msg; 64702ac6454SAndrew Thompson 64802ac6454SAndrew Thompson NGI_GET_MSG(item, msg); 64902ac6454SAndrew Thompson /* Deal with message according to cookie and command */ 65002ac6454SAndrew Thompson switch (msg->header.typecookie) { 65102ac6454SAndrew Thompson case NGM_UDBP_COOKIE: 65202ac6454SAndrew Thompson switch (msg->header.cmd) { 65302ac6454SAndrew Thompson case NGM_UDBP_GET_STATUS: 65402ac6454SAndrew Thompson { 65502ac6454SAndrew Thompson struct ngudbpstat *stats; 65602ac6454SAndrew Thompson 65702ac6454SAndrew Thompson NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 65802ac6454SAndrew Thompson if (!resp) { 65902ac6454SAndrew Thompson error = ENOMEM; 66002ac6454SAndrew Thompson break; 66102ac6454SAndrew Thompson } 66202ac6454SAndrew Thompson stats = (struct ngudbpstat *)resp->data; 66302ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 66402ac6454SAndrew Thompson stats->packets_in = sc->sc_packets_in; 66502ac6454SAndrew Thompson stats->packets_out = sc->sc_packets_out; 66602ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 66702ac6454SAndrew Thompson break; 66802ac6454SAndrew Thompson } 66902ac6454SAndrew Thompson case NGM_UDBP_SET_FLAG: 67002ac6454SAndrew Thompson if (msg->header.arglen != sizeof(uint32_t)) { 67102ac6454SAndrew Thompson error = EINVAL; 67202ac6454SAndrew Thompson break; 67302ac6454SAndrew Thompson } 67402ac6454SAndrew Thompson DPRINTF("flags = 0x%08x\n", 67502ac6454SAndrew Thompson *((uint32_t *)msg->data)); 67602ac6454SAndrew Thompson break; 67702ac6454SAndrew Thompson default: 67802ac6454SAndrew Thompson error = EINVAL; /* unknown command */ 67902ac6454SAndrew Thompson break; 68002ac6454SAndrew Thompson } 68102ac6454SAndrew Thompson break; 68202ac6454SAndrew Thompson default: 68302ac6454SAndrew Thompson error = EINVAL; /* unknown cookie type */ 68402ac6454SAndrew Thompson break; 68502ac6454SAndrew Thompson } 68602ac6454SAndrew Thompson 68702ac6454SAndrew Thompson /* Take care of synchronous response, if any */ 68802ac6454SAndrew Thompson NG_RESPOND_MSG(error, node, item, resp); 68902ac6454SAndrew Thompson NG_FREE_MSG(msg); 69002ac6454SAndrew Thompson return (error); 69102ac6454SAndrew Thompson } 69202ac6454SAndrew Thompson 69302ac6454SAndrew Thompson /* 69402ac6454SAndrew Thompson * Accept data from the hook and queue it for output. 69502ac6454SAndrew Thompson */ 69602ac6454SAndrew Thompson static int 69702ac6454SAndrew Thompson ng_udbp_rcvdata(hook_p hook, item_p item) 69802ac6454SAndrew Thompson { 69902ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 70002ac6454SAndrew Thompson struct ng_bt_mbufq *queue_ptr; 70102ac6454SAndrew Thompson struct mbuf *m; 70202ac6454SAndrew Thompson struct ng_tag_prio *ptag; 70302ac6454SAndrew Thompson int error; 70402ac6454SAndrew Thompson 70502ac6454SAndrew Thompson if (sc == NULL) { 70602ac6454SAndrew Thompson NG_FREE_ITEM(item); 70702ac6454SAndrew Thompson return (EHOSTDOWN); 70802ac6454SAndrew Thompson } 70902ac6454SAndrew Thompson NGI_GET_M(item, m); 71002ac6454SAndrew Thompson NG_FREE_ITEM(item); 71102ac6454SAndrew Thompson 71202ac6454SAndrew Thompson /* 71302ac6454SAndrew Thompson * Now queue the data for when it can be sent 71402ac6454SAndrew Thompson */ 71502ac6454SAndrew Thompson ptag = (void *)m_tag_locate(m, NGM_GENERIC_COOKIE, 71602ac6454SAndrew Thompson NG_TAG_PRIO, NULL); 71702ac6454SAndrew Thompson 71802ac6454SAndrew Thompson if (ptag && (ptag->priority > NG_PRIO_CUTOFF)) 71902ac6454SAndrew Thompson queue_ptr = &sc->sc_xmitq_hipri; 72002ac6454SAndrew Thompson else 72102ac6454SAndrew Thompson queue_ptr = &sc->sc_xmitq; 72202ac6454SAndrew Thompson 72302ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 72402ac6454SAndrew Thompson 72502ac6454SAndrew Thompson if (NG_BT_MBUFQ_FULL(queue_ptr)) { 72602ac6454SAndrew Thompson NG_BT_MBUFQ_DROP(queue_ptr); 72702ac6454SAndrew Thompson NG_FREE_M(m); 72802ac6454SAndrew Thompson error = ENOBUFS; 72902ac6454SAndrew Thompson } else { 73002ac6454SAndrew Thompson NG_BT_MBUFQ_ENQUEUE(queue_ptr, m); 73102ac6454SAndrew Thompson /* 73202ac6454SAndrew Thompson * start bulk-out transfer, if not already started: 73302ac6454SAndrew Thompson */ 734a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); 73502ac6454SAndrew Thompson error = 0; 73602ac6454SAndrew Thompson } 73702ac6454SAndrew Thompson 73802ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 73902ac6454SAndrew Thompson 74002ac6454SAndrew Thompson return (error); 74102ac6454SAndrew Thompson } 74202ac6454SAndrew Thompson 74302ac6454SAndrew Thompson /* 74402ac6454SAndrew Thompson * Do local shutdown processing.. 74502ac6454SAndrew Thompson * We are a persistant device, we refuse to go away, and 74602ac6454SAndrew Thompson * only remove our links and reset ourself. 74702ac6454SAndrew Thompson */ 74802ac6454SAndrew Thompson static int 74902ac6454SAndrew Thompson ng_udbp_rmnode(node_p node) 75002ac6454SAndrew Thompson { 75102ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 75202ac6454SAndrew Thompson 75302ac6454SAndrew Thompson /* Let old node go */ 75402ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(node, NULL); 75502ac6454SAndrew Thompson NG_NODE_UNREF(node); /* forget it ever existed */ 75602ac6454SAndrew Thompson 75702ac6454SAndrew Thompson if (sc == NULL) { 75802ac6454SAndrew Thompson goto done; 75902ac6454SAndrew Thompson } 76002ac6454SAndrew Thompson /* Create Netgraph node */ 76102ac6454SAndrew Thompson if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { 76202ac6454SAndrew Thompson printf("%s: Could not create Netgraph node\n", 76302ac6454SAndrew Thompson sc->sc_name); 76402ac6454SAndrew Thompson sc->sc_node = NULL; 76502ac6454SAndrew Thompson goto done; 76602ac6454SAndrew Thompson } 76702ac6454SAndrew Thompson /* Name node */ 76802ac6454SAndrew Thompson if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { 76902ac6454SAndrew Thompson printf("%s: Could not name Netgraph node\n", 77002ac6454SAndrew Thompson sc->sc_name); 77102ac6454SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 77202ac6454SAndrew Thompson sc->sc_node = NULL; 77302ac6454SAndrew Thompson goto done; 77402ac6454SAndrew Thompson } 77502ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 77602ac6454SAndrew Thompson 77702ac6454SAndrew Thompson done: 77802ac6454SAndrew Thompson if (sc) { 77902ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 78002ac6454SAndrew Thompson } 78102ac6454SAndrew Thompson return (0); 78202ac6454SAndrew Thompson } 78302ac6454SAndrew Thompson 78402ac6454SAndrew Thompson /* 78502ac6454SAndrew Thompson * This is called once we've already connected a new hook to the other node. 78602ac6454SAndrew Thompson * It gives us a chance to balk at the last minute. 78702ac6454SAndrew Thompson */ 78802ac6454SAndrew Thompson static int 78902ac6454SAndrew Thompson ng_udbp_connect(hook_p hook) 79002ac6454SAndrew Thompson { 79102ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 79202ac6454SAndrew Thompson 79302ac6454SAndrew Thompson /* probably not at splnet, force outward queueing */ 79402ac6454SAndrew Thompson NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 79502ac6454SAndrew Thompson 79602ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 79702ac6454SAndrew Thompson 79802ac6454SAndrew Thompson sc->sc_flags |= (UDBP_FLAG_READ_STALL | 79902ac6454SAndrew Thompson UDBP_FLAG_WRITE_STALL); 80002ac6454SAndrew Thompson 80102ac6454SAndrew Thompson /* start bulk-in transfer */ 802a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); 80302ac6454SAndrew Thompson 80402ac6454SAndrew Thompson /* start bulk-out transfer */ 805a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); 80602ac6454SAndrew Thompson 80702ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 80802ac6454SAndrew Thompson 80902ac6454SAndrew Thompson return (0); 81002ac6454SAndrew Thompson } 81102ac6454SAndrew Thompson 81202ac6454SAndrew Thompson /* 81302ac6454SAndrew Thompson * Dook disconnection 81402ac6454SAndrew Thompson * 81502ac6454SAndrew Thompson * For this type, removal of the last link destroys the node 81602ac6454SAndrew Thompson */ 81702ac6454SAndrew Thompson static int 81802ac6454SAndrew Thompson ng_udbp_disconnect(hook_p hook) 81902ac6454SAndrew Thompson { 82002ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 82102ac6454SAndrew Thompson int error = 0; 82202ac6454SAndrew Thompson 82302ac6454SAndrew Thompson if (sc != NULL) { 82402ac6454SAndrew Thompson 82502ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 82602ac6454SAndrew Thompson 82702ac6454SAndrew Thompson if (hook != sc->sc_hook) { 82802ac6454SAndrew Thompson error = EINVAL; 82902ac6454SAndrew Thompson } else { 83002ac6454SAndrew Thompson 83102ac6454SAndrew Thompson /* stop bulk-in transfer */ 832a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD_CS]); 833a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD]); 83402ac6454SAndrew Thompson 83502ac6454SAndrew Thompson /* stop bulk-out transfer */ 836a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR_CS]); 837a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR]); 83802ac6454SAndrew Thompson 83902ac6454SAndrew Thompson /* cleanup queues */ 84002ac6454SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq); 84102ac6454SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq_hipri); 84202ac6454SAndrew Thompson 84302ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 84402ac6454SAndrew Thompson m_freem(sc->sc_bulk_in_buffer); 84502ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 84602ac6454SAndrew Thompson } 84702ac6454SAndrew Thompson sc->sc_hook = NULL; 84802ac6454SAndrew Thompson } 84902ac6454SAndrew Thompson 85002ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 85102ac6454SAndrew Thompson } 85202ac6454SAndrew Thompson if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 85302ac6454SAndrew Thompson && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 85402ac6454SAndrew Thompson ng_rmnode_self(NG_HOOK_NODE(hook)); 85502ac6454SAndrew Thompson 85602ac6454SAndrew Thompson return (error); 85702ac6454SAndrew Thompson } 858