freebsd-dev/sys/dev/usb/urio.c
Josef Karthauser 528d1a7fbc Replace the FOO_DEBUG definitions with USB_DEBUG, and switch the
debugging levels to off by default.  Now that debug levels can be
tweaked by sysctl we don't need to go through hoops to get the
different usb parts to produce debug data.
2002-07-31 14:34:36 +00:00

711 lines
16 KiB
C

/*
* Copyright (c) 2000 Iwasa Kazmi
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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.
*
* This code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
* This code includes software developed by the NetBSD Foundation, Inc. and
* its contributors.
*/
/* $FreeBSD$ */
/*
* 2000/3/24 added NetBSD/OpenBSD support (from Alex Nemirovsky)
* 2000/3/07 use two bulk-pipe handles for read and write (Dirk)
* 2000/3/06 change major number(143), and copyright header
* some fix for 4.0 (Dirk)
* 2000/3/05 codes for FreeBSD 4.x - CURRENT (Thanks to Dirk-Willem van Gulik)
* 2000/3/01 remove retry code from urioioctl()
* change method of bulk transfer (no interrupt)
* 2000/2/28 small fixes for new rio_usb.h
* 2000/2/24 first version.
*/
#include <sys/param.h>
#include <sys/systm.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>
#include <sys/ioccom.h>
#endif
#include <sys/fcntl.h>
#include <sys/filio.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/tty.h>
#include <sys/file.h>
#if __FreeBSD_version >= 500014
#include <sys/selinfo.h>
#else
#include <sys/select.h>
#endif
#include <sys/vnode.h>
#include <sys/poll.h>
#include <sys/sysctl.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/rio500_usb.h>
#ifdef USB_DEBUG
#define DPRINTF(x) if (uriodebug) logprintf x
#define DPRINTFN(n,x) if (uriodebug>(n)) logprintf x
int uriodebug = 0;
SYSCTL_INT(_debug_usb, OID_AUTO, urio, CTLFLAG_RW,
&uriodebug, 0, "urio debug level");
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
/* difference of usbd interface */
#if defined(__FreeBSD__)
#if (__FreeBSD__ >= 4)
#define USBDI 1
#else
#define USBDI 0
#endif
#elif defined(__NetBSD__) || defined(__OpenBSD__)
#define USBDI 1
#endif
#define RIO_OUT 0
#define RIO_IN 1
#define RIO_NODIR 2
#if defined(__NetBSD__)
int urioopen(dev_t, int, int, struct proc *);
int urioclose(dev_t, int, int, struct proc *p);
int urioread(dev_t, struct uio *uio, int);
int uriowrite(dev_t, struct uio *uio, int);
int urioioctl(dev_t, u_long, caddr_t, int, struct proc *);
cdev_decl(urio);
#define RIO_UE_GET_DIR(p) ((UE_GET_DIR(p) == UE_DIR_IN) ? RIO_IN :\
((UE_GET_DIR(p) == UE_DIR_OUT) ? RIO_OUT :\
RIO_NODIR))
#elif defined(__FreeBSD__)
d_open_t urioopen;
d_close_t urioclose;
d_read_t urioread;
d_write_t uriowrite;
d_ioctl_t urioioctl;
#define URIO_CDEV_MAJOR 143
#if (__FreeBSD__ >= 4)
Static struct cdevsw urio_cdevsw = {
urioopen, urioclose, urioread, uriowrite,
urioioctl, nopoll, nommap, nostrategy,
"urio", URIO_CDEV_MAJOR,nodump, nopsize,
0,
#if __FreeBSD_version < 500014
-1
#endif
};
#define RIO_UE_GET_DIR(p) ((UE_GET_DIR(p) == UE_DIR_IN) ? RIO_IN :\
((UE_GET_DIR(p) == UE_DIR_OUT) ? RIO_OUT :\
RIO_NODIR))
#else
Static struct cdevsw urio_cdevsw = {
urioopen, urioclose, urioread, uriowrite,
urioioctl, nostop, nullreset, nodevtotty,
seltrue, nommap, nostrat,
"urio", NULL, -1
};
#define USBBASEDEVICE bdevice
#define RIO_UE_GET_DIR(p) UE_GET_IN(p)
#endif
#endif /*defined(__FreeBSD__)*/
#define URIO_BBSIZE 1024
struct urio_softc {
USBBASEDEVICE sc_dev;
usbd_device_handle sc_udev;
usbd_interface_handle sc_iface;
int sc_opened;
usbd_pipe_handle sc_pipeh_in;
usbd_pipe_handle sc_pipeh_out;
int sc_epaddr[2];
int sc_refcnt;
#if defined(__FreeBSD__)
dev_t sc_dev_t;
#endif /* defined(__FreeBSD__) */
#if defined(__NetBSD__) || defined(__OpenBSD__)
u_char sc_dying;
#endif
};
#define URIOUNIT(n) (minor(n))
#define RIO_RW_TIMEOUT 4000 /* ms */
USB_DECLARE_DRIVER(urio);
USB_MATCH(urio)
{
USB_MATCH_START(urio, uaa);
usb_device_descriptor_t *dd;
DPRINTFN(10,("urio_match\n"));
if (!uaa->iface)
return UMATCH_NONE;
dd = usbd_get_device_descriptor(uaa->device);
if (dd &&
((UGETW(dd->idVendor) == USB_VENDOR_DIAMOND &&
UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND_RIO500USB) ||
(UGETW(dd->idVendor) == USB_VENDOR_DIAMOND2 &&
(UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND2_RIO600USB ||
UGETW(dd->idProduct) == USB_PRODUCT_DIAMOND2_RIO800USB))))
return UMATCH_VENDOR_PRODUCT;
else
return UMATCH_NONE;
}
USB_ATTACH(urio)
{
USB_ATTACH_START(urio, sc, uaa);
char devinfo[1024];
usbd_device_handle udev;
usbd_interface_handle iface;
u_int8_t epcount;
#if defined(__NetBSD__) || defined(__OpenBSD__)
u_int8_t niface;
#endif
usbd_status r;
char * ermsg = "<none>";
int i;
DPRINTFN(10,("urio_attach: sc=%p\n", sc));
usbd_devinfo(uaa->device, 0, devinfo);
USB_ATTACH_SETUP;
printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
sc->sc_udev = udev = uaa->device;
#if defined(__FreeBSD__)
if ((!uaa->device) || (!uaa->iface)) {
ermsg = "device or iface";
goto nobulk;
}
sc->sc_iface = iface = uaa->iface;
#elif defined(__NetBSD__) || defined(__OpenBSD__)
if (!udev) {
ermsg = "device";
goto nobulk;
}
r = usbd_interface_count(udev, &niface);
if (r) {
ermsg = "iface";
goto nobulk;
}
r = usbd_device2interface_handle(udev, 0, &iface);
if (r) {
ermsg = "iface";
goto nobulk;
}
sc->sc_iface = iface;
#endif
sc->sc_opened = 0;
sc->sc_pipeh_in = 0;
sc->sc_pipeh_out = 0;
sc->sc_refcnt = 0;
r = usbd_endpoint_count(iface, &epcount);
if (r != USBD_NORMAL_COMPLETION) {
ermsg = "endpoints";
goto nobulk;
}
sc->sc_epaddr[RIO_OUT] = 0xff;
sc->sc_epaddr[RIO_IN] = 0x00;
for (i = 0; i < epcount; i++) {
usb_endpoint_descriptor_t *edesc =
usbd_interface2endpoint_descriptor(iface, i);
int d;
if (!edesc) {
ermsg = "interface endpoint";
goto nobulk;
}
d = RIO_UE_GET_DIR(edesc->bEndpointAddress);
if (d != RIO_NODIR)
sc->sc_epaddr[d] = edesc->bEndpointAddress;
}
if ( sc->sc_epaddr[RIO_OUT] == 0xff ||
sc->sc_epaddr[RIO_IN] == 0x00) {
ermsg = "Rio I&O";
goto nobulk;
}
#if defined(__FreeBSD__)
#if (__FreeBSD__ >= 4)
/* XXX no error trapping, no storing of dev_t */
sc->sc_dev_t = make_dev(&urio_cdevsw, device_get_unit(self),
UID_ROOT, GID_OPERATOR,
0644, "urio%d", device_get_unit(self));
#endif
#elif defined(__NetBSD__) || defined(__OpenBSD__)
usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
USBDEV(sc->sc_dev));
#endif
DPRINTFN(10, ("urio_attach: %p\n", sc->sc_udev));
USB_ATTACH_SUCCESS_RETURN;
nobulk:
printf("%s: could not find %s\n", USBDEVNAME(sc->sc_dev),ermsg);
USB_ATTACH_ERROR_RETURN;
}
int
urioopen(dev_t dev, int flag, int mode, usb_proc_ptr p)
{
#if (USBDI >= 1)
struct urio_softc * sc;
#endif
int unit = URIOUNIT(dev);
USB_GET_SC_OPEN(urio, unit, sc);
DPRINTFN(5, ("urioopen: flag=%d, mode=%d, unit=%d\n",
flag, mode, unit));
if (sc->sc_opened)
return EBUSY;
if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD))
return EACCES;
sc->sc_opened = 1;
sc->sc_pipeh_in = 0;
sc->sc_pipeh_out = 0;
if (usbd_open_pipe(sc->sc_iface,
sc->sc_epaddr[RIO_IN], 0, &sc->sc_pipeh_in)
!= USBD_NORMAL_COMPLETION)
{
sc->sc_pipeh_in = 0;
return EIO;
};
if (usbd_open_pipe(sc->sc_iface,
sc->sc_epaddr[RIO_OUT], 0, &sc->sc_pipeh_out)
!= USBD_NORMAL_COMPLETION)
{
usbd_close_pipe(sc->sc_pipeh_in);
sc->sc_pipeh_in = 0;
sc->sc_pipeh_out = 0;
return EIO;
};
return 0;
}
int
urioclose(dev_t dev, int flag, int mode, usb_proc_ptr p)
{
#if (USBDI >= 1)
struct urio_softc * sc;
#endif
int unit = URIOUNIT(dev);
USB_GET_SC(urio, unit, sc);
DPRINTFN(5, ("urioclose: flag=%d, mode=%d, unit=%d\n", flag, mode, unit));
if (sc->sc_pipeh_in)
usbd_close_pipe(sc->sc_pipeh_in);
if (sc->sc_pipeh_out)
usbd_close_pipe(sc->sc_pipeh_out);
sc->sc_pipeh_in = 0;
sc->sc_pipeh_out = 0;
sc->sc_opened = 0;
sc->sc_refcnt = 0;
return 0;
}
int
urioread(dev_t dev, struct uio *uio, int flag)
{
#if (USBDI >= 1)
struct urio_softc * sc;
usbd_xfer_handle reqh;
#else
usbd_request_handle reqh;
usbd_private_handle r_priv;
void *r_buff;
usbd_status r_status;
#endif
int unit = URIOUNIT(dev);
usbd_status r;
char buf[URIO_BBSIZE];
u_int32_t n, tn;
int error = 0;
USB_GET_SC(urio, unit, sc);
DPRINTFN(5, ("urioread: %d\n", unit));
if (!sc->sc_opened)
return EIO;
#if (USBDI >= 1)
sc->sc_refcnt++;
reqh = usbd_alloc_xfer(sc->sc_udev);
#else
reqh = usbd_alloc_request();
#endif
if (reqh == 0)
return ENOMEM;
while ((n = min(URIO_BBSIZE, uio->uio_resid)) != 0) {
DPRINTFN(1, ("urioread: start transfer %d bytes\n", n));
tn = n;
#if (USBDI >= 1)
usbd_setup_xfer(reqh, sc->sc_pipeh_in, 0, buf, tn,
0, RIO_RW_TIMEOUT, 0);
#else
r = usbd_setup_request(reqh, sc->sc_pipeh_in, 0, buf, tn,
0, RIO_RW_TIMEOUT, 0);
if (r != USBD_NORMAL_COMPLETION) {
error = EIO;
break;
}
#endif
r = usbd_sync_transfer(reqh);
if (r != USBD_NORMAL_COMPLETION) {
DPRINTFN(1, ("urioread: error=%d\n", r));
usbd_clear_endpoint_stall(sc->sc_pipeh_in);
tn = 0;
error = EIO;
break;
}
#if (USBDI >= 1)
usbd_get_xfer_status(reqh, 0, 0, &tn, 0);
#else
usbd_get_request_status(reqh, &r_priv, &r_buff, &tn, &r_status);
#endif
DPRINTFN(1, ("urioread: got %d bytes\n", tn));
error = uiomove(buf, tn, uio);
if (error || tn < n)
break;
}
#if (USBDI >= 1)
usbd_free_xfer(reqh);
#else
usbd_free_request(reqh);
#endif
return error;
}
int
uriowrite(dev_t dev, struct uio *uio, int flag)
{
#if (USBDI >= 1)
struct urio_softc * sc;
usbd_xfer_handle reqh;
#else
usbd_request_handle reqh;
#endif
int unit = URIOUNIT(dev);
usbd_status r;
char buf[URIO_BBSIZE];
u_int32_t n;
int error = 0;
USB_GET_SC(urio, unit, sc);
DPRINTFN(5, ("uriowrite: %d\n", unit));
if (!sc->sc_opened)
return EIO;
#if (USBDI >= 1)
sc->sc_refcnt++;
reqh = usbd_alloc_xfer(sc->sc_udev);
#else
reqh = usbd_alloc_request();
#endif
if (reqh == 0)
return EIO;
while ((n = min(URIO_BBSIZE, uio->uio_resid)) != 0) {
error = uiomove(buf, n, uio);
if (error)
break;
DPRINTFN(1, ("uriowrite: transfer %d bytes\n", n));
#if (USBDI >= 1)
usbd_setup_xfer(reqh, sc->sc_pipeh_out, 0, buf, n,
0, RIO_RW_TIMEOUT, 0);
#else
r = usbd_setup_request(reqh, sc->sc_pipeh_out, 0, buf, n,
0, RIO_RW_TIMEOUT, 0);
if (r != USBD_NORMAL_COMPLETION) {
error = EIO;
break;
}
#endif
r = usbd_sync_transfer(reqh);
if (r != USBD_NORMAL_COMPLETION) {
DPRINTFN(1, ("uriowrite: error=%d\n", r));
usbd_clear_endpoint_stall(sc->sc_pipeh_out);
error = EIO;
break;
}
#if (USBDI >= 1)
usbd_get_xfer_status(reqh, 0, 0, 0, 0);
#endif
}
#if (USBDI >= 1)
usbd_free_xfer(reqh);
#else
usbd_free_request(reqh);
#endif
return error;
}
int
urioioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p)
{
#if (USBDI >= 1)
struct urio_softc * sc;
#endif
int unit = URIOUNIT(dev);
struct RioCommand *rio_cmd;
int requesttype, len;
struct iovec iov;
struct uio uio;
usb_device_request_t req;
int req_flags = 0, req_actlen = 0;
void *ptr = 0;
int error = 0;
usbd_status r;
USB_GET_SC(urio, unit, sc);
switch (cmd) {
case RIO_RECV_COMMAND:
if (!(flag & FWRITE))
return EPERM;
rio_cmd = (struct RioCommand *)addr;
if (rio_cmd == NULL)
return EINVAL;
len = rio_cmd->length;
requesttype = rio_cmd->requesttype | UT_READ_VENDOR_DEVICE;
DPRINTFN(1,("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n",
requesttype, rio_cmd->request, rio_cmd->value, rio_cmd->index, len));
break;
case RIO_SEND_COMMAND:
if (!(flag & FWRITE))
return EPERM;
rio_cmd = (struct RioCommand *)addr;
if (rio_cmd == NULL)
return EINVAL;
len = rio_cmd->length;
requesttype = rio_cmd->requesttype | UT_WRITE_VENDOR_DEVICE;
DPRINTFN(1,("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x\n",
requesttype, rio_cmd->request, rio_cmd->value, rio_cmd->index, len));
break;
default:
return EINVAL;
break;
}
/* Send rio control message */
req.bmRequestType = requesttype;
req.bRequest = rio_cmd->request;
USETW(req.wValue, rio_cmd->value);
USETW(req.wIndex, rio_cmd->index);
USETW(req.wLength, len);
if (len < 0 || len > 32767)
return EINVAL;
if (len != 0) {
iov.iov_base = (caddr_t)rio_cmd->buffer;
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 =
req.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, &req,
ptr, req_flags, &req_actlen,
USBD_DEFAULT_TIMEOUT);
if (r == USBD_NORMAL_COMPLETION) {
error = 0;
if (len != 0) {
if (uio.uio_rw == UIO_READ) {
error = uiomove(ptr, len, &uio);
}
}
} else {
error = EIO;
}
ret:
if (ptr)
free(ptr, M_TEMP);
return error;
}
#if defined(__NetBSD__) || defined(__OpenBSD__)
int
urio_activate(device_ptr_t self, enum devact act)
{
struct urio_softc *sc = (struct urio_softc *)self;
switch (act) {
case DVACT_ACTIVATE:
return (EOPNOTSUPP);
break;
case DVACT_DEACTIVATE:
sc->sc_dying = 1;
break;
}
return (0);
}
USB_DETACH(urio)
{
USB_DETACH_START(urio, sc);
struct urio_endpoint *sce;
int i, dir;
int s;
#if defined(__NetBSD__) || defined(__OpenBSD__)
int maj, mn;
DPRINTF(("urio_detach: sc=%p flags=%d\n", sc, flags));
#elif defined(__FreeBSD__)
DPRINTF(("urio_detach: sc=%p\n", sc));
#endif
sc->sc_dying = 1;
/* Abort all pipes. Causes processes waiting for transfer to wake. */
#if 0
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
for (dir = OUT; dir <= IN; dir++) {
sce = &sc->sc_endpoints[i][dir];
if (sce && sce->pipeh)
usbd_abort_pipe(sce->pipeh);
}
}
s = splusb();
if (--sc->sc_refcnt >= 0) {
/* Wake everyone */
for (i = 0; i < USB_MAX_ENDPOINTS; i++)
wakeup(&sc->sc_endpoints[i][IN]);
/* Wait for processes to go away. */
usb_detach_wait(USBDEV(sc->sc_dev));
}
splx(s);
#else
if (sc->sc_pipeh_in)
usbd_abort_pipe(sc->sc_pipeh_in);
if (sc->sc_pipeh_out)
usbd_abort_pipe(sc->sc_pipeh_out);
s = splusb();
if (--sc->sc_refcnt >= 0) {
/* Wait for processes to go away. */
usb_detach_wait(USBDEV(sc->sc_dev));
}
splx(s);
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__)
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == urioopen)
break;
/* Nuke the vnodes for any open instances (calls close). */
mn = self->dv_unit * USB_MAX_ENDPOINTS;
vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR);
#endif
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
USBDEV(sc->sc_dev));
return (0);
}
#endif /* defined(__NetBSD__) || defined(__OpenBSD__) */
#if defined(__FreeBSD__)
Static int
urio_detach(device_t self)
{
struct urio_softc *sc = device_get_softc(self);
DPRINTF(("%s: disconnected\n", USBDEVNAME(self)));
destroy_dev(sc->sc_dev_t);
/* XXX not implemented yet */
device_set_desc(self, NULL);
return 0;
}
#if (__FreeBSD__ >= 4)
DRIVER_MODULE(urio, uhub, urio_driver, urio_devclass, usbd_driver_load, 0);
#else
CDEV_DRIVER_MODULE(urio, uhub, urio_driver, urio_devclass,
URIO_CDEV_MAJOR, urio_cdevsw, usbd_driver_load, 0);
#endif
#endif