Home | History | Annotate | Line # | Download | only in usb
usb.c revision 1.3
      1 /*	$NetBSD: usb.c,v 1.3 1998/08/01 18:16:20 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 extern struct cfdriver usb_cd;
     93 
     94 struct cfattach usb_ca = {
     95 	sizeof(struct usb_softc), usb_match, usb_attach
     96 };
     97 
     98 int
     99 usb_match(parent, match, aux)
    100 	struct device *parent;
    101 	struct cfdata *match;
    102 	void *aux;
    103 {
    104 	DPRINTF(("usbd_match\n"));
    105 	return (1);
    106 }
    107 
    108 void
    109 usb_attach(parent, self, aux)
    110 	struct device *parent;
    111 	struct device *self;
    112 	void *aux;
    113 {
    114 	struct usb_softc *sc = (struct usb_softc *)self;
    115 	usbd_device_handle dev;
    116 	usbd_status r;
    117 
    118 	DPRINTF(("usbd_attach\n"));
    119 	sc->sc_bus = aux;
    120 	sc->sc_bus->usbctl = sc;
    121 
    122 	printf("\n");
    123 	sc->sc_running = 1;
    124 	sc->sc_bus->use_polling = 1;
    125 	sc->sc_port.power = USB_MAX_POWER;
    126 	r = usbd_new_device(&sc->sc_dev, sc->sc_bus, 0, 0, 0, &sc->sc_port);
    127 	if (r == USBD_NORMAL_COMPLETION) {
    128 		dev = sc->sc_port.device;
    129 		if (!dev->hub) {
    130 			sc->sc_running = 0;
    131 			printf("%s: root device is not a hub\n",
    132 			       sc->sc_dev.dv_xname);
    133 			return;
    134 		}
    135 		sc->sc_bus->root_hub = dev;
    136 		dev->hub->explore(&sc->sc_dev, sc->sc_bus->root_hub);
    137 	} else {
    138 		printf("%s: root hub problem, error=%d\n",
    139 		       sc->sc_dev.dv_xname, r);
    140 		sc->sc_running = 0;
    141 	}
    142 	sc->sc_bus->use_polling = 0;
    143 }
    144 
    145 int
    146 usbctlprint(aux, pnp)
    147 	void *aux;
    148 	const char *pnp;
    149 {
    150 	/* only "usb"es can attach to host controllers */
    151 	if (pnp)
    152 		printf("usb at %s", pnp);
    153 
    154 	return (UNCONF);
    155 }
    156 
    157 int
    158 usbopen(dev, flag, mode, p)
    159 	dev_t dev;
    160 	int flag, mode;
    161 	struct proc *p;
    162 {
    163 	int unit = USBUNIT(dev);
    164 	struct usb_softc *sc;
    165 
    166 	if (unit >= usb_cd.cd_ndevs)
    167 		return (ENXIO);
    168 	sc = usb_cd.cd_devs[unit];
    169 	if (sc == 0 || !sc->sc_running)
    170 		return (ENXIO);
    171 
    172 	return (0);
    173 }
    174 
    175 int
    176 usbclose(dev, flag, mode, p)
    177 	dev_t dev;
    178 	int flag, mode;
    179 	struct proc *p;
    180 {
    181 	return (0);
    182 }
    183 
    184 int
    185 usbioctl(dev, cmd, data, flag, p)
    186 	dev_t dev;
    187 	u_long cmd;
    188 	caddr_t data;
    189 	int flag;
    190 	struct proc *p;
    191 {
    192 	int unit = USBUNIT(dev);
    193 	struct usb_softc *sc = usb_cd.cd_devs[unit];
    194 
    195 	if (sc == 0 || !sc->sc_running)
    196 		return (ENXIO);
    197 	switch (cmd) {
    198 #ifdef USB_DEBUG
    199 	case USB_SETDEBUG:
    200 		usbdebug = uhcidebug = ohcidebug = *(int *)data;
    201 		break;
    202 #endif
    203 	case USB_DISCOVER:
    204 		usb_discover(sc);
    205 		break;
    206 	case USB_REQUEST:
    207 	{
    208 		struct usb_ctl_request *ur = (void *)data;
    209 		int len = UGETW(ur->request.wLength);
    210 		struct iovec iov;
    211 		struct uio uio;
    212 		void *ptr = 0;
    213 		int addr = ur->addr;
    214 		usbd_status r;
    215 		int error = 0;
    216 
    217 		if (len < 0 || len > 32768)
    218 			return EINVAL;
    219 		if (addr < 0 || addr >= USB_MAX_DEVICES ||
    220 		    sc->sc_bus->devices[addr] == 0)
    221 			return EINVAL;
    222 		if (len != 0) {
    223 			iov.iov_base = (caddr_t)ur->data;
    224 			iov.iov_len = len;
    225 			uio.uio_iov = &iov;
    226 			uio.uio_iovcnt = 1;
    227 			uio.uio_resid = len;
    228 			uio.uio_offset = 0;
    229 			uio.uio_segflg = UIO_USERSPACE;
    230 			uio.uio_rw =
    231 				ur->request.bmRequestType & UT_READ ?
    232 				UIO_READ : UIO_WRITE;
    233 			uio.uio_procp = p;
    234 			ptr = malloc(len, M_TEMP, M_WAITOK);
    235 			if (uio.uio_rw == UIO_WRITE) {
    236 				error = uiomove(ptr, len, &uio);
    237 				if (error)
    238 					goto ret;
    239 			}
    240 		}
    241 		r = usbd_do_request(sc->sc_bus->devices[addr],
    242 				    &ur->request, ptr);
    243 		if (r) {
    244 			error = EIO;
    245 			goto ret;
    246 		}
    247 		if (len != 0) {
    248 			if (uio.uio_rw == UIO_READ) {
    249 				error = uiomove(ptr, len, &uio);
    250 				if (error)
    251 					goto ret;
    252 			}
    253 		}
    254 	ret:
    255 		if (ptr)
    256 			free(ptr, M_TEMP);
    257 		return (error);
    258 		break;
    259 	}
    260 
    261 	case USB_DEVICEINFO:
    262 	{
    263 		struct usb_device_info *di = (void *)data;
    264 		int addr = di->addr;
    265 		usbd_device_handle dev;
    266 		struct usbd_port *p;
    267 		int i, r, s;
    268 
    269 		if (addr < 1 || addr >= USB_MAX_DEVICES)
    270 			return (EINVAL);
    271 		dev = sc->sc_bus->devices[addr];
    272 		if (dev == 0)
    273 			return (ENXIO);
    274 		di->config = dev->config;
    275 		usbd_devinfo_vp(dev, di->product, di->vendor);
    276 		usbd_printBCD(di->revision, UGETW(dev->ddesc.bcdDevice));
    277 		di->class = dev->ddesc.bDeviceClass;
    278 		di->power = dev->self_powered ? 0 : dev->power;
    279 		di->lowspeed = dev->lowspeed;
    280 		if (dev->hub) {
    281 			for (i = 0;
    282 			     i < sizeof(di->ports) / sizeof(di->ports[0]) &&
    283 				     i < dev->hub->hubdesc.bNbrPorts;
    284 			     i++) {
    285 				p = &dev->hub->ports[i];
    286 				if (p->device)
    287 					r = p->device->address;
    288 				else {
    289 					s = UGETW(p->status.wPortStatus);
    290 					if (s & UPS_PORT_ENABLED)
    291 						r = USB_PORT_ENABLED;
    292 					else if (s & UPS_SUSPEND)
    293 						r = USB_PORT_SUSPENDED;
    294 					else if (s & UPS_PORT_POWER)
    295 						r = USB_PORT_POWERED;
    296 					else
    297 						r = USB_PORT_DISABLED;
    298 				}
    299 				di->ports[i] = r;
    300 			}
    301 			di->nports = dev->hub->hubdesc.bNbrPorts;
    302 		} else
    303 			di->nports = 0;
    304 		break;
    305 	}
    306 
    307 	case USB_DEVICESTATS:
    308 		*(struct usb_device_stats *)data = sc->sc_bus->stats;
    309 		break;
    310 
    311 	default:
    312 		return (ENXIO);
    313 	}
    314 	return (0);
    315 }
    316 
    317 int
    318 usbpoll(dev, events, p)
    319 	dev_t dev;
    320 	int events;
    321 	struct proc *p;
    322 {
    323 	int unit = USBUNIT(dev);
    324 	struct usb_softc *sc = usb_cd.cd_devs[unit];
    325 	int revents, s;
    326 
    327 	DPRINTFN(2, ("usbpoll: sc=%p events=0x%x\n", sc, events));
    328 	s = splusb();
    329 	revents = 0;
    330 	if (events & (POLLOUT | POLLWRNORM))
    331 		if (sc->sc_bus->needs_explore)
    332 			revents |= events & (POLLOUT | POLLWRNORM);
    333 	DPRINTFN(2, ("usbpoll: revents=0x%x\n", revents));
    334 	if (revents == 0) {
    335 		if (events & (POLLOUT | POLLWRNORM)) {
    336 			DPRINTFN(2, ("usbpoll: selrecord\n"));
    337 			selrecord(p, &sc->sc_consel);
    338 		}
    339 	}
    340 	splx(s);
    341 	return (revents);
    342 }
    343 
    344 int
    345 usb_bus_count()
    346 {
    347 	int i, n;
    348 
    349 	for (i = n = 0; i < usb_cd.cd_ndevs; i++)
    350 		if (usb_cd.cd_devs[i])
    351 			n++;
    352 	return (n);
    353 }
    354 
    355 usbd_status
    356 usb_get_bus_handle(n, h)
    357 	int n;
    358 	usbd_bus_handle *h;
    359 {
    360 	int i;
    361 
    362 	for (i = 0; i < usb_cd.cd_ndevs; i++)
    363 		if (usb_cd.cd_devs[i] && n-- == 0) {
    364 			*h = usb_cd.cd_devs[i];
    365 			return (USBD_NORMAL_COMPLETION);
    366 		}
    367 	return (USBD_INVAL);
    368 }
    369 
    370 usbd_status
    371 usb_discover(sc)
    372 	struct usb_softc *sc;
    373 {
    374 	int s;
    375 
    376 	/* Explore device tree from the root */
    377 	/* We need mutual exclusion while traversing the device tree. */
    378 	s = splusb();
    379 	while (sc->sc_exploring)
    380 		tsleep(&sc->sc_exploring, PRIBIO, "usbdis", 0);
    381 	sc->sc_exploring = 1;
    382 	sc->sc_bus->needs_explore = 0;
    383 	splx(s);
    384 
    385 	sc->sc_bus->root_hub->hub->explore(&sc->sc_dev, sc->sc_bus->root_hub);
    386 
    387 	s = splusb();
    388 	sc->sc_exploring = 0;
    389 	wakeup(&sc->sc_exploring);
    390 	splx(s);
    391 	/* XXX should we start over if sc_needsexplore is set again? */
    392 	return (0);
    393 }
    394 
    395 void
    396 usb_needs_explore(bus)
    397 	usbd_bus_handle bus;
    398 {
    399 	bus->needs_explore = 1;
    400 	selwakeup(&bus->usbctl->sc_consel);
    401 }
    402