xref: /netbsd-src/sys/dev/usb/usb.c (revision 93f9db1b75d415b78f73ed629beeb86235153473)
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@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