freebsd-dev/sys/dev/usb/udbp.c
Warner Losh 05adc69b08 Prefer device_printf to printf + device_get_nameunit. This saves
about 100 bytes.

Approved by: re (blanket)
2007-07-05 15:25:32 +00:00

861 lines
23 KiB
C

/*-
* 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);
}