Home | History | Annotate | Line # | Download | only in usb
usb.c revision 1.2
      1 /*	$NetBSD: usb.c,v 1.2 1998/07/25 15:22:11 augustss Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Author: Lennart Augustsson <augustss (at) carlstedt.se>
      8  *         Carlstedt Research & Technology
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 /*
     40  * USB spec: http://www.teleport.com/cgi-bin/mailmerge.cgi/~usb/cgiform.tpl
     41  * More USB specs at http://www.usb.org/developers/index.shtml
     42  */
     43 
     44 #include "opt_usbverbose.h"
     45 
     46 #include <sys/param.h>
     47 #include <sys/systm.h>
     48 #include <sys/kernel.h>
     49 #include <sys/malloc.h>
     50 #include <sys/device.h>
     51 #include <sys/poll.h>
     52 #include <sys/proc.h>
     53 #include <sys/select.h>
     54 
     55 #include <dev/usb/usb.h>
     56 
     57 #include <dev/usb/usbdi.h>
     58 #include <dev/usb/usbdivar.h>
     59 #include <dev/usb/usb_quirks.h>
     60 
     61 #ifdef USB_DEBUG
     62 #define DPRINTF(x)	if (usbdebug) printf x
     63 #define DPRINTFN(n,x)	if (usbdebug>(n)) printf x
     64 int	usbdebug = 0;
     65 int	uhcidebug;
     66 int	ohcidebug;
     67 #else
     68 #define DPRINTF(x)
     69 #define DPRINTFN(n,x)
     70 #endif
     71 
     72 #define USBUNIT(dev) (minor(dev))
     73 
     74 struct usb_softc {
     75 	struct device sc_dev;		/* base device */
     76 	usbd_bus_handle sc_bus;		/* USB controller */
     77 	struct usbd_port sc_port;	/* dummy port for root hub */
     78 	char sc_running;
     79 	char sc_exploring;
     80 	struct selinfo sc_consel;	/* waiting for connect change */
     81 };
     82 
     83 int usb_match __P((struct device *, struct cfdata *, void *));
     84 void usb_attach __P((struct device *, struct device *, void *));
     85 int usbopen __P((dev_t, int, int, struct proc *));
     86 int usbclose __P((dev_t, int, int, struct proc *));
     87 int usbioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
     88 int usbpoll __P((dev_t, int, struct proc *));
     89 
     90 usbd_status usb_discover __P((struct usb_softc *));
     91 
     92 int usbd_use_polling;		/* use polling instead of interrupts */
     93 
     94 extern struct cfdriver usb_cd;
     95 
     96 struct cfattach usb_ca = {
     97 	sizeof(struct usb_softc), usb_match, usb_attach
     98 };
     99 
    100 int
    101 usb_match(parent, match, aux)
    102 	struct device *parent;
    103 	struct cfdata *match;
    104 	void *aux;
    105 {
    106 	DPRINTF(("usbd_match\n"));
    107 	return (1);
    108 }
    109 
    110 void
    111 usb_attach(parent, self, aux)
    112 	struct device *parent;
    113 	struct device *self;
    114 	void *aux;
    115 {
    116 	struct usb_softc *sc = (struct usb_softc *)self;
    117 	usbd_device_handle dev;
    118 	usbd_status r;
    119 
    120 	DPRINTF(("usbd_attach\n"));
    121 	sc->sc_bus = aux;
    122 	sc->sc_bus->usbctl = sc;
    123 
    124 	printf("\n");
    125 	sc->sc_running = 1;
    126 	usbd_use_polling = 1;
    127 	sc->sc_port.power = USB_MAX_POWER;
    128 	r = usbd_new_device(&sc->sc_dev, sc->sc_bus, 0, 0, 0, &sc->sc_port);
    129 	if (r == USBD_NORMAL_COMPLETION) {
    130 		dev = sc->sc_port.device;
    131 		if (!dev->hub) {
    132 			sc->sc_running = 0;
    133 			printf("%s: root device is not a hub\n",
    134 			       sc->sc_dev.dv_xname);
    135 			return;
    136 		}
    137 		sc->sc_bus->root_hub = dev;
    138 		dev->hub->explore(&sc->sc_dev, sc->sc_bus->root_hub);
    139 	} else {
    140 		printf("%s: root hub problem, error=%d\n",
    141 		       sc->sc_dev.dv_xname, r);
    142 		sc->sc_running = 0;
    143 	}
    144 	usbd_use_polling = 0;
    145 }
    146 
    147 int
    148 usbctlprint(aux, pnp)
    149 	void *aux;
    150 	const char *pnp;
    151 {
    152 	/* only "usb"es can attach to host controllers */
    153 	if (pnp)
    154 		printf("usb at %s", pnp);
    155 
    156 	return (UNCONF);
    157 }
    158 
    159 int
    160 usbopen(dev, flag, mode, p)
    161 	dev_t dev;
    162 	int flag, mode;
    163 	struct proc *p;
    164 {
    165 	int unit = USBUNIT(dev);
    166 	struct usb_softc *sc;
    167 
    168 	if (unit >= usb_cd.cd_ndevs)
    169 		return (ENXIO);
    170 	sc = usb_cd.cd_devs[unit];
    171 	if (sc == 0 || !sc->sc_running)
    172 		return (ENXIO);
    173 
    174 	return (0);
    175 }
    176 
    177 int
    178 usbclose(dev, flag, mode, p)
    179 	dev_t dev;
    180 	int flag, mode;
    181 	struct proc *p;
    182 {
    183 	return (0);
    184 }
    185 
    186 int
    187 usbioctl(dev, cmd, data, flag, p)
    188 	dev_t dev;
    189 	u_long cmd;
    190 	caddr_t data;
    191 	int flag;
    192 	struct proc *p;
    193 {
    194 	int unit = USBUNIT(dev);
    195 	struct usb_softc *sc = usb_cd.cd_devs[unit];
    196 
    197 	if (sc == 0 || !sc->sc_running)
    198 		return (ENXIO);
    199 	switch (cmd) {
    200 #ifdef USB_DEBUG
    201 	case USB_SETDEBUG:
    202 		usbdebug = uhcidebug = ohcidebug = *(int *)data;
    203 		break;
    204 #endif
    205 	case USB_DISCOVER:
    206 		usb_discover(sc);
    207 		break;
    208 	case USB_REQUEST:
    209 	{
    210 		struct usb_ctl_request *ur = (void *)data;
    211 		int len = UGETW(ur->request.wLength);
    212 		struct iovec iov;
    213 		struct uio uio;
    214 		void *ptr = 0;
    215 		int addr = ur->addr;
    216 		usbd_status r;
    217 		int error = 0;
    218 
    219 		if (len < 0 || len > 32768)
    220 			return EINVAL;
    221 		if (addr < 0 || addr >= USB_MAX_DEVICES ||
    222 		    sc->sc_bus->devices[addr] == 0)
    223 			return EINVAL;
    224 		if (len != 0) {
    225 			iov.iov_base = (caddr_t)ur->data;
    226 			iov.iov_len = len;
    227 			uio.uio_iov = &iov;
    228 			uio.uio_iovcnt = 1;
    229 			uio.uio_resid = len;
    230 			uio.uio_offset = 0;
    231 			uio.uio_segflg = UIO_USERSPACE;
    232 			uio.uio_rw =
    233 				ur->request.bmRequestType & UT_READ ?
    234 				UIO_READ : UIO_WRITE;
    235 			uio.uio_procp = p;
    236 			ptr = malloc(len, M_TEMP, M_WAITOK);
    237 			if (uio.uio_rw == UIO_WRITE) {
    238 				error = uiomove(ptr, len, &uio);
    239 				if (error)
    240 					goto ret;
    241 			}
    242 		}
    243 		r = usbd_do_request(sc->sc_bus->devices[addr],
    244 				    &ur->request, ptr);
    245 		if (r) {
    246 			error = EIO;
    247 			goto ret;
    248 		}
    249 		if (len != 0) {
    250 			if (uio.uio_rw == UIO_READ) {
    251 				error = uiomove(ptr, len, &uio);
    252 				if (error)
    253 					goto ret;
    254 			}
    255 		}
    256 	ret:
    257 		if (ptr)
    258 			free(ptr, M_TEMP);
    259 		return (error);
    260 		break;
    261 	}
    262 
    263 	case USB_DEVICEINFO:
    264 	{
    265 		struct usb_device_info *di = (void *)data;
    266 		int addr = di->addr;
    267 		usbd_device_handle dev;
    268 		struct usbd_port *p;
    269 		int i, r, s;
    270 
    271 		if (addr < 1 || addr >= USB_MAX_DEVICES)
    272 			return (EINVAL);
    273 		dev = sc->sc_bus->devices[addr];
    274 		if (dev == 0)
    275 			return (ENXIO);
    276 		di->config = dev->config;
    277 		usbd_devinfo_vp(dev, di->product, di->vendor);
    278 		usbd_printBCD(di->revision, UGETW(dev->ddesc.bcdDevice));
    279 		di->class = dev->ddesc.bDeviceClass;
    280 		di->power = dev->self_powered ? 0 : dev->power;
    281 		di->lowspeed = dev->lowspeed;
    282 		if (dev->hub) {
    283 			for (i = 0;
    284 			     i < sizeof(di->ports) / sizeof(di->ports[0]) &&
    285 				     i < dev->hub->hubdesc.bNbrPorts;
    286 			     i++) {
    287 				p = &dev->hub->ports[i];
    288 				if (p->device)
    289 					r = p->device->address;
    290 				else {
    291 					s = UGETW(p->status.wPortStatus);
    292 					if (s & UPS_PORT_ENABLED)
    293 						r = USB_PORT_ENABLED;
    294 					else if (s & UPS_SUSPEND)
    295 						r = USB_PORT_SUSPENDED;
    296 					else if (s & UPS_PORT_POWER)
    297 						r = USB_PORT_POWERED;
    298 					else
    299 						r = USB_PORT_DISABLED;
    300 				}
    301 				di->ports[i] = r;
    302 			}
    303 			di->nports = dev->hub->hubdesc.bNbrPorts;
    304 		} else
    305 			di->nports = 0;
    306 		break;
    307 	}
    308 
    309 	case USB_DEVICESTATS:
    310 		*(struct usb_device_stats *)data = sc->sc_bus->stats;
    311 		break;
    312 
    313 	default:
    314 		return (ENXIO);
    315 	}
    316 	return (0);
    317 }
    318 
    319 int
    320 usbpoll(dev, events, p)
    321 	dev_t dev;
    322 	int events;
    323 	struct proc *p;
    324 {
    325 	int unit = USBUNIT(dev);
    326 	struct usb_softc *sc = usb_cd.cd_devs[unit];
    327 	int revents, s;
    328 
    329 	DPRINTFN(2, ("usbpoll: sc=%p events=0x%x\n", sc, events));
    330 	s = splusb();
    331 	revents = 0;
    332 	if (events & (POLLOUT | POLLWRNORM))
    333 		if (sc->sc_bus->needs_explore)
    334 			revents |= events & (POLLOUT | POLLWRNORM);
    335 	DPRINTFN(2, ("usbpoll: revents=0x%x\n", revents));
    336 	if (revents == 0) {
    337 		if (events & (POLLOUT | POLLWRNORM)) {
    338 			DPRINTFN(2, ("usbpoll: selrecord\n"));
    339 			selrecord(p, &sc->sc_consel);
    340 		}
    341 	}
    342 	splx(s);
    343 	return (revents);
    344 }
    345 
    346 int
    347 usb_bus_count()
    348 {
    349 	int i, n;
    350 
    351 	for (i = n = 0; i < usb_cd.cd_ndevs; i++)
    352 		if (usb_cd.cd_devs[i])
    353 			n++;
    354 	return (n);
    355 }
    356 
    357 usbd_status
    358 usb_get_bus_handle(n, h)
    359 	int n;
    360 	usbd_bus_handle *h;
    361 {
    362 	int i;
    363 
    364 	for (i = 0; i < usb_cd.cd_ndevs; i++)
    365 		if (usb_cd.cd_devs[i] && n-- == 0) {
    366 			*h = usb_cd.cd_devs[i];
    367 			return (USBD_NORMAL_COMPLETION);
    368 		}
    369 	return (USBD_INVAL);
    370 }
    371 
    372 usbd_status
    373 usb_discover(sc)
    374 	struct usb_softc *sc;
    375 {
    376 	int s;
    377 
    378 	/* Explore device tree from the root */
    379 	/* We need mutual exclusion while traversing the device tree. */
    380 	s = splusb();
    381 	while (sc->sc_exploring)
    382 		tsleep(&sc->sc_exploring, PRIBIO, "usbdis", 0);
    383 	sc->sc_exploring = 1;
    384 	sc->sc_bus->needs_explore = 0;
    385 	splx(s);
    386 
    387 	sc->sc_bus->root_hub->hub->explore(&sc->sc_dev, sc->sc_bus->root_hub);
    388 
    389 	s = splusb();
    390 	sc->sc_exploring = 0;
    391 	wakeup(&sc->sc_exploring);
    392 	splx(s);
    393 	/* XXX should we start over if sc_needsexplore is set again? */
    394 	return (0);
    395 }
    396 
    397 void
    398 usb_needs_explore(bus)
    399 	usbd_bus_handle bus;
    400 {
    401 	bus->needs_explore = 1;
    402 	selwakeup(&bus->usbctl->sc_consel);
    403 }
    404