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