xref: /freebsd/sys/dev/usb/template/usb_template_kbd.c (revision 5bf5ca772c6de2d53344a78cf461447cc322ccea)
1 /* $FreeBSD$ */
2 /*-
3  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4  *
5  * Copyright (c) 2010 Hans Petter Selasky
6  * Copyright (c) 2018 The FreeBSD Foundation
7  * All rights reserved.
8  *
9  * Portions of this software were developed by Edward Tomasz Napierala
10  * under sponsorship from the FreeBSD Foundation.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 /*
35  * This file contains the USB template for an USB Keyboard Device.
36  */
37 
38 #ifdef USB_GLOBAL_INCLUDE_FILE
39 #include USB_GLOBAL_INCLUDE_FILE
40 #else
41 #include <sys/stdint.h>
42 #include <sys/stddef.h>
43 #include <sys/param.h>
44 #include <sys/queue.h>
45 #include <sys/types.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/bus.h>
49 #include <sys/module.h>
50 #include <sys/lock.h>
51 #include <sys/mutex.h>
52 #include <sys/condvar.h>
53 #include <sys/sysctl.h>
54 #include <sys/sx.h>
55 #include <sys/unistd.h>
56 #include <sys/callout.h>
57 #include <sys/malloc.h>
58 #include <sys/priv.h>
59 
60 #include <dev/usb/usb.h>
61 #include <dev/usb/usbdi.h>
62 #include <dev/usb/usb_core.h>
63 #include <dev/usb/usb_cdc.h>
64 #include <dev/usb/usb_ioctl.h>
65 #include <dev/usb/usb_util.h>
66 
67 #include <dev/usb/template/usb_template.h>
68 #endif			/* USB_GLOBAL_INCLUDE_FILE */
69 
70 enum {
71 	KBD_LANG_INDEX,
72 	KBD_INTERFACE_INDEX,
73 	KBD_MANUFACTURER_INDEX,
74 	KBD_PRODUCT_INDEX,
75 	KBD_SERIAL_NUMBER_INDEX,
76 	KBD_MAX_INDEX,
77 };
78 
79 #define	KBD_DEFAULT_INTERFACE		"Keyboard Interface"
80 #define	KBD_DEFAULT_MANUFACTURER	"FreeBSD foundation"
81 #define	KBD_DEFAULT_PRODUCT		"Keyboard Test Device"
82 #define	KBD_DEFAULT_SERIAL_NUMBER	"March 2008"
83 
84 static struct usb_string_descriptor	kbd_interface;
85 static struct usb_string_descriptor	kbd_manufacturer;
86 static struct usb_string_descriptor	kbd_product;
87 static struct usb_string_descriptor	kbd_serial_number;
88 
89 static struct sysctl_ctx_list		kbd_ctx_list;
90 
91 /* prototypes */
92 
93 static const struct usb_temp_packet_size keyboard_intr_mps = {
94 	.mps[USB_SPEED_LOW] = 16,
95 	.mps[USB_SPEED_FULL] = 16,
96 	.mps[USB_SPEED_HIGH] = 16,
97 };
98 
99 static const struct usb_temp_interval keyboard_intr_interval = {
100 	.bInterval[USB_SPEED_LOW] = 2,	/* 2 ms */
101 	.bInterval[USB_SPEED_FULL] = 2,	/* 2 ms */
102 	.bInterval[USB_SPEED_HIGH] = 5,	/* 2 ms */
103 };
104 
105 /* The following HID descriptor was dumped from a HP keyboard. */
106 
107 static uint8_t keyboard_hid_descriptor[] = {
108 	0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07,
109 	0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01,
110 	0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01,
111 	0x75, 0x08, 0x81, 0x01, 0x95, 0x03, 0x75, 0x01,
112 	0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91, 0x02,
113 	0x95, 0x05, 0x75, 0x01, 0x91, 0x01, 0x95, 0x06,
114 	0x75, 0x08, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05,
115 	0x07, 0x19, 0x00, 0x2a, 0xff, 0x00, 0x81, 0x00,
116 	0xc0
117 };
118 
119 static const struct usb_temp_endpoint_desc keyboard_ep_0 = {
120 	.ppRawDesc = NULL,		/* no raw descriptors */
121 	.pPacketSize = &keyboard_intr_mps,
122 	.pIntervals = &keyboard_intr_interval,
123 	.bEndpointAddress = UE_DIR_IN,
124 	.bmAttributes = UE_INTERRUPT,
125 };
126 
127 static const struct usb_temp_endpoint_desc *keyboard_endpoints[] = {
128 	&keyboard_ep_0,
129 	NULL,
130 };
131 
132 static const uint8_t keyboard_raw_desc[] = {
133 	0x09, 0x21, 0x10, 0x01, 0x00, 0x01, 0x22, sizeof(keyboard_hid_descriptor),
134 	0x00
135 };
136 
137 static const void *keyboard_iface_0_desc[] = {
138 	keyboard_raw_desc,
139 	NULL,
140 };
141 
142 static const struct usb_temp_interface_desc keyboard_iface_0 = {
143 	.ppRawDesc = keyboard_iface_0_desc,
144 	.ppEndpoints = keyboard_endpoints,
145 	.bInterfaceClass = UICLASS_HID,
146 	.bInterfaceSubClass = UISUBCLASS_BOOT,
147 	.bInterfaceProtocol = UIPROTO_BOOT_KEYBOARD,
148 	.iInterface = KBD_INTERFACE_INDEX,
149 };
150 
151 static const struct usb_temp_interface_desc *keyboard_interfaces[] = {
152 	&keyboard_iface_0,
153 	NULL,
154 };
155 
156 static const struct usb_temp_config_desc keyboard_config_desc = {
157 	.ppIfaceDesc = keyboard_interfaces,
158 	.bmAttributes = UC_BUS_POWERED,
159 	.bMaxPower = 25,		/* 50 mA */
160 	.iConfiguration = KBD_PRODUCT_INDEX,
161 };
162 
163 static const struct usb_temp_config_desc *keyboard_configs[] = {
164 	&keyboard_config_desc,
165 	NULL,
166 };
167 
168 static usb_temp_get_string_desc_t keyboard_get_string_desc;
169 static usb_temp_get_vendor_desc_t keyboard_get_vendor_desc;
170 
171 struct usb_temp_device_desc usb_template_kbd = {
172 	.getStringDesc = &keyboard_get_string_desc,
173 	.getVendorDesc = &keyboard_get_vendor_desc,
174 	.ppConfigDesc = keyboard_configs,
175 	.idVendor = USB_TEMPLATE_VENDOR,
176 	.idProduct = 0x00CB,
177 	.bcdDevice = 0x0100,
178 	.bDeviceClass = UDCLASS_COMM,
179 	.bDeviceSubClass = 0,
180 	.bDeviceProtocol = 0,
181 	.iManufacturer = KBD_MANUFACTURER_INDEX,
182 	.iProduct = KBD_PRODUCT_INDEX,
183 	.iSerialNumber = KBD_SERIAL_NUMBER_INDEX,
184 };
185 
186 /*------------------------------------------------------------------------*
187  *      keyboard_get_vendor_desc
188  *
189  * Return values:
190  * NULL: Failure. No such vendor descriptor.
191  * Else: Success. Pointer to vendor descriptor is returned.
192  *------------------------------------------------------------------------*/
193 static const void *
194 keyboard_get_vendor_desc(const struct usb_device_request *req, uint16_t *plen)
195 {
196 	if ((req->bmRequestType == 0x81) && (req->bRequest == 0x06) &&
197 	    (req->wValue[0] == 0x00) && (req->wValue[1] == 0x22) &&
198 	    (req->wIndex[1] == 0) && (req->wIndex[0] == 0)) {
199 
200 		*plen = sizeof(keyboard_hid_descriptor);
201 		return (keyboard_hid_descriptor);
202 	}
203 	return (NULL);
204 }
205 
206 /*------------------------------------------------------------------------*
207  *	keyboard_get_string_desc
208  *
209  * Return values:
210  * NULL: Failure. No such string.
211  * Else: Success. Pointer to string descriptor is returned.
212  *------------------------------------------------------------------------*/
213 static const void *
214 keyboard_get_string_desc(uint16_t lang_id, uint8_t string_index)
215 {
216 	static const void *ptr[KBD_MAX_INDEX] = {
217 		[KBD_LANG_INDEX] = &usb_string_lang_en,
218 		[KBD_INTERFACE_INDEX] = &kbd_interface,
219 		[KBD_MANUFACTURER_INDEX] = &kbd_manufacturer,
220 		[KBD_PRODUCT_INDEX] = &kbd_product,
221 		[KBD_SERIAL_NUMBER_INDEX] = &kbd_serial_number,
222 	};
223 
224 	if (string_index == 0) {
225 		return (&usb_string_lang_en);
226 	}
227 	if (lang_id != 0x0409) {
228 		return (NULL);
229 	}
230 	if (string_index < KBD_MAX_INDEX) {
231 		return (ptr[string_index]);
232 	}
233 	return (NULL);
234 }
235 
236 static void
237 kbd_init(void *arg __unused)
238 {
239 	struct sysctl_oid *parent;
240 	char parent_name[3];
241 
242 	usb_make_str_desc(&kbd_interface, sizeof(kbd_interface),
243 	    KBD_DEFAULT_INTERFACE);
244 	usb_make_str_desc(&kbd_manufacturer, sizeof(kbd_manufacturer),
245 	    KBD_DEFAULT_MANUFACTURER);
246 	usb_make_str_desc(&kbd_product, sizeof(kbd_product),
247 	    KBD_DEFAULT_PRODUCT);
248 	usb_make_str_desc(&kbd_serial_number, sizeof(kbd_serial_number),
249 	    KBD_DEFAULT_SERIAL_NUMBER);
250 
251 	snprintf(parent_name, sizeof(parent_name), "%d", USB_TEMP_KBD);
252 	sysctl_ctx_init(&kbd_ctx_list);
253 
254 	parent = SYSCTL_ADD_NODE(&kbd_ctx_list,
255 	    SYSCTL_STATIC_CHILDREN(_hw_usb_templates), OID_AUTO,
256 	    parent_name, CTLFLAG_RW,
257 	    0, "USB Keyboard device side template");
258 	SYSCTL_ADD_U16(&kbd_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
259 	    "vendor_id", CTLFLAG_RWTUN,
260 	    &usb_template_kbd.idVendor, 1, "Vendor identifier");
261 	SYSCTL_ADD_U16(&kbd_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
262 	    "product_id", CTLFLAG_RWTUN,
263 	    &usb_template_kbd.idProduct, 1, "Product identifier");
264 #if 0
265 	SYSCTL_ADD_PROC(&kbd_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
266 	    "interface", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
267 	    &kbd_interface, sizeof(kbd_interface), usb_temp_sysctl,
268 	    "A", "Interface string");
269 #endif
270 	SYSCTL_ADD_PROC(&kbd_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
271 	    "manufacturer", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
272 	    &kbd_manufacturer, sizeof(kbd_manufacturer), usb_temp_sysctl,
273 	    "A", "Manufacturer string");
274 	SYSCTL_ADD_PROC(&kbd_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
275 	    "product", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
276 	    &kbd_product, sizeof(kbd_product), usb_temp_sysctl,
277 	    "A", "Product string");
278 	SYSCTL_ADD_PROC(&kbd_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
279 	    "serial_number", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
280 	    &kbd_serial_number, sizeof(kbd_serial_number), usb_temp_sysctl,
281 	    "A", "Serial number string");
282 }
283 
284 static void
285 kbd_uninit(void *arg __unused)
286 {
287 
288 	sysctl_ctx_free(&kbd_ctx_list);
289 }
290 
291 SYSINIT(kbd_init, SI_SUB_LOCK, SI_ORDER_FIRST, kbd_init, NULL);
292 SYSUNINIT(kbd_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, kbd_uninit, NULL);
293