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