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