1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2010 Hans Petter Selasky
5 * Copyright (c) 2018 The FreeBSD Foundation
6 * All rights reserved.
7 *
8 * Portions of this software were developed by Edward Tomasz Napierala
9 * under sponsorship from the FreeBSD Foundation.
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 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/*
34 * This file contains the USB template for an USB Keyboard Device.
35 */
36
37#ifdef USB_GLOBAL_INCLUDE_FILE
38#include USB_GLOBAL_INCLUDE_FILE
39#else
40#include <sys/stdint.h>
41#include <sys/stddef.h>
42#include <sys/param.h>
43#include <sys/queue.h>
44#include <sys/types.h>
45#include <sys/systm.h>
46#include <sys/kernel.h>
47#include <sys/bus.h>
48#include <sys/module.h>
49#include <sys/lock.h>
50#include <sys/mutex.h>
51#include <sys/condvar.h>
52#include <sys/sysctl.h>
53#include <sys/sx.h>
54#include <sys/unistd.h>
55#include <sys/callout.h>
56#include <sys/malloc.h>
57#include <sys/priv.h>
58
59#include <dev/usb/usb.h>
60#include <dev/usb/usbdi.h>
61#include <dev/usb/usb_core.h>
62#include <dev/usb/usb_cdc.h>
63#include <dev/usb/usb_ioctl.h>
64#include <dev/usb/usb_util.h>
65
66#include <dev/usb/template/usb_template.h>
67#endif			/* USB_GLOBAL_INCLUDE_FILE */
68
69enum {
70	KBD_LANG_INDEX,
71	KBD_INTERFACE_INDEX,
72	KBD_MANUFACTURER_INDEX,
73	KBD_PRODUCT_INDEX,
74	KBD_SERIAL_NUMBER_INDEX,
75	KBD_MAX_INDEX,
76};
77
78#define	KBD_DEFAULT_VENDOR_ID		USB_TEMPLATE_VENDOR
79#define	KBD_DEFAULT_PRODUCT_ID		0x27db
80#define	KBD_DEFAULT_INTERFACE		"Keyboard Interface"
81#define	KBD_DEFAULT_MANUFACTURER	USB_TEMPLATE_MANUFACTURER
82#define	KBD_DEFAULT_PRODUCT		"Keyboard Test Device"
83#define	KBD_DEFAULT_SERIAL_NUMBER	"March 2008"
84
85static struct usb_string_descriptor	kbd_interface;
86static struct usb_string_descriptor	kbd_manufacturer;
87static struct usb_string_descriptor	kbd_product;
88static struct usb_string_descriptor	kbd_serial_number;
89
90static struct sysctl_ctx_list		kbd_ctx_list;
91
92/* prototypes */
93
94static const struct usb_temp_packet_size keyboard_intr_mps = {
95	.mps[USB_SPEED_LOW] = 16,
96	.mps[USB_SPEED_FULL] = 16,
97	.mps[USB_SPEED_HIGH] = 16,
98};
99
100static const struct usb_temp_interval keyboard_intr_interval = {
101	.bInterval[USB_SPEED_LOW] = 2,	/* 2 ms */
102	.bInterval[USB_SPEED_FULL] = 2,	/* 2 ms */
103	.bInterval[USB_SPEED_HIGH] = 5,	/* 2 ms */
104};
105
106/* The following HID descriptor was dumped from a HP keyboard. */
107
108static uint8_t keyboard_hid_descriptor[] = {
109	0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07,
110	0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01,
111	0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01,
112	0x75, 0x08, 0x81, 0x01, 0x95, 0x03, 0x75, 0x01,
113	0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91, 0x02,
114	0x95, 0x05, 0x75, 0x01, 0x91, 0x01, 0x95, 0x06,
115	0x75, 0x08, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05,
116	0x07, 0x19, 0x00, 0x2a, 0xff, 0x00, 0x81, 0x00,
117	0xc0
118};
119
120static const struct usb_temp_endpoint_desc keyboard_ep_0 = {
121	.ppRawDesc = NULL,		/* no raw descriptors */
122	.pPacketSize = &keyboard_intr_mps,
123	.pIntervals = &keyboard_intr_interval,
124	.bEndpointAddress = UE_DIR_IN,
125	.bmAttributes = UE_INTERRUPT,
126};
127
128static const struct usb_temp_endpoint_desc *keyboard_endpoints[] = {
129	&keyboard_ep_0,
130	NULL,
131};
132
133static const uint8_t keyboard_raw_desc[] = {
134	0x09, 0x21, 0x10, 0x01, 0x00, 0x01, 0x22, sizeof(keyboard_hid_descriptor),
135	0x00
136};
137
138static const void *keyboard_iface_0_desc[] = {
139	keyboard_raw_desc,
140	NULL,
141};
142
143static const struct usb_temp_interface_desc keyboard_iface_0 = {
144	.ppRawDesc = keyboard_iface_0_desc,
145	.ppEndpoints = keyboard_endpoints,
146	.bInterfaceClass = UICLASS_HID,
147	.bInterfaceSubClass = UISUBCLASS_BOOT,
148	.bInterfaceProtocol = UIPROTO_BOOT_KEYBOARD,
149	.iInterface = KBD_INTERFACE_INDEX,
150};
151
152static const struct usb_temp_interface_desc *keyboard_interfaces[] = {
153	&keyboard_iface_0,
154	NULL,
155};
156
157static const struct usb_temp_config_desc keyboard_config_desc = {
158	.ppIfaceDesc = keyboard_interfaces,
159	.bmAttributes = 0,
160	.bMaxPower = 0,
161	.iConfiguration = KBD_PRODUCT_INDEX,
162};
163
164static const struct usb_temp_config_desc *keyboard_configs[] = {
165	&keyboard_config_desc,
166	NULL,
167};
168
169static usb_temp_get_string_desc_t keyboard_get_string_desc;
170static usb_temp_get_vendor_desc_t keyboard_get_vendor_desc;
171
172struct usb_temp_device_desc usb_template_kbd = {
173	.getStringDesc = &keyboard_get_string_desc,
174	.getVendorDesc = &keyboard_get_vendor_desc,
175	.ppConfigDesc = keyboard_configs,
176	.idVendor = KBD_DEFAULT_VENDOR_ID,
177	.idProduct = KBD_DEFAULT_PRODUCT_ID,
178	.bcdDevice = 0x0100,
179	.bDeviceClass = UDCLASS_COMM,
180	.bDeviceSubClass = 0,
181	.bDeviceProtocol = 0,
182	.iManufacturer = KBD_MANUFACTURER_INDEX,
183	.iProduct = KBD_PRODUCT_INDEX,
184	.iSerialNumber = KBD_SERIAL_NUMBER_INDEX,
185};
186
187/*------------------------------------------------------------------------*
188 *      keyboard_get_vendor_desc
189 *
190 * Return values:
191 * NULL: Failure. No such vendor descriptor.
192 * Else: Success. Pointer to vendor descriptor is returned.
193 *------------------------------------------------------------------------*/
194static const void *
195keyboard_get_vendor_desc(const struct usb_device_request *req, uint16_t *plen)
196{
197	if ((req->bmRequestType == 0x81) && (req->bRequest == 0x06) &&
198	    (req->wValue[0] == 0x00) && (req->wValue[1] == 0x22) &&
199	    (req->wIndex[1] == 0) && (req->wIndex[0] == 0)) {
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 *------------------------------------------------------------------------*/
213static const void *
214keyboard_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
236static void
237kbd_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 | CTLFLAG_MPSAFE,
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
284static void
285kbd_uninit(void *arg __unused)
286{
287
288	sysctl_ctx_free(&kbd_ctx_list);
289}
290
291SYSINIT(kbd_init, SI_SUB_LOCK, SI_ORDER_FIRST, kbd_init, NULL);
292SYSUNINIT(kbd_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, kbd_uninit, NULL);
293