102ac6454SAndrew Thompson /*- 27282444bSPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 37282444bSPedro F. Giffuni * 402ac6454SAndrew Thompson * Copyright (c) 1996-2000 Whistle Communications, Inc. 502ac6454SAndrew Thompson * All rights reserved. 602ac6454SAndrew Thompson * 702ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 802ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 902ac6454SAndrew Thompson * are met: 1002ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1202ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1302ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1402ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1502ac6454SAndrew Thompson * 3. Neither the name of author nor the names of its 1602ac6454SAndrew Thompson * contributors may be used to endorse or promote products derived 1702ac6454SAndrew Thompson * from this software without specific prior written permission. 1802ac6454SAndrew Thompson * 1902ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY NICK HIBMA AND CONTRIBUTORS 2002ac6454SAndrew Thompson * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2102ac6454SAndrew Thompson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2202ac6454SAndrew Thompson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS 2302ac6454SAndrew Thompson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2402ac6454SAndrew Thompson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2502ac6454SAndrew Thompson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2602ac6454SAndrew Thompson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2702ac6454SAndrew Thompson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2802ac6454SAndrew Thompson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2902ac6454SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE. 3002ac6454SAndrew Thompson * 3102ac6454SAndrew Thompson */ 3202ac6454SAndrew Thompson 3302ac6454SAndrew Thompson #include <sys/cdefs.h> 3402ac6454SAndrew Thompson __FBSDID("$FreeBSD$"); 3502ac6454SAndrew Thompson 3602ac6454SAndrew Thompson /* Driver for arbitrary double bulk pipe devices. 3702ac6454SAndrew Thompson * The driver assumes that there will be the same driver on the other side. 3802ac6454SAndrew Thompson * 3902ac6454SAndrew Thompson * XXX Some more information on what the framing of the IP packets looks like. 4002ac6454SAndrew Thompson * 4102ac6454SAndrew Thompson * To take full advantage of bulk transmission, packets should be chosen 4202ac6454SAndrew Thompson * between 1k and 5k in size (1k to make sure the sending side starts 4302ac6454SAndrew Thompson * streaming, and <5k to avoid overflowing the system with small TDs). 4402ac6454SAndrew Thompson */ 4502ac6454SAndrew Thompson 4602ac6454SAndrew Thompson 4702ac6454SAndrew Thompson /* probe/attach/detach: 4802ac6454SAndrew Thompson * Connect the driver to the hardware and netgraph 4902ac6454SAndrew Thompson * 5002ac6454SAndrew Thompson * The reason we submit a bulk in transfer is that USB does not know about 5102ac6454SAndrew Thompson * interrupts. The bulk transfer continuously polls the device for data. 5202ac6454SAndrew Thompson * While the device has no data available, the device NAKs the TDs. As soon 5302ac6454SAndrew Thompson * as there is data, the transfer happens and the data comes flowing in. 5402ac6454SAndrew Thompson * 5502ac6454SAndrew Thompson * In case you were wondering, interrupt transfers happen exactly that way. 5602ac6454SAndrew Thompson * It therefore doesn't make sense to use the interrupt pipe to signal 5702ac6454SAndrew Thompson * 'data ready' and then schedule a bulk transfer to fetch it. That would 5802ac6454SAndrew Thompson * incur a 2ms delay at least, without reducing bandwidth requirements. 5902ac6454SAndrew Thompson * 6002ac6454SAndrew Thompson */ 6102ac6454SAndrew Thompson 62ed6d949aSAndrew Thompson #include <sys/stdint.h> 63ed6d949aSAndrew Thompson #include <sys/stddef.h> 64ed6d949aSAndrew Thompson #include <sys/param.h> 65ed6d949aSAndrew Thompson #include <sys/queue.h> 66ed6d949aSAndrew Thompson #include <sys/types.h> 67ed6d949aSAndrew Thompson #include <sys/systm.h> 68ed6d949aSAndrew Thompson #include <sys/kernel.h> 69ed6d949aSAndrew Thompson #include <sys/bus.h> 70ed6d949aSAndrew Thompson #include <sys/module.h> 71ed6d949aSAndrew Thompson #include <sys/lock.h> 72ed6d949aSAndrew Thompson #include <sys/mutex.h> 73ed6d949aSAndrew Thompson #include <sys/condvar.h> 74ed6d949aSAndrew Thompson #include <sys/sysctl.h> 75ed6d949aSAndrew Thompson #include <sys/sx.h> 76ed6d949aSAndrew Thompson #include <sys/unistd.h> 77ed6d949aSAndrew Thompson #include <sys/callout.h> 78ed6d949aSAndrew Thompson #include <sys/malloc.h> 79ed6d949aSAndrew Thompson #include <sys/priv.h> 80ed6d949aSAndrew Thompson 8102ac6454SAndrew Thompson #include <dev/usb/usb.h> 82ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 83ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 84ed6d949aSAndrew Thompson #include "usbdevs.h" 8502ac6454SAndrew Thompson 8602ac6454SAndrew Thompson #define USB_DEBUG_VAR udbp_debug 8702ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 8802ac6454SAndrew Thompson 8902ac6454SAndrew Thompson #include <sys/mbuf.h> 9002ac6454SAndrew Thompson 9102ac6454SAndrew Thompson #include <netgraph/ng_message.h> 9202ac6454SAndrew Thompson #include <netgraph/netgraph.h> 9302ac6454SAndrew Thompson #include <netgraph/ng_parse.h> 9402ac6454SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h> 9502ac6454SAndrew Thompson 9602ac6454SAndrew Thompson #include <dev/usb/misc/udbp.h> 9702ac6454SAndrew Thompson 98b850ecc1SAndrew Thompson #ifdef USB_DEBUG 9902ac6454SAndrew Thompson static int udbp_debug = 0; 10002ac6454SAndrew Thompson 101*f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 102*f8d2b1f3SPawel Biernacki "USB udbp"); 103ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RWTUN, 10402ac6454SAndrew Thompson &udbp_debug, 0, "udbp debug level"); 10502ac6454SAndrew Thompson #endif 10602ac6454SAndrew Thompson 10702ac6454SAndrew Thompson #define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in 10802ac6454SAndrew Thompson * msecs */ 10902ac6454SAndrew Thompson #define UDBP_BUFFERSIZE MCLBYTES /* maximum number of bytes in one 11002ac6454SAndrew Thompson * transfer */ 11102ac6454SAndrew Thompson #define UDBP_T_WR 0 11202ac6454SAndrew Thompson #define UDBP_T_RD 1 11302ac6454SAndrew Thompson #define UDBP_T_WR_CS 2 11402ac6454SAndrew Thompson #define UDBP_T_RD_CS 3 11502ac6454SAndrew Thompson #define UDBP_T_MAX 4 11602ac6454SAndrew Thompson #define UDBP_Q_MAXLEN 50 11702ac6454SAndrew Thompson 11802ac6454SAndrew Thompson struct udbp_softc { 11902ac6454SAndrew Thompson 12002ac6454SAndrew Thompson struct mtx sc_mtx; 12102ac6454SAndrew Thompson struct ng_bt_mbufq sc_xmitq_hipri; /* hi-priority transmit queue */ 12202ac6454SAndrew Thompson struct ng_bt_mbufq sc_xmitq; /* low-priority transmit queue */ 12302ac6454SAndrew Thompson 124760bc48eSAndrew Thompson struct usb_xfer *sc_xfer[UDBP_T_MAX]; 12502ac6454SAndrew Thompson node_p sc_node; /* back pointer to node */ 12602ac6454SAndrew Thompson hook_p sc_hook; /* pointer to the hook */ 12702ac6454SAndrew Thompson struct mbuf *sc_bulk_in_buffer; 12802ac6454SAndrew Thompson 12902ac6454SAndrew Thompson uint32_t sc_packets_in; /* packets in from downstream */ 13002ac6454SAndrew Thompson uint32_t sc_packets_out; /* packets out towards downstream */ 13102ac6454SAndrew Thompson 13202ac6454SAndrew Thompson uint8_t sc_flags; 13302ac6454SAndrew Thompson #define UDBP_FLAG_READ_STALL 0x01 /* read transfer stalled */ 13402ac6454SAndrew Thompson #define UDBP_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ 13502ac6454SAndrew Thompson 13602ac6454SAndrew Thompson uint8_t sc_name[16]; 13702ac6454SAndrew Thompson }; 13802ac6454SAndrew Thompson 13902ac6454SAndrew Thompson /* prototypes */ 14002ac6454SAndrew Thompson 14102ac6454SAndrew Thompson static int udbp_modload(module_t mod, int event, void *data); 14202ac6454SAndrew Thompson 14302ac6454SAndrew Thompson static device_probe_t udbp_probe; 14402ac6454SAndrew Thompson static device_attach_t udbp_attach; 14502ac6454SAndrew Thompson static device_detach_t udbp_detach; 14602ac6454SAndrew Thompson 147e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_read_callback; 148e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_read_clear_stall_callback; 149e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_write_callback; 150e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_write_clear_stall_callback; 15102ac6454SAndrew Thompson 15202ac6454SAndrew Thompson static void udbp_bulk_read_complete(node_p, hook_p, void *, int); 15302ac6454SAndrew Thompson 15402ac6454SAndrew Thompson static ng_constructor_t ng_udbp_constructor; 15502ac6454SAndrew Thompson static ng_rcvmsg_t ng_udbp_rcvmsg; 15602ac6454SAndrew Thompson static ng_shutdown_t ng_udbp_rmnode; 15702ac6454SAndrew Thompson static ng_newhook_t ng_udbp_newhook; 15802ac6454SAndrew Thompson static ng_connect_t ng_udbp_connect; 15902ac6454SAndrew Thompson static ng_rcvdata_t ng_udbp_rcvdata; 16002ac6454SAndrew Thompson static ng_disconnect_t ng_udbp_disconnect; 16102ac6454SAndrew Thompson 16202ac6454SAndrew Thompson /* Parse type for struct ngudbpstat */ 16302ac6454SAndrew Thompson static const struct ng_parse_struct_field 16402ac6454SAndrew Thompson ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO; 16502ac6454SAndrew Thompson 16602ac6454SAndrew Thompson static const struct ng_parse_type ng_udbp_stat_type = { 16702ac6454SAndrew Thompson &ng_parse_struct_type, 16802ac6454SAndrew Thompson &ng_udbp_stat_type_fields 16902ac6454SAndrew Thompson }; 17002ac6454SAndrew Thompson 17102ac6454SAndrew Thompson /* List of commands and how to convert arguments to/from ASCII */ 17202ac6454SAndrew Thompson static const struct ng_cmdlist ng_udbp_cmdlist[] = { 17302ac6454SAndrew Thompson { 17402ac6454SAndrew Thompson NGM_UDBP_COOKIE, 17502ac6454SAndrew Thompson NGM_UDBP_GET_STATUS, 17602ac6454SAndrew Thompson "getstatus", 17702ac6454SAndrew Thompson NULL, 17802ac6454SAndrew Thompson &ng_udbp_stat_type, 17902ac6454SAndrew Thompson }, 18002ac6454SAndrew Thompson { 18102ac6454SAndrew Thompson NGM_UDBP_COOKIE, 18202ac6454SAndrew Thompson NGM_UDBP_SET_FLAG, 18302ac6454SAndrew Thompson "setflag", 18402ac6454SAndrew Thompson &ng_parse_int32_type, 18502ac6454SAndrew Thompson NULL 18602ac6454SAndrew Thompson }, 18702ac6454SAndrew Thompson {0} 18802ac6454SAndrew Thompson }; 18902ac6454SAndrew Thompson 19002ac6454SAndrew Thompson /* Netgraph node type descriptor */ 19102ac6454SAndrew Thompson static struct ng_type ng_udbp_typestruct = { 19202ac6454SAndrew Thompson .version = NG_ABI_VERSION, 19302ac6454SAndrew Thompson .name = NG_UDBP_NODE_TYPE, 19402ac6454SAndrew Thompson .constructor = ng_udbp_constructor, 19502ac6454SAndrew Thompson .rcvmsg = ng_udbp_rcvmsg, 19602ac6454SAndrew Thompson .shutdown = ng_udbp_rmnode, 19702ac6454SAndrew Thompson .newhook = ng_udbp_newhook, 19802ac6454SAndrew Thompson .connect = ng_udbp_connect, 19902ac6454SAndrew Thompson .rcvdata = ng_udbp_rcvdata, 20002ac6454SAndrew Thompson .disconnect = ng_udbp_disconnect, 20102ac6454SAndrew Thompson .cmdlist = ng_udbp_cmdlist, 20202ac6454SAndrew Thompson }; 20302ac6454SAndrew Thompson 20402ac6454SAndrew Thompson /* USB config */ 205760bc48eSAndrew Thompson static const struct usb_config udbp_config[UDBP_T_MAX] = { 20602ac6454SAndrew Thompson 20702ac6454SAndrew Thompson [UDBP_T_WR] = { 20802ac6454SAndrew Thompson .type = UE_BULK, 20902ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 21002ac6454SAndrew Thompson .direction = UE_DIR_OUT, 2114eae601eSAndrew Thompson .bufsize = UDBP_BUFFERSIZE, 2124eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 2134eae601eSAndrew Thompson .callback = &udbp_bulk_write_callback, 2144eae601eSAndrew Thompson .timeout = UDBP_TIMEOUT, 21502ac6454SAndrew Thompson }, 21602ac6454SAndrew Thompson 21702ac6454SAndrew Thompson [UDBP_T_RD] = { 21802ac6454SAndrew Thompson .type = UE_BULK, 21902ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 22002ac6454SAndrew Thompson .direction = UE_DIR_IN, 2214eae601eSAndrew Thompson .bufsize = UDBP_BUFFERSIZE, 2224eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 2234eae601eSAndrew Thompson .callback = &udbp_bulk_read_callback, 22402ac6454SAndrew Thompson }, 22502ac6454SAndrew Thompson 22602ac6454SAndrew Thompson [UDBP_T_WR_CS] = { 22702ac6454SAndrew Thompson .type = UE_CONTROL, 22802ac6454SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 22902ac6454SAndrew Thompson .direction = UE_DIR_ANY, 230760bc48eSAndrew Thompson .bufsize = sizeof(struct usb_device_request), 2314eae601eSAndrew Thompson .callback = &udbp_bulk_write_clear_stall_callback, 2324eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 2334eae601eSAndrew Thompson .interval = 50, /* 50ms */ 23402ac6454SAndrew Thompson }, 23502ac6454SAndrew Thompson 23602ac6454SAndrew Thompson [UDBP_T_RD_CS] = { 23702ac6454SAndrew Thompson .type = UE_CONTROL, 23802ac6454SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 23902ac6454SAndrew Thompson .direction = UE_DIR_ANY, 240760bc48eSAndrew Thompson .bufsize = sizeof(struct usb_device_request), 2414eae601eSAndrew Thompson .callback = &udbp_bulk_read_clear_stall_callback, 2424eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 2434eae601eSAndrew Thompson .interval = 50, /* 50ms */ 24402ac6454SAndrew Thompson }, 24502ac6454SAndrew Thompson }; 24602ac6454SAndrew Thompson 24702ac6454SAndrew Thompson static devclass_t udbp_devclass; 24802ac6454SAndrew Thompson 24902ac6454SAndrew Thompson static device_method_t udbp_methods[] = { 25002ac6454SAndrew Thompson /* Device interface */ 25102ac6454SAndrew Thompson DEVMETHOD(device_probe, udbp_probe), 25202ac6454SAndrew Thompson DEVMETHOD(device_attach, udbp_attach), 25302ac6454SAndrew Thompson DEVMETHOD(device_detach, udbp_detach), 25461bfd867SSofian Brabez 25561bfd867SSofian Brabez DEVMETHOD_END 25602ac6454SAndrew Thompson }; 25702ac6454SAndrew Thompson 25802ac6454SAndrew Thompson static driver_t udbp_driver = { 25902ac6454SAndrew Thompson .name = "udbp", 26002ac6454SAndrew Thompson .methods = udbp_methods, 26102ac6454SAndrew Thompson .size = sizeof(struct udbp_softc), 26202ac6454SAndrew Thompson }; 26302ac6454SAndrew Thompson 264f809f280SWarner Losh static const STRUCT_USB_HOST_ID udbp_devs[] = { 265ae9fd9ccSBruce M Simpson {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U258, 0)}, 266f809f280SWarner Losh {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_TURBOCONNECT, 0)}, 267f809f280SWarner Losh {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_GADGETZERO, 0)}, 268f809f280SWarner Losh {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2301, 0)}, 269f809f280SWarner Losh {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2302, 0)}, 2701d4c696aSBruce M Simpson {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL27A1, 0)}, 271f809f280SWarner Losh {USB_VPI(USB_VENDOR_ANCHOR, USB_PRODUCT_ANCHOR_EZLINK, 0)}, 272f809f280SWarner Losh {USB_VPI(USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL620USB, 0)}, 273f809f280SWarner Losh }; 274f809f280SWarner Losh 2759aef556dSAndrew Thompson DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_devclass, udbp_modload, 0); 27602ac6454SAndrew Thompson MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 27702ac6454SAndrew Thompson MODULE_DEPEND(udbp, usb, 1, 1, 1); 278910cb8feSAndrew Thompson MODULE_VERSION(udbp, 1); 279f809f280SWarner Losh USB_PNP_HOST_INFO(udbp_devs); 28002ac6454SAndrew Thompson 28102ac6454SAndrew Thompson static int 28202ac6454SAndrew Thompson udbp_modload(module_t mod, int event, void *data) 28302ac6454SAndrew Thompson { 28402ac6454SAndrew Thompson int error; 28502ac6454SAndrew Thompson 28602ac6454SAndrew Thompson switch (event) { 28702ac6454SAndrew Thompson case MOD_LOAD: 28802ac6454SAndrew Thompson error = ng_newtype(&ng_udbp_typestruct); 28902ac6454SAndrew Thompson if (error != 0) { 29002ac6454SAndrew Thompson printf("%s: Could not register " 29102ac6454SAndrew Thompson "Netgraph node type, error=%d\n", 29202ac6454SAndrew Thompson NG_UDBP_NODE_TYPE, error); 29302ac6454SAndrew Thompson } 29402ac6454SAndrew Thompson break; 29502ac6454SAndrew Thompson 29602ac6454SAndrew Thompson case MOD_UNLOAD: 29702ac6454SAndrew Thompson error = ng_rmtype(&ng_udbp_typestruct); 29802ac6454SAndrew Thompson break; 29902ac6454SAndrew Thompson 30002ac6454SAndrew Thompson default: 30102ac6454SAndrew Thompson error = EOPNOTSUPP; 30202ac6454SAndrew Thompson break; 30302ac6454SAndrew Thompson } 30402ac6454SAndrew Thompson return (error); 30502ac6454SAndrew Thompson } 30602ac6454SAndrew Thompson 30702ac6454SAndrew Thompson static int 30802ac6454SAndrew Thompson udbp_probe(device_t dev) 30902ac6454SAndrew Thompson { 310760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 31102ac6454SAndrew Thompson 3129cfbe3e7SHans Petter Selasky if (uaa->usb_mode != USB_MODE_HOST) 31302ac6454SAndrew Thompson return (ENXIO); 3149cfbe3e7SHans Petter Selasky if (uaa->info.bConfigIndex != 0) 31502ac6454SAndrew Thompson return (ENXIO); 3169cfbe3e7SHans Petter Selasky if (uaa->info.bIfaceIndex != 0) 3179cfbe3e7SHans Petter Selasky return (ENXIO); 3189cfbe3e7SHans Petter Selasky 3199cfbe3e7SHans Petter Selasky return (usbd_lookup_id_by_uaa(udbp_devs, sizeof(udbp_devs), uaa)); 32002ac6454SAndrew Thompson } 32102ac6454SAndrew Thompson 32202ac6454SAndrew Thompson static int 32302ac6454SAndrew Thompson udbp_attach(device_t dev) 32402ac6454SAndrew Thompson { 325760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 32602ac6454SAndrew Thompson struct udbp_softc *sc = device_get_softc(dev); 32702ac6454SAndrew Thompson int error; 32802ac6454SAndrew Thompson 329a593f6b8SAndrew Thompson device_set_usb_desc(dev); 33002ac6454SAndrew Thompson 33102ac6454SAndrew Thompson snprintf(sc->sc_name, sizeof(sc->sc_name), 33202ac6454SAndrew Thompson "%s", device_get_nameunit(dev)); 33302ac6454SAndrew Thompson 33402ac6454SAndrew Thompson mtx_init(&sc->sc_mtx, "udbp lock", NULL, MTX_DEF | MTX_RECURSE); 33502ac6454SAndrew Thompson 336a593f6b8SAndrew Thompson error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 33702ac6454SAndrew Thompson sc->sc_xfer, udbp_config, UDBP_T_MAX, sc, &sc->sc_mtx); 33802ac6454SAndrew Thompson if (error) { 339a593f6b8SAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 34002ac6454SAndrew Thompson goto detach; 34102ac6454SAndrew Thompson } 34202ac6454SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_xmitq, UDBP_Q_MAXLEN); 34302ac6454SAndrew Thompson 34402ac6454SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_xmitq_hipri, UDBP_Q_MAXLEN); 34502ac6454SAndrew Thompson 34602ac6454SAndrew Thompson /* create Netgraph node */ 34702ac6454SAndrew Thompson 34802ac6454SAndrew Thompson if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { 34902ac6454SAndrew Thompson printf("%s: Could not create Netgraph node\n", 35002ac6454SAndrew Thompson sc->sc_name); 35102ac6454SAndrew Thompson sc->sc_node = NULL; 35202ac6454SAndrew Thompson goto detach; 35302ac6454SAndrew Thompson } 35402ac6454SAndrew Thompson /* name node */ 35502ac6454SAndrew Thompson 35602ac6454SAndrew Thompson if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { 35702ac6454SAndrew Thompson printf("%s: Could not name node\n", 35802ac6454SAndrew Thompson sc->sc_name); 35902ac6454SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 36002ac6454SAndrew Thompson sc->sc_node = NULL; 36102ac6454SAndrew Thompson goto detach; 36202ac6454SAndrew Thompson } 36302ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 36402ac6454SAndrew Thompson 36502ac6454SAndrew Thompson /* the device is now operational */ 36602ac6454SAndrew Thompson 36702ac6454SAndrew Thompson return (0); /* success */ 36802ac6454SAndrew Thompson 36902ac6454SAndrew Thompson detach: 37002ac6454SAndrew Thompson udbp_detach(dev); 37102ac6454SAndrew Thompson return (ENOMEM); /* failure */ 37202ac6454SAndrew Thompson } 37302ac6454SAndrew Thompson 37402ac6454SAndrew Thompson static int 37502ac6454SAndrew Thompson udbp_detach(device_t dev) 37602ac6454SAndrew Thompson { 37702ac6454SAndrew Thompson struct udbp_softc *sc = device_get_softc(dev); 37802ac6454SAndrew Thompson 37902ac6454SAndrew Thompson /* destroy Netgraph node */ 38002ac6454SAndrew Thompson 38102ac6454SAndrew Thompson if (sc->sc_node != NULL) { 38202ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, NULL); 38302ac6454SAndrew Thompson ng_rmnode_self(sc->sc_node); 38402ac6454SAndrew Thompson sc->sc_node = NULL; 38502ac6454SAndrew Thompson } 38602ac6454SAndrew Thompson /* free USB transfers, if any */ 38702ac6454SAndrew Thompson 388a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UDBP_T_MAX); 38902ac6454SAndrew Thompson 39002ac6454SAndrew Thompson mtx_destroy(&sc->sc_mtx); 39102ac6454SAndrew Thompson 39202ac6454SAndrew Thompson /* destroy queues */ 39302ac6454SAndrew Thompson 39402ac6454SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq); 39502ac6454SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq_hipri); 39602ac6454SAndrew Thompson 39702ac6454SAndrew Thompson /* extra check */ 39802ac6454SAndrew Thompson 39902ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 40002ac6454SAndrew Thompson m_freem(sc->sc_bulk_in_buffer); 40102ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 40202ac6454SAndrew Thompson } 40302ac6454SAndrew Thompson return (0); /* success */ 40402ac6454SAndrew Thompson } 40502ac6454SAndrew Thompson 40602ac6454SAndrew Thompson static void 407ed6d949aSAndrew Thompson udbp_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 40802ac6454SAndrew Thompson { 409ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 410ed6d949aSAndrew Thompson struct usb_page_cache *pc; 41102ac6454SAndrew Thompson struct mbuf *m; 412ed6d949aSAndrew Thompson int actlen; 413ed6d949aSAndrew Thompson 414ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 41502ac6454SAndrew Thompson 41602ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 41702ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 41802ac6454SAndrew Thompson 41902ac6454SAndrew Thompson /* allocate new mbuf */ 42002ac6454SAndrew Thompson 421c6499eccSGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 42202ac6454SAndrew Thompson 42302ac6454SAndrew Thompson if (m == NULL) { 42402ac6454SAndrew Thompson goto tr_setup; 42502ac6454SAndrew Thompson } 42602ac6454SAndrew Thompson 4272a8c860fSRobert Watson if (!(MCLGET(m, M_NOWAIT))) { 42802ac6454SAndrew Thompson m_freem(m); 42902ac6454SAndrew Thompson goto tr_setup; 43002ac6454SAndrew Thompson } 431ed6d949aSAndrew Thompson m->m_pkthdr.len = m->m_len = actlen; 43202ac6454SAndrew Thompson 433ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 434ed6d949aSAndrew Thompson usbd_copy_out(pc, 0, m->m_data, actlen); 43502ac6454SAndrew Thompson 43602ac6454SAndrew Thompson sc->sc_bulk_in_buffer = m; 43702ac6454SAndrew Thompson 438ed6d949aSAndrew Thompson DPRINTF("received package %d bytes\n", actlen); 43902ac6454SAndrew Thompson 44002ac6454SAndrew Thompson case USB_ST_SETUP: 44102ac6454SAndrew Thompson tr_setup: 44202ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 44302ac6454SAndrew Thompson ng_send_fn(sc->sc_node, NULL, &udbp_bulk_read_complete, NULL, 0); 44402ac6454SAndrew Thompson return; 44502ac6454SAndrew Thompson } 44602ac6454SAndrew Thompson if (sc->sc_flags & UDBP_FLAG_READ_STALL) { 447a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); 44802ac6454SAndrew Thompson return; 44902ac6454SAndrew Thompson } 450ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 451a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 45202ac6454SAndrew Thompson return; 45302ac6454SAndrew Thompson 45402ac6454SAndrew Thompson default: /* Error */ 455ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 45602ac6454SAndrew Thompson /* try to clear stall first */ 45702ac6454SAndrew Thompson sc->sc_flags |= UDBP_FLAG_READ_STALL; 458a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]); 45902ac6454SAndrew Thompson } 46002ac6454SAndrew Thompson return; 46102ac6454SAndrew Thompson 46202ac6454SAndrew Thompson } 46302ac6454SAndrew Thompson } 46402ac6454SAndrew Thompson 46502ac6454SAndrew Thompson static void 466ed6d949aSAndrew Thompson udbp_bulk_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 46702ac6454SAndrew Thompson { 468ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 469760bc48eSAndrew Thompson struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_RD]; 47002ac6454SAndrew Thompson 471a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 47202ac6454SAndrew Thompson DPRINTF("stall cleared\n"); 47302ac6454SAndrew Thompson sc->sc_flags &= ~UDBP_FLAG_READ_STALL; 474a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 47502ac6454SAndrew Thompson } 47602ac6454SAndrew Thompson } 47702ac6454SAndrew Thompson 47802ac6454SAndrew Thompson static void 47902ac6454SAndrew Thompson udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2) 48002ac6454SAndrew Thompson { 48102ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 48202ac6454SAndrew Thompson struct mbuf *m; 48302ac6454SAndrew Thompson int error; 48402ac6454SAndrew Thompson 48502ac6454SAndrew Thompson if (sc == NULL) { 48602ac6454SAndrew Thompson return; 48702ac6454SAndrew Thompson } 48802ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 48902ac6454SAndrew Thompson 49002ac6454SAndrew Thompson m = sc->sc_bulk_in_buffer; 49102ac6454SAndrew Thompson 49202ac6454SAndrew Thompson if (m) { 49302ac6454SAndrew Thompson 49402ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 49502ac6454SAndrew Thompson 49602ac6454SAndrew Thompson if ((sc->sc_hook == NULL) || 49702ac6454SAndrew Thompson NG_HOOK_NOT_VALID(sc->sc_hook)) { 49802ac6454SAndrew Thompson DPRINTF("No upstream hook\n"); 49902ac6454SAndrew Thompson goto done; 50002ac6454SAndrew Thompson } 50102ac6454SAndrew Thompson sc->sc_packets_in++; 50202ac6454SAndrew Thompson 50302ac6454SAndrew Thompson NG_SEND_DATA_ONLY(error, sc->sc_hook, m); 50402ac6454SAndrew Thompson 50502ac6454SAndrew Thompson m = NULL; 50602ac6454SAndrew Thompson } 50702ac6454SAndrew Thompson done: 50802ac6454SAndrew Thompson if (m) { 50902ac6454SAndrew Thompson m_freem(m); 51002ac6454SAndrew Thompson } 51102ac6454SAndrew Thompson /* start USB bulk-in transfer, if not already started */ 51202ac6454SAndrew Thompson 513a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); 51402ac6454SAndrew Thompson 51502ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 51602ac6454SAndrew Thompson } 51702ac6454SAndrew Thompson 51802ac6454SAndrew Thompson static void 519ed6d949aSAndrew Thompson udbp_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 52002ac6454SAndrew Thompson { 521ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 522ed6d949aSAndrew Thompson struct usb_page_cache *pc; 52302ac6454SAndrew Thompson struct mbuf *m; 52402ac6454SAndrew Thompson 52502ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 52602ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 52702ac6454SAndrew Thompson 52802ac6454SAndrew Thompson sc->sc_packets_out++; 52902ac6454SAndrew Thompson 53002ac6454SAndrew Thompson case USB_ST_SETUP: 53102ac6454SAndrew Thompson if (sc->sc_flags & UDBP_FLAG_WRITE_STALL) { 532a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); 53302ac6454SAndrew Thompson return; 53402ac6454SAndrew Thompson } 53502ac6454SAndrew Thompson /* get next mbuf, if any */ 53602ac6454SAndrew Thompson 53702ac6454SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq_hipri, m); 53802ac6454SAndrew Thompson if (m == NULL) { 53902ac6454SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq, m); 54002ac6454SAndrew Thompson if (m == NULL) { 54102ac6454SAndrew Thompson DPRINTF("Data queue is empty\n"); 54202ac6454SAndrew Thompson return; 54302ac6454SAndrew Thompson } 54402ac6454SAndrew Thompson } 54502ac6454SAndrew Thompson if (m->m_pkthdr.len > MCLBYTES) { 54602ac6454SAndrew Thompson DPRINTF("truncating large packet " 54702ac6454SAndrew Thompson "from %d to %d bytes\n", m->m_pkthdr.len, 54802ac6454SAndrew Thompson MCLBYTES); 54902ac6454SAndrew Thompson m->m_pkthdr.len = MCLBYTES; 55002ac6454SAndrew Thompson } 551ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 552ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 55302ac6454SAndrew Thompson 554ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 555ed6d949aSAndrew Thompson 556ed6d949aSAndrew Thompson DPRINTF("packet out: %d bytes\n", m->m_pkthdr.len); 55702ac6454SAndrew Thompson 55802ac6454SAndrew Thompson m_freem(m); 55902ac6454SAndrew Thompson 560a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 56102ac6454SAndrew Thompson return; 56202ac6454SAndrew Thompson 56302ac6454SAndrew Thompson default: /* Error */ 564ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 56502ac6454SAndrew Thompson /* try to clear stall first */ 56602ac6454SAndrew Thompson sc->sc_flags |= UDBP_FLAG_WRITE_STALL; 567a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); 56802ac6454SAndrew Thompson } 56902ac6454SAndrew Thompson return; 57002ac6454SAndrew Thompson 57102ac6454SAndrew Thompson } 57202ac6454SAndrew Thompson } 57302ac6454SAndrew Thompson 57402ac6454SAndrew Thompson static void 575ed6d949aSAndrew Thompson udbp_bulk_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 57602ac6454SAndrew Thompson { 577ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 578760bc48eSAndrew Thompson struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_WR]; 57902ac6454SAndrew Thompson 580a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 58102ac6454SAndrew Thompson DPRINTF("stall cleared\n"); 58202ac6454SAndrew Thompson sc->sc_flags &= ~UDBP_FLAG_WRITE_STALL; 583a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 58402ac6454SAndrew Thompson } 58502ac6454SAndrew Thompson } 58602ac6454SAndrew Thompson 58702ac6454SAndrew Thompson /*********************************************************************** 58802ac6454SAndrew Thompson * Start of Netgraph methods 58902ac6454SAndrew Thompson **********************************************************************/ 59002ac6454SAndrew Thompson 59102ac6454SAndrew Thompson /* 59202ac6454SAndrew Thompson * If this is a device node so this work is done in the attach() 59302ac6454SAndrew Thompson * routine and the constructor will return EINVAL as you should not be able 59402ac6454SAndrew Thompson * to create nodes that depend on hardware (unless you can add the hardware :) 59502ac6454SAndrew Thompson */ 59602ac6454SAndrew Thompson static int 59702ac6454SAndrew Thompson ng_udbp_constructor(node_p node) 59802ac6454SAndrew Thompson { 59902ac6454SAndrew Thompson return (EINVAL); 60002ac6454SAndrew Thompson } 60102ac6454SAndrew Thompson 60202ac6454SAndrew Thompson /* 60302ac6454SAndrew Thompson * Give our ok for a hook to be added... 60402ac6454SAndrew Thompson * If we are not running this might kick a device into life. 60502ac6454SAndrew Thompson * Possibly decode information out of the hook name. 60602ac6454SAndrew Thompson * Add the hook's private info to the hook structure. 60702ac6454SAndrew Thompson * (if we had some). In this example, we assume that there is a 60802ac6454SAndrew Thompson * an array of structs, called 'channel' in the private info, 60902ac6454SAndrew Thompson * one for each active channel. The private 61002ac6454SAndrew Thompson * pointer of each hook points to the appropriate UDBP_hookinfo struct 61102ac6454SAndrew Thompson * so that the source of an input packet is easily identified. 61202ac6454SAndrew Thompson */ 61302ac6454SAndrew Thompson static int 61402ac6454SAndrew Thompson ng_udbp_newhook(node_p node, hook_p hook, const char *name) 61502ac6454SAndrew Thompson { 61602ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 61702ac6454SAndrew Thompson int32_t error = 0; 61802ac6454SAndrew Thompson 61902ac6454SAndrew Thompson if (strcmp(name, NG_UDBP_HOOK_NAME)) { 62002ac6454SAndrew Thompson return (EINVAL); 62102ac6454SAndrew Thompson } 62202ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 62302ac6454SAndrew Thompson 62402ac6454SAndrew Thompson if (sc->sc_hook != NULL) { 62502ac6454SAndrew Thompson error = EISCONN; 62602ac6454SAndrew Thompson } else { 62702ac6454SAndrew Thompson sc->sc_hook = hook; 62802ac6454SAndrew Thompson NG_HOOK_SET_PRIVATE(hook, NULL); 62902ac6454SAndrew Thompson } 63002ac6454SAndrew Thompson 63102ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 63202ac6454SAndrew Thompson 63302ac6454SAndrew Thompson return (error); 63402ac6454SAndrew Thompson } 63502ac6454SAndrew Thompson 63602ac6454SAndrew Thompson /* 63702ac6454SAndrew Thompson * Get a netgraph control message. 63802ac6454SAndrew Thompson * Check it is one we understand. If needed, send a response. 63902ac6454SAndrew Thompson * We could save the address for an async action later, but don't here. 64002ac6454SAndrew Thompson * Always free the message. 64102ac6454SAndrew Thompson * The response should be in a malloc'd region that the caller can 'free'. 64202ac6454SAndrew Thompson * A response is not required. 64302ac6454SAndrew Thompson * Theoretically you could respond defferently to old message types if 64402ac6454SAndrew Thompson * the cookie in the header didn't match what we consider to be current 64502ac6454SAndrew Thompson * (so that old userland programs could continue to work). 64602ac6454SAndrew Thompson */ 64702ac6454SAndrew Thompson static int 64802ac6454SAndrew Thompson ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook) 64902ac6454SAndrew Thompson { 65002ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 65102ac6454SAndrew Thompson struct ng_mesg *resp = NULL; 65202ac6454SAndrew Thompson int error = 0; 65302ac6454SAndrew Thompson struct ng_mesg *msg; 65402ac6454SAndrew Thompson 65502ac6454SAndrew Thompson NGI_GET_MSG(item, msg); 65602ac6454SAndrew Thompson /* Deal with message according to cookie and command */ 65702ac6454SAndrew Thompson switch (msg->header.typecookie) { 65802ac6454SAndrew Thompson case NGM_UDBP_COOKIE: 65902ac6454SAndrew Thompson switch (msg->header.cmd) { 66002ac6454SAndrew Thompson case NGM_UDBP_GET_STATUS: 66102ac6454SAndrew Thompson { 66202ac6454SAndrew Thompson struct ngudbpstat *stats; 66302ac6454SAndrew Thompson 66402ac6454SAndrew Thompson NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 66502ac6454SAndrew Thompson if (!resp) { 66602ac6454SAndrew Thompson error = ENOMEM; 66702ac6454SAndrew Thompson break; 66802ac6454SAndrew Thompson } 66902ac6454SAndrew Thompson stats = (struct ngudbpstat *)resp->data; 67002ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 67102ac6454SAndrew Thompson stats->packets_in = sc->sc_packets_in; 67202ac6454SAndrew Thompson stats->packets_out = sc->sc_packets_out; 67302ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 67402ac6454SAndrew Thompson break; 67502ac6454SAndrew Thompson } 67602ac6454SAndrew Thompson case NGM_UDBP_SET_FLAG: 67702ac6454SAndrew Thompson if (msg->header.arglen != sizeof(uint32_t)) { 67802ac6454SAndrew Thompson error = EINVAL; 67902ac6454SAndrew Thompson break; 68002ac6454SAndrew Thompson } 68102ac6454SAndrew Thompson DPRINTF("flags = 0x%08x\n", 68202ac6454SAndrew Thompson *((uint32_t *)msg->data)); 68302ac6454SAndrew Thompson break; 68402ac6454SAndrew Thompson default: 68502ac6454SAndrew Thompson error = EINVAL; /* unknown command */ 68602ac6454SAndrew Thompson break; 68702ac6454SAndrew Thompson } 68802ac6454SAndrew Thompson break; 68902ac6454SAndrew Thompson default: 69002ac6454SAndrew Thompson error = EINVAL; /* unknown cookie type */ 69102ac6454SAndrew Thompson break; 69202ac6454SAndrew Thompson } 69302ac6454SAndrew Thompson 69402ac6454SAndrew Thompson /* Take care of synchronous response, if any */ 69502ac6454SAndrew Thompson NG_RESPOND_MSG(error, node, item, resp); 69602ac6454SAndrew Thompson NG_FREE_MSG(msg); 69702ac6454SAndrew Thompson return (error); 69802ac6454SAndrew Thompson } 69902ac6454SAndrew Thompson 70002ac6454SAndrew Thompson /* 70102ac6454SAndrew Thompson * Accept data from the hook and queue it for output. 70202ac6454SAndrew Thompson */ 70302ac6454SAndrew Thompson static int 70402ac6454SAndrew Thompson ng_udbp_rcvdata(hook_p hook, item_p item) 70502ac6454SAndrew Thompson { 70602ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 70702ac6454SAndrew Thompson struct ng_bt_mbufq *queue_ptr; 70802ac6454SAndrew Thompson struct mbuf *m; 70902ac6454SAndrew Thompson struct ng_tag_prio *ptag; 71002ac6454SAndrew Thompson int error; 71102ac6454SAndrew Thompson 71202ac6454SAndrew Thompson if (sc == NULL) { 71302ac6454SAndrew Thompson NG_FREE_ITEM(item); 71402ac6454SAndrew Thompson return (EHOSTDOWN); 71502ac6454SAndrew Thompson } 71602ac6454SAndrew Thompson NGI_GET_M(item, m); 71702ac6454SAndrew Thompson NG_FREE_ITEM(item); 71802ac6454SAndrew Thompson 71902ac6454SAndrew Thompson /* 72002ac6454SAndrew Thompson * Now queue the data for when it can be sent 72102ac6454SAndrew Thompson */ 72202ac6454SAndrew Thompson ptag = (void *)m_tag_locate(m, NGM_GENERIC_COOKIE, 72302ac6454SAndrew Thompson NG_TAG_PRIO, NULL); 72402ac6454SAndrew Thompson 72502ac6454SAndrew Thompson if (ptag && (ptag->priority > NG_PRIO_CUTOFF)) 72602ac6454SAndrew Thompson queue_ptr = &sc->sc_xmitq_hipri; 72702ac6454SAndrew Thompson else 72802ac6454SAndrew Thompson queue_ptr = &sc->sc_xmitq; 72902ac6454SAndrew Thompson 73002ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 73102ac6454SAndrew Thompson 73202ac6454SAndrew Thompson if (NG_BT_MBUFQ_FULL(queue_ptr)) { 73302ac6454SAndrew Thompson NG_BT_MBUFQ_DROP(queue_ptr); 73402ac6454SAndrew Thompson NG_FREE_M(m); 73502ac6454SAndrew Thompson error = ENOBUFS; 73602ac6454SAndrew Thompson } else { 73702ac6454SAndrew Thompson NG_BT_MBUFQ_ENQUEUE(queue_ptr, m); 73802ac6454SAndrew Thompson /* 73902ac6454SAndrew Thompson * start bulk-out transfer, if not already started: 74002ac6454SAndrew Thompson */ 741a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); 74202ac6454SAndrew Thompson error = 0; 74302ac6454SAndrew Thompson } 74402ac6454SAndrew Thompson 74502ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 74602ac6454SAndrew Thompson 74702ac6454SAndrew Thompson return (error); 74802ac6454SAndrew Thompson } 74902ac6454SAndrew Thompson 75002ac6454SAndrew Thompson /* 75102ac6454SAndrew Thompson * Do local shutdown processing.. 75220733245SPedro F. Giffuni * We are a persistent device, we refuse to go away, and 75302ac6454SAndrew Thompson * only remove our links and reset ourself. 75402ac6454SAndrew Thompson */ 75502ac6454SAndrew Thompson static int 75602ac6454SAndrew Thompson ng_udbp_rmnode(node_p node) 75702ac6454SAndrew Thompson { 75802ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 75902ac6454SAndrew Thompson 76002ac6454SAndrew Thompson /* Let old node go */ 76102ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(node, NULL); 76202ac6454SAndrew Thompson NG_NODE_UNREF(node); /* forget it ever existed */ 76302ac6454SAndrew Thompson 76402ac6454SAndrew Thompson if (sc == NULL) { 76502ac6454SAndrew Thompson goto done; 76602ac6454SAndrew Thompson } 76702ac6454SAndrew Thompson /* Create Netgraph node */ 76802ac6454SAndrew Thompson if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { 76902ac6454SAndrew Thompson printf("%s: Could not create Netgraph node\n", 77002ac6454SAndrew Thompson sc->sc_name); 77102ac6454SAndrew Thompson sc->sc_node = NULL; 77202ac6454SAndrew Thompson goto done; 77302ac6454SAndrew Thompson } 77402ac6454SAndrew Thompson /* Name node */ 77502ac6454SAndrew Thompson if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { 77602ac6454SAndrew Thompson printf("%s: Could not name Netgraph node\n", 77702ac6454SAndrew Thompson sc->sc_name); 77802ac6454SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 77902ac6454SAndrew Thompson sc->sc_node = NULL; 78002ac6454SAndrew Thompson goto done; 78102ac6454SAndrew Thompson } 78202ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 78302ac6454SAndrew Thompson 78402ac6454SAndrew Thompson done: 78502ac6454SAndrew Thompson if (sc) { 78602ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 78702ac6454SAndrew Thompson } 78802ac6454SAndrew Thompson return (0); 78902ac6454SAndrew Thompson } 79002ac6454SAndrew Thompson 79102ac6454SAndrew Thompson /* 79202ac6454SAndrew Thompson * This is called once we've already connected a new hook to the other node. 79302ac6454SAndrew Thompson * It gives us a chance to balk at the last minute. 79402ac6454SAndrew Thompson */ 79502ac6454SAndrew Thompson static int 79602ac6454SAndrew Thompson ng_udbp_connect(hook_p hook) 79702ac6454SAndrew Thompson { 79802ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 79902ac6454SAndrew Thompson 80002ac6454SAndrew Thompson /* probably not at splnet, force outward queueing */ 80102ac6454SAndrew Thompson NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 80202ac6454SAndrew Thompson 80302ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 80402ac6454SAndrew Thompson 80502ac6454SAndrew Thompson sc->sc_flags |= (UDBP_FLAG_READ_STALL | 80602ac6454SAndrew Thompson UDBP_FLAG_WRITE_STALL); 80702ac6454SAndrew Thompson 80802ac6454SAndrew Thompson /* start bulk-in transfer */ 809a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); 81002ac6454SAndrew Thompson 81102ac6454SAndrew Thompson /* start bulk-out transfer */ 812a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); 81302ac6454SAndrew Thompson 81402ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 81502ac6454SAndrew Thompson 81602ac6454SAndrew Thompson return (0); 81702ac6454SAndrew Thompson } 81802ac6454SAndrew Thompson 81902ac6454SAndrew Thompson /* 82002ac6454SAndrew Thompson * Dook disconnection 82102ac6454SAndrew Thompson * 82202ac6454SAndrew Thompson * For this type, removal of the last link destroys the node 82302ac6454SAndrew Thompson */ 82402ac6454SAndrew Thompson static int 82502ac6454SAndrew Thompson ng_udbp_disconnect(hook_p hook) 82602ac6454SAndrew Thompson { 82702ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 82802ac6454SAndrew Thompson int error = 0; 82902ac6454SAndrew Thompson 83002ac6454SAndrew Thompson if (sc != NULL) { 83102ac6454SAndrew Thompson 83202ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 83302ac6454SAndrew Thompson 83402ac6454SAndrew Thompson if (hook != sc->sc_hook) { 83502ac6454SAndrew Thompson error = EINVAL; 83602ac6454SAndrew Thompson } else { 83702ac6454SAndrew Thompson 83802ac6454SAndrew Thompson /* stop bulk-in transfer */ 839a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD_CS]); 840a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD]); 84102ac6454SAndrew Thompson 84202ac6454SAndrew Thompson /* stop bulk-out transfer */ 843a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR_CS]); 844a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR]); 84502ac6454SAndrew Thompson 84602ac6454SAndrew Thompson /* cleanup queues */ 84702ac6454SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq); 84802ac6454SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq_hipri); 84902ac6454SAndrew Thompson 85002ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 85102ac6454SAndrew Thompson m_freem(sc->sc_bulk_in_buffer); 85202ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 85302ac6454SAndrew Thompson } 85402ac6454SAndrew Thompson sc->sc_hook = NULL; 85502ac6454SAndrew Thompson } 85602ac6454SAndrew Thompson 85702ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 85802ac6454SAndrew Thompson } 85902ac6454SAndrew Thompson if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 86002ac6454SAndrew Thompson && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 86102ac6454SAndrew Thompson ng_rmnode_self(NG_HOOK_NODE(hook)); 86202ac6454SAndrew Thompson 86302ac6454SAndrew Thompson return (error); 86402ac6454SAndrew Thompson } 865