blob: 834a8913089637e8ad3b4ef57874494c2e02fab6 [file] [log] [blame]
Patrick Georgi1bd30502015-01-26 20:17:49 +01001/*
2 * This file is part of the libpayload project.
3 *
4 * Copyright (C) 2015 Google Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <libpayload.h>
31#include <arch/cache.h>
32#include <assert.h>
33#include <endian.h>
34#include <queue.h>
35#include <stdio.h>
36#include <stdint.h>
37#include <stdlib.h>
38#include <usb/usb.h>
39
40#include <udc/udc.h>
41
42#ifdef DEBUG
43#define debug(x...) printf(x)
44#else
45#define debug(x...) do {} while (0)
46#endif
47
48#define min(a, b) (((a) < (b)) ? (a) : (b))
49
Patrick Georgif4227c42015-06-18 00:15:00 +020050static unsigned short strings_lang_id = 0;
51static unsigned char strings_count = 0;
52static const char **strings;
53
54void udc_add_strings(unsigned short lang_id, unsigned char count,
55 const char **str)
56{
57 strings_lang_id = lang_id;
58 strings_count = count;
59 strings = str;
60}
61
Patrick Georgiea0bdf22015-02-16 17:00:59 +010062/* determine if an additional zero length packet is necessary for
63 * a transfer */
64static unsigned int zlp(struct usbdev_ctrl *this, const int epnum,
65 const int len, const int explen)
66{
67 const unsigned int mps = this->ep_mps[epnum][1];
68
69 /* zero length transfers are handled explicitly */
70 if (len == 0)
71 return 0;
72 /* host expects exactly the right amount, so no zlp necessary */
73 if (len == explen)
74 return 0;
75 /* last packet will be short -> host knows that transfer is over */
76 if ((len % mps) != 0)
77 return 0;
78
79 /* otherwise we need an extra zero length packet */
80 return 1;
81}
Patrick Georgi1bd30502015-01-26 20:17:49 +010082
83static struct usbdev_configuration *fetch_config(struct usbdev_ctrl *this,
84 int id)
85{
86 struct usbdev_configuration *config;
87 SLIST_FOREACH(config, &this->configs, list) {
88 debug("checking descriptor %d\n",
89 config->descriptor.bConfigurationValue);
90 if (config->descriptor.bConfigurationValue == id)
91 return config;
92 }
93 return NULL;
94}
95
Patrick Georgibd6901e2015-02-16 17:00:59 +010096static void cease_operation(struct usbdev_ctrl *this)
Patrick Georgi1bd30502015-01-26 20:17:49 +010097{
Patrick Georgi1bd30502015-01-26 20:17:49 +010098 int i;
99 for (i = 1; i < 16; i++) {
100 /* disable endpoints */
101 this->halt_ep(this, i, 0);
102 this->halt_ep(this, i, 1);
103 }
104
Patrick Georgibd6901e2015-02-16 17:00:59 +0100105}
106
107static void enable_interface(struct usbdev_ctrl *this, int iface_num)
108{
109 struct usbdev_configuration *config = this->current_config;
110 struct usbdev_interface *iface = &config->interfaces[iface_num];
111
112 /* first: shut down all endpoints except EP0 */
113 cease_operation(this);
114
Patrick Georgi1bd30502015-01-26 20:17:49 +0100115 /* now enable all configured endpoints */
116 int epcount = iface->descriptor.bNumEndpoints;
Patrick Georgibd6901e2015-02-16 17:00:59 +0100117 int i;
Patrick Georgi1bd30502015-01-26 20:17:49 +0100118 for (i = 0; i < epcount; i++) {
119 int ep = iface->eps[i].bEndpointAddress;
120 int mps = iface->eps[i].wMaxPacketSize;
121 int in_dir = 0;
122 if (ep & 0x80) {
123 in_dir = 1;
124 ep &= 0x7f;
125 }
126 int ep_type = iface->eps[i].bmAttributes & 0x3;
127 this->start_ep(this, ep, in_dir, ep_type, mps);
128 }
129
Patrick Georgiea0bdf22015-02-16 17:00:59 +0100130 this->current_iface = iface;
131
Patrick Georgi1bd30502015-01-26 20:17:49 +0100132 // gadget specific configuration
133 if (iface->init)
134 iface->init(this);
135}
136
137/**
138 * handle default control transfers on EP 0
139 *
140 * returns 1 if transfer was handled
141 */
142static int setup_ep0(struct usbdev_ctrl *this, dev_req_t *dr)
143{
144 if ((dr->bmRequestType == 0x00) &&
145 (dr->bRequest == SET_ADDRESS)) {
146 this->set_address(this, dr->wValue & 0x7f);
147
148 /* status phase IN */
149 this->enqueue_packet(this, 0, 1, NULL, 0, 0, 0);
150 return 1;
151 } else
152 if ((dr->bmRequestType == 0x00) &&
153 (dr->bRequest == SET_CONFIGURATION)) {
154 struct usbdev_configuration *config =
155 fetch_config(this, dr->wValue);
156
Patrick Georgi1bd30502015-01-26 20:17:49 +0100157 if (dr->wValue == 0)
Patrick Georgibd6901e2015-02-16 17:00:59 +0100158 cease_operation(this);
Patrick Georgi1bd30502015-01-26 20:17:49 +0100159
160 if (config == NULL) {
161 this->stall(this, 0, 0, 1);
162 this->stall(this, 0, 1, 1);
163 return 1;
164 }
165
166 /* status phase IN */
167 this->enqueue_packet(this, 0, 1, NULL, 0, 0, 0);
168
Patrick Georgidc83d352015-03-10 12:57:11 +0100169 this->current_config = config;
170 this->current_config_id = dr->wValue;
171
172 /* activate first interface */
Patrick Georgi1bd30502015-01-26 20:17:49 +0100173 enable_interface(this, 0);
174 this->initialized = 1;
175 return 1;
176 } else
177 if ((dr->bmRequestType == 0x80) &&
178 (dr->bRequest == GET_CONFIGURATION)) {
179 unsigned char *res = dma_malloc(1);
180 res[0] = this->current_config_id;
181
182 /* data phase IN */
Patrick Georgi3c6e5db2015-03-10 12:58:55 +0100183 this->enqueue_packet(this, 0, 1, res, min(1, dr->wLength),
184 0, 1);
Patrick Georgi1bd30502015-01-26 20:17:49 +0100185
Patrick Georgi3c6e5db2015-03-10 12:58:55 +0100186 /* status phase OUT */
Patrick Georgi1bd30502015-01-26 20:17:49 +0100187 this->enqueue_packet(this, 0, 0, NULL, 0, 0, 0);
188 return 1;
189 } else
190 // ENDPOINT_HALT
191 if ((dr->bmRequestType == 0x02) && // endpoint
192 (dr->bRequest == CLEAR_FEATURE) &&
193 (dr->wValue == 0)) {
194 int ep = dr->wIndex;
195 /* clear STALL */
196 this->stall(this, ep & 0xf, ep & 0x80, 0);
197
198 /* status phase IN */
199 this->enqueue_packet(this, 0, 1, NULL, 0, 0, 0);
200 return 1;
201 } else
202 // ENDPOINT_HALT
203 if ((dr->bmRequestType == 0x02) && // endpoint
204 (dr->bRequest == SET_FEATURE) &&
205 (dr->wValue == 0)) {
206 int ep = dr->wIndex;
207 /* set STALL */
208 this->stall(this, ep & 0xf, ep & 0x80, 1);
209
210 /* status phase IN */
211 this->enqueue_packet(this, 0, 1, NULL, 0, 0, 0);
212 return 1;
213 } else
214 // DEVICE_REMOTE_WAKEUP
215 if ((dr->bmRequestType == 0x00) &&
216 (dr->bRequest == CLEAR_FEATURE) &&
217 (dr->wValue == 1)) {
218 this->remote_wakeup = 0;
219
220 /* status phase IN */
221 this->enqueue_packet(this, 0, 1, NULL, 0, 0, 0);
222 return 1;
223 } else
224 // DEVICE_REMOTE_WAKEUP
225 if ((dr->bmRequestType == 0x00) &&
226 (dr->bRequest == SET_FEATURE) &&
227 (dr->wValue == 1)) {
228 this->remote_wakeup = 1;
229
230 /* status phase IN */
231 this->enqueue_packet(this, 0, 1, NULL, 0, 0, 0);
232 return 1;
233 } else
234 if ((dr->bmRequestType == 0x82) && // endpoint
235 (dr->bRequest == GET_STATUS)) {
236 unsigned char *res = dma_malloc(2);
237 int ep = dr->wIndex;
238 /* is EP halted? */
239 res[0] = this->ep_halted[ep & 0xf][(ep & 0x80) ? 1 : 0];
240 res[1] = 0;
241
242 /* data phase IN */
243 this->enqueue_packet(this, 0, 1, res,
244 min(2, dr->wLength), 0, 1);
245
246 // status phase OUT
247 this->enqueue_packet(this, 0, 0, NULL, 0, 0, 0);
248 return 1;
249 } else
250 if ((dr->bmRequestType == 0x80) &&
251 (dr->bRequest == GET_STATUS)) {
252 unsigned char *res = dma_malloc(2);
253 res[0] = 1; // self powered
254 if (this->remote_wakeup)
255 res[0] |= 2;
256
257 res[1] = 0;
258
259 /* data phase IN */
260 this->enqueue_packet(this, 0, 1, res,
261 min(2, dr->wLength), 0, 1);
262
263 // status phase OUT
264 this->enqueue_packet(this, 0, 0, NULL, 0, 0, 0);
265 return 1;
266 } else
267 if ((dr->bmRequestType == 0x80) &&
268 (dr->bRequest == GET_DESCRIPTOR) &&
269 ((dr->wValue & 0xff00) == 0x0200)) {
270 int i, j;
271 /* config descriptor #id
272 * since config = 0 is undefined, but descriptors
273 * should start at 0, add 1 to have them match up.
274 */
275 int id = (dr->wValue & 0xff) + 1;
276 struct usbdev_configuration *config = fetch_config(this, id);
277 if (config == NULL) {
278 this->stall(this, 0, 0, 1);
279 this->stall(this, 0, 1, 1);
280 return 1;
281 }
282 debug("descriptor found, should be %d bytes\n",
283 config->descriptor.wTotalLength);
284
285 uint8_t *data = dma_malloc(config->descriptor.wTotalLength);
286 uint8_t *head = data;
287
288 memcpy(head, &config->descriptor,
289 sizeof(configuration_descriptor_t));
290 head += sizeof(configuration_descriptor_t);
291
292 for (i = 0; i < config->descriptor.bNumInterfaces; i++) {
293 memcpy(head, &config->interfaces[i].descriptor,
294 sizeof(interface_descriptor_t));
295 head += sizeof(interface_descriptor_t);
296 for (j = 0;
297 j < config->interfaces[i].descriptor.bNumEndpoints;
298 j++) {
299 memcpy(head, &config->interfaces[i].eps[j],
300 sizeof(endpoint_descriptor_t));
301 head += sizeof(endpoint_descriptor_t);
302 }
303 }
304 int size = config->descriptor.wTotalLength;
305 assert((head - data) == config->descriptor.wTotalLength);
306
307 /* data phase IN */
308 this->enqueue_packet(this, 0, 1, data,
309 min(size, dr->wLength),
Patrick Georgiea0bdf22015-02-16 17:00:59 +0100310 zlp(this, 0, size, dr->wLength), 1);
Patrick Georgi1bd30502015-01-26 20:17:49 +0100311
312 /* status phase OUT */
313 this->enqueue_packet(this, 0, 0, NULL, 0, 0, 0);
314 return 1;
315 } else
316 if ((dr->bmRequestType == 0x80) &&
317 (dr->bRequest == GET_DESCRIPTOR) &&
Patrick Georgif4227c42015-06-18 00:15:00 +0200318 ((dr->wValue & 0xff00) == 0x0300)) {
319 int id = (dr->wValue & 0xff);
320 if (id == 0) {
321 if (strings_lang_id == 0)
322 return 0;
323
324 uint8_t *data = dma_malloc(4);
325 data[0] = 0x04; // length
326 data[1] = 0x03; // string descriptor
327 data[2] = strings_lang_id & 0xff;
328 data[3] = strings_lang_id >> 8;
329 /* data phase IN */
330 this->enqueue_packet(this, 0, 1,
331 data,
332 min(data[0], dr->wLength),
333 zlp(this, 0, data[0], dr->wLength),
334 1);
335
336 /* status phase OUT */
337 this->enqueue_packet(this, 0, 0, NULL, 0, 0, 0);
338 } else {
339 if (strings_lang_id == 0)
340 return 0;
341
342 int lang = dr->wIndex;
343 if (lang != strings_lang_id)
344 return 0;
345
346 if (id > strings_count)
347 return 0;
348
349 int s_len = strlen(strings[id]);
350 int d_len = s_len * 2;
351
352 uint8_t *data = dma_malloc(d_len + 2);
Furquan Shaikha4b718c2015-06-23 12:33:53 -0700353 memset(data, 0, d_len + 2);
Patrick Georgif4227c42015-06-18 00:15:00 +0200354 data[0] = d_len + 2; // length
355 data[1] = 0x03; // string descriptor
356 int i;
357 for (i = 0; i < s_len; i++)
358 data[i * 2 + 2] = strings[id][i];
359
360 /* data phase IN */
361 this->enqueue_packet(this, 0, 1,
362 data,
363 min(d_len + 2, dr->wLength),
364 zlp(this, 0, d_len + 2, dr->wLength),
365 1);
366
367 /* status phase OUT */
368 this->enqueue_packet(this, 0, 0, NULL, 0, 0, 0);
369 }
370 return 1;
371 } else
372 if ((dr->bmRequestType == 0x80) &&
373 (dr->bRequest == GET_DESCRIPTOR) &&
Patrick Georgi1bd30502015-01-26 20:17:49 +0100374 ((dr->wValue & 0xff00) == 0x0100)) {
375 device_descriptor_t *dd = dma_malloc(sizeof(*dd));
376 memcpy(dd, &this->device_descriptor, sizeof(*dd));
377 dd->bNumConfigurations = this->config_count;
378
379 /* data phase IN */
380 this->enqueue_packet(this, 0, 1, (void *)dd,
381 min(sizeof(*dd), dr->wLength),
Patrick Georgiea0bdf22015-02-16 17:00:59 +0100382 zlp(this, 0, sizeof(*dd), dr->wLength), 1);
Patrick Georgi1bd30502015-01-26 20:17:49 +0100383
384 /* status phase OUT */
385 this->enqueue_packet(this, 0, 0, NULL, 0, 0, 0);
386 return 1;
387 }
388 return 0;
389}
390
391void udc_add_gadget(struct usbdev_ctrl *this,
392 struct usbdev_configuration *config)
393{
394 int i, size;
395 SLIST_INSERT_HEAD(&this->configs, config, list);
396
397 size = sizeof(configuration_descriptor_t);
398
399 for (i = 0; i < config->descriptor.bNumInterfaces; i++) {
400 size += sizeof(config->interfaces[i].descriptor);
401 size += config->interfaces[i].descriptor.bNumEndpoints *
402 sizeof(endpoint_descriptor_t);
403 }
404 config->descriptor.wTotalLength = size;
405 config->descriptor.bConfigurationValue = ++this->config_count;
406}
407
408void udc_handle_setup(struct usbdev_ctrl *this, int ep, dev_req_t *dr)
409{
410 if ((ep == 0) && setup_ep0(this, dr))
411 return;
412
413 if (this->current_config &&
414 this->current_config->interfaces[0].handle_setup &&
415 this->current_config->interfaces[0].handle_setup(this, ep, dr))
416 return;
417
418 /* no successful SETUP transfer should end up here, report error */
419 this->halt_ep(this, ep, 0);
420 this->halt_ep(this, ep, 1);
421}