xref: /freebsd/sys/dev/usb/usb_util.c (revision 45cd29412eadbb0e8c40590a94b10663addac17a)
102ac6454SAndrew Thompson /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
48ed5bb59SHans Petter Selasky  * Copyright (c) 2008-2022 Hans Petter Selasky
502ac6454SAndrew Thompson  *
602ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
702ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
802ac6454SAndrew Thompson  * are met:
902ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1002ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1102ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1202ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1302ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1402ac6454SAndrew Thompson  *
1502ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1602ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1702ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1802ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1902ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2002ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2102ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2202ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2302ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2402ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2502ac6454SAndrew Thompson  * SUCH DAMAGE.
2602ac6454SAndrew Thompson  */
2702ac6454SAndrew Thompson 
28d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE
29d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE
30d2b99310SHans Petter Selasky #else
31ed6d949aSAndrew Thompson #include <sys/stdint.h>
32ed6d949aSAndrew Thompson #include <sys/stddef.h>
33ed6d949aSAndrew Thompson #include <sys/param.h>
34ed6d949aSAndrew Thompson #include <sys/queue.h>
35ed6d949aSAndrew Thompson #include <sys/types.h>
36ed6d949aSAndrew Thompson #include <sys/systm.h>
37ed6d949aSAndrew Thompson #include <sys/kernel.h>
38ed6d949aSAndrew Thompson #include <sys/bus.h>
39ed6d949aSAndrew Thompson #include <sys/module.h>
40ed6d949aSAndrew Thompson #include <sys/lock.h>
41ed6d949aSAndrew Thompson #include <sys/mutex.h>
42ed6d949aSAndrew Thompson #include <sys/condvar.h>
43ed6d949aSAndrew Thompson #include <sys/sysctl.h>
44ed6d949aSAndrew Thompson #include <sys/sx.h>
45ed6d949aSAndrew Thompson #include <sys/unistd.h>
46ed6d949aSAndrew Thompson #include <sys/callout.h>
47ed6d949aSAndrew Thompson #include <sys/malloc.h>
48ed6d949aSAndrew Thompson #include <sys/priv.h>
49ed6d949aSAndrew Thompson 
5002ac6454SAndrew Thompson #include <dev/usb/usb.h>
51ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
52ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
5302ac6454SAndrew Thompson 
5402ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
5502ac6454SAndrew Thompson #include <dev/usb/usb_util.h>
5602ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
5702ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
5802ac6454SAndrew Thompson #include <dev/usb/usb_request.h>
5902ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
6002ac6454SAndrew Thompson 
6102ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
6202ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
63d2b99310SHans Petter Selasky #endif			/* USB_GLOBAL_INCLUDE_FILE */
6402ac6454SAndrew Thompson 
6502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
66a593f6b8SAndrew Thompson  *	device_set_usb_desc
6702ac6454SAndrew Thompson  *
6802ac6454SAndrew Thompson  * This function can be called at probe or attach to set the USB
6902ac6454SAndrew Thompson  * device supplied textual description for the given device.
7002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
7102ac6454SAndrew Thompson void
device_set_usb_desc(device_t dev)72a593f6b8SAndrew Thompson device_set_usb_desc(device_t dev)
7302ac6454SAndrew Thompson {
74760bc48eSAndrew Thompson 	struct usb_attach_arg *uaa;
75760bc48eSAndrew Thompson 	struct usb_device *udev;
7602ac6454SAndrew Thompson 	char *temp_p;
776950c75fSHans Petter Selasky 	uint8_t do_unlock;
7802ac6454SAndrew Thompson 
7902ac6454SAndrew Thompson 	if (dev == NULL) {
8002ac6454SAndrew Thompson 		/* should not happen */
8102ac6454SAndrew Thompson 		return;
8202ac6454SAndrew Thompson 	}
8302ac6454SAndrew Thompson 	uaa = device_get_ivars(dev);
8402ac6454SAndrew Thompson 	if (uaa == NULL) {
8502ac6454SAndrew Thompson 		/* can happen if called at the wrong time */
8602ac6454SAndrew Thompson 		return;
8702ac6454SAndrew Thompson 	}
8802ac6454SAndrew Thompson 	udev = uaa->device;
8902ac6454SAndrew Thompson 
906950c75fSHans Petter Selasky 	/* Protect scratch area */
9164cb5e2aSHans Petter Selasky 	do_unlock = usbd_ctrl_lock(udev);
926950c75fSHans Petter Selasky 	temp_p = (char *)udev->scratch.data;
93*45cd2941SChristos Margiolis 	usb_devinfo(udev, temp_p, sizeof(udev->scratch.data));
946950c75fSHans Petter Selasky 	if (do_unlock)
9564cb5e2aSHans Petter Selasky 		usbd_ctrl_unlock(udev);
966950c75fSHans Petter Selasky 
9702ac6454SAndrew Thompson 	device_set_desc_copy(dev, temp_p);
9802ac6454SAndrew Thompson 	device_printf(dev, "<%s> on %s\n", temp_p,
9902ac6454SAndrew Thompson 	    device_get_nameunit(udev->bus->bdev));
10002ac6454SAndrew Thompson }
10102ac6454SAndrew Thompson 
10202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
103a593f6b8SAndrew Thompson  *	 usb_pause_mtx - factored out code
10402ac6454SAndrew Thompson  *
10502ac6454SAndrew Thompson  * This function will delay the code by the passed number of system
10602ac6454SAndrew Thompson  * ticks. The passed mutex "mtx" will be dropped while waiting, if
1073ddd1777SHans Petter Selasky  * "mtx" is different from NULL.
10802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
10902ac6454SAndrew Thompson void
usb_pause_mtx(struct mtx * mtx,int timo)1103ddd1777SHans Petter Selasky usb_pause_mtx(struct mtx *mtx, int timo)
11102ac6454SAndrew Thompson {
11202ac6454SAndrew Thompson 	if (mtx != NULL)
11302ac6454SAndrew Thompson 		mtx_unlock(mtx);
11402ac6454SAndrew Thompson 
11502ac6454SAndrew Thompson 	/*
1163ddd1777SHans Petter Selasky 	 * Add one tick to the timeout so that we don't return too
1173ddd1777SHans Petter Selasky 	 * early! Note that pause() will assert that the passed
1183ddd1777SHans Petter Selasky 	 * timeout is positive and non-zero!
11902ac6454SAndrew Thompson 	 */
1203ddd1777SHans Petter Selasky 	pause("USBWAIT", timo + 1);
12102ac6454SAndrew Thompson 
12202ac6454SAndrew Thompson 	if (mtx != NULL)
12302ac6454SAndrew Thompson 		mtx_lock(mtx);
12402ac6454SAndrew Thompson }
12502ac6454SAndrew Thompson 
12602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
127a593f6b8SAndrew Thompson  *	usb_printbcd
12802ac6454SAndrew Thompson  *
12902ac6454SAndrew Thompson  * This function will print the version number "bcd" to the string
13002ac6454SAndrew Thompson  * pointed to by "p" having a maximum length of "p_len" bytes
13102ac6454SAndrew Thompson  * including the terminating zero.
13202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
13302ac6454SAndrew Thompson void
usb_printbcd(char * p,uint16_t p_len,uint16_t bcd)134a593f6b8SAndrew Thompson usb_printbcd(char *p, uint16_t p_len, uint16_t bcd)
13502ac6454SAndrew Thompson {
13602ac6454SAndrew Thompson 	if (snprintf(p, p_len, "%x.%02x", bcd >> 8, bcd & 0xff)) {
13702ac6454SAndrew Thompson 		/* ignore any errors */
13802ac6454SAndrew Thompson 	}
13902ac6454SAndrew Thompson }
14002ac6454SAndrew Thompson 
14102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
142a593f6b8SAndrew Thompson  *	usb_trim_spaces
14302ac6454SAndrew Thompson  *
14402ac6454SAndrew Thompson  * This function removes spaces at the beginning and the end of the string
14502ac6454SAndrew Thompson  * pointed to by the "p" argument.
14602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
14702ac6454SAndrew Thompson void
usb_trim_spaces(char * p)148a593f6b8SAndrew Thompson usb_trim_spaces(char *p)
14902ac6454SAndrew Thompson {
15002ac6454SAndrew Thompson 	char *q;
15102ac6454SAndrew Thompson 	char *e;
15202ac6454SAndrew Thompson 
15302ac6454SAndrew Thompson 	if (p == NULL)
15402ac6454SAndrew Thompson 		return;
15502ac6454SAndrew Thompson 	q = e = p;
15602ac6454SAndrew Thompson 	while (*q == ' ')		/* skip leading spaces */
15702ac6454SAndrew Thompson 		q++;
15802ac6454SAndrew Thompson 	while ((*p = *q++))		/* copy string */
15902ac6454SAndrew Thompson 		if (*p++ != ' ')	/* remember last non-space */
16002ac6454SAndrew Thompson 			e = p;
16102ac6454SAndrew Thompson 	*e = 0;				/* kill trailing spaces */
16202ac6454SAndrew Thompson }
16302ac6454SAndrew Thompson 
16402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
165a593f6b8SAndrew Thompson  *	usb_make_str_desc - convert an ASCII string into a UNICODE string
16602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
16702ac6454SAndrew Thompson uint8_t
usb_make_str_desc(void * ptr,uint16_t max_len,const char * s)168a593f6b8SAndrew Thompson usb_make_str_desc(void *ptr, uint16_t max_len, const char *s)
16902ac6454SAndrew Thompson {
170760bc48eSAndrew Thompson 	struct usb_string_descriptor *p = ptr;
17102ac6454SAndrew Thompson 	uint8_t totlen;
17202ac6454SAndrew Thompson 	int j;
17302ac6454SAndrew Thompson 
17402ac6454SAndrew Thompson 	if (max_len < 2) {
17502ac6454SAndrew Thompson 		/* invalid length */
17602ac6454SAndrew Thompson 		return (0);
17702ac6454SAndrew Thompson 	}
17802ac6454SAndrew Thompson 	max_len = ((max_len / 2) - 1);
17902ac6454SAndrew Thompson 
18002ac6454SAndrew Thompson 	j = strlen(s);
18102ac6454SAndrew Thompson 
18202ac6454SAndrew Thompson 	if (j < 0) {
18302ac6454SAndrew Thompson 		j = 0;
18402ac6454SAndrew Thompson 	}
18502ac6454SAndrew Thompson 	if (j > 126) {
18602ac6454SAndrew Thompson 		j = 126;
18702ac6454SAndrew Thompson 	}
18802ac6454SAndrew Thompson 	if (max_len > j) {
18902ac6454SAndrew Thompson 		max_len = j;
19002ac6454SAndrew Thompson 	}
19102ac6454SAndrew Thompson 	totlen = (max_len + 1) * 2;
19202ac6454SAndrew Thompson 
19302ac6454SAndrew Thompson 	p->bLength = totlen;
19402ac6454SAndrew Thompson 	p->bDescriptorType = UDESC_STRING;
19502ac6454SAndrew Thompson 
19602ac6454SAndrew Thompson 	while (max_len--) {
19702ac6454SAndrew Thompson 		USETW2(p->bString[max_len], 0, s[max_len]);
19802ac6454SAndrew Thompson 	}
19902ac6454SAndrew Thompson 	return (totlen);
20002ac6454SAndrew Thompson }
2018ed5bb59SHans Petter Selasky 
2028ed5bb59SHans Petter Selasky /*------------------------------------------------------------------------*
2038ed5bb59SHans Petter Selasky  *	usb_check_request - prevent damaging USB requests
2048ed5bb59SHans Petter Selasky  *
2058ed5bb59SHans Petter Selasky  * Return values:
2068ed5bb59SHans Petter Selasky  * 0: Access allowed
2078ed5bb59SHans Petter Selasky  * Else: No access
2088ed5bb59SHans Petter Selasky  *------------------------------------------------------------------------*/
2098ed5bb59SHans Petter Selasky #if USB_HAVE_UGEN
2108ed5bb59SHans Petter Selasky int
usb_check_request(struct usb_device * udev,struct usb_device_request * req)2118ed5bb59SHans Petter Selasky usb_check_request(struct usb_device *udev, struct usb_device_request *req)
2128ed5bb59SHans Petter Selasky {
2138ed5bb59SHans Petter Selasky 	struct usb_endpoint *ep;
2148ed5bb59SHans Petter Selasky 	int error;
2158ed5bb59SHans Petter Selasky 
2168ed5bb59SHans Petter Selasky 	/*
2178ed5bb59SHans Petter Selasky 	 * Avoid requests that would damage the bus integrity:
2188ed5bb59SHans Petter Selasky 	 */
2198ed5bb59SHans Petter Selasky 	if (((req->bmRequestType == UT_WRITE_DEVICE) &&
2208ed5bb59SHans Petter Selasky 	    (req->bRequest == UR_SET_ADDRESS)) ||
2218ed5bb59SHans Petter Selasky 	    ((req->bmRequestType == UT_WRITE_DEVICE) &&
2228ed5bb59SHans Petter Selasky 	    (req->bRequest == UR_SET_CONFIG)) ||
2238ed5bb59SHans Petter Selasky 	    ((req->bmRequestType == UT_WRITE_INTERFACE) &&
2248ed5bb59SHans Petter Selasky 	    (req->bRequest == UR_SET_INTERFACE))) {
2258ed5bb59SHans Petter Selasky 		/*
2268ed5bb59SHans Petter Selasky 		 * These requests can be useful for testing USB drivers.
2278ed5bb59SHans Petter Selasky 		 */
2288ed5bb59SHans Petter Selasky 		error = priv_check(curthread, PRIV_DRIVER);
2298ed5bb59SHans Petter Selasky 		if (error)
2308ed5bb59SHans Petter Selasky 			return (error);
2318ed5bb59SHans Petter Selasky 	}
2328ed5bb59SHans Petter Selasky 
2338ed5bb59SHans Petter Selasky 	/*
2348ed5bb59SHans Petter Selasky 	 * Special case - handle clearing of stall
2358ed5bb59SHans Petter Selasky 	 */
2368ed5bb59SHans Petter Selasky 	if (req->bmRequestType == UT_WRITE_ENDPOINT) {
2378ed5bb59SHans Petter Selasky 		ep = usbd_get_ep_by_addr(udev, req->wIndex[0]);
2388ed5bb59SHans Petter Selasky 		if (ep == NULL)
2398ed5bb59SHans Petter Selasky 			return (EINVAL);
2408ed5bb59SHans Petter Selasky 		if ((req->bRequest == UR_CLEAR_FEATURE) &&
2418ed5bb59SHans Petter Selasky 		    (UGETW(req->wValue) == UF_ENDPOINT_HALT))
2428ed5bb59SHans Petter Selasky 			usbd_clear_data_toggle(udev, ep);
2438ed5bb59SHans Petter Selasky 	}
2448ed5bb59SHans Petter Selasky 
2458ed5bb59SHans Petter Selasky 	/* TODO: add more checks to verify the interface index */
2468ed5bb59SHans Petter Selasky 
2478ed5bb59SHans Petter Selasky 	return (0);
2488ed5bb59SHans Petter Selasky }
2498ed5bb59SHans Petter Selasky #endif
250