summaryrefslogtreecommitdiffstats
path: root/drivers/usb/emul/sandbox_keyb.c
blob: f562fb7eca2ffbb5adc8fadbfb56cd073413a383 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// SPDX-License-Identifier: GPL-2.0+
/*
 * (C) Copyright 2015 Google, Inc
 * Written by Simon Glass <sjg@chromium.org>
 */

#include <common.h>
#include <dm.h>
#include <log.h>
#include <os.h>
#include <scsi.h>
#include <usb.h>

/*
 * This driver emulates a USB keyboard using the USB HID specification (boot
 * protocol)
 */

enum {
	SANDBOX_KEYB_EP_IN		= 1,	/* endpoints */
};

enum cmd_phase {
	PHASE_START,
	PHASE_DATA,
	PHASE_STATUS,
};

enum {
	STRINGID_MANUFACTURER = 1,
	STRINGID_PRODUCT,
	STRINGID_SERIAL,

	STRINGID_COUNT,
};

/**
 * struct sandbox_keyb_priv - private state for this driver
 *
 */
struct sandbox_keyb_priv {
	struct membuff in;
};

struct sandbox_keyb_plat {
	struct usb_string keyb_strings[STRINGID_COUNT];
};

static struct usb_device_descriptor keyb_device_desc = {
	.bLength =		sizeof(keyb_device_desc),
	.bDescriptorType =	USB_DT_DEVICE,

	.bcdUSB =		__constant_cpu_to_le16(0x0100),

	.bDeviceClass =		0,
	.bDeviceSubClass =	0,
	.bDeviceProtocol =	0,

	.idVendor =		__constant_cpu_to_le16(0x1234),
	.idProduct =		__constant_cpu_to_le16(0x5679),
	.iManufacturer =	STRINGID_MANUFACTURER,
	.iProduct =		STRINGID_PRODUCT,
	.iSerialNumber =	STRINGID_SERIAL,
	.bNumConfigurations =	1,
};

static struct usb_config_descriptor keyb_config0 = {
	.bLength		= sizeof(keyb_config0),
	.bDescriptorType	= USB_DT_CONFIG,

	/* wTotalLength is set up by usb-emul-uclass */
	.bNumInterfaces		= 2,
	.bConfigurationValue	= 0,
	.iConfiguration		= 0,
	.bmAttributes		= 1 << 7 | 1 << 5,
	.bMaxPower		= 50,
};

static struct usb_interface_descriptor keyb_interface0 = {
	.bLength		= sizeof(keyb_interface0),
	.bDescriptorType	= USB_DT_INTERFACE,

	.bInterfaceNumber	= 0,
	.bAlternateSetting	= 0,
	.bNumEndpoints		= 1,
	.bInterfaceClass	= USB_CLASS_HID,
	.bInterfaceSubClass	= USB_SUB_HID_BOOT,
	.bInterfaceProtocol	= USB_PROT_HID_KEYBOARD,
	.iInterface		= 0,
};

static struct usb_class_hid_descriptor keyb_report0 = {
	.bLength		= sizeof(keyb_report0),
	.bDescriptorType	= USB_DT_HID,
	.bcdCDC			= 0x101,
	.bCountryCode		= 0,
	.bNumDescriptors	= 1,
	.bDescriptorType0	= USB_DT_HID_REPORT,
	.wDescriptorLength0	= 0x3f,
};

static struct usb_endpoint_descriptor keyb_endpoint0_in = {
	.bLength		= USB_DT_ENDPOINT_SIZE,
	.bDescriptorType	= USB_DT_ENDPOINT,

	.bEndpointAddress	= SANDBOX_KEYB_EP_IN | USB_ENDPOINT_DIR_MASK,
	.bmAttributes		= USB_ENDPOINT_XFER_BULK |
					USB_ENDPOINT_XFER_ISOC,
	.wMaxPacketSize		= __constant_cpu_to_le16(8),
	.bInterval		= 0xa,
};

static struct usb_interface_descriptor keyb_interface1 = {
	.bLength		= sizeof(keyb_interface1),
	.bDescriptorType	= USB_DT_INTERFACE,

	.bInterfaceNumber	= 1,
	.bAlternateSetting	= 0,
	.bNumEndpoints		= 1,
	.bInterfaceClass	= USB_CLASS_HID,
	.bInterfaceSubClass	= USB_SUB_HID_BOOT,
	.bInterfaceProtocol	= USB_PROT_HID_MOUSE,
	.iInterface		= 0,
};

static struct usb_class_hid_descriptor keyb_report1 = {
	.bLength		= sizeof(struct usb_class_hid_descriptor),
	.bDescriptorType	= USB_DT_HID,
	.bcdCDC			= 0x101,
	.bCountryCode		= 0,
	.bNumDescriptors	= 1,
	.bDescriptorType0	= USB_DT_HID_REPORT,
	.wDescriptorLength0	= 0x32,
};

static struct usb_endpoint_descriptor keyb_endpoint1_in = {
	.bLength		= USB_DT_ENDPOINT_SIZE,
	.bDescriptorType	= USB_DT_ENDPOINT,

	.bEndpointAddress	= SANDBOX_KEYB_EP_IN | USB_ENDPOINT_DIR_MASK,
	.bmAttributes		= USB_ENDPOINT_XFER_BULK |
					USB_ENDPOINT_XFER_ISOC,
	.wMaxPacketSize		= __constant_cpu_to_le16(8),
	.bInterval		= 0xa,
};

static void *keyb_desc_list[] = {
	&keyb_device_desc,
	&keyb_config0,
	&keyb_interface0,
	&keyb_report0,
	&keyb_endpoint0_in,
	&keyb_interface1,
	&keyb_report1,
	&keyb_endpoint1_in,
	NULL,
};

/**
 * sandbox_usb_keyb_add_string() - provide a USB scancode buffer
 *
 * @dev:	the keyboard emulation device
 * @scancode:	scancode buffer with USB_KBD_BOOT_REPORT_SIZE bytes
 */
int sandbox_usb_keyb_add_string(struct udevice *dev,
				const char scancode[USB_KBD_BOOT_REPORT_SIZE])
{
	struct sandbox_keyb_priv *priv = dev_get_priv(dev);
	int ret;

	ret = membuff_put(&priv->in, scancode, USB_KBD_BOOT_REPORT_SIZE);
	if (ret != USB_KBD_BOOT_REPORT_SIZE)
		return -ENOSPC;

	return 0;
}

static int sandbox_keyb_control(struct udevice *dev, struct usb_device *udev,
				unsigned long pipe, void *buff, int len,
				struct devrequest *setup)
{
	debug("pipe=%lx\n", pipe);

	return -EIO;
}

static int sandbox_keyb_interrupt(struct udevice *dev, struct usb_device *udev,
		unsigned long pipe, void *buffer, int length, int interval,
		bool nonblock)
{
	struct sandbox_keyb_priv *priv = dev_get_priv(dev);
	uint8_t *data = buffer;

	memset(data, '\0', length);
	if (length < USB_KBD_BOOT_REPORT_SIZE)
		return 0;

	membuff_get(&priv->in, buffer, USB_KBD_BOOT_REPORT_SIZE);

	return 0;
}

static int sandbox_keyb_bind(struct udevice *dev)
{
	struct sandbox_keyb_plat *plat = dev_get_platdata(dev);
	struct usb_string *fs;

	fs = plat->keyb_strings;
	fs[0].id = STRINGID_MANUFACTURER;
	fs[0].s = "sandbox";
	fs[1].id = STRINGID_PRODUCT;
	fs[1].s = "keyboard";
	fs[2].id = STRINGID_SERIAL;
	fs[2].s = dev->name;

	return usb_emul_setup_device(dev, plat->keyb_strings, keyb_desc_list);
}

static int sandbox_keyb_probe(struct udevice *dev)
{
	struct sandbox_keyb_priv *priv = dev_get_priv(dev);

	/* Provide an 80 character keyboard buffer */
	return membuff_new(&priv->in, 80 * USB_KBD_BOOT_REPORT_SIZE);
}

static const struct dm_usb_ops sandbox_usb_keyb_ops = {
	.control	= sandbox_keyb_control,
	.interrupt	= sandbox_keyb_interrupt,
};

static const struct udevice_id sandbox_usb_keyb_ids[] = {
	{ .compatible = "sandbox,usb-keyb" },
	{ }
};

U_BOOT_DRIVER(usb_sandbox_keyb) = {
	.name	= "usb_sandbox_keyb",
	.id	= UCLASS_USB_EMUL,
	.of_match = sandbox_usb_keyb_ids,
	.bind	= sandbox_keyb_bind,
	.probe	= sandbox_keyb_probe,
	.ops	= &sandbox_usb_keyb_ops,
	.priv_auto_alloc_size = sizeof(struct sandbox_keyb_priv),
	.platdata_auto_alloc_size = sizeof(struct sandbox_keyb_plat),
};