1184610Salfred/* $FreeBSD$ */
2184610Salfred/*-
3184610Salfred * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4184610Salfred *
5184610Salfred * Redistribution and use in source and binary forms, with or without
6184610Salfred * modification, are permitted provided that the following conditions
7184610Salfred * are met:
8184610Salfred * 1. Redistributions of source code must retain the above copyright
9184610Salfred *    notice, this list of conditions and the following disclaimer.
10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer in the
12184610Salfred *    documentation and/or other materials provided with the distribution.
13184610Salfred *
14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24184610Salfred * SUCH DAMAGE.
25184610Salfred */
26184610Salfred
27246122Shselasky#ifdef USB_GLOBAL_INCLUDE_FILE
28246122Shselasky#include USB_GLOBAL_INCLUDE_FILE
29246122Shselasky#else
30194677Sthompsa#include <sys/stdint.h>
31194677Sthompsa#include <sys/stddef.h>
32194677Sthompsa#include <sys/param.h>
33194677Sthompsa#include <sys/queue.h>
34194677Sthompsa#include <sys/types.h>
35194677Sthompsa#include <sys/systm.h>
36194677Sthompsa#include <sys/kernel.h>
37194677Sthompsa#include <sys/bus.h>
38194677Sthompsa#include <sys/module.h>
39194677Sthompsa#include <sys/lock.h>
40194677Sthompsa#include <sys/mutex.h>
41194677Sthompsa#include <sys/condvar.h>
42194677Sthompsa#include <sys/sysctl.h>
43194677Sthompsa#include <sys/sx.h>
44194677Sthompsa#include <sys/unistd.h>
45194677Sthompsa#include <sys/callout.h>
46194677Sthompsa#include <sys/malloc.h>
47194677Sthompsa#include <sys/priv.h>
48194677Sthompsa
49188942Sthompsa#include <dev/usb/usb.h>
50194677Sthompsa#include <dev/usb/usbdi.h>
51184610Salfred
52188942Sthompsa#include <dev/usb/usb_core.h>
53188942Sthompsa#include <dev/usb/usb_debug.h>
54188942Sthompsa#include <dev/usb/usb_process.h>
55188942Sthompsa#include <dev/usb/usb_device.h>
56188942Sthompsa#include <dev/usb/usb_busdma.h>
57188942Sthompsa#include <dev/usb/usb_transfer.h>
58184610Salfred
59194677Sthompsa#include <ddb/ddb.h>
60194677Sthompsa#include <ddb/db_sym.h>
61246122Shselasky#endif			/* USB_GLOBAL_INCLUDE_FILE */
62194677Sthompsa
63184610Salfred/*
64184610Salfred * Define this unconditionally in case a kernel module is loaded that
65184610Salfred * has been compiled with debugging options.
66184610Salfred */
67194228Sthompsaint	usb_debug = 0;
68184610Salfred
69192502SthompsaSYSCTL_NODE(_hw, OID_AUTO, usb, CTLFLAG_RW, 0, "USB debugging");
70242126ShselaskySYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN,
71194228Sthompsa    &usb_debug, 0, "Debug level");
72199675SthompsaTUNABLE_INT("hw.usb.debug", &usb_debug);
73199675Sthompsa
74241987Shselasky#ifdef USB_DEBUG
75241987Shselasky/*
76241987Shselasky * Sysctls to modify timings/delays
77241987Shselasky */
78241987Shselaskystatic SYSCTL_NODE(_hw_usb, OID_AUTO, timings, CTLFLAG_RW, 0, "Timings");
79241987Shselaskystatic int usb_timings_sysctl_handler(SYSCTL_HANDLER_ARGS);
80241987Shselasky
81241987ShselaskyTUNABLE_INT("hw.usb.timings.port_reset_delay", (int *)&usb_port_reset_delay);
82242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_reset_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
83241987Shselasky    &usb_port_reset_delay, sizeof(usb_port_reset_delay),
84241987Shselasky    usb_timings_sysctl_handler, "IU", "Port Reset Delay");
85241987ShselaskyTUNABLE_INT("hw.usb.timings.port_root_reset_delay", (int *)&usb_port_root_reset_delay);
86242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_root_reset_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
87241987Shselasky    &usb_port_root_reset_delay, sizeof(usb_port_root_reset_delay),
88241987Shselasky    usb_timings_sysctl_handler, "IU", "Root Port Reset Delay");
89241987ShselaskyTUNABLE_INT("hw.usb.timings.port_reset_recovery", (int *)&usb_port_reset_recovery);
90242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_reset_recovery, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
91241987Shselasky    &usb_port_reset_recovery, sizeof(usb_port_reset_recovery),
92241987Shselasky    usb_timings_sysctl_handler, "IU", "Port Reset Recovery");
93241987ShselaskyTUNABLE_INT("hw.usb.timings.port_powerup_delay", (int *)&usb_port_powerup_delay);
94242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_powerup_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
95241987Shselasky    &usb_port_powerup_delay, sizeof(usb_port_powerup_delay),
96241987Shselasky    usb_timings_sysctl_handler, "IU", "Port PowerUp Delay");
97241987ShselaskyTUNABLE_INT("hw.usb.timings.port_resume_delay", (int *)&usb_port_resume_delay);
98242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_resume_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
99241987Shselasky    &usb_port_resume_delay, sizeof(usb_port_resume_delay),
100241987Shselasky    usb_timings_sysctl_handler, "IU", "Port Resume Delay");
101241987ShselaskyTUNABLE_INT("hw.usb.timings.set_address_settle", (int *)&usb_set_address_settle);
102242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, set_address_settle, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
103241987Shselasky    &usb_set_address_settle, sizeof(usb_set_address_settle),
104241987Shselasky    usb_timings_sysctl_handler, "IU", "Set Address Settle");
105241987ShselaskyTUNABLE_INT("hw.usb.timings.resume_delay", (int *)&usb_resume_delay);
106242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, resume_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
107241987Shselasky    &usb_resume_delay, sizeof(usb_resume_delay),
108241987Shselasky    usb_timings_sysctl_handler, "IU", "Resume Delay");
109241987ShselaskyTUNABLE_INT("hw.usb.timings.resume_wait", (int *)&usb_resume_wait);
110242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, resume_wait, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
111241987Shselasky    &usb_resume_wait, sizeof(usb_resume_wait),
112241987Shselasky    usb_timings_sysctl_handler, "IU", "Resume Wait");
113241987ShselaskyTUNABLE_INT("hw.usb.timings.resume_recovery", (int *)&usb_resume_recovery);
114242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, resume_recovery, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
115241987Shselasky    &usb_resume_recovery, sizeof(usb_resume_recovery),
116241987Shselasky    usb_timings_sysctl_handler, "IU", "Resume Recovery");
117241987ShselaskyTUNABLE_INT("hw.usb.timings.extra_power_up_time", (int *)&usb_extra_power_up_time);
118242126ShselaskySYSCTL_PROC(_hw_usb_timings, OID_AUTO, extra_power_up_time, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN,
119241987Shselasky    &usb_extra_power_up_time, sizeof(usb_extra_power_up_time),
120241987Shselasky    usb_timings_sysctl_handler, "IU", "Extra PowerUp Time");
121241987Shselasky#endif
122241987Shselasky
123184610Salfred/*------------------------------------------------------------------------*
124194228Sthompsa *	usb_dump_iface
125184610Salfred *
126184610Salfred * This function dumps information about an USB interface.
127184610Salfred *------------------------------------------------------------------------*/
128184610Salfredvoid
129194228Sthompsausb_dump_iface(struct usb_interface *iface)
130184610Salfred{
131194228Sthompsa	printf("usb_dump_iface: iface=%p\n", iface);
132184610Salfred	if (iface == NULL) {
133184610Salfred		return;
134184610Salfred	}
135184610Salfred	printf(" iface=%p idesc=%p altindex=%d\n",
136184610Salfred	    iface, iface->idesc, iface->alt_index);
137184610Salfred}
138184610Salfred
139184610Salfred/*------------------------------------------------------------------------*
140194228Sthompsa *	usb_dump_device
141184610Salfred *
142184610Salfred * This function dumps information about an USB device.
143184610Salfred *------------------------------------------------------------------------*/
144184610Salfredvoid
145194228Sthompsausb_dump_device(struct usb_device *udev)
146184610Salfred{
147194228Sthompsa	printf("usb_dump_device: dev=%p\n", udev);
148184610Salfred	if (udev == NULL) {
149184610Salfred		return;
150184610Salfred	}
151184610Salfred	printf(" bus=%p \n"
152184610Salfred	    " address=%d config=%d depth=%d speed=%d self_powered=%d\n"
153184610Salfred	    " power=%d langid=%d\n",
154184610Salfred	    udev->bus,
155184610Salfred	    udev->address, udev->curr_config_no, udev->depth, udev->speed,
156184610Salfred	    udev->flags.self_powered, udev->power, udev->langid);
157184610Salfred}
158184610Salfred
159184610Salfred/*------------------------------------------------------------------------*
160194228Sthompsa *	usb_dump_queue
161184610Salfred *
162193644Sthompsa * This function dumps the USB transfer that are queued up on an USB endpoint.
163184610Salfred *------------------------------------------------------------------------*/
164184610Salfredvoid
165194228Sthompsausb_dump_queue(struct usb_endpoint *ep)
166184610Salfred{
167192984Sthompsa	struct usb_xfer *xfer;
168239214Shselasky	usb_stream_t x;
169184610Salfred
170194228Sthompsa	printf("usb_dump_queue: endpoint=%p xfer: ", ep);
171239214Shselasky	for (x = 0; x != USB_MAX_EP_STREAMS; x++) {
172239214Shselasky		TAILQ_FOREACH(xfer, &ep->endpoint_q[x].head, wait_entry)
173239214Shselasky			printf(" %p", xfer);
174184610Salfred	}
175184610Salfred	printf("\n");
176184610Salfred}
177184610Salfred
178184610Salfred/*------------------------------------------------------------------------*
179194228Sthompsa *	usb_dump_endpoint
180184610Salfred *
181193644Sthompsa * This function dumps information about an USB endpoint.
182184610Salfred *------------------------------------------------------------------------*/
183184610Salfredvoid
184194228Sthompsausb_dump_endpoint(struct usb_endpoint *ep)
185184610Salfred{
186193644Sthompsa	if (ep) {
187194228Sthompsa		printf("usb_dump_endpoint: endpoint=%p", ep);
188184610Salfred
189184610Salfred		printf(" edesc=%p isoc_next=%d toggle_next=%d",
190193644Sthompsa		    ep->edesc, ep->isoc_next, ep->toggle_next);
191184610Salfred
192193644Sthompsa		if (ep->edesc) {
193184610Salfred			printf(" bEndpointAddress=0x%02x",
194193644Sthompsa			    ep->edesc->bEndpointAddress);
195184610Salfred		}
196184610Salfred		printf("\n");
197194228Sthompsa		usb_dump_queue(ep);
198184610Salfred	} else {
199194228Sthompsa		printf("usb_dump_endpoint: endpoint=NULL\n");
200184610Salfred	}
201184610Salfred}
202184610Salfred
203184610Salfred/*------------------------------------------------------------------------*
204194228Sthompsa *	usb_dump_xfer
205184610Salfred *
206184610Salfred * This function dumps information about an USB transfer.
207184610Salfred *------------------------------------------------------------------------*/
208184610Salfredvoid
209194228Sthompsausb_dump_xfer(struct usb_xfer *xfer)
210184610Salfred{
211192984Sthompsa	struct usb_device *udev;
212194228Sthompsa	printf("usb_dump_xfer: xfer=%p\n", xfer);
213184610Salfred	if (xfer == NULL) {
214184610Salfred		return;
215184610Salfred	}
216193644Sthompsa	if (xfer->endpoint == NULL) {
217193644Sthompsa		printf("xfer %p: endpoint=NULL\n",
218184610Salfred		    xfer);
219184610Salfred		return;
220184610Salfred	}
221187173Sthompsa	udev = xfer->xroot->udev;
222184610Salfred	printf("xfer %p: udev=%p vid=0x%04x pid=0x%04x addr=%d "
223193644Sthompsa	    "endpoint=%p ep=0x%02x attr=0x%02x\n",
224187173Sthompsa	    xfer, udev,
225187173Sthompsa	    UGETW(udev->ddesc.idVendor),
226187173Sthompsa	    UGETW(udev->ddesc.idProduct),
227193644Sthompsa	    udev->address, xfer->endpoint,
228193644Sthompsa	    xfer->endpoint->edesc->bEndpointAddress,
229193644Sthompsa	    xfer->endpoint->edesc->bmAttributes);
230184610Salfred}
231241987Shselasky
232241987Shselasky#ifdef USB_DEBUG
233241987Shselaskyunsigned int usb_port_reset_delay	= USB_PORT_RESET_DELAY;
234241987Shselaskyunsigned int usb_port_root_reset_delay	= USB_PORT_ROOT_RESET_DELAY;
235241987Shselaskyunsigned int usb_port_reset_recovery	= USB_PORT_RESET_RECOVERY;
236241987Shselaskyunsigned int usb_port_powerup_delay	= USB_PORT_POWERUP_DELAY;
237241987Shselaskyunsigned int usb_port_resume_delay	= USB_PORT_RESUME_DELAY;
238241987Shselaskyunsigned int usb_set_address_settle	= USB_SET_ADDRESS_SETTLE;
239241987Shselaskyunsigned int usb_resume_delay		= USB_RESUME_DELAY;
240241987Shselaskyunsigned int usb_resume_wait		= USB_RESUME_WAIT;
241241987Shselaskyunsigned int usb_resume_recovery	= USB_RESUME_RECOVERY;
242241987Shselaskyunsigned int usb_extra_power_up_time	= USB_EXTRA_POWER_UP_TIME;
243241987Shselasky
244241987Shselasky/*------------------------------------------------------------------------*
245241987Shselasky *	usb_timings_sysctl_handler
246241987Shselasky *
247241987Shselasky * This function updates timings variables, adjusting them where necessary.
248241987Shselasky *------------------------------------------------------------------------*/
249241987Shselaskystatic int usb_timings_sysctl_handler(SYSCTL_HANDLER_ARGS)
250241987Shselasky{
251241987Shselasky	int error = 0;
252241987Shselasky	unsigned int val;
253241987Shselasky
254241987Shselasky	/*
255241987Shselasky	 * Attempt to get a coherent snapshot by making a copy of the data.
256241987Shselasky	 */
257241987Shselasky	if (arg1)
258241987Shselasky		val = *(unsigned int *)arg1;
259241987Shselasky	else
260241987Shselasky		val = arg2;
261241987Shselasky	error = SYSCTL_OUT(req, &val, sizeof(int));
262241987Shselasky	if (error || !req->newptr)
263241987Shselasky		return (error);
264241987Shselasky
265241987Shselasky	if (!arg1)
266241987Shselasky		return EPERM;
267241987Shselasky
268241987Shselasky	error = SYSCTL_IN(req, &val, sizeof(unsigned int));
269241987Shselasky	if (error)
270241987Shselasky		return (error);
271241987Shselasky
272241987Shselasky	/*
273241987Shselasky	 * Now make sure the values are decent, and certainly no lower than
274241987Shselasky	 * what the USB spec prescribes.
275241987Shselasky	 */
276241987Shselasky	unsigned int *p = (unsigned int *)arg1;
277241987Shselasky	if (p == &usb_port_reset_delay) {
278241987Shselasky		if (val < USB_PORT_RESET_DELAY_SPEC)
279241987Shselasky			return (EINVAL);
280241987Shselasky	} else if (p == &usb_port_root_reset_delay) {
281241987Shselasky		if (val < USB_PORT_ROOT_RESET_DELAY_SPEC)
282241987Shselasky			return (EINVAL);
283241987Shselasky	} else if (p == &usb_port_reset_recovery) {
284241987Shselasky		if (val < USB_PORT_RESET_RECOVERY_SPEC)
285241987Shselasky			return (EINVAL);
286241987Shselasky	} else if (p == &usb_port_powerup_delay) {
287241987Shselasky		if (val < USB_PORT_POWERUP_DELAY_SPEC)
288241987Shselasky			return (EINVAL);
289241987Shselasky	} else if (p == &usb_port_resume_delay) {
290241987Shselasky		if (val < USB_PORT_RESUME_DELAY_SPEC)
291241987Shselasky			return (EINVAL);
292241987Shselasky	} else if (p == &usb_set_address_settle) {
293241987Shselasky		if (val < USB_SET_ADDRESS_SETTLE_SPEC)
294241987Shselasky			return (EINVAL);
295241987Shselasky	} else if (p == &usb_resume_delay) {
296241987Shselasky		if (val < USB_RESUME_DELAY_SPEC)
297241987Shselasky			return (EINVAL);
298241987Shselasky	} else if (p == &usb_resume_wait) {
299241987Shselasky		if (val < USB_RESUME_WAIT_SPEC)
300241987Shselasky			return (EINVAL);
301241987Shselasky	} else if (p == &usb_resume_recovery) {
302241987Shselasky		if (val < USB_RESUME_RECOVERY_SPEC)
303241987Shselasky			return (EINVAL);
304241987Shselasky	} else if (p == &usb_extra_power_up_time) {
305241987Shselasky		if (val < USB_EXTRA_POWER_UP_TIME_SPEC)
306241987Shselasky			return (EINVAL);
307241987Shselasky	} else {
308241987Shselasky		/* noop */
309241987Shselasky	}
310241987Shselasky
311241987Shselasky	*p = val;
312241987Shselasky	return 0;
313241987Shselasky}
314241987Shselasky#endif
315