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 Mouse 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
70enum {
71	MOUSE_LANG_INDEX,
72	MOUSE_INTERFACE_INDEX,
73	MOUSE_MANUFACTURER_INDEX,
74	MOUSE_PRODUCT_INDEX,
75	MOUSE_SERIAL_NUMBER_INDEX,
76	MOUSE_MAX_INDEX,
77};
78
79#define	MOUSE_DEFAULT_VENDOR_ID		USB_TEMPLATE_VENDOR
80#define	MOUSE_DEFAULT_PRODUCT_ID	0x27da
81#define	MOUSE_DEFAULT_INTERFACE		"Mouse interface"
82#define	MOUSE_DEFAULT_MANUFACTURER	USB_TEMPLATE_MANUFACTURER
83#define	MOUSE_DEFAULT_PRODUCT		"Mouse Test Interface"
84#define	MOUSE_DEFAULT_SERIAL_NUMBER	"March 2008"
85
86static struct usb_string_descriptor	mouse_interface;
87static struct usb_string_descriptor	mouse_manufacturer;
88static struct usb_string_descriptor	mouse_product;
89static struct usb_string_descriptor	mouse_serial_number;
90
91static struct sysctl_ctx_list		mouse_ctx_list;
92
93/* prototypes */
94
95/* The following HID descriptor was dumped from a HP mouse. */
96
97static uint8_t mouse_hid_descriptor[] = {
98	0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01,
99	0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
100	0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01,
101	0x81, 0x02, 0x95, 0x05, 0x81, 0x03, 0x05, 0x01,
102	0x09, 0x30, 0x09, 0x31, 0x09, 0x38, 0x15, 0x81,
103	0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, 0x81, 0x06,
104	0xc0, 0xc0
105};
106
107static const struct usb_temp_packet_size mouse_intr_mps = {
108	.mps[USB_SPEED_LOW] = 8,
109	.mps[USB_SPEED_FULL] = 8,
110	.mps[USB_SPEED_HIGH] = 8,
111};
112
113static const struct usb_temp_interval mouse_intr_interval = {
114	.bInterval[USB_SPEED_LOW] = 2,		/* 2ms */
115	.bInterval[USB_SPEED_FULL] = 2,		/* 2ms */
116	.bInterval[USB_SPEED_HIGH] = 5,		/* 2ms */
117};
118
119static const struct usb_temp_endpoint_desc mouse_ep_0 = {
120	.ppRawDesc = NULL,		/* no raw descriptors */
121	.pPacketSize = &mouse_intr_mps,
122	.pIntervals = &mouse_intr_interval,
123	.bEndpointAddress = UE_DIR_IN,
124	.bmAttributes = UE_INTERRUPT,
125};
126
127static const struct usb_temp_endpoint_desc *mouse_endpoints[] = {
128	&mouse_ep_0,
129	NULL,
130};
131
132static const uint8_t mouse_raw_desc[] = {
133	0x09, 0x21, 0x10, 0x01, 0x00, 0x01, 0x22, sizeof(mouse_hid_descriptor),
134	0x00
135};
136
137static const void *mouse_iface_0_desc[] = {
138	mouse_raw_desc,
139	NULL,
140};
141
142static const struct usb_temp_interface_desc mouse_iface_0 = {
143	.ppRawDesc = mouse_iface_0_desc,
144	.ppEndpoints = mouse_endpoints,
145	.bInterfaceClass = UICLASS_HID,
146	.bInterfaceSubClass = UISUBCLASS_BOOT,
147	.bInterfaceProtocol = UIPROTO_MOUSE,
148	.iInterface = MOUSE_INTERFACE_INDEX,
149};
150
151static const struct usb_temp_interface_desc *mouse_interfaces[] = {
152	&mouse_iface_0,
153	NULL,
154};
155
156static const struct usb_temp_config_desc mouse_config_desc = {
157	.ppIfaceDesc = mouse_interfaces,
158	.bmAttributes = 0,
159	.bMaxPower = 0,
160	.iConfiguration = MOUSE_INTERFACE_INDEX,
161};
162
163static const struct usb_temp_config_desc *mouse_configs[] = {
164	&mouse_config_desc,
165	NULL,
166};
167
168static usb_temp_get_string_desc_t mouse_get_string_desc;
169static usb_temp_get_vendor_desc_t mouse_get_vendor_desc;
170
171struct usb_temp_device_desc usb_template_mouse = {
172	.getStringDesc = &mouse_get_string_desc,
173	.getVendorDesc = &mouse_get_vendor_desc,
174	.ppConfigDesc = mouse_configs,
175	.idVendor = MOUSE_DEFAULT_VENDOR_ID,
176	.idProduct = MOUSE_DEFAULT_PRODUCT_ID,
177	.bcdDevice = 0x0100,
178	.bDeviceClass = UDCLASS_COMM,
179	.bDeviceSubClass = 0,
180	.bDeviceProtocol = 0,
181	.iManufacturer = MOUSE_MANUFACTURER_INDEX,
182	.iProduct = MOUSE_PRODUCT_INDEX,
183	.iSerialNumber = MOUSE_SERIAL_NUMBER_INDEX,
184};
185
186/*------------------------------------------------------------------------*
187 *      mouse_get_vendor_desc
188 *
189 * Return values:
190 * NULL: Failure. No such vendor descriptor.
191 * Else: Success. Pointer to vendor descriptor is returned.
192 *------------------------------------------------------------------------*/
193static const void *
194mouse_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(mouse_hid_descriptor);
201		return (mouse_hid_descriptor);
202	}
203	return (NULL);
204}
205
206/*------------------------------------------------------------------------*
207 *	mouse_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 *
214mouse_get_string_desc(uint16_t lang_id, uint8_t string_index)
215{
216	static const void *ptr[MOUSE_MAX_INDEX] = {
217		[MOUSE_LANG_INDEX] = &usb_string_lang_en,
218		[MOUSE_INTERFACE_INDEX] = &mouse_interface,
219		[MOUSE_MANUFACTURER_INDEX] = &mouse_manufacturer,
220		[MOUSE_PRODUCT_INDEX] = &mouse_product,
221		[MOUSE_SERIAL_NUMBER_INDEX] = &mouse_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 < MOUSE_MAX_INDEX) {
231		return (ptr[string_index]);
232	}
233	return (NULL);
234}
235
236static void
237mouse_init(void *arg __unused)
238{
239	struct sysctl_oid *parent;
240	char parent_name[3];
241
242	usb_make_str_desc(&mouse_interface, sizeof(mouse_interface),
243	    MOUSE_DEFAULT_INTERFACE);
244	usb_make_str_desc(&mouse_manufacturer, sizeof(mouse_manufacturer),
245	    MOUSE_DEFAULT_MANUFACTURER);
246	usb_make_str_desc(&mouse_product, sizeof(mouse_product),
247	    MOUSE_DEFAULT_PRODUCT);
248	usb_make_str_desc(&mouse_serial_number, sizeof(mouse_serial_number),
249	    MOUSE_DEFAULT_SERIAL_NUMBER);
250
251	snprintf(parent_name, sizeof(parent_name), "%d", USB_TEMP_MOUSE);
252	sysctl_ctx_init(&mouse_ctx_list);
253
254	parent = SYSCTL_ADD_NODE(&mouse_ctx_list,
255	    SYSCTL_STATIC_CHILDREN(_hw_usb_templates), OID_AUTO,
256	    parent_name, CTLFLAG_RW,
257	    0, "USB Mouse device side template");
258	SYSCTL_ADD_U16(&mouse_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
259	    "vendor_id", CTLFLAG_RWTUN,
260	    &usb_template_mouse.idVendor, 1, "Vendor identifier");
261	SYSCTL_ADD_U16(&mouse_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
262	    "product_id", CTLFLAG_RWTUN,
263	    &usb_template_mouse.idProduct, 1, "Product identifier");
264#if 0
265	SYSCTL_ADD_PROC(&mouse_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
266	    "interface", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
267	    &mouse_interface, sizeof(mouse_interface), usb_temp_sysctl,
268	    "A", "Interface string");
269#endif
270	SYSCTL_ADD_PROC(&mouse_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
271	    "manufacturer", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
272	    &mouse_manufacturer, sizeof(mouse_manufacturer), usb_temp_sysctl,
273	    "A", "Manufacturer string");
274	SYSCTL_ADD_PROC(&mouse_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
275	    "product", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
276	    &mouse_product, sizeof(mouse_product), usb_temp_sysctl,
277	    "A", "Product string");
278	SYSCTL_ADD_PROC(&mouse_ctx_list, SYSCTL_CHILDREN(parent), OID_AUTO,
279	    "serial_number", CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
280	    &mouse_serial_number, sizeof(mouse_serial_number), usb_temp_sysctl,
281	    "A", "Serial number string");
282}
283
284static void
285mouse_uninit(void *arg __unused)
286{
287
288	sysctl_ctx_free(&mouse_ctx_list);
289}
290
291SYSINIT(mouse_init, SI_SUB_LOCK, SI_ORDER_FIRST, mouse_init, NULL);
292SYSUNINIT(mouse_uninit, SI_SUB_LOCK, SI_ORDER_FIRST, mouse_uninit, NULL);
293