summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/ugen.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/ugen.c')
-rw-r--r--sys/dev/usb/ugen.c980
1 files changed, 891 insertions, 89 deletions
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c
index 856e0dc..341cc93 100644
--- a/sys/dev/usb/ugen.c
+++ b/sys/dev/usb/ugen.c
@@ -1,11 +1,13 @@
+/* $NetBSD: ugen.c,v 1.9 1999/01/03 01:03:22 augustss Exp $ */
/* FreeBSD $Id$ */
/*
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
- * Author: Lennart Augustsson <augustss@carlstedt.se>
- * Carlstedt Research & Technology
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) at
+ * Carlstedt Research & Technology.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,150 +38,950 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include <dev/usb/usb_port.h>
#include <sys/param.h>
#include <sys/systm.h>
-#include <sys/proc.h>
-#include <sys/malloc.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#if defined(__NetBSD__)
#include <sys/device.h>
+#include <sys/ioctl.h>
#elif defined(__FreeBSD__)
#include <sys/module.h>
#include <sys/bus.h>
-#endif
-#include <sys/uio.h>
+#include <sys/ioccom.h>
#include <sys/conf.h>
-#include <sys/syslog.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#endif
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/poll.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/usbdevs.h>
-#include <dev/usb/usb_quirks.h>
#ifdef USB_DEBUG
-#define DPRINTF(x) if (usbdebug) printf x
-#define DPRINTFN(n,x) if (usbdebug>(n)) printf x
-extern int usbdebug;
+#define DPRINTF(x) if (ugendebug) printf x
+#define DPRINTFN(n,x) if (ugendebug>(n)) printf x
+int ugendebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
-struct ugen_softc {
- bdevice sc_dev;
- usbd_device_handle sc_udev; /* device */
- usbd_interface_handle sc_iface; /* interface */
- int sc_ifaceno;
- int sc_bulk;
+struct ugen_endpoint {
+ struct ugen_softc *sc;
+ usb_endpoint_descriptor_t *edesc;
+ usbd_interface_handle iface;
+ int state;
+#define UGEN_OPEN 0x01 /* device is open */
+#define UGEN_ASLP 0x02 /* waiting for data */
+#define UGEN_SHORT_OK 0x04 /* short xfers are OK */
+ usbd_pipe_handle pipeh;
+ struct clist q;
+ struct selinfo rsel;
+ void *ibuf;
};
-#if defined(__NetBSD__)
-int ugen_match __P((struct device *, struct cfdata *, void *));
-void ugen_attach __P((struct device *, struct device *, void *));
+#define UGEN_CHUNK 128 /* chunk size for read */
+#define UGEN_IBSIZE 1020 /* buffer size */
+#define UGEN_BBSIZE 1024
+
+struct ugen_softc {
+ bdevice sc_dev; /* base device */
+ struct usbd_device *sc_udev;
-extern struct cfdriver ugen_cd;
+ struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2];
+#define OUT 0 /* index order is important, from UE_OUT */
+#define IN 1 /* from UE_IN */
-struct cfattach ugen_ca = {
- sizeof(struct ugen_softc), ugen_match, ugen_attach
+ int sc_disconnected; /* device is gone */
};
-#elif defined(__FreeBSD__)
-static device_probe_t ugen_match;
-static device_attach_t ugen_attach;
-static device_detach_t ugen_detach;
-static devclass_t ugen_devclass;
+/* XXX NWH Supposedly in USB_DECLARE_DRIVER_NAME_INIT
+int ugen_match __P((bdevice *, struct cfdata *, void *));
+void ugen_attach __P((bdevice *, struct device *, void *));
+*/
-static device_method_t ugen_methods[] = {
- DEVMETHOD(device_probe, ugen_match),
- DEVMETHOD(device_attach, ugen_attach),
- DEVMETHOD(device_detach, ugen_detach),
- {0,0}
-};
+int ugenopen __P((dev_t, int, int, struct proc *));
+int ugenclose __P((dev_t, int, int, struct proc *p));
+int ugenread __P((dev_t, struct uio *uio, int));
+int ugenwrite __P((dev_t, struct uio *uio, int));
+int ugenioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
+int ugenpoll __P((dev_t, int, struct proc *));
+void ugenintr __P((usbd_request_handle reqh, usbd_private_handle addr,
+ usbd_status status));
+void ugen_disco __P((void *));
-static driver_t ugen_driver = {
- "ugen",
- ugen_methods,
- DRIVER_TYPE_MISC,
- sizeof(struct ugen_softc)
-};
-#endif
+int ugen_set_config __P((struct ugen_softc *sc, int configno));
+usb_config_descriptor_t *ugen_get_cdesc __P((struct ugen_softc *sc, int index,
+ int *lenp));
+usbd_status ugen_set_interface __P((struct ugen_softc *, int, int));
+int ugen_get_alt_index __P((struct ugen_softc *sc, int ifaceidx));
+#define UGENUNIT(n) (((n) >> 4) & 0xf)
+#define UGENENDPOINT(n) ((n) & 0xf)
+
+USB_DECLARE_DRIVER(ugen);
+
+USB_MATCH(ugen)
+{
+ USB_MATCH_START(ugen, uaa);
+
+ if (uaa->usegeneric)
+ return (UMATCH_GENERIC);
+ else
+ return (UMATCH_NONE);
+}
+
+USB_ATTACH(ugen)
+{
+ USB_ATTACH_START(ugen, sc, uaa);
+ char devinfo[1024];
+ usbd_status r;
+ int conf;
+
+ usbd_devinfo(uaa->device, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ sc->sc_udev = uaa->device;
+ conf = 1; /* XXX should not hard code 1 */
+ r = ugen_set_config(sc, conf);
+ if (r != USBD_NORMAL_COMPLETION) {
+ printf("%s: setting configuration %d failed\n",
+ USBDEVNAME(sc->sc_dev), conf);
+ sc->sc_disconnected = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+int
+ugen_set_config(sc, configno)
+ struct ugen_softc *sc;
+ int configno;
+{
+ usbd_device_handle dev = sc->sc_udev;
+ usbd_interface_handle iface;
+ usb_endpoint_descriptor_t *ed;
+ struct ugen_endpoint *sce;
+ u_int8_t niface, nendpt;
+ int ifaceno, endptno, endpt;
+ usbd_status r;
+
+ DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n",
+ USBDEVNAME(sc->sc_dev), configno, sc));
+ if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) {
+ /* Avoid setting the current value. */
+ r = usbd_set_config_no(dev, configno, 0);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ }
+
+ r = usbd_interface_count(dev, &niface);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints);
+ for (ifaceno = 0; ifaceno < niface; ifaceno++) {
+ DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno));
+ r = usbd_device2interface_handle(dev, ifaceno, &iface);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ r = usbd_endpoint_count(iface, &nendpt);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)]
+ [UE_GET_IN(endpt)];
+ DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x"
+ "(%d,%d), sce=%p\n",
+ endptno, endpt, UE_GET_ADDR(endpt),
+ UE_GET_IN(endpt), sce));
+ sce->sc = sc;
+ sce->edesc = ed;
+ sce->iface = iface;
+ }
+ }
+ return (USBD_NORMAL_COMPLETION);
+}
+
+void
+ugen_disco(p)
+ void *p;
+{
+ struct ugen_softc *sc = p;
+ sc->sc_disconnected = 1;
+}
-#if defined(__NetBSD__)
int
-ugen_match(parent, match, aux)
- struct device *parent;
- struct cfdata *match;
- void *aux;
+ugenopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
{
- struct usb_attach_arg *uaa = aux;
+ int unit = UGENUNIT(dev);
+ int endpt = UGENENDPOINT(dev);
+ usb_endpoint_descriptor_t *edesc;
+ struct ugen_endpoint *sce;
+ int dir, isize;
+ usbd_status r;
+
+ USB_GET_SC_OPEN(ugen, unit, sc);
+ DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n",
+ flag, mode, unit, endpt));
+
+ if (sc->sc_disconnected)
+ return (EIO);
+
+ if (endpt == USB_CONTROL_ENDPOINT) {
+ /*if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD))
+ return (EACCES);*/
+ sce = &sc->sc_endpoints[USB_CONTROL_ENDPOINT][OUT];
+ if (sce->state & UGEN_OPEN)
+ return (EBUSY);
+ } else {
+ switch (flag & (FWRITE|FREAD)) {
+ case FWRITE:
+ dir = OUT;
+ break;
+ case FREAD:
+ dir = IN;
+ break;
+ default:
+ return (EACCES);
+ }
+ sce = &sc->sc_endpoints[endpt][dir];
+ DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n",
+ sc, endpt, dir, sce));
+ if (sce->state & UGEN_OPEN)
+ return (EBUSY);
+ edesc = sce->edesc;
+ if (!edesc)
+ return (ENXIO);
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ isize = UGETW(edesc->wMaxPacketSize);
+ if (isize == 0) /* shouldn't happen */
+ return (EINVAL);
+ sce->ibuf = malloc(isize, M_USB, M_WAITOK);
+ DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n",
+ endpt, isize));
+#if defined(__NetBSD__)
+ if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1)
+ return (ENOMEM);
#elif defined(__FreeBSD__)
-static int
-ugen_match(device_t device)
+ clist_alloc_cblocks(&sce->q, UGEN_IBSIZE, 0);
+#endif
+ r = usbd_open_pipe_intr(sce->iface,
+ edesc->bEndpointAddress,
+ USBD_SHORT_XFER_OK, &sce->pipeh, sce,
+ sce->ibuf, isize, ugenintr);
+ if (r != USBD_NORMAL_COMPLETION) {
+ free(sce->ibuf, M_USB);
+#if defined(__NetBSD__)
+ clfree(&sce->q);
+#elif defined(__FreeBSD__)
+ clist_free_cblocks(&sce->q);
+#endif
+ return (EIO);
+ }
+ usbd_set_disco(sce->pipeh, ugen_disco, sc);
+ DPRINTFN(5, ("ugenopen: interrupt open done\n"));
+ break;
+ case UE_BULK:
+ r = usbd_open_pipe(sce->iface,
+ edesc->bEndpointAddress, 0,
+ &sce->pipeh);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EIO);
+ break;
+ case UE_CONTROL:
+ case UE_ISOCHRONOUS:
+ return (EINVAL);
+ }
+ }
+ sce->state |= UGEN_OPEN;
+ return (0);
+}
+
+int
+ugenclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
{
- struct usb_attach_arg *uaa = device_get_ivars(device);
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_endpoint *sce;
+ int dir;
+
+ DPRINTFN(5, ("ugenclose: flag=%d, mode=%d\n", flag, mode));
+ if (sc->sc_disconnected)
+ return (EIO);
+
+ if (endpt == USB_CONTROL_ENDPOINT) {
+ DPRINTFN(5, ("ugenclose: close control\n"));
+ sc->sc_endpoints[endpt][OUT].state = 0;
+ return (0);
+ }
+
+ flag = FWRITE | FREAD; /* XXX bug if generic open/close */
+
+ /* The open modes have been joined, so check for both modes. */
+ for (dir = OUT; dir <= IN; dir++) {
+ if (flag & (dir == OUT ? FWRITE : FREAD)) {
+ sce = &sc->sc_endpoints[endpt][dir];
+ if (!sce || !sce->pipeh) /* XXX */
+ continue; /* XXX */
+ DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n",
+ endpt, dir, sce));
+ sce->state = 0;
+
+ usbd_abort_pipe(sce->pipeh);
+ usbd_close_pipe(sce->pipeh);
+ sce->pipeh = 0;
+
+ if (sce->ibuf) {
+ free(sce->ibuf, M_USB);
+ sce->ibuf = 0;
+ }
+ }
+ }
+
+ return (0);
+}
+
+int
+ugenread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][IN];
+ u_int32_t n, tn;
+ char buf[UGEN_BBSIZE];
+ usbd_request_handle reqh;
+ usbd_status r;
+ int s;
+ int error = 0;
+ u_char buffer[UGEN_CHUNK];
+
+ DPRINTFN(5, ("ugenread: %d:%d\n", UGENUNIT(dev), UGENENDPOINT(dev)));
+ if (sc->sc_disconnected)
+ return (EIO);
+
+#ifdef DIAGNOSTIC
+ if (!sce->edesc) {
+ printf("ugenread: no edesc\n");
+ return (EIO);
+ }
+ if (!sce->pipeh) {
+ printf("ugenread: no pipe\n");
+ return (EIO);
+ }
#endif
- usb_interface_descriptor_t *id;
-
- DPRINTFN(10,("ugen_match\n"));
- if (uaa->usegeneric)
- return UMATCH_GENERIC;
- else
- return UMATCH_NONE;
+
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ /* Block until activity occured. */
+ s = splusb();
+ while (sce->q.c_cc == 0) {
+ if (flag & IO_NDELAY) {
+ splx(s);
+ return (EWOULDBLOCK);
+ }
+ sce->state |= UGEN_ASLP;
+ DPRINTFN(5, ("ugenread: sleep on %p\n", sc));
+ error = tsleep((caddr_t)sce, PZERO | PCATCH,
+ "ugenri", 0);
+ DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
+ if (error) {
+ sce->state &= ~UGEN_ASLP;
+ splx(s);
+ return (error);
+ }
+ }
+ splx(s);
+
+ /* Transfer as many chunks as possible. */
+ while (sce->q.c_cc > 0 && uio->uio_resid > 0) {
+ n = min(sce->q.c_cc, uio->uio_resid);
+ if (n > sizeof(buffer))
+ n = sizeof(buffer);
+
+ /* Remove a small chunk from the input queue. */
+ q_to_b(&sce->q, buffer, n);
+ DPRINTFN(5, ("ugenread: got %d chars\n", n));
+
+ /* Copy the data to the user process. */
+ error = uiomove(buffer, n, uio);
+ if (error)
+ break;
+ }
+ break;
+ case UE_BULK:
+ reqh = usbd_alloc_request();
+ if (reqh == 0)
+ return (ENOMEM);
+ while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0) {
+ DPRINTFN(1, ("ugenread: transfer %d bytes\n", n));
+ r = usbd_bulk_transfer(reqh, sce->pipeh, 0, buf,
+ &tn, "ugenrb");
+ if (r != USBD_NORMAL_COMPLETION) {
+ if (r == USBD_INTERRUPTED)
+ error = EINTR;
+ else
+ error = EIO;
+ break;
+ }
+ error = uiomove(buf, tn, uio);
+ if (error || tn < n)
+ break;
+ }
+ usbd_free_request(reqh);
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ return (error);
}
-#if defined(__NetBSD__)
+int
+ugenwrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][IN];
+ size_t n;
+ int error = 0;
+ char buf[UGEN_BBSIZE];
+ usbd_request_handle reqh;
+ usbd_status r;
+
+ if (sc->sc_disconnected)
+ return (EIO);
+
+#ifdef DIAGNOSTIC
+ if (!sce->edesc) {
+ printf("ugenwrite: no edesc\n");
+ return (EIO);
+ }
+ if (!sce->pipeh) {
+ printf("ugenwrite: no pipe\n");
+ return (EIO);
+ }
+#endif
+
+ DPRINTF(("ugenwrite\n"));
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_BULK:
+ reqh = usbd_alloc_request();
+ if (reqh == 0)
+ return (EIO);
+ while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0) {
+ error = uiomove(buf, n, uio);
+ if (error)
+ break;
+ DPRINTFN(1, ("ugenwrite: transfer %d bytes\n", n));
+ r = usbd_bulk_transfer(reqh, sce->pipeh, 0, buf,
+ &n, "ugenwb");
+ if (r != USBD_NORMAL_COMPLETION) {
+ if (r == USBD_INTERRUPTED)
+ error = EINTR;
+ else
+ error = EIO;
+ break;
+ }
+ }
+ usbd_free_request(reqh);
+ break;
+ default:
+ return (ENXIO);
+ }
+ return (error);
+}
+
+
void
-ugen_attach(parent, self, aux)
- struct device *parent;
- struct device *self;
- void *aux;
+ugenintr(reqh, addr, status)
+ usbd_request_handle reqh;
+ usbd_private_handle addr;
+ usbd_status status;
{
- struct ugen_softc *sc = (struct ugen_softc *)self;
- struct usb_attach_arg *uaa = aux;
-#elif defined(__FreeBSD__)
-static int
-ugen_attach(device_t self)
+ struct ugen_endpoint *sce = addr;
+ /*struct ugen_softc *sc = sce->sc;*/
+ usbd_private_handle priv;
+ void *buffer;
+ u_int32_t count;
+ usbd_status xstatus;
+ u_char *ibuf;
+
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ DPRINTF(("ugenintr: status=%d\n", status));
+ usbd_clear_endpoint_stall_async(sce->pipeh);
+ return;
+ }
+
+ (void)usbd_get_request_status(reqh, &priv, &buffer, &count, &xstatus);
+ ibuf = sce->ibuf;
+
+ DPRINTFN(5, ("ugenintr: reqh=%p status=%d count=%d\n",
+ reqh, xstatus, count));
+ DPRINTFN(5, (" data = %02x %02x %02x\n",
+ ibuf[0], ibuf[1], ibuf[2]));
+
+ (void)b_to_q(ibuf, count, &sce->q);
+
+ if (sce->state & UGEN_ASLP) {
+ sce->state &= ~UGEN_ASLP;
+ DPRINTFN(5, ("ugen_intr: waking %p\n", sce));
+ wakeup((caddr_t)sce);
+ }
+ selwakeup(&sce->rsel);
+}
+
+usbd_status
+ugen_set_interface(sc, ifaceidx, altno)
+ struct ugen_softc *sc;
+ int ifaceidx, altno;
{
- struct ugen_softc *sc = device_get_softc(self);
- struct usb_attach_arg *uaa = device_get_ivars(self);
+ usbd_interface_handle iface;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status r;
+ struct ugen_endpoint *sce;
+ u_int8_t niface, nendpt, endptno, endpt;
+
+ DPRINTFN(15, ("ugen_set_interface %d %d\n", ifaceidx, altno));
+
+ r = usbd_interface_count(sc->sc_udev, &niface);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ if (ifaceidx < 0 || ifaceidx >= niface)
+ return (USBD_INVAL);
+
+ r = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ r = usbd_endpoint_count(iface, &nendpt);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][UE_GET_IN(endpt)];
+ sce->sc = 0;
+ sce->edesc = 0;
+ sce->iface = 0;
+ }
+
+ /* change setting */
+ r = usbd_set_interface(iface, altno);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+
+ r = usbd_endpoint_count(iface, &nendpt);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (r);
+ for (endptno = 0; endptno < nendpt; endptno++) {
+ ed = usbd_interface2endpoint_descriptor(iface,endptno);
+ endpt = ed->bEndpointAddress;
+ sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][UE_GET_IN(endpt)];
+ sce->sc = sc;
+ sce->edesc = ed;
+ sce->iface = iface;
+ }
+ return (0);
+}
+
+/* Retrieve a complete descriptor for a certain device and index. */
+usb_config_descriptor_t *
+ugen_get_cdesc(sc, index, lenp)
+ struct ugen_softc *sc;
+ int index;
+ int *lenp;
+{
+ usb_config_descriptor_t *cdesc, *tdesc, cdescr;
+ int len;
+ usbd_status r;
+
+ if (index == USB_CURRENT_CONFIG_INDEX) {
+ tdesc = usbd_get_config_descriptor(sc->sc_udev);
+ len = UGETW(tdesc->wTotalLength);
+ if (lenp)
+ *lenp = len;
+ cdesc = malloc(len, M_TEMP, M_WAITOK);
+ memcpy(cdesc, tdesc, len);
+ DPRINTFN(5,("ugen_get_cdesc: current, len=%d\n", len));
+ } else {
+ r = usbd_get_config_desc(sc->sc_udev, index, &cdescr);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (0);
+ len = UGETW(cdescr.wTotalLength);
+ DPRINTFN(5,("ugen_get_cdesc: index=%d, len=%d\n", index, len));
+ if (lenp)
+ *lenp = len;
+ cdesc = malloc(len, M_TEMP, M_WAITOK);
+ r = usbd_get_config_desc_full(sc->sc_udev, index, cdesc, len);
+ if (r != USBD_NORMAL_COMPLETION) {
+ free(cdesc, M_TEMP);
+ return (0);
+ }
+ }
+ return (cdesc);
+}
+
+int
+ugen_get_alt_index(sc, ifaceidx)
+ struct ugen_softc *sc;
+ int ifaceidx;
+{
+ usbd_interface_handle iface;
+ usbd_status r;
+
+ r = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (-1);
+ return (usbd_get_interface_altindex(iface));
+}
+
+int
+ugenioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_endpoint *sce;
+ usbd_status r;
+ usbd_interface_handle iface;
+ struct usb_config_desc *cd;
+ usb_config_descriptor_t *cdesc;
+ struct usb_interface_desc *id;
+ usb_interface_descriptor_t *idesc;
+ struct usb_endpoint_desc *ed;
+ usb_endpoint_descriptor_t *edesc;
+ struct usb_alt_interface *ai;
+ struct usb_string_desc *si;
+ u_int8_t conf, alt;
+
+ DPRINTFN(5, ("ugenioctl: cmd=%08lx\n", cmd));
+ if (sc->sc_disconnected)
+ return (EIO);
+
+ switch (cmd) {
+ case FIONBIO:
+ /* All handled in the upper FS layer. */
+ return (0);
+ case USB_SET_SHORT_XFER:
+ /* This flag only affects read */
+ sce = &sc->sc_endpoints[endpt][IN];
+#ifdef DIAGNOSTIC
+ if (!sce->pipeh) {
+ printf("ugenioctl: no pipe\n");
+ return (EIO);
+ }
#endif
- usbd_device_handle dev = uaa->device;
- usb_device_descriptor_t *udd = &dev->ddesc;
- char devinfo[1024];
+ if (*(int *)addr)
+ sce->state |= UGEN_SHORT_OK;
+ else
+ sce->state &= ~UGEN_SHORT_OK;
+ return (0);
+ default:
+ break;
+ }
- usbd_devinfo(dev, 0, devinfo);
-#if defined(__FreeBSD__)
- device_set_desc(self, devinfo);
- printf("%s%d", device_get_name(self), device_get_unit(self));
+ if (endpt != USB_CONTROL_ENDPOINT)
+ return (EINVAL);
+
+ switch (cmd) {
+#ifdef USB_DEBUG
+ case USB_SETDEBUG:
+ ugendebug = *(int *)addr;
+ break;
#endif
- printf(": %s (device class %d/%d)\n", devinfo,
- udd->bDeviceClass, udd->bDeviceSubClass);
- sc->sc_dev = self;
+ case USB_GET_CONFIG:
+ r = usbd_get_config(sc->sc_udev, &conf);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EIO);
+ *(int *)addr = conf;
+ break;
+ case USB_SET_CONFIG:
+ if (!(flag & FWRITE))
+ return (EPERM);
+ r = ugen_set_config(sc, *(int *)addr);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EIO);
+ break;
+ case USB_GET_ALTINTERFACE:
+ ai = (struct usb_alt_interface *)addr;
+ r = usbd_device2interface_handle(sc->sc_udev,
+ ai->interface_index, &iface);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EINVAL);
+ idesc = usbd_get_interface_descriptor(iface);
+ if (!idesc)
+ return (EIO);
+ ai->alt_no = idesc->bAlternateSetting;
+ break;
+ case USB_SET_ALTINTERFACE:
+ if (!(flag & FWRITE))
+ return (EPERM);
+ ai = (struct usb_alt_interface *)addr;
+ r = usbd_device2interface_handle(sc->sc_udev,
+ ai->interface_index, &iface);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EINVAL);
+ r = ugen_set_interface(sc, ai->interface_index, ai->alt_no);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EINVAL);
+ break;
+ case USB_GET_NO_ALT:
+ ai = (struct usb_alt_interface *)addr;
+ cdesc = ugen_get_cdesc(sc, ai->config_index, 0);
+ if (!cdesc)
+ return (EINVAL);
+ idesc = usbd_find_idesc(cdesc, ai->interface_index, 0);
+ if (!idesc)
+ return (EINVAL);
+ ai->alt_no = usbd_get_no_alts(cdesc, idesc->bInterfaceNumber);
+ break;
+ case USB_GET_DEVICE_DESC:
+ *(usb_device_descriptor_t *)addr =
+ *usbd_get_device_descriptor(sc->sc_udev);
+ break;
+ case USB_GET_CONFIG_DESC:
+ cd = (struct usb_config_desc *)addr;
+ cdesc = ugen_get_cdesc(sc, cd->config_index, 0);
+ if (!cdesc)
+ return (EINVAL);
+ cd->desc = *cdesc;
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_INTERFACE_DESC:
+ id = (struct usb_interface_desc *)addr;
+ cdesc = ugen_get_cdesc(sc, id->config_index, 0);
+ if (!cdesc)
+ return (EINVAL);
+ if (id->config_index == USB_CURRENT_CONFIG_INDEX &&
+ id->alt_index == USB_CURRENT_ALT_INDEX)
+ alt = ugen_get_alt_index(sc, id->interface_index);
+ else
+ alt = id->alt_index;
+ idesc = usbd_find_idesc(cdesc, id->interface_index, alt);
+ if (!idesc) {
+ free(cdesc, M_TEMP);
+ return (EINVAL);
+ }
+ id->desc = *idesc;
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_ENDPOINT_DESC:
+ ed = (struct usb_endpoint_desc *)addr;
+ cdesc = ugen_get_cdesc(sc, ed->config_index, 0);
+ if (!cdesc)
+ return (EINVAL);
+ if (ed->config_index == USB_CURRENT_CONFIG_INDEX &&
+ ed->alt_index == USB_CURRENT_ALT_INDEX)
+ alt = ugen_get_alt_index(sc, ed->interface_index);
+ else
+ alt = ed->alt_index;
+ edesc = usbd_find_edesc(cdesc, ed->interface_index,
+ alt, ed->endpoint_index);
+ if (!edesc) {
+ free(cdesc, M_TEMP);
+ return (EINVAL);
+ }
+ ed->desc = *edesc;
+ free(cdesc, M_TEMP);
+ break;
+ case USB_GET_FULL_DESC:
+ {
+ int len;
+ struct iovec iov;
+ struct uio uio;
+ struct usb_full_desc *fd = (struct usb_full_desc *)addr;
+ int error;
+
+ cdesc = ugen_get_cdesc(sc, fd->config_index, &len);
+ if (len > fd->size)
+ len = fd->size;
+ iov.iov_base = (caddr_t)fd->data;
+ iov.iov_len = len;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = len;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_USERSPACE;
+ uio.uio_rw = UIO_READ;
+ uio.uio_procp = p;
+ error = uiomove(cdesc, len, &uio);
+ free(cdesc, M_TEMP);
+ return (error);
+ }
+ case USB_GET_STRING_DESC:
+ si = (struct usb_string_desc *)addr;
+ r = usbd_get_string_desc(sc->sc_udev, si->string_index,
+ si->language_id, &si->desc);
+ if (r != USBD_NORMAL_COMPLETION)
+ return (EINVAL);
+ break;
+ case USB_DO_REQUEST:
+ {
+ struct usb_ctl_request *ur = (void *)addr;
+ int len = UGETW(ur->request.wLength);
+ struct iovec iov;
+ struct uio uio;
+ void *ptr = 0;
+ usbd_status r;
+ int error = 0;
+
+ if (!(flag & FWRITE))
+ return (EPERM);
+ /* Avoid requests that would damage the bus integrity. */
+ if ((ur->request.bmRequestType == UT_WRITE_DEVICE &&
+ ur->request.bRequest == UR_SET_ADDRESS) ||
+ (ur->request.bmRequestType == UT_WRITE_DEVICE &&
+ ur->request.bRequest == UR_SET_CONFIG) ||
+ (ur->request.bmRequestType == UT_WRITE_INTERFACE &&
+ ur->request.bRequest == UR_SET_INTERFACE))
+ return (EINVAL);
+
+ if (len < 0 || len > 32767)
+ return (EINVAL);
+ if (len != 0) {
+ iov.iov_base = (caddr_t)ur->data;
+ iov.iov_len = len;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = len;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_USERSPACE;
+ uio.uio_rw =
+ ur->request.bmRequestType & UT_READ ?
+ UIO_READ : UIO_WRITE;
+ uio.uio_procp = p;
+ ptr = malloc(len, M_TEMP, M_WAITOK);
+ if (uio.uio_rw == UIO_WRITE) {
+ error = uiomove(ptr, len, &uio);
+ if (error)
+ goto ret;
+ }
+ }
+ r = usbd_do_request_flags(sc->sc_udev, &ur->request,
+ ptr, ur->flags, &ur->actlen);
+ if (r) {
+ error = EIO;
+ goto ret;
+ }
+ if (len != 0) {
+ if (uio.uio_rw == UIO_READ) {
+ error = uiomove(ptr, len, &uio);
+ if (error)
+ goto ret;
+ }
+ }
+ ret:
+ if (ptr)
+ free(ptr, M_TEMP);
+ return (error);
+ }
+ case USB_GET_DEVICEINFO:
+ usbd_fill_deviceinfo(sc->sc_udev,
+ (struct usb_device_info *)addr);
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+ugenpoll(dev, events, p)
+ dev_t dev;
+ int events;
+ struct proc *p;
+{
+ USB_GET_SC(ugen, UGENUNIT(dev), sc);
+ /* XXX */
+ struct ugen_endpoint *sce;
+ int revents = 0;
+ int s;
+
+ if (sc->sc_disconnected)
+ return (EIO);
- ATTACH_SUCCESS_RETURN;
+ sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
+#ifdef DIAGNOSTIC
+ if (!sce->edesc) {
+ printf("ugenwrite: no edesc\n");
+ return (EIO);
+ }
+ if (!sce->pipeh) {
+ printf("ugenpoll: no pipe\n");
+ return (EIO);
+ }
+#endif
+ s = splusb();
+ switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (sce->q.c_cc > 0)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ selrecord(p, &sce->rsel);
+ }
+ break;
+ case UE_BULK:
+ /*
+ * We have no easy way of determining if a read will
+ * yield any data or a write will happen.
+ * Pretend they will.
+ */
+ revents |= events &
+ (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM);
+ break;
+ default:
+ break;
+ }
+ splx(s);
+ return (revents);
}
#if defined(__FreeBSD__)
static int
ugen_detach(device_t self)
-{
- /* we need to cast away the const returned by
- * device_get_desc
- */
+{
+ struct ugen_softc *sc = device_get_softc(self);
char *devinfo = (char *) device_get_desc(self);
if (devinfo) {
device_set_desc(self, NULL);
free(devinfo, M_USB);
}
-
return 0;
}
-DRIVER_MODULE(ugen, usb, ugen_driver, ugen_devclass, usb_driver_load, 0);
+DRIVER_MODULE(ugen, usb, ugen_driver, ugen_devclass, usbd_driver_load, 0);
#endif
OpenPOWER on IntegriCloud