diff options
Diffstat (limited to 'sys/legacy/dev/usb/udbp.c')
-rw-r--r-- | sys/legacy/dev/usb/udbp.c | 860 |
1 files changed, 860 insertions, 0 deletions
diff --git a/sys/legacy/dev/usb/udbp.c b/sys/legacy/dev/usb/udbp.c new file mode 100644 index 0000000..523184f --- /dev/null +++ b/sys/legacy/dev/usb/udbp.c @@ -0,0 +1,860 @@ +/*- + * Copyright (c) 1996-2000 Whistle Communications, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of author nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY NICK HIBMA AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* Driver for arbitrary double bulk pipe devices. + * The driver assumes that there will be the same driver on the other side. + * + * XXX Some more information on what the framing of the IP packets looks like. + * + * To take full advantage of bulk transmission, packets should be chosen + * between 1k and 5k in size (1k to make sure the sending side starts + * straming, and <5k to avoid overflowing the system with small TDs). + */ + + +/* probe/attach/detach: + * Connect the driver to the hardware and netgraph + * + * udbp_setup_out_transfer(sc); + * Setup an outbound transfer. Only one transmit can be active at the same + * time. + * XXX If it is required that the driver is able to queue multiple requests + * let me know. That is slightly difficult, due to the fact that we + * cannot call usbd_alloc_xfer in int context. + * + * udbp_setup_in_transfer(sc) + * Prepare an in transfer that will be waiting for data to come in. It + * is submitted and sits there until data is available. + * The callback resubmits a new transfer on completion. + * + * The reason we submit a bulk in transfer is that USB does not know about + * interrupts. The bulk transfer continuously polls the device for data. + * While the device has no data available, the device NAKs the TDs. As soon + * as there is data, the transfer happens and the data comes flowing in. + * + * In case you were wondering, interrupt transfers happen exactly that way. + * It therefore doesn't make sense to use the interrupt pipe to signal + * 'data ready' and then schedule a bulk transfer to fetch it. That would + * incur a 2ms delay at least, without reducing bandwidth requirements. + * + */ + + + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/selinfo.h> +#include <sys/poll.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ctype.h> +#include <sys/errno.h> +#include <sys/sysctl.h> +#include <net/if.h> +#include <machine/bus.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbhid.h> + +#include "usbdevs.h" + + +#include <netgraph/ng_message.h> +#include <netgraph/ng_parse.h> +#include <dev/usb/udbp.h> +#include <netgraph/netgraph.h> + +#ifdef USB_DEBUG +#define DPRINTF(x) if (udbpdebug) printf x +#define DPRINTFN(n,x) if (udbpdebug>(n)) printf x +int udbpdebug = 0; +SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp"); +SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RW, + &udbpdebug, 0, "udbp debug level"); +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define MS_TO_TICKS(ms) ((ms) * hz / 1000) + +#define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in msecs */ +#define UDBP_BUFFERSIZE 2048 /* maximum number of bytes in one transfer */ + + +struct udbp_softc { + device_t sc_dev; /* base device */ + usbd_interface_handle sc_iface; + + usbd_pipe_handle sc_bulkin_pipe; + int sc_bulkin; + usbd_xfer_handle sc_bulkin_xfer; + void *sc_bulkin_buffer; + int sc_bulkin_bufferlen; + int sc_bulkin_datalen; + + usbd_pipe_handle sc_bulkout_pipe; + int sc_bulkout; + usbd_xfer_handle sc_bulkout_xfer; + void *sc_bulkout_buffer; + int sc_bulkout_bufferlen; + int sc_bulkout_datalen; + + int flags; +# define DISCONNECTED 0x01 +# define OUT_BUSY 0x02 +# define NETGRAPH_INITIALISED 0x04 + node_p node; /* back pointer to node */ + hook_p hook; /* pointer to the hook */ + u_int packets_in; /* packets in from downstream */ + u_int packets_out; /* packets out towards downstream */ + struct ifqueue xmitq_hipri; /* hi-priority transmit queue */ + struct ifqueue xmitq; /* low-priority transmit queue */ + +}; +typedef struct udbp_softc *udbp_p; + + + +static ng_constructor_t ng_udbp_constructor; +static ng_rcvmsg_t ng_udbp_rcvmsg; +static ng_shutdown_t ng_udbp_rmnode; +static ng_newhook_t ng_udbp_newhook; +static ng_connect_t ng_udbp_connect; +static ng_rcvdata_t ng_udbp_rcvdata; +static ng_disconnect_t ng_udbp_disconnect; + +/* Parse type for struct ngudbpstat */ +static const struct ng_parse_struct_field + ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO; +static const struct ng_parse_type ng_udbp_stat_type = { + &ng_parse_struct_type, + &ng_udbp_stat_type_fields +}; + +/* List of commands and how to convert arguments to/from ASCII */ +static const struct ng_cmdlist ng_udbp_cmdlist[] = { + { + NGM_UDBP_COOKIE, + NGM_UDBP_GET_STATUS, + "getstatus", + NULL, + &ng_udbp_stat_type, + }, + { + NGM_UDBP_COOKIE, + NGM_UDBP_SET_FLAG, + "setflag", + &ng_parse_int32_type, + NULL + }, + { 0 } +}; + +/* Netgraph node type descriptor */ +static struct ng_type ng_udbp_typestruct = { + .version = NG_ABI_VERSION, + .name = NG_UDBP_NODE_TYPE, + .constructor = ng_udbp_constructor, + .rcvmsg = ng_udbp_rcvmsg, + .shutdown = ng_udbp_rmnode, + .newhook = ng_udbp_newhook, + .connect = ng_udbp_connect, + .rcvdata = ng_udbp_rcvdata, + .disconnect = ng_udbp_disconnect, + .cmdlist = ng_udbp_cmdlist, +}; + +static int udbp_setup_in_transfer (udbp_p sc); +static void udbp_in_transfer_cb (usbd_xfer_handle xfer, + usbd_private_handle priv, + usbd_status err); + +static int udbp_setup_out_transfer (udbp_p sc); +static void udbp_out_transfer_cb (usbd_xfer_handle xfer, + usbd_private_handle priv, + usbd_status err); + +static device_probe_t udbp_match; +static device_attach_t udbp_attach; +static device_detach_t udbp_detach; + +static device_method_t udbp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, udbp_match), + DEVMETHOD(device_attach, udbp_attach), + DEVMETHOD(device_detach, udbp_detach), + + { 0, 0 } +}; + +static driver_t udbp_driver = { + "udbp", + udbp_methods, + sizeof(struct udbp_softc) +}; + +static devclass_t udbp_devclass; + +static int +udbp_match(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + usb_interface_descriptor_t *id; + if (!uaa->iface) + return (UMATCH_NONE); + id = usbd_get_interface_descriptor(uaa->iface); + + /* XXX Julian, add the id of the device if you have one to test + * things with. run 'usbdevs -v' and note the 3 ID's that appear. + * The Vendor Id and Product Id are in hex and the Revision Id is in + * bcd. But as usual if the revision is 0x101 then you should compare + * the revision id in the device descriptor with 0x101 + * Or go search the file usbdevs.h. Maybe the device is already in + * there. + */ + if ((uaa->vendor == USB_VENDOR_NETCHIP && + uaa->product == USB_PRODUCT_NETCHIP_TURBOCONNECT)) + return (UMATCH_VENDOR_PRODUCT); + + if ((uaa->vendor == USB_VENDOR_PROLIFIC && + (uaa->product == USB_PRODUCT_PROLIFIC_PL2301 || + uaa->product == USB_PRODUCT_PROLIFIC_PL2302))) + return (UMATCH_VENDOR_PRODUCT); + + if ((uaa->vendor == USB_VENDOR_ANCHOR && + uaa->product == USB_PRODUCT_ANCHOR_EZLINK)) + return (UMATCH_VENDOR_PRODUCT); + + if ((uaa->vendor == USB_VENDOR_GENESYS && + uaa->product == USB_PRODUCT_GENESYS_GL620USB)) + return (UMATCH_VENDOR_PRODUCT); + + return (UMATCH_NONE); +} + +static int +udbp_attach(device_t self) +{ + struct udbp_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); + usbd_interface_handle iface = uaa->iface; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed, *ed_bulkin = NULL, *ed_bulkout = NULL; + usbd_status err; + int i; + static int ngudbp_done_init=0; + + sc->flags |= DISCONNECTED; + /* fetch the interface handle for the first interface */ + (void) usbd_device2interface_handle(uaa->device, 0, &iface); + id = usbd_get_interface_descriptor(iface); + sc->sc_dev = self; + + /* Find the two first bulk endpoints */ + for (i = 0 ; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(iface, i); + if (!ed) { + device_printf(self, "could not read endpoint descriptor\n"); + return ENXIO; + } + + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN + && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { + ed_bulkin = ed; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT + && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { + ed_bulkout = ed; + } + + if (ed_bulkin && ed_bulkout) /* found all we need */ + break; + } + + /* Verify that we goething sensible */ + if (ed_bulkin == NULL || ed_bulkout == NULL) { + device_printf(self, "bulk-in and/or bulk-out endpoint not found\n"); + return ENXIO; + } + + if (ed_bulkin->wMaxPacketSize[0] != ed_bulkout->wMaxPacketSize[0] || + ed_bulkin->wMaxPacketSize[1] != ed_bulkout->wMaxPacketSize[1]) { + device_printf(self, + "bulk-in and bulk-out have different packet sizes %d %d %d %d\n", + ed_bulkin->wMaxPacketSize[0], + ed_bulkout->wMaxPacketSize[0], + ed_bulkin->wMaxPacketSize[1], + ed_bulkout->wMaxPacketSize[1]); + return ENXIO; + } + + sc->sc_bulkin = ed_bulkin->bEndpointAddress; + sc->sc_bulkout = ed_bulkout->bEndpointAddress; + + DPRINTF(("%s: Bulk-in: 0x%02x, bulk-out 0x%02x, packet size = %d\n", + device_get_nameunit(sc->sc_dev), sc->sc_bulkin, sc->sc_bulkout, + ed_bulkin->wMaxPacketSize[0])); + + /* Allocate the in transfer struct */ + sc->sc_bulkin_xfer = usbd_alloc_xfer(uaa->device); + if (!sc->sc_bulkin_xfer) { + goto bad; + } + sc->sc_bulkout_xfer = usbd_alloc_xfer(uaa->device); + if (!sc->sc_bulkout_xfer) { + goto bad; + } + sc->sc_bulkin_buffer = malloc(UDBP_BUFFERSIZE, M_USBDEV, M_WAITOK); + if (!sc->sc_bulkin_buffer) { + goto bad; + } + sc->sc_bulkout_buffer = malloc(UDBP_BUFFERSIZE, M_USBDEV, M_WAITOK); + if (!sc->sc_bulkout_xfer || !sc->sc_bulkout_buffer) { + goto bad; + } + sc->sc_bulkin_bufferlen = UDBP_BUFFERSIZE; + sc->sc_bulkout_bufferlen = UDBP_BUFFERSIZE; + + /* We have decided on which endpoints to use, now open the pipes */ + err = usbd_open_pipe(iface, sc->sc_bulkin, + USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe); + if (err) { + device_printf(self, "cannot open bulk-in pipe (addr %d)\n", + sc->sc_bulkin); + goto bad; + } + err = usbd_open_pipe(iface, sc->sc_bulkout, + USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); + if (err) { + device_printf(self, "cannot open bulk-out pipe (addr %d)\n", + sc->sc_bulkout); + goto bad; + } + + if (!ngudbp_done_init){ + ngudbp_done_init=1; + if (ng_newtype(&ng_udbp_typestruct)) { + printf("ngudbp install failed\n"); + goto bad; + } + } + + if ((err = ng_make_node_common(&ng_udbp_typestruct, &sc->node)) == 0) { + char nodename[128]; + sprintf(nodename, "%s", device_get_nameunit(sc->sc_dev)); + if ((err = ng_name_node(sc->node, nodename))) { + NG_NODE_UNREF(sc->node); + sc->node = NULL; + goto bad; + } else { + NG_NODE_SET_PRIVATE(sc->node, sc); + sc->xmitq.ifq_maxlen = IFQ_MAXLEN; + sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN; + mtx_init(&sc->xmitq.ifq_mtx, "usb_xmitq", NULL, + MTX_DEF); + mtx_init(&sc->xmitq_hipri.ifq_mtx, + "usb_xmitq_hipri", NULL, MTX_DEF); + } + } + sc->flags = NETGRAPH_INITIALISED; + /* sc->flags &= ~DISCONNECTED; */ /* XXX */ + + + /* the device is now operational */ + + + /* schedule the first incoming xfer */ + err = udbp_setup_in_transfer(sc); + if (err) { + goto bad; + } + return 0; +bad: +#if 0 /* probably done in udbp_detach() */ + if (sc->sc_bulkout_buffer) { + free(sc->sc_bulkout_buffer, M_USBDEV); + } + if (sc->sc_bulkin_buffer) { + free(sc->sc_bulkin_buffer, M_USBDEV); + } + if (sc->sc_bulkout_xfer) { + usbd_free_xfer(sc->sc_bulkout_xfer); + } + if (sc->sc_bulkin_xfer) { + usbd_free_xfer(sc->sc_bulkin_xfer); + } +#endif + udbp_detach(self); + return ENXIO; +} + + +static int +udbp_detach(device_t self) +{ + struct udbp_softc *sc = device_get_softc(self); + + sc->flags |= DISCONNECTED; + + DPRINTF(("%s: disconnected\n", device_get_nameunit(self))); + + if (sc->sc_bulkin_pipe) { + usbd_abort_pipe(sc->sc_bulkin_pipe); + usbd_close_pipe(sc->sc_bulkin_pipe); + } + if (sc->sc_bulkout_pipe) { + usbd_abort_pipe(sc->sc_bulkout_pipe); + usbd_close_pipe(sc->sc_bulkout_pipe); + } + + if (sc->flags & NETGRAPH_INITIALISED) { + ng_rmnode_self(sc->node); + NG_NODE_SET_PRIVATE(sc->node, NULL); + NG_NODE_UNREF(sc->node); + sc->node = NULL; /* Paranoid */ + } + + if (sc->sc_bulkin_xfer) + usbd_free_xfer(sc->sc_bulkin_xfer); + if (sc->sc_bulkout_xfer) + usbd_free_xfer(sc->sc_bulkout_xfer); + + if (sc->sc_bulkin_buffer) + free(sc->sc_bulkin_buffer, M_USBDEV); + if (sc->sc_bulkout_buffer) + free(sc->sc_bulkout_buffer, M_USBDEV); + return 0; +} + + +static int +udbp_setup_in_transfer(udbp_p sc) +{ + void *priv = sc; /* XXX this should probably be some pointer to + * struct describing the transfer (mbuf?) + * See also below. + */ + usbd_status err; + + /* XXX + * How should we arrange for 2 extra bytes at the start of the + * packet? + */ + + /* Initialise a USB transfer and then schedule it */ + + (void) usbd_setup_xfer( sc->sc_bulkin_xfer, + sc->sc_bulkin_pipe, + priv, + sc->sc_bulkin_buffer, + sc->sc_bulkin_bufferlen, + USBD_SHORT_XFER_OK, + USBD_NO_TIMEOUT, + udbp_in_transfer_cb); + + err = usbd_transfer(sc->sc_bulkin_xfer); + if (err && err != USBD_IN_PROGRESS) { + DPRINTF(("%s: failed to setup in-transfer, %s\n", + device_get_nameunit(sc->sc_dev), usbd_errstr(err))); + return(err); + } + + return (USBD_NORMAL_COMPLETION); +} + +static void +udbp_in_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status err) +{ + udbp_p sc = priv; /* XXX see priv above */ + int s; + int len; + struct mbuf *m; + + if (err) { + if (err != USBD_CANCELLED) { + DPRINTF(("%s: bulk-out transfer failed: %s\n", + device_get_nameunit(sc->sc_dev), usbd_errstr(err))); + } else { + /* USBD_CANCELLED happens at unload of the driver */ + return; + } + + /* Transfer has failed, packet is not received */ + } else { + + len = xfer->actlen; + + s = splimp(); /* block network stuff too */ + if (sc->hook) { + /* get packet from device and send on */ + m = m_devget(sc->sc_bulkin_buffer, len, 0, NULL, NULL); + NG_SEND_DATA_ONLY(err, sc->hook, m); + } + splx(s); + + } + /* schedule the next in transfer */ + udbp_setup_in_transfer(sc); +} + + +static int +udbp_setup_out_transfer(udbp_p sc) +{ + void *priv = sc; /* XXX this should probably be some pointer to + * struct describing the transfer (mbuf?) + * See also below. + */ + int pktlen; + usbd_status err; + int s, s1; + struct mbuf *m; + + + s = splusb(); + if (sc->flags & OUT_BUSY) + panic("out transfer already in use, we should add queuing"); + sc->flags |= OUT_BUSY; + splx(s); + s1 = splimp(); /* Queueing happens at splnet */ + IF_DEQUEUE(&sc->xmitq_hipri, m); + if (m == NULL) { + IF_DEQUEUE(&sc->xmitq, m); + } + splx(s1); + + if (!m) { + sc->flags &= ~OUT_BUSY; + return (USBD_NORMAL_COMPLETION); + } + + pktlen = m->m_pkthdr.len; + if (pktlen > sc->sc_bulkout_bufferlen) { + device_printf(sc->sc_dev, "Packet too large, %d > %d\n", + pktlen, sc->sc_bulkout_bufferlen); + return (USBD_IOERROR); + } + + m_copydata(m, 0, pktlen, sc->sc_bulkout_buffer); + m_freem(m); + + /* Initialise a USB transfer and then schedule it */ + + (void) usbd_setup_xfer( sc->sc_bulkout_xfer, + sc->sc_bulkout_pipe, + priv, + sc->sc_bulkout_buffer, + pktlen, + USBD_SHORT_XFER_OK, + UDBP_TIMEOUT, + udbp_out_transfer_cb); + + err = usbd_transfer(sc->sc_bulkout_xfer); + if (err && err != USBD_IN_PROGRESS) { + DPRINTF(("%s: failed to setup out-transfer, %s\n", + device_get_nameunit(sc->sc_dev), usbd_errstr(err))); + return(err); + } + + return (USBD_NORMAL_COMPLETION); +} + +static void +udbp_out_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status err) +{ + udbp_p sc = priv; /* XXX see priv above */ + int s; + + if (err) { + DPRINTF(("%s: bulk-out transfer failed: %s\n", + device_get_nameunit(sc->sc_dev), usbd_errstr(err))); + /* Transfer has failed, packet is not transmitted */ + /* XXX Invalidate packet */ + return; + } + + /* packet has been transmitted */ + + s = splusb(); /* mark the buffer available */ + sc->flags &= ~OUT_BUSY; + udbp_setup_out_transfer(sc); + splx(s); +} + +DRIVER_MODULE(udbp, uhub, udbp_driver, udbp_devclass, usbd_driver_load, 0); +MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); +MODULE_DEPEND(udbp, usb, 1, 1, 1); + + +/*********************************************************************** + * Start of Netgraph methods + **********************************************************************/ + +/* + * If this is a device node so this work is done in the attach() + * routine and the constructor will return EINVAL as you should not be able + * to create nodes that depend on hardware (unless you can add the hardware :) + */ +static int +ng_udbp_constructor(node_p node) +{ + return (EINVAL); +} + +/* + * Give our ok for a hook to be added... + * If we are not running this might kick a device into life. + * Possibly decode information out of the hook name. + * Add the hook's private info to the hook structure. + * (if we had some). In this example, we assume that there is a + * an array of structs, called 'channel' in the private info, + * one for each active channel. The private + * pointer of each hook points to the appropriate UDBP_hookinfo struct + * so that the source of an input packet is easily identified. + */ +static int +ng_udbp_newhook(node_p node, hook_p hook, const char *name) +{ + const udbp_p sc = NG_NODE_PRIVATE(node); + +#if 0 + /* Possibly start up the device if it's not already going */ + if ((sc->flags & SCF_RUNNING) == 0) { + ng_udbp_start_hardware(sc); + } +#endif + + if (strcmp(name, NG_UDBP_HOOK_NAME) == 0) { + sc->hook = hook; + NG_HOOK_SET_PRIVATE(hook, NULL); + } else { + return (EINVAL); /* not a hook we know about */ + } + return(0); +} + +/* + * Get a netgraph control message. + * Check it is one we understand. If needed, send a response. + * We could save the address for an async action later, but don't here. + * Always free the message. + * The response should be in a malloc'd region that the caller can 'free'. + * A response is not required. + * Theoretically you could respond defferently to old message types if + * the cookie in the header didn't match what we consider to be current + * (so that old userland programs could continue to work). + */ +static int +ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook) +{ + const udbp_p sc = NG_NODE_PRIVATE(node); + struct ng_mesg *resp = NULL; + int error = 0; + struct ng_mesg *msg; + + NGI_GET_MSG(item, msg); + /* Deal with message according to cookie and command */ + switch (msg->header.typecookie) { + case NGM_UDBP_COOKIE: + switch (msg->header.cmd) { + case NGM_UDBP_GET_STATUS: + { + struct ngudbpstat *stats; + + NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); + if (!resp) { + error = ENOMEM; + break; + } + stats = (struct ngudbpstat *) resp->data; + stats->packets_in = sc->packets_in; + stats->packets_out = sc->packets_out; + break; + } + case NGM_UDBP_SET_FLAG: + if (msg->header.arglen != sizeof(u_int32_t)) { + error = EINVAL; + break; + } + sc->flags = *((u_int32_t *) msg->data); + break; + default: + error = EINVAL; /* unknown command */ + break; + } + break; + default: + error = EINVAL; /* unknown cookie type */ + break; + } + + /* Take care of synchronous response, if any */ + NG_RESPOND_MSG(error, node, item, resp); + NG_FREE_MSG(msg); + return(error); +} + +/* + * Accept data from the hook and queue it for output. + */ +static int +ng_udbp_rcvdata(hook_p hook, item_p item) +{ + const udbp_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + int error; + struct ifqueue *xmitq_p; + int s; + struct mbuf *m; + struct ng_tag_prio *ptag; + + NGI_GET_M(item, m); + NG_FREE_ITEM(item); + + /* + * Now queue the data for when it can be sent + */ + if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE, + NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) ) + xmitq_p = (&sc->xmitq_hipri); + else + xmitq_p = (&sc->xmitq); + + s = splusb(); + IF_LOCK(xmitq_p); + if (_IF_QFULL(xmitq_p)) { + _IF_DROP(xmitq_p); + IF_UNLOCK(xmitq_p); + splx(s); + error = ENOBUFS; + goto bad; + } + _IF_ENQUEUE(xmitq_p, m); + IF_UNLOCK(xmitq_p); + if (!(sc->flags & OUT_BUSY)) + udbp_setup_out_transfer(sc); + splx(s); + return (0); + +bad: /* + * It was an error case. + * check if we need to free the mbuf, and then return the error + */ + NG_FREE_M(m); + return (error); +} + +/* + * Do local shutdown processing.. + * We are a persistant device, we refuse to go away, and + * only remove our links and reset ourself. + */ +static int +ng_udbp_rmnode(node_p node) +{ + const udbp_p sc = NG_NODE_PRIVATE(node); + int err; + + if (sc->flags & DISCONNECTED) { + /* + * WE are really going away.. hardware must have gone. + * Assume that the hardware drive part will clear up the + * sc, in fact it may already have done so.. + * In which case we may have just segfaulted..XXX + */ + return (0); + } + + /* stolen from attach routine */ + /* Drain the queues */ + IF_DRAIN(&sc->xmitq_hipri); + IF_DRAIN(&sc->xmitq); + + sc->packets_in = 0; /* reset stats */ + sc->packets_out = 0; + NG_NODE_UNREF(node); /* forget it ever existed */ + + if ((err = ng_make_node_common(&ng_udbp_typestruct, &sc->node)) == 0) { + char nodename[128]; + sprintf(nodename, "%s", device_get_nameunit(sc->sc_dev)); + if ((err = ng_name_node(sc->node, nodename))) { + NG_NODE_UNREF(sc->node); /* out damned spot! */ + sc->flags &= ~NETGRAPH_INITIALISED; + sc->node = NULL; + } else { + NG_NODE_SET_PRIVATE(sc->node, sc); + } + } + return (err); +} + +/* + * This is called once we've already connected a new hook to the other node. + * It gives us a chance to balk at the last minute. + */ +static int +ng_udbp_connect(hook_p hook) +{ + /* probably not at splnet, force outward queueing */ + NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); + /* be really amiable and just say "YUP that's OK by me! " */ + return (0); +} + +/* + * Dook disconnection + * + * For this type, removal of the last link destroys the node + */ +static int +ng_udbp_disconnect(hook_p hook) +{ + const udbp_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + sc->hook = NULL; + + if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) + && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) + ng_rmnode_self(NG_HOOK_NODE(hook)); + return (0); +} + |