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 /* 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 /* probe/attach/detach: 4502ac6454SAndrew Thompson * Connect the driver to the hardware and netgraph 4602ac6454SAndrew Thompson * 4702ac6454SAndrew Thompson * The reason we submit a bulk in transfer is that USB does not know about 4802ac6454SAndrew Thompson * interrupts. The bulk transfer continuously polls the device for data. 4902ac6454SAndrew Thompson * While the device has no data available, the device NAKs the TDs. As soon 5002ac6454SAndrew Thompson * as there is data, the transfer happens and the data comes flowing in. 5102ac6454SAndrew Thompson * 5202ac6454SAndrew Thompson * In case you were wondering, interrupt transfers happen exactly that way. 5302ac6454SAndrew Thompson * It therefore doesn't make sense to use the interrupt pipe to signal 5402ac6454SAndrew Thompson * 'data ready' and then schedule a bulk transfer to fetch it. That would 5502ac6454SAndrew Thompson * incur a 2ms delay at least, without reducing bandwidth requirements. 5602ac6454SAndrew Thompson * 5702ac6454SAndrew Thompson */ 5802ac6454SAndrew Thompson 59ed6d949aSAndrew Thompson #include <sys/stdint.h> 60ed6d949aSAndrew Thompson #include <sys/stddef.h> 61ed6d949aSAndrew Thompson #include <sys/param.h> 62ed6d949aSAndrew Thompson #include <sys/queue.h> 63ed6d949aSAndrew Thompson #include <sys/types.h> 64ed6d949aSAndrew Thompson #include <sys/systm.h> 65ed6d949aSAndrew Thompson #include <sys/kernel.h> 66ed6d949aSAndrew Thompson #include <sys/bus.h> 67ed6d949aSAndrew Thompson #include <sys/module.h> 68ed6d949aSAndrew Thompson #include <sys/lock.h> 69ed6d949aSAndrew Thompson #include <sys/mutex.h> 70ed6d949aSAndrew Thompson #include <sys/condvar.h> 71ed6d949aSAndrew Thompson #include <sys/sysctl.h> 72ed6d949aSAndrew Thompson #include <sys/sx.h> 73ed6d949aSAndrew Thompson #include <sys/unistd.h> 74ed6d949aSAndrew Thompson #include <sys/callout.h> 75ed6d949aSAndrew Thompson #include <sys/malloc.h> 76ed6d949aSAndrew Thompson #include <sys/priv.h> 77ed6d949aSAndrew Thompson 7802ac6454SAndrew Thompson #include <dev/usb/usb.h> 79ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 80ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 81ed6d949aSAndrew Thompson #include "usbdevs.h" 8202ac6454SAndrew Thompson 8302ac6454SAndrew Thompson #define USB_DEBUG_VAR udbp_debug 8402ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 8502ac6454SAndrew Thompson 8602ac6454SAndrew Thompson #include <sys/mbuf.h> 8702ac6454SAndrew Thompson 8802ac6454SAndrew Thompson #include <netgraph/ng_message.h> 8902ac6454SAndrew Thompson #include <netgraph/netgraph.h> 9002ac6454SAndrew Thompson #include <netgraph/ng_parse.h> 9102ac6454SAndrew Thompson #include <netgraph/bluetooth/include/ng_bluetooth.h> 9202ac6454SAndrew Thompson 9302ac6454SAndrew Thompson #include <dev/usb/misc/udbp.h> 9402ac6454SAndrew Thompson 95b850ecc1SAndrew Thompson #ifdef USB_DEBUG 9602ac6454SAndrew Thompson static int udbp_debug = 0; 9702ac6454SAndrew Thompson 98f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 99f8d2b1f3SPawel Biernacki "USB udbp"); 100ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RWTUN, 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 struct mtx sc_mtx; 11702ac6454SAndrew Thompson struct ng_bt_mbufq sc_xmitq_hipri; /* hi-priority transmit queue */ 11802ac6454SAndrew Thompson struct ng_bt_mbufq sc_xmitq; /* low-priority transmit queue */ 11902ac6454SAndrew Thompson 120760bc48eSAndrew Thompson struct usb_xfer *sc_xfer[UDBP_T_MAX]; 12102ac6454SAndrew Thompson node_p sc_node; /* back pointer to node */ 12202ac6454SAndrew Thompson hook_p sc_hook; /* pointer to the hook */ 12302ac6454SAndrew Thompson struct mbuf *sc_bulk_in_buffer; 12402ac6454SAndrew Thompson 12502ac6454SAndrew Thompson uint32_t sc_packets_in; /* packets in from downstream */ 12602ac6454SAndrew Thompson uint32_t sc_packets_out; /* packets out towards downstream */ 12702ac6454SAndrew Thompson 12802ac6454SAndrew Thompson uint8_t sc_flags; 12902ac6454SAndrew Thompson #define UDBP_FLAG_READ_STALL 0x01 /* read transfer stalled */ 13002ac6454SAndrew Thompson #define UDBP_FLAG_WRITE_STALL 0x02 /* write transfer stalled */ 13102ac6454SAndrew Thompson 13202ac6454SAndrew Thompson uint8_t sc_name[16]; 13302ac6454SAndrew Thompson }; 13402ac6454SAndrew Thompson 13502ac6454SAndrew Thompson /* prototypes */ 13602ac6454SAndrew Thompson 13702ac6454SAndrew Thompson static int udbp_modload(module_t mod, int event, void *data); 13802ac6454SAndrew Thompson 13902ac6454SAndrew Thompson static device_probe_t udbp_probe; 14002ac6454SAndrew Thompson static device_attach_t udbp_attach; 14102ac6454SAndrew Thompson static device_detach_t udbp_detach; 14202ac6454SAndrew Thompson 143e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_read_callback; 144e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_read_clear_stall_callback; 145e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_write_callback; 146e0a69b51SAndrew Thompson static usb_callback_t udbp_bulk_write_clear_stall_callback; 14702ac6454SAndrew Thompson 14802ac6454SAndrew Thompson static void udbp_bulk_read_complete(node_p, hook_p, void *, int); 14902ac6454SAndrew Thompson 15002ac6454SAndrew Thompson static ng_constructor_t ng_udbp_constructor; 15102ac6454SAndrew Thompson static ng_rcvmsg_t ng_udbp_rcvmsg; 15202ac6454SAndrew Thompson static ng_shutdown_t ng_udbp_rmnode; 15302ac6454SAndrew Thompson static ng_newhook_t ng_udbp_newhook; 15402ac6454SAndrew Thompson static ng_connect_t ng_udbp_connect; 15502ac6454SAndrew Thompson static ng_rcvdata_t ng_udbp_rcvdata; 15602ac6454SAndrew Thompson static ng_disconnect_t ng_udbp_disconnect; 15702ac6454SAndrew Thompson 15802ac6454SAndrew Thompson /* Parse type for struct ngudbpstat */ 15902ac6454SAndrew Thompson static const struct ng_parse_struct_field 16002ac6454SAndrew Thompson ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO; 16102ac6454SAndrew Thompson 16202ac6454SAndrew Thompson static const struct ng_parse_type ng_udbp_stat_type = { 16302ac6454SAndrew Thompson &ng_parse_struct_type, 16402ac6454SAndrew Thompson &ng_udbp_stat_type_fields 16502ac6454SAndrew Thompson }; 16602ac6454SAndrew Thompson 16702ac6454SAndrew Thompson /* List of commands and how to convert arguments to/from ASCII */ 16802ac6454SAndrew Thompson static const struct ng_cmdlist ng_udbp_cmdlist[] = { 16902ac6454SAndrew Thompson { 17002ac6454SAndrew Thompson NGM_UDBP_COOKIE, 17102ac6454SAndrew Thompson NGM_UDBP_GET_STATUS, 17202ac6454SAndrew Thompson "getstatus", 17302ac6454SAndrew Thompson NULL, 17402ac6454SAndrew Thompson &ng_udbp_stat_type, 17502ac6454SAndrew Thompson }, 17602ac6454SAndrew Thompson { 17702ac6454SAndrew Thompson NGM_UDBP_COOKIE, 17802ac6454SAndrew Thompson NGM_UDBP_SET_FLAG, 17902ac6454SAndrew Thompson "setflag", 18002ac6454SAndrew Thompson &ng_parse_int32_type, 18102ac6454SAndrew Thompson NULL 18202ac6454SAndrew Thompson }, 18302ac6454SAndrew Thompson {0} 18402ac6454SAndrew Thompson }; 18502ac6454SAndrew Thompson 18602ac6454SAndrew Thompson /* Netgraph node type descriptor */ 18702ac6454SAndrew Thompson static struct ng_type ng_udbp_typestruct = { 18802ac6454SAndrew Thompson .version = NG_ABI_VERSION, 18902ac6454SAndrew Thompson .name = NG_UDBP_NODE_TYPE, 19002ac6454SAndrew Thompson .constructor = ng_udbp_constructor, 19102ac6454SAndrew Thompson .rcvmsg = ng_udbp_rcvmsg, 19202ac6454SAndrew Thompson .shutdown = ng_udbp_rmnode, 19302ac6454SAndrew Thompson .newhook = ng_udbp_newhook, 19402ac6454SAndrew Thompson .connect = ng_udbp_connect, 19502ac6454SAndrew Thompson .rcvdata = ng_udbp_rcvdata, 19602ac6454SAndrew Thompson .disconnect = ng_udbp_disconnect, 19702ac6454SAndrew Thompson .cmdlist = ng_udbp_cmdlist, 19802ac6454SAndrew Thompson }; 19902ac6454SAndrew Thompson 20002ac6454SAndrew Thompson /* USB config */ 201760bc48eSAndrew Thompson static const struct usb_config udbp_config[UDBP_T_MAX] = { 20202ac6454SAndrew Thompson [UDBP_T_WR] = { 20302ac6454SAndrew Thompson .type = UE_BULK, 20402ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 20502ac6454SAndrew Thompson .direction = UE_DIR_OUT, 2064eae601eSAndrew Thompson .bufsize = UDBP_BUFFERSIZE, 2074eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 2084eae601eSAndrew Thompson .callback = &udbp_bulk_write_callback, 2094eae601eSAndrew Thompson .timeout = UDBP_TIMEOUT, 21002ac6454SAndrew Thompson }, 21102ac6454SAndrew Thompson 21202ac6454SAndrew Thompson [UDBP_T_RD] = { 21302ac6454SAndrew Thompson .type = UE_BULK, 21402ac6454SAndrew Thompson .endpoint = UE_ADDR_ANY, 21502ac6454SAndrew Thompson .direction = UE_DIR_IN, 2164eae601eSAndrew Thompson .bufsize = UDBP_BUFFERSIZE, 2174eae601eSAndrew Thompson .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 2184eae601eSAndrew Thompson .callback = &udbp_bulk_read_callback, 21902ac6454SAndrew Thompson }, 22002ac6454SAndrew Thompson 22102ac6454SAndrew Thompson [UDBP_T_WR_CS] = { 22202ac6454SAndrew Thompson .type = UE_CONTROL, 22302ac6454SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 22402ac6454SAndrew Thompson .direction = UE_DIR_ANY, 225760bc48eSAndrew Thompson .bufsize = sizeof(struct usb_device_request), 2264eae601eSAndrew Thompson .callback = &udbp_bulk_write_clear_stall_callback, 2274eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 2284eae601eSAndrew Thompson .interval = 50, /* 50ms */ 22902ac6454SAndrew Thompson }, 23002ac6454SAndrew Thompson 23102ac6454SAndrew Thompson [UDBP_T_RD_CS] = { 23202ac6454SAndrew Thompson .type = UE_CONTROL, 23302ac6454SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 23402ac6454SAndrew Thompson .direction = UE_DIR_ANY, 235760bc48eSAndrew Thompson .bufsize = sizeof(struct usb_device_request), 2364eae601eSAndrew Thompson .callback = &udbp_bulk_read_clear_stall_callback, 2374eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 2384eae601eSAndrew Thompson .interval = 50, /* 50ms */ 23902ac6454SAndrew Thompson }, 24002ac6454SAndrew Thompson }; 24102ac6454SAndrew Thompson 24202ac6454SAndrew Thompson static device_method_t udbp_methods[] = { 24302ac6454SAndrew Thompson /* Device interface */ 24402ac6454SAndrew Thompson DEVMETHOD(device_probe, udbp_probe), 24502ac6454SAndrew Thompson DEVMETHOD(device_attach, udbp_attach), 24602ac6454SAndrew Thompson DEVMETHOD(device_detach, udbp_detach), 24761bfd867SSofian Brabez 24861bfd867SSofian Brabez DEVMETHOD_END 24902ac6454SAndrew Thompson }; 25002ac6454SAndrew Thompson 25102ac6454SAndrew Thompson static driver_t udbp_driver = { 25202ac6454SAndrew Thompson .name = "udbp", 25302ac6454SAndrew Thompson .methods = udbp_methods, 25402ac6454SAndrew Thompson .size = sizeof(struct udbp_softc), 25502ac6454SAndrew Thompson }; 25602ac6454SAndrew Thompson 257f809f280SWarner Losh static const STRUCT_USB_HOST_ID udbp_devs[] = { 258ae9fd9ccSBruce M Simpson {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U258, 0)}, 259f809f280SWarner Losh {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_TURBOCONNECT, 0)}, 260f809f280SWarner Losh {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_GADGETZERO, 0)}, 261f809f280SWarner Losh {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2301, 0)}, 262f809f280SWarner Losh {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2302, 0)}, 2631d4c696aSBruce M Simpson {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL27A1, 0)}, 264f809f280SWarner Losh {USB_VPI(USB_VENDOR_ANCHOR, USB_PRODUCT_ANCHOR_EZLINK, 0)}, 265f809f280SWarner Losh {USB_VPI(USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL620USB, 0)}, 266f809f280SWarner Losh }; 267f809f280SWarner Losh 268bc9372d7SJohn Baldwin DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_modload, NULL); 26902ac6454SAndrew Thompson MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); 27002ac6454SAndrew Thompson MODULE_DEPEND(udbp, usb, 1, 1, 1); 271910cb8feSAndrew Thompson MODULE_VERSION(udbp, 1); 272f809f280SWarner Losh USB_PNP_HOST_INFO(udbp_devs); 27302ac6454SAndrew Thompson 27402ac6454SAndrew Thompson static int 27502ac6454SAndrew Thompson udbp_modload(module_t mod, int event, void *data) 27602ac6454SAndrew Thompson { 27702ac6454SAndrew Thompson int error; 27802ac6454SAndrew Thompson 27902ac6454SAndrew Thompson switch (event) { 28002ac6454SAndrew Thompson case MOD_LOAD: 28102ac6454SAndrew Thompson error = ng_newtype(&ng_udbp_typestruct); 28202ac6454SAndrew Thompson if (error != 0) { 28302ac6454SAndrew Thompson printf("%s: Could not register " 28402ac6454SAndrew Thompson "Netgraph node type, error=%d\n", 28502ac6454SAndrew Thompson NG_UDBP_NODE_TYPE, error); 28602ac6454SAndrew Thompson } 28702ac6454SAndrew Thompson break; 28802ac6454SAndrew Thompson 28902ac6454SAndrew Thompson case MOD_UNLOAD: 29002ac6454SAndrew Thompson error = ng_rmtype(&ng_udbp_typestruct); 29102ac6454SAndrew Thompson break; 29202ac6454SAndrew Thompson 29302ac6454SAndrew Thompson default: 29402ac6454SAndrew Thompson error = EOPNOTSUPP; 29502ac6454SAndrew Thompson break; 29602ac6454SAndrew Thompson } 29702ac6454SAndrew Thompson return (error); 29802ac6454SAndrew Thompson } 29902ac6454SAndrew Thompson 30002ac6454SAndrew Thompson static int 30102ac6454SAndrew Thompson udbp_probe(device_t dev) 30202ac6454SAndrew Thompson { 303760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 30402ac6454SAndrew Thompson 3059cfbe3e7SHans Petter Selasky if (uaa->usb_mode != USB_MODE_HOST) 30602ac6454SAndrew Thompson return (ENXIO); 3079cfbe3e7SHans Petter Selasky if (uaa->info.bConfigIndex != 0) 30802ac6454SAndrew Thompson return (ENXIO); 3099cfbe3e7SHans Petter Selasky if (uaa->info.bIfaceIndex != 0) 3109cfbe3e7SHans Petter Selasky return (ENXIO); 3119cfbe3e7SHans Petter Selasky 3129cfbe3e7SHans Petter Selasky return (usbd_lookup_id_by_uaa(udbp_devs, sizeof(udbp_devs), uaa)); 31302ac6454SAndrew Thompson } 31402ac6454SAndrew Thompson 31502ac6454SAndrew Thompson static int 31602ac6454SAndrew Thompson udbp_attach(device_t dev) 31702ac6454SAndrew Thompson { 318760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 31902ac6454SAndrew Thompson struct udbp_softc *sc = device_get_softc(dev); 32002ac6454SAndrew Thompson int error; 32102ac6454SAndrew Thompson 322a593f6b8SAndrew Thompson device_set_usb_desc(dev); 32302ac6454SAndrew Thompson 32402ac6454SAndrew Thompson snprintf(sc->sc_name, sizeof(sc->sc_name), 32502ac6454SAndrew Thompson "%s", device_get_nameunit(dev)); 32602ac6454SAndrew Thompson 32702ac6454SAndrew Thompson mtx_init(&sc->sc_mtx, "udbp lock", NULL, MTX_DEF | MTX_RECURSE); 32802ac6454SAndrew Thompson 329a593f6b8SAndrew Thompson error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, 33002ac6454SAndrew Thompson sc->sc_xfer, udbp_config, UDBP_T_MAX, sc, &sc->sc_mtx); 33102ac6454SAndrew Thompson if (error) { 332a593f6b8SAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 33302ac6454SAndrew Thompson goto detach; 33402ac6454SAndrew Thompson } 33502ac6454SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_xmitq, UDBP_Q_MAXLEN); 33602ac6454SAndrew Thompson 33702ac6454SAndrew Thompson NG_BT_MBUFQ_INIT(&sc->sc_xmitq_hipri, UDBP_Q_MAXLEN); 33802ac6454SAndrew Thompson 33902ac6454SAndrew Thompson /* create Netgraph node */ 34002ac6454SAndrew Thompson 34102ac6454SAndrew Thompson if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { 34202ac6454SAndrew Thompson printf("%s: Could not create Netgraph node\n", 34302ac6454SAndrew Thompson sc->sc_name); 34402ac6454SAndrew Thompson sc->sc_node = NULL; 34502ac6454SAndrew Thompson goto detach; 34602ac6454SAndrew Thompson } 34702ac6454SAndrew Thompson /* name node */ 34802ac6454SAndrew Thompson 34902ac6454SAndrew Thompson if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { 35002ac6454SAndrew Thompson printf("%s: Could not name node\n", 35102ac6454SAndrew Thompson sc->sc_name); 35202ac6454SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 35302ac6454SAndrew Thompson sc->sc_node = NULL; 35402ac6454SAndrew Thompson goto detach; 35502ac6454SAndrew Thompson } 35602ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 35702ac6454SAndrew Thompson 35802ac6454SAndrew Thompson /* the device is now operational */ 35902ac6454SAndrew Thompson 36002ac6454SAndrew Thompson return (0); /* success */ 36102ac6454SAndrew Thompson 36202ac6454SAndrew Thompson detach: 36302ac6454SAndrew Thompson udbp_detach(dev); 36402ac6454SAndrew Thompson return (ENOMEM); /* failure */ 36502ac6454SAndrew Thompson } 36602ac6454SAndrew Thompson 36702ac6454SAndrew Thompson static int 36802ac6454SAndrew Thompson udbp_detach(device_t dev) 36902ac6454SAndrew Thompson { 37002ac6454SAndrew Thompson struct udbp_softc *sc = device_get_softc(dev); 37102ac6454SAndrew Thompson 37202ac6454SAndrew Thompson /* destroy Netgraph node */ 37302ac6454SAndrew Thompson 37402ac6454SAndrew Thompson if (sc->sc_node != NULL) { 37502ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, NULL); 37602ac6454SAndrew Thompson ng_rmnode_self(sc->sc_node); 37702ac6454SAndrew Thompson sc->sc_node = NULL; 37802ac6454SAndrew Thompson } 37902ac6454SAndrew Thompson /* free USB transfers, if any */ 38002ac6454SAndrew Thompson 381a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, UDBP_T_MAX); 38202ac6454SAndrew Thompson 38302ac6454SAndrew Thompson mtx_destroy(&sc->sc_mtx); 38402ac6454SAndrew Thompson 38502ac6454SAndrew Thompson /* destroy queues */ 38602ac6454SAndrew Thompson 38702ac6454SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq); 38802ac6454SAndrew Thompson NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq_hipri); 38902ac6454SAndrew Thompson 39002ac6454SAndrew Thompson /* extra check */ 39102ac6454SAndrew Thompson 39202ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 39302ac6454SAndrew Thompson m_freem(sc->sc_bulk_in_buffer); 39402ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 39502ac6454SAndrew Thompson } 39602ac6454SAndrew Thompson return (0); /* success */ 39702ac6454SAndrew Thompson } 39802ac6454SAndrew Thompson 39902ac6454SAndrew Thompson static void 400ed6d949aSAndrew Thompson udbp_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 40102ac6454SAndrew Thompson { 402ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 403ed6d949aSAndrew Thompson struct usb_page_cache *pc; 40402ac6454SAndrew Thompson struct mbuf *m; 405ed6d949aSAndrew Thompson int actlen; 406ed6d949aSAndrew Thompson 407ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 40802ac6454SAndrew Thompson 40902ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 41002ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 41102ac6454SAndrew Thompson 41202ac6454SAndrew Thompson /* allocate new mbuf */ 41302ac6454SAndrew Thompson 414c6499eccSGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 41502ac6454SAndrew Thompson 41602ac6454SAndrew Thompson if (m == NULL) { 41702ac6454SAndrew Thompson goto tr_setup; 41802ac6454SAndrew Thompson } 41902ac6454SAndrew Thompson 4202a8c860fSRobert Watson if (!(MCLGET(m, M_NOWAIT))) { 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 static void 458ed6d949aSAndrew Thompson udbp_bulk_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 45902ac6454SAndrew Thompson { 460ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 461760bc48eSAndrew Thompson struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_RD]; 46202ac6454SAndrew Thompson 463a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 46402ac6454SAndrew Thompson DPRINTF("stall cleared\n"); 46502ac6454SAndrew Thompson sc->sc_flags &= ~UDBP_FLAG_READ_STALL; 466a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 46702ac6454SAndrew Thompson } 46802ac6454SAndrew Thompson } 46902ac6454SAndrew Thompson 47002ac6454SAndrew Thompson static void 47102ac6454SAndrew Thompson udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2) 47202ac6454SAndrew Thompson { 47302ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 47402ac6454SAndrew Thompson struct mbuf *m; 47502ac6454SAndrew Thompson int error; 47602ac6454SAndrew Thompson 47702ac6454SAndrew Thompson if (sc == NULL) { 47802ac6454SAndrew Thompson return; 47902ac6454SAndrew Thompson } 48002ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 48102ac6454SAndrew Thompson 48202ac6454SAndrew Thompson m = sc->sc_bulk_in_buffer; 48302ac6454SAndrew Thompson 48402ac6454SAndrew Thompson if (m) { 48502ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 48602ac6454SAndrew Thompson 48702ac6454SAndrew Thompson if ((sc->sc_hook == NULL) || 48802ac6454SAndrew Thompson NG_HOOK_NOT_VALID(sc->sc_hook)) { 48902ac6454SAndrew Thompson DPRINTF("No upstream hook\n"); 49002ac6454SAndrew Thompson goto done; 49102ac6454SAndrew Thompson } 49202ac6454SAndrew Thompson sc->sc_packets_in++; 49302ac6454SAndrew Thompson 49402ac6454SAndrew Thompson NG_SEND_DATA_ONLY(error, sc->sc_hook, m); 49502ac6454SAndrew Thompson 49602ac6454SAndrew Thompson m = NULL; 49702ac6454SAndrew Thompson } 49802ac6454SAndrew Thompson done: 49902ac6454SAndrew Thompson if (m) { 50002ac6454SAndrew Thompson m_freem(m); 50102ac6454SAndrew Thompson } 50202ac6454SAndrew Thompson /* start USB bulk-in transfer, if not already started */ 50302ac6454SAndrew Thompson 504a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); 50502ac6454SAndrew Thompson 50602ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 50702ac6454SAndrew Thompson } 50802ac6454SAndrew Thompson 50902ac6454SAndrew Thompson static void 510ed6d949aSAndrew Thompson udbp_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 51102ac6454SAndrew Thompson { 512ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 513ed6d949aSAndrew Thompson struct usb_page_cache *pc; 51402ac6454SAndrew Thompson struct mbuf *m; 51502ac6454SAndrew Thompson 51602ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 51702ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 51802ac6454SAndrew Thompson 51902ac6454SAndrew Thompson sc->sc_packets_out++; 52002ac6454SAndrew Thompson 52102ac6454SAndrew Thompson case USB_ST_SETUP: 52202ac6454SAndrew Thompson if (sc->sc_flags & UDBP_FLAG_WRITE_STALL) { 523a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); 52402ac6454SAndrew Thompson return; 52502ac6454SAndrew Thompson } 52602ac6454SAndrew Thompson /* get next mbuf, if any */ 52702ac6454SAndrew Thompson 52802ac6454SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq_hipri, m); 52902ac6454SAndrew Thompson if (m == NULL) { 53002ac6454SAndrew Thompson NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq, m); 53102ac6454SAndrew Thompson if (m == NULL) { 53202ac6454SAndrew Thompson DPRINTF("Data queue is empty\n"); 53302ac6454SAndrew Thompson return; 53402ac6454SAndrew Thompson } 53502ac6454SAndrew Thompson } 53602ac6454SAndrew Thompson if (m->m_pkthdr.len > MCLBYTES) { 53702ac6454SAndrew Thompson DPRINTF("truncating large packet " 53802ac6454SAndrew Thompson "from %d to %d bytes\n", m->m_pkthdr.len, 53902ac6454SAndrew Thompson MCLBYTES); 54002ac6454SAndrew Thompson m->m_pkthdr.len = MCLBYTES; 54102ac6454SAndrew Thompson } 542ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 543ed6d949aSAndrew Thompson usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 54402ac6454SAndrew Thompson 545ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 546ed6d949aSAndrew Thompson 547ed6d949aSAndrew Thompson DPRINTF("packet out: %d bytes\n", m->m_pkthdr.len); 54802ac6454SAndrew Thompson 54902ac6454SAndrew Thompson m_freem(m); 55002ac6454SAndrew Thompson 551a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 55202ac6454SAndrew Thompson return; 55302ac6454SAndrew Thompson 55402ac6454SAndrew Thompson default: /* Error */ 555ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 55602ac6454SAndrew Thompson /* try to clear stall first */ 55702ac6454SAndrew Thompson sc->sc_flags |= UDBP_FLAG_WRITE_STALL; 558a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]); 55902ac6454SAndrew Thompson } 56002ac6454SAndrew Thompson return; 56102ac6454SAndrew Thompson } 56202ac6454SAndrew Thompson } 56302ac6454SAndrew Thompson 56402ac6454SAndrew Thompson static void 565ed6d949aSAndrew Thompson udbp_bulk_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) 56602ac6454SAndrew Thompson { 567ed6d949aSAndrew Thompson struct udbp_softc *sc = usbd_xfer_softc(xfer); 568760bc48eSAndrew Thompson struct usb_xfer *xfer_other = sc->sc_xfer[UDBP_T_WR]; 56902ac6454SAndrew Thompson 570a593f6b8SAndrew Thompson if (usbd_clear_stall_callback(xfer, xfer_other)) { 57102ac6454SAndrew Thompson DPRINTF("stall cleared\n"); 57202ac6454SAndrew Thompson sc->sc_flags &= ~UDBP_FLAG_WRITE_STALL; 573a593f6b8SAndrew Thompson usbd_transfer_start(xfer_other); 57402ac6454SAndrew Thompson } 57502ac6454SAndrew Thompson } 57602ac6454SAndrew Thompson 57702ac6454SAndrew Thompson /*********************************************************************** 57802ac6454SAndrew Thompson * Start of Netgraph methods 57902ac6454SAndrew Thompson **********************************************************************/ 58002ac6454SAndrew Thompson 58102ac6454SAndrew Thompson /* 58202ac6454SAndrew Thompson * If this is a device node so this work is done in the attach() 58302ac6454SAndrew Thompson * routine and the constructor will return EINVAL as you should not be able 58402ac6454SAndrew Thompson * to create nodes that depend on hardware (unless you can add the hardware :) 58502ac6454SAndrew Thompson */ 58602ac6454SAndrew Thompson static int 58702ac6454SAndrew Thompson ng_udbp_constructor(node_p node) 58802ac6454SAndrew Thompson { 58902ac6454SAndrew Thompson return (EINVAL); 59002ac6454SAndrew Thompson } 59102ac6454SAndrew Thompson 59202ac6454SAndrew Thompson /* 59302ac6454SAndrew Thompson * Give our ok for a hook to be added... 59402ac6454SAndrew Thompson * If we are not running this might kick a device into life. 59502ac6454SAndrew Thompson * Possibly decode information out of the hook name. 59602ac6454SAndrew Thompson * Add the hook's private info to the hook structure. 59702ac6454SAndrew Thompson * (if we had some). In this example, we assume that there is a 59802ac6454SAndrew Thompson * an array of structs, called 'channel' in the private info, 59902ac6454SAndrew Thompson * one for each active channel. The private 60002ac6454SAndrew Thompson * pointer of each hook points to the appropriate UDBP_hookinfo struct 60102ac6454SAndrew Thompson * so that the source of an input packet is easily identified. 60202ac6454SAndrew Thompson */ 60302ac6454SAndrew Thompson static int 60402ac6454SAndrew Thompson ng_udbp_newhook(node_p node, hook_p hook, const char *name) 60502ac6454SAndrew Thompson { 60602ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 60702ac6454SAndrew Thompson int32_t error = 0; 60802ac6454SAndrew Thompson 60902ac6454SAndrew Thompson if (strcmp(name, NG_UDBP_HOOK_NAME)) { 61002ac6454SAndrew Thompson return (EINVAL); 61102ac6454SAndrew Thompson } 61202ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 61302ac6454SAndrew Thompson 61402ac6454SAndrew Thompson if (sc->sc_hook != NULL) { 61502ac6454SAndrew Thompson error = EISCONN; 61602ac6454SAndrew Thompson } else { 61702ac6454SAndrew Thompson sc->sc_hook = hook; 61802ac6454SAndrew Thompson NG_HOOK_SET_PRIVATE(hook, NULL); 61902ac6454SAndrew Thompson } 62002ac6454SAndrew Thompson 62102ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 62202ac6454SAndrew Thompson 62302ac6454SAndrew Thompson return (error); 62402ac6454SAndrew Thompson } 62502ac6454SAndrew Thompson 62602ac6454SAndrew Thompson /* 62702ac6454SAndrew Thompson * Get a netgraph control message. 62802ac6454SAndrew Thompson * Check it is one we understand. If needed, send a response. 62902ac6454SAndrew Thompson * We could save the address for an async action later, but don't here. 63002ac6454SAndrew Thompson * Always free the message. 63102ac6454SAndrew Thompson * The response should be in a malloc'd region that the caller can 'free'. 63202ac6454SAndrew Thompson * A response is not required. 63302ac6454SAndrew Thompson * Theoretically you could respond defferently to old message types if 63402ac6454SAndrew Thompson * the cookie in the header didn't match what we consider to be current 63502ac6454SAndrew Thompson * (so that old userland programs could continue to work). 63602ac6454SAndrew Thompson */ 63702ac6454SAndrew Thompson static int 63802ac6454SAndrew Thompson ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook) 63902ac6454SAndrew Thompson { 64002ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 64102ac6454SAndrew Thompson struct ng_mesg *resp = NULL; 64202ac6454SAndrew Thompson int error = 0; 64302ac6454SAndrew Thompson struct ng_mesg *msg; 64402ac6454SAndrew Thompson 64502ac6454SAndrew Thompson NGI_GET_MSG(item, msg); 64602ac6454SAndrew Thompson /* Deal with message according to cookie and command */ 64702ac6454SAndrew Thompson switch (msg->header.typecookie) { 64802ac6454SAndrew Thompson case NGM_UDBP_COOKIE: 64902ac6454SAndrew Thompson switch (msg->header.cmd) { 65002ac6454SAndrew Thompson case NGM_UDBP_GET_STATUS: 65102ac6454SAndrew Thompson { 65202ac6454SAndrew Thompson struct ngudbpstat *stats; 65302ac6454SAndrew Thompson 65402ac6454SAndrew Thompson NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 65502ac6454SAndrew Thompson if (!resp) { 65602ac6454SAndrew Thompson error = ENOMEM; 65702ac6454SAndrew Thompson break; 65802ac6454SAndrew Thompson } 65902ac6454SAndrew Thompson stats = (struct ngudbpstat *)resp->data; 66002ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 66102ac6454SAndrew Thompson stats->packets_in = sc->sc_packets_in; 66202ac6454SAndrew Thompson stats->packets_out = sc->sc_packets_out; 66302ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 66402ac6454SAndrew Thompson break; 66502ac6454SAndrew Thompson } 66602ac6454SAndrew Thompson case NGM_UDBP_SET_FLAG: 66702ac6454SAndrew Thompson if (msg->header.arglen != sizeof(uint32_t)) { 66802ac6454SAndrew Thompson error = EINVAL; 66902ac6454SAndrew Thompson break; 67002ac6454SAndrew Thompson } 67102ac6454SAndrew Thompson DPRINTF("flags = 0x%08x\n", 67202ac6454SAndrew Thompson *((uint32_t *)msg->data)); 67302ac6454SAndrew Thompson break; 67402ac6454SAndrew Thompson default: 67502ac6454SAndrew Thompson error = EINVAL; /* unknown command */ 67602ac6454SAndrew Thompson break; 67702ac6454SAndrew Thompson } 67802ac6454SAndrew Thompson break; 67902ac6454SAndrew Thompson default: 68002ac6454SAndrew Thompson error = EINVAL; /* unknown cookie type */ 68102ac6454SAndrew Thompson break; 68202ac6454SAndrew Thompson } 68302ac6454SAndrew Thompson 68402ac6454SAndrew Thompson /* Take care of synchronous response, if any */ 68502ac6454SAndrew Thompson NG_RESPOND_MSG(error, node, item, resp); 68602ac6454SAndrew Thompson NG_FREE_MSG(msg); 68702ac6454SAndrew Thompson return (error); 68802ac6454SAndrew Thompson } 68902ac6454SAndrew Thompson 69002ac6454SAndrew Thompson /* 69102ac6454SAndrew Thompson * Accept data from the hook and queue it for output. 69202ac6454SAndrew Thompson */ 69302ac6454SAndrew Thompson static int 69402ac6454SAndrew Thompson ng_udbp_rcvdata(hook_p hook, item_p item) 69502ac6454SAndrew Thompson { 69602ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 69702ac6454SAndrew Thompson struct ng_bt_mbufq *queue_ptr; 69802ac6454SAndrew Thompson struct mbuf *m; 69902ac6454SAndrew Thompson struct ng_tag_prio *ptag; 70002ac6454SAndrew Thompson int error; 70102ac6454SAndrew Thompson 70202ac6454SAndrew Thompson if (sc == NULL) { 70302ac6454SAndrew Thompson NG_FREE_ITEM(item); 70402ac6454SAndrew Thompson return (EHOSTDOWN); 70502ac6454SAndrew Thompson } 70602ac6454SAndrew Thompson NGI_GET_M(item, m); 70702ac6454SAndrew Thompson NG_FREE_ITEM(item); 70802ac6454SAndrew Thompson 70902ac6454SAndrew Thompson /* 71002ac6454SAndrew Thompson * Now queue the data for when it can be sent 71102ac6454SAndrew Thompson */ 71202ac6454SAndrew Thompson ptag = (void *)m_tag_locate(m, NGM_GENERIC_COOKIE, 71302ac6454SAndrew Thompson NG_TAG_PRIO, NULL); 71402ac6454SAndrew Thompson 71502ac6454SAndrew Thompson if (ptag && (ptag->priority > NG_PRIO_CUTOFF)) 71602ac6454SAndrew Thompson queue_ptr = &sc->sc_xmitq_hipri; 71702ac6454SAndrew Thompson else 71802ac6454SAndrew Thompson queue_ptr = &sc->sc_xmitq; 71902ac6454SAndrew Thompson 72002ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 72102ac6454SAndrew Thompson 72202ac6454SAndrew Thompson if (NG_BT_MBUFQ_FULL(queue_ptr)) { 72302ac6454SAndrew Thompson NG_BT_MBUFQ_DROP(queue_ptr); 72402ac6454SAndrew Thompson NG_FREE_M(m); 72502ac6454SAndrew Thompson error = ENOBUFS; 72602ac6454SAndrew Thompson } else { 72702ac6454SAndrew Thompson NG_BT_MBUFQ_ENQUEUE(queue_ptr, m); 72802ac6454SAndrew Thompson /* 72902ac6454SAndrew Thompson * start bulk-out transfer, if not already started: 73002ac6454SAndrew Thompson */ 731a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); 73202ac6454SAndrew Thompson error = 0; 73302ac6454SAndrew Thompson } 73402ac6454SAndrew Thompson 73502ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 73602ac6454SAndrew Thompson 73702ac6454SAndrew Thompson return (error); 73802ac6454SAndrew Thompson } 73902ac6454SAndrew Thompson 74002ac6454SAndrew Thompson /* 74102ac6454SAndrew Thompson * Do local shutdown processing.. 74220733245SPedro F. Giffuni * We are a persistent device, we refuse to go away, and 74302ac6454SAndrew Thompson * only remove our links and reset ourself. 74402ac6454SAndrew Thompson */ 74502ac6454SAndrew Thompson static int 74602ac6454SAndrew Thompson ng_udbp_rmnode(node_p node) 74702ac6454SAndrew Thompson { 74802ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(node); 74902ac6454SAndrew Thompson 75002ac6454SAndrew Thompson /* Let old node go */ 75102ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(node, NULL); 75202ac6454SAndrew Thompson NG_NODE_UNREF(node); /* forget it ever existed */ 75302ac6454SAndrew Thompson 75402ac6454SAndrew Thompson if (sc == NULL) { 75502ac6454SAndrew Thompson goto done; 75602ac6454SAndrew Thompson } 75702ac6454SAndrew Thompson /* Create Netgraph node */ 75802ac6454SAndrew Thompson if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) { 75902ac6454SAndrew Thompson printf("%s: Could not create Netgraph node\n", 76002ac6454SAndrew Thompson sc->sc_name); 76102ac6454SAndrew Thompson sc->sc_node = NULL; 76202ac6454SAndrew Thompson goto done; 76302ac6454SAndrew Thompson } 76402ac6454SAndrew Thompson /* Name node */ 76502ac6454SAndrew Thompson if (ng_name_node(sc->sc_node, sc->sc_name) != 0) { 76602ac6454SAndrew Thompson printf("%s: Could not name Netgraph node\n", 76702ac6454SAndrew Thompson sc->sc_name); 76802ac6454SAndrew Thompson NG_NODE_UNREF(sc->sc_node); 76902ac6454SAndrew Thompson sc->sc_node = NULL; 77002ac6454SAndrew Thompson goto done; 77102ac6454SAndrew Thompson } 77202ac6454SAndrew Thompson NG_NODE_SET_PRIVATE(sc->sc_node, sc); 77302ac6454SAndrew Thompson 77402ac6454SAndrew Thompson done: 77502ac6454SAndrew Thompson if (sc) { 77602ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 77702ac6454SAndrew Thompson } 77802ac6454SAndrew Thompson return (0); 77902ac6454SAndrew Thompson } 78002ac6454SAndrew Thompson 78102ac6454SAndrew Thompson /* 78202ac6454SAndrew Thompson * This is called once we've already connected a new hook to the other node. 78302ac6454SAndrew Thompson * It gives us a chance to balk at the last minute. 78402ac6454SAndrew Thompson */ 78502ac6454SAndrew Thompson static int 78602ac6454SAndrew Thompson ng_udbp_connect(hook_p hook) 78702ac6454SAndrew Thompson { 78802ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 78902ac6454SAndrew Thompson 790*f42bb99aSWarner Losh /* force outward queueing */ 79102ac6454SAndrew Thompson NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 79202ac6454SAndrew Thompson 79302ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 79402ac6454SAndrew Thompson 79502ac6454SAndrew Thompson sc->sc_flags |= (UDBP_FLAG_READ_STALL | 79602ac6454SAndrew Thompson UDBP_FLAG_WRITE_STALL); 79702ac6454SAndrew Thompson 79802ac6454SAndrew Thompson /* start bulk-in transfer */ 799a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_RD]); 80002ac6454SAndrew Thompson 80102ac6454SAndrew Thompson /* start bulk-out transfer */ 802a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_xfer[UDBP_T_WR]); 80302ac6454SAndrew Thompson 80402ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 80502ac6454SAndrew Thompson 80602ac6454SAndrew Thompson return (0); 80702ac6454SAndrew Thompson } 80802ac6454SAndrew Thompson 80902ac6454SAndrew Thompson /* 81002ac6454SAndrew Thompson * Dook disconnection 81102ac6454SAndrew Thompson * 81202ac6454SAndrew Thompson * For this type, removal of the last link destroys the node 81302ac6454SAndrew Thompson */ 81402ac6454SAndrew Thompson static int 81502ac6454SAndrew Thompson ng_udbp_disconnect(hook_p hook) 81602ac6454SAndrew Thompson { 81702ac6454SAndrew Thompson struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 81802ac6454SAndrew Thompson int error = 0; 81902ac6454SAndrew Thompson 82002ac6454SAndrew Thompson if (sc != NULL) { 82102ac6454SAndrew Thompson mtx_lock(&sc->sc_mtx); 82202ac6454SAndrew Thompson 82302ac6454SAndrew Thompson if (hook != sc->sc_hook) { 82402ac6454SAndrew Thompson error = EINVAL; 82502ac6454SAndrew Thompson } else { 82602ac6454SAndrew Thompson /* stop bulk-in transfer */ 827a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD_CS]); 828a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_RD]); 82902ac6454SAndrew Thompson 83002ac6454SAndrew Thompson /* stop bulk-out transfer */ 831a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR_CS]); 832a593f6b8SAndrew Thompson usbd_transfer_stop(sc->sc_xfer[UDBP_T_WR]); 83302ac6454SAndrew Thompson 83402ac6454SAndrew Thompson /* cleanup queues */ 83502ac6454SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq); 83602ac6454SAndrew Thompson NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq_hipri); 83702ac6454SAndrew Thompson 83802ac6454SAndrew Thompson if (sc->sc_bulk_in_buffer) { 83902ac6454SAndrew Thompson m_freem(sc->sc_bulk_in_buffer); 84002ac6454SAndrew Thompson sc->sc_bulk_in_buffer = NULL; 84102ac6454SAndrew Thompson } 84202ac6454SAndrew Thompson sc->sc_hook = NULL; 84302ac6454SAndrew Thompson } 84402ac6454SAndrew Thompson 84502ac6454SAndrew Thompson mtx_unlock(&sc->sc_mtx); 84602ac6454SAndrew Thompson } 84702ac6454SAndrew Thompson if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 84802ac6454SAndrew Thompson && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 84902ac6454SAndrew Thompson ng_rmnode_self(NG_HOOK_NODE(hook)); 85002ac6454SAndrew Thompson 85102ac6454SAndrew Thompson return (error); 85202ac6454SAndrew Thompson } 853