xref: /freebsd/sys/dev/usb/usb_hid.c (revision 71625ec9ad2a9bc8c09784fbd23b759830e0ee5f)
102ac6454SAndrew Thompson /*	$NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $	*/
202ac6454SAndrew Thompson /*-
3*b61a5730SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
4718cf2ccSPedro F. Giffuni  *
502ac6454SAndrew Thompson  * Copyright (c) 1998 The NetBSD Foundation, Inc.
602ac6454SAndrew Thompson  * All rights reserved.
702ac6454SAndrew Thompson  *
802ac6454SAndrew Thompson  * This code is derived from software contributed to The NetBSD Foundation
902ac6454SAndrew Thompson  * by Lennart Augustsson (lennart@augustsson.net) at
1002ac6454SAndrew Thompson  * Carlstedt Research & Technology.
1102ac6454SAndrew Thompson  *
1202ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
1302ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
1402ac6454SAndrew Thompson  * are met:
1502ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1602ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1702ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1802ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1902ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
2002ac6454SAndrew Thompson  *
2102ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2202ac6454SAndrew Thompson  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2302ac6454SAndrew Thompson  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2402ac6454SAndrew Thompson  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2502ac6454SAndrew Thompson  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2602ac6454SAndrew Thompson  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2702ac6454SAndrew Thompson  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2802ac6454SAndrew Thompson  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2902ac6454SAndrew Thompson  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3002ac6454SAndrew Thompson  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3102ac6454SAndrew Thompson  * POSSIBILITY OF SUCH DAMAGE.
3202ac6454SAndrew Thompson  */
3302ac6454SAndrew Thompson 
34d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE
35d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE
36d2b99310SHans Petter Selasky #else
37ed6d949aSAndrew Thompson #include <sys/stdint.h>
38ed6d949aSAndrew Thompson #include <sys/stddef.h>
39ed6d949aSAndrew Thompson #include <sys/param.h>
40ed6d949aSAndrew Thompson #include <sys/queue.h>
41ed6d949aSAndrew Thompson #include <sys/types.h>
42ed6d949aSAndrew Thompson #include <sys/systm.h>
43ed6d949aSAndrew Thompson #include <sys/kernel.h>
44ed6d949aSAndrew Thompson #include <sys/bus.h>
45ed6d949aSAndrew Thompson #include <sys/module.h>
46ed6d949aSAndrew Thompson #include <sys/lock.h>
47ed6d949aSAndrew Thompson #include <sys/mutex.h>
48ed6d949aSAndrew Thompson #include <sys/condvar.h>
49ed6d949aSAndrew Thompson #include <sys/sysctl.h>
50ed6d949aSAndrew Thompson #include <sys/sx.h>
51ed6d949aSAndrew Thompson #include <sys/unistd.h>
52ed6d949aSAndrew Thompson #include <sys/callout.h>
53ed6d949aSAndrew Thompson #include <sys/malloc.h>
54ed6d949aSAndrew Thompson #include <sys/priv.h>
55ed6d949aSAndrew Thompson 
5602ac6454SAndrew Thompson #include <dev/usb/usb.h>
57ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
58ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
5902ac6454SAndrew Thompson #include <dev/usb/usbhid.h>
6002ac6454SAndrew Thompson 
61a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_debug
6202ac6454SAndrew Thompson 
6302ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
6402ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
6502ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
6602ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
6702ac6454SAndrew Thompson #include <dev/usb/usb_request.h>
68d2b99310SHans Petter Selasky #endif			/* USB_GLOBAL_INCLUDE_FILE */
6902ac6454SAndrew Thompson 
7002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
7102ac6454SAndrew Thompson  *	hid_get_descriptor_from_usb
7202ac6454SAndrew Thompson  *
7302ac6454SAndrew Thompson  * This function will search for a HID descriptor between two USB
7402ac6454SAndrew Thompson  * interface descriptors.
7502ac6454SAndrew Thompson  *
7602ac6454SAndrew Thompson  * Return values:
7702ac6454SAndrew Thompson  * NULL: No more HID descriptors.
7802ac6454SAndrew Thompson  * Else: Pointer to HID descriptor.
7902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
80760bc48eSAndrew Thompson struct usb_hid_descriptor *
hid_get_descriptor_from_usb(struct usb_config_descriptor * cd,struct usb_interface_descriptor * id)81760bc48eSAndrew Thompson hid_get_descriptor_from_usb(struct usb_config_descriptor *cd,
82760bc48eSAndrew Thompson     struct usb_interface_descriptor *id)
8302ac6454SAndrew Thompson {
84760bc48eSAndrew Thompson 	struct usb_descriptor *desc = (void *)id;
8502ac6454SAndrew Thompson 
8602ac6454SAndrew Thompson 	if (desc == NULL) {
8702ac6454SAndrew Thompson 		return (NULL);
8802ac6454SAndrew Thompson 	}
89a593f6b8SAndrew Thompson 	while ((desc = usb_desc_foreach(cd, desc))) {
9002ac6454SAndrew Thompson 		if ((desc->bDescriptorType == UDESC_HID) &&
9102ac6454SAndrew Thompson 		    (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) {
9202ac6454SAndrew Thompson 			return (void *)desc;
9302ac6454SAndrew Thompson 		}
9402ac6454SAndrew Thompson 		if (desc->bDescriptorType == UDESC_INTERFACE) {
9502ac6454SAndrew Thompson 			break;
9602ac6454SAndrew Thompson 		}
9702ac6454SAndrew Thompson 	}
9802ac6454SAndrew Thompson 	return (NULL);
9902ac6454SAndrew Thompson }
10002ac6454SAndrew Thompson 
10102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
102a593f6b8SAndrew Thompson  *	usbd_req_get_hid_desc
10302ac6454SAndrew Thompson  *
10402ac6454SAndrew Thompson  * This function will read out an USB report descriptor from the USB
10502ac6454SAndrew Thompson  * device.
10602ac6454SAndrew Thompson  *
10702ac6454SAndrew Thompson  * Return values:
10802ac6454SAndrew Thompson  * NULL: Failure.
10902ac6454SAndrew Thompson  * Else: Success. The pointer should eventually be passed to free().
11002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
111e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_hid_desc(struct usb_device * udev,struct mtx * mtx,void ** descp,uint16_t * sizep,struct malloc_type * mem,uint8_t iface_index)112a593f6b8SAndrew Thompson usbd_req_get_hid_desc(struct usb_device *udev, struct mtx *mtx,
11302ac6454SAndrew Thompson     void **descp, uint16_t *sizep,
114e0a69b51SAndrew Thompson     struct malloc_type *mem, uint8_t iface_index)
11502ac6454SAndrew Thompson {
116a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
117760bc48eSAndrew Thompson 	struct usb_hid_descriptor *hid;
118e0a69b51SAndrew Thompson 	usb_error_t err;
11902ac6454SAndrew Thompson 
12002ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
12102ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
12202ac6454SAndrew Thompson 	}
12302ac6454SAndrew Thompson 	hid = hid_get_descriptor_from_usb
124a593f6b8SAndrew Thompson 	    (usbd_get_config_descriptor(udev), iface->idesc);
12502ac6454SAndrew Thompson 
12602ac6454SAndrew Thompson 	if (hid == NULL) {
12702ac6454SAndrew Thompson 		return (USB_ERR_IOERROR);
12802ac6454SAndrew Thompson 	}
12902ac6454SAndrew Thompson 	*sizep = UGETW(hid->descrs[0].wDescriptorLength);
13002ac6454SAndrew Thompson 	if (*sizep == 0) {
13102ac6454SAndrew Thompson 		return (USB_ERR_IOERROR);
13202ac6454SAndrew Thompson 	}
13302ac6454SAndrew Thompson 	if (mtx)
13402ac6454SAndrew Thompson 		mtx_unlock(mtx);
13502ac6454SAndrew Thompson 
13602ac6454SAndrew Thompson 	*descp = malloc(*sizep, mem, M_ZERO | M_WAITOK);
13702ac6454SAndrew Thompson 
13802ac6454SAndrew Thompson 	if (mtx)
13902ac6454SAndrew Thompson 		mtx_lock(mtx);
14002ac6454SAndrew Thompson 
14102ac6454SAndrew Thompson 	if (*descp == NULL) {
14202ac6454SAndrew Thompson 		return (USB_ERR_NOMEM);
14302ac6454SAndrew Thompson 	}
144a593f6b8SAndrew Thompson 	err = usbd_req_get_report_descriptor
14502ac6454SAndrew Thompson 	    (udev, mtx, *descp, *sizep, iface_index);
14602ac6454SAndrew Thompson 
14702ac6454SAndrew Thompson 	if (err) {
14802ac6454SAndrew Thompson 		free(*descp, mem);
14902ac6454SAndrew Thompson 		*descp = NULL;
15002ac6454SAndrew Thompson 		return (err);
15102ac6454SAndrew Thompson 	}
15202ac6454SAndrew Thompson 	return (USB_ERR_NORMAL_COMPLETION);
15302ac6454SAndrew Thompson }
154