File:  [DragonFly] / src / sys / dev / usbmisc / ufm / ufm.c
Revision 1.7: download - view: text, annotated - select for diffs
Thu May 13 23:49:21 2004 UTC (10 years, 3 months ago) by dillon
Branches: MAIN
CVS tags: HEAD
device switch 1/many: Remove d_autoq, add d_clone (where d_autoq was).

d_autoq was used to allow the device port dispatch to mix old-style synchronous
calls with new style messaging calls within a particular device.  It was never
used for that purpose.

d_clone will be more fully implemented as work continues.  We are going to
install d_port in the dev_t (struct specinfo) structure itself and d_clone
will be needed to allow devices to 'revector' the port on a minor-number
by minor-number basis, in particular allowing minor numbers to be directly
dispatched to distinct threads.  This is something we will be needing later
on.

    1: /*
    2:  * Copyright (c) 2001 M. Warner Losh
    3:  * All rights reserved.
    4:  *
    5:  * Redistribution and use in source and binary forms, with or without
    6:  * modification, are permitted provided that the following conditions
    7:  * are met:
    8:  * 1. Redistributions of source code must retain the above copyright
    9:  *    notice, this list of conditions, and the following disclaimer.
   10:  * 2. Redistributions in binary form must reproduce the above copyright
   11:  *    notice, this list of conditions and the following disclaimer in the
   12:  *    documentation and/or other materials provided with the distribution.
   13:  *
   14:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17:  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
   18:  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24:  * SUCH DAMAGE.
   25:  *
   26:  * This code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
   27:  * This code includes software developed by the NetBSD Foundation, Inc. and
   28:  * its contributors.
   29:  */
   30: 
   31: /*
   32:  * $FreeBSD: src/sys/dev/usb/ufm.c,v 1.16 2003/10/04 21:41:01 joe Exp $
   33:  * $DragonFly: src/sys/dev/usbmisc/ufm/ufm.c,v 1.7 2004/05/13 23:49:21 dillon Exp $
   34:  */
   35: 
   36: #include <sys/param.h>
   37: #include <sys/systm.h>
   38: #include <sys/kernel.h>
   39: #include <sys/malloc.h>
   40: #if defined(__NetBSD__)
   41: #include <sys/device.h>
   42: #include <sys/ioctl.h>
   43: #elif defined(__FreeBSD__) || defined(__DragonFly__)
   44: #include <sys/module.h>
   45: #include <sys/bus.h>
   46: #include <sys/ioccom.h>
   47: #endif
   48: #include <sys/fcntl.h>
   49: #include <sys/filio.h>
   50: #include <sys/conf.h>
   51: #include <sys/uio.h>
   52: #include <sys/tty.h>
   53: #include <sys/file.h>
   54: #if defined(__FreeBSD__) && __FreeBSD_version >= 500014
   55: #include <sys/selinfo.h>
   56: #else
   57: #include <sys/select.h>
   58: #endif
   59: #include <sys/vnode.h>
   60: #include <sys/poll.h>
   61: #include <sys/sysctl.h>
   62: 
   63: #include <bus/usb/usb.h>
   64: #include <bus/usb/usbdi.h>
   65: #include <bus/usb/usbdi_util.h>
   66: 
   67: #include <bus/usb/usbdevs.h>
   68: #include <bus/usb/dsbr100io.h>
   69: 
   70: #ifdef USB_DEBUG
   71: #define DPRINTF(x)	if (ufmdebug) logprintf x
   72: #define DPRINTFN(n,x)	if (ufmdebug>(n)) logprintf x
   73: int	ufmdebug = 0;
   74: SYSCTL_NODE(_hw_usb, OID_AUTO, ufm, CTLFLAG_RW, 0, "USB ufm");
   75: SYSCTL_INT(_hw_usb_ufm, OID_AUTO, debug, CTLFLAG_RW,
   76: 	   &ufmdebug, 0, "ufm debug level");
   77: #else
   78: #define DPRINTF(x)
   79: #define DPRINTFN(n,x)
   80: #endif
   81: 
   82: #if defined(__NetBSD__) || defined(__OpenBSD__)
   83: int ufmopen(dev_t, int, int, usb_proc_ptr);
   84: int ufmclose(dev_t, int, int, usb_proc_ptr);
   85: int ufmioctl(dev_t, u_long, caddr_t, int, usb_proc_ptr);
   86: 
   87: cdev_decl(ufm);
   88: #elif defined(__FreeBSD__) || defined(__DragonFly__)
   89: d_open_t  ufmopen;
   90: d_close_t ufmclose;
   91: d_ioctl_t ufmioctl;
   92: 
   93: #define UFM_CDEV_MAJOR	200
   94: 
   95: Static struct cdevsw ufm_cdevsw = {
   96:  	/* name */	"ufm",	
   97: 	/* cmaj */	UFM_CDEV_MAJOR,	
   98: 	/* flags */	0,
   99: 	/* port */	NULL,
  100: 	/* clone */	NULL,
  101: 	ufmopen,	ufmclose,	noread,		nowrite,
  102:  	ufmioctl,	nopoll,		nommap,		nostrategy,
  103: 	nodump,		nopsize
  104: };
  105: #endif  /*defined(__FreeBSD__)*/
  106: 
  107: #define FM_CMD0		0x00
  108: #define FM_CMD_SET_FREQ	0x01
  109: #define FM_CMD2		0x02
  110: 
  111: struct ufm_softc {
  112:  	USBBASEDEVICE sc_dev;
  113: 	usbd_device_handle sc_udev;
  114: 	usbd_interface_handle sc_iface;
  115: 
  116: 	int sc_opened;
  117: 	int sc_epaddr;
  118: 	int sc_freq;
  119: 
  120: 	int sc_refcnt;
  121: #if defined(__NetBSD__) || defined(__OpenBSD__)
  122: 	u_char sc_dying;
  123: #endif
  124: };
  125: 
  126: #define UFMUNIT(n) (minor(n))
  127: 
  128: USB_DECLARE_DRIVER(ufm);
  129: 
  130: USB_MATCH(ufm)
  131: {
  132: 	USB_MATCH_START(ufm, uaa);
  133: 	usb_device_descriptor_t *dd;
  134: 
  135: 	DPRINTFN(10,("ufm_match\n"));
  136: 	if (!uaa->iface)
  137: 		return UMATCH_NONE;
  138: 
  139: 	dd = usbd_get_device_descriptor(uaa->device);
  140: 
  141: 	if (dd &&
  142: 	    ((UGETW(dd->idVendor) == USB_VENDOR_CYPRESS &&
  143: 	    UGETW(dd->idProduct) == USB_PRODUCT_CYPRESS_FMRADIO)))
  144: 		return UMATCH_VENDOR_PRODUCT;
  145: 	else
  146: 		return UMATCH_NONE;
  147: }
  148: 
  149: USB_ATTACH(ufm)
  150: {
  151: 	USB_ATTACH_START(ufm, sc, uaa);
  152: 	char devinfo[1024];
  153: 	usb_endpoint_descriptor_t *edesc;
  154: 	usbd_device_handle udev;
  155: 	usbd_interface_handle iface;
  156: 	u_int8_t epcount;
  157: #if defined(__NetBSD__) || defined(__OpenBSD__)
  158: 	u_int8_t niface;
  159: #endif
  160: 	usbd_status r;
  161: 	char * ermsg = "<none>";
  162: 
  163: 	DPRINTFN(10,("ufm_attach: sc=%p\n", sc));
  164: 	usbd_devinfo(uaa->device, 0, devinfo);
  165: 	USB_ATTACH_SETUP;
  166: 	printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
  167: 
  168: 	sc->sc_udev = udev = uaa->device;
  169: 
  170: #if defined(__FreeBSD__) || defined(__DragonFly__)
  171:  	if ((!uaa->device) || (!uaa->iface)) {
  172: 		ermsg = "device or iface";
  173:  		goto nobulk;
  174: 	}
  175: 	sc->sc_iface = iface = uaa->iface;
  176: #elif defined(__NetBSD__) || defined(__OpenBSD__)
  177:  	if (!udev) {
  178: 		ermsg = "device";
  179:  		goto nobulk;
  180: 	}
  181: 	r = usbd_interface_count(udev, &niface);
  182: 	if (r) {
  183: 		ermsg = "iface";
  184: 		goto nobulk;
  185: 	}
  186: 	r = usbd_device2interface_handle(udev, 0, &iface);
  187: 	if (r) {
  188: 		ermsg = "iface";
  189: 		goto nobulk;
  190: 	}
  191: 	sc->sc_iface = iface;
  192: #endif
  193: 	sc->sc_opened = 0;
  194: 	sc->sc_refcnt = 0;
  195: 
  196: 	r = usbd_endpoint_count(iface, &epcount);
  197: 	if (r != USBD_NORMAL_COMPLETION) {
  198: 		ermsg = "endpoints";
  199: 		goto nobulk;
  200: 	}
  201: 
  202: 	edesc = usbd_interface2endpoint_descriptor(iface, 0);
  203: 	if (!edesc) {
  204: 		ermsg = "interface endpoint";
  205: 		goto nobulk;
  206: 	}
  207: 	sc->sc_epaddr = edesc->bEndpointAddress;
  208: 
  209: #if defined(__FreeBSD__) || defined(__DragonFly__)
  210: 	/* XXX no error trapping, no storing of dev_t */
  211: 	(void) make_dev(&ufm_cdevsw, device_get_unit(self),
  212: 			UID_ROOT, GID_OPERATOR,
  213: 			0644, "ufm%d", device_get_unit(self));
  214: #elif defined(__NetBSD__) || defined(__OpenBSD__)
  215: 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
  216: 			   USBDEV(sc->sc_dev));
  217: #endif
  218: 
  219: 	DPRINTFN(10, ("ufm_attach: %p\n", sc->sc_udev));
  220: 
  221: 	USB_ATTACH_SUCCESS_RETURN;
  222: 
  223:  nobulk:
  224: 	printf("%s: could not find %s\n", USBDEVNAME(sc->sc_dev),ermsg);
  225: 	USB_ATTACH_ERROR_RETURN;
  226: }
  227: 
  228: 
  229: int
  230: ufmopen(dev_t dev, int flag, int mode, usb_proc_ptr td)
  231: {
  232: 	struct ufm_softc *sc;
  233: 
  234: 	int unit = UFMUNIT(dev);
  235: 	USB_GET_SC_OPEN(ufm, unit, sc);
  236: 
  237: 	DPRINTFN(5, ("ufmopen: flag=%d, mode=%d, unit=%d\n",
  238: 		     flag, mode, unit));
  239: 
  240: 	if (sc->sc_opened)
  241: 		return (EBUSY);
  242: 
  243: 	if ((flag & (FWRITE|FREAD)) != (FWRITE|FREAD))
  244: 		return (EACCES);
  245: 
  246: 	sc->sc_opened = 1;
  247: 	return (0);
  248: }
  249: 
  250: int
  251: ufmclose(dev_t dev, int flag, int mode, usb_proc_ptr td)
  252: {
  253: 	struct ufm_softc *sc;
  254: 
  255: 	int unit = UFMUNIT(dev);
  256: 	USB_GET_SC(ufm, unit, sc);
  257: 
  258: 	DPRINTFN(5, ("ufmclose: flag=%d, mode=%d, unit=%d\n", flag, mode, unit));
  259: 	sc->sc_opened = 0;
  260: 	sc->sc_refcnt = 0;
  261: 	return 0;
  262: }
  263: 
  264: static int
  265: ufm_do_req(struct ufm_softc *sc, u_int8_t reqtype, u_int8_t request,
  266:     u_int16_t value, u_int16_t index, u_int8_t len, void *retbuf)
  267: {
  268: 	int s;
  269: 	usb_device_request_t req;
  270: 	usbd_status err;
  271: 
  272: 	s = splusb();
  273: 	req.bmRequestType = reqtype;
  274: 	req.bRequest = request;
  275: 	USETW(req.wValue, value);
  276: 	USETW(req.wIndex, index);
  277: 	USETW(req.wLength, len);
  278: 	err = usbd_do_request_flags(sc->sc_udev, &req, retbuf, 0, NULL,
  279: 	    USBD_DEFAULT_TIMEOUT);
  280: 	splx(s);
  281: 	if (err) {
  282: 		printf("usbd_do_request_flags returned %#x\n", err);
  283: 		return (EIO);
  284: 	}
  285: 	return (0);
  286: }
  287: 
  288: static int
  289: ufm_set_freq(struct ufm_softc *sc, caddr_t addr)
  290: {
  291: 	int freq = *(int *)addr;
  292: 	u_int8_t ret;
  293: 
  294: 	/*
  295: 	 * Freq now is in Hz.  We need to convert it to the frequency
  296: 	 * that the radio wants.  This frequency is 10.7MHz above
  297: 	 * the actual frequency.  We then need to convert to
  298: 	 * units of 12.5kHz.  We add one to the IFM to make rounding
  299: 	 * easier.
  300: 	 */
  301: 	sc->sc_freq = freq;
  302: 	freq = (freq + 10700001) / 12500;
  303: 	/* This appears to set the frequency */
  304: 	if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD_SET_FREQ, freq >> 8,
  305: 	    freq, 1, &ret) != 0)
  306: 		return (EIO);
  307: 	/* Not sure what this does */
  308: 	if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x96, 0xb7, 1,
  309: 	    &ret) != 0)
  310: 		return (EIO);
  311: 	return (0);
  312: }
  313: 
  314: static int
  315: ufm_get_freq(struct ufm_softc *sc, caddr_t addr)
  316: {
  317: 	int *valp = (int *)addr;
  318: 	*valp = sc->sc_freq;
  319: 	return (0);
  320: }
  321: 
  322: static int
  323: ufm_start(struct ufm_softc *sc, caddr_t addr)
  324: {
  325: 	u_int8_t ret;
  326: 
  327: 	if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x00, 0xc7,
  328: 	    1, &ret))
  329: 		return (EIO);
  330: 	if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD2, 0x01, 0x00,
  331: 	    1, &ret))
  332: 		return (EIO);
  333: 	if (ret & 0x1)
  334: 		return (EIO);
  335: 	return (0);
  336: }
  337: 
  338: static int
  339: ufm_stop(struct ufm_softc *sc, caddr_t addr)
  340: {
  341: 	u_int8_t ret;
  342: 
  343: 	if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x16, 0x1C,
  344: 	    1, &ret))
  345: 		return (EIO);
  346: 	if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD2, 0x00, 0x00,
  347: 	    1, &ret))
  348: 		return (EIO);
  349: 	return (0);
  350: }
  351: 
  352: static int
  353: ufm_get_stat(struct ufm_softc *sc, caddr_t addr)
  354: {
  355: 	u_int8_t ret;
  356: 
  357: 	/*
  358: 	 * Note, there's a 240ms settle time before the status
  359: 	 * will be valid, so tsleep that amount.  hz/4 is a good
  360: 	 * approximation of that.  Since this is a short sleep
  361: 	 * we don't try to catch any signals to keep things
  362: 	 * simple.
  363: 	 */
  364: 	tsleep(sc, 0, "ufmwait", hz/4);
  365: 	if (ufm_do_req(sc, UT_READ_VENDOR_DEVICE, FM_CMD0, 0x00, 0x24,
  366: 	    1, &ret))
  367: 		return (EIO);
  368: 	*(int *)addr = ret;
  369: 
  370: 	return (0);
  371: }
  372: 
  373: int
  374: ufmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr td)
  375: {
  376: 	struct ufm_softc *sc;
  377: 
  378: 	int unit = UFMUNIT(dev);
  379: 	int error = 0;
  380: 
  381: 	USB_GET_SC(ufm, unit, sc);
  382: 
  383: 	switch (cmd) {
  384: 	case FM_SET_FREQ:
  385: 		error = ufm_set_freq(sc, addr);
  386: 		break;
  387: 	case FM_GET_FREQ:
  388: 		error = ufm_get_freq(sc, addr);
  389: 		break;
  390: 	case FM_START:
  391: 		error = ufm_start(sc, addr);
  392: 		break;
  393: 	case FM_STOP:
  394: 		error = ufm_stop(sc, addr);
  395: 		break;
  396: 	case FM_GET_STAT:
  397: 		error = ufm_get_stat(sc, addr);
  398: 		break;
  399: 	default:
  400: 		return ENOTTY;
  401: 		break;
  402: 	}
  403: 	return error;
  404: }
  405: 
  406: 
  407: #if defined(__NetBSD__) || defined(__OpenBSD__)
  408: int
  409: ufm_activate(device_ptr_t self, enum devact act)
  410: {
  411: 	struct ufm_softc *sc = (struct ufm_softc *)self;
  412: 
  413: 	switch (act) {
  414: 	case DVACT_ACTIVATE:
  415: 		return (EOPNOTSUPP);
  416: 		break;
  417: 
  418: 	case DVACT_DEACTIVATE:
  419: 		sc->sc_dying = 1;
  420: 		break;
  421: 	}
  422: 	return (0);
  423: }
  424: 
  425: USB_DETACH(ufm)
  426: {
  427: 	USB_DETACH_START(ufm, sc);
  428: 	struct ufm_endpoint *sce;
  429: 	int i, dir;
  430: 	int s;
  431: #if defined(__NetBSD__) || defined(__OpenBSD__)
  432: 	int maj, mn;
  433: 
  434: 	DPRINTF(("ufm_detach: sc=%p flags=%d\n", sc, flags));
  435: #elif defined(__FreeBSD__) || defined(__DragonFly__)
  436: 	DPRINTF(("ufm_detach: sc=%p\n", sc));
  437: #endif
  438: 
  439: 	sc->sc_dying = 1;
  440: 
  441: 	s = splusb();
  442: 	if (--sc->sc_refcnt >= 0) {
  443: 		/* Wait for processes to go away. */
  444: 		usb_detach_wait(USBDEV(sc->sc_dev));
  445: 	}
  446: 	splx(s);
  447: 
  448: #if defined(__NetBSD__) || defined(__OpenBSD__)
  449: 	/* locate the major number */
  450: 	for (maj = 0; maj < nchrdev; maj++)
  451: 		if (cdevsw[maj].d_open == ufmopen)
  452: 			break;
  453: 
  454: 	/* Nuke the vnodes for any open instances (calls close). */
  455: 	mn = self->dv_unit * USB_MAX_ENDPOINTS;
  456: 	vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR);
  457: #elif defined(__FreeBSD__) || defined(__DragonFly__)
  458: 	/* XXX not implemented yet */
  459: #endif
  460: 
  461: 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
  462: 			   USBDEV(sc->sc_dev));
  463: 
  464: 	return (0);
  465: }
  466: #endif /* defined(__NetBSD__) || defined(__OpenBSD__) */
  467: 
  468: #if defined(__FreeBSD__) || defined(__DragonFly__)
  469: Static int
  470: ufm_detach(device_t self)
  471: {
  472: 	DPRINTF(("%s: disconnected\n", USBDEVNAME(self)));
  473: 	return 0;
  474: }
  475: 
  476: DRIVER_MODULE(ufm, uhub, ufm_driver, ufm_devclass, usbd_driver_load, 0);
  477: #endif