1 /*
2 * Copyright (c) 2008-2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <dev/usb.h>
9
10 #include <lk/debug.h>
11 #include <lk/trace.h>
12 #include <lk/err.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <assert.h>
16 #include <lk/list.h>
17 #include <dev/usbc.h>
18 #include <lk/init.h>
19
20 /*
21 * USB device side support. Handles configuration and main high level host requests.
22 * USB device controller must pump the stack via usbc_callback() and implement the
23 * usbc function interface.
24 */
25
26 /* TODO: make big endian safe */
27
28 #define LOCAL_TRACE 0
29
30 #define MAX_STRINGS 8
31 static struct {
32 bool active;
33 uint8_t active_config;
34
35 usb_config *config;
36
37 struct list_node cb_list;
38
39 usb_string strings[MAX_STRINGS];
40 } usb;
41
42 typedef struct {
43 struct list_node node;
44 usb_callback_t cb;
45 void *cookie;
46 } usb_callback_container_t;
47
48 static void usb_do_callbacks(usb_callback_op_t op, const union usb_callback_args *args);
49
append_desc_data(usb_descriptor * desc,const void * dat,size_t len)50 static status_t append_desc_data(usb_descriptor *desc, const void *dat, size_t len) {
51 uint8_t *ptr = malloc(desc->len + len);
52 if (!ptr) {
53 return ERR_NO_MEMORY;
54 }
55
56 memcpy(ptr, desc->desc, desc->len);
57 memcpy(ptr + desc->len, dat, len);
58
59 /* free the old buffer if it wasn't marked static */
60 if ((desc->flags & USB_DESC_FLAG_STATIC) == 0)
61 free(desc->desc);
62 desc->flags &= ~USB_DESC_FLAG_STATIC;
63
64 desc->desc = ptr;
65 desc->len += len;
66
67 return NO_ERROR;
68 }
69
usb_get_current_iface_num(const usb_descriptor * desc)70 static uint8_t usb_get_current_iface_num(const usb_descriptor *desc) {
71 DEBUG_ASSERT(desc);
72
73 return ((uint8_t *)desc->desc)[4];
74 }
75
usb_get_current_iface_num_highspeed(void)76 uint8_t usb_get_current_iface_num_highspeed(void) {
77 return usb_get_current_iface_num(&usb.config->highspeed.config);
78 }
79
usb_get_current_iface_num_lowspeed(void)80 uint8_t usb_get_current_iface_num_lowspeed(void) {
81 return usb_get_current_iface_num(&usb.config->lowspeed.config);
82 }
83
84 /* returns the interface number assigned or error */
usb_append_interface(usb_descriptor * desc,const uint8_t * int_descr,size_t len)85 static int usb_append_interface(usb_descriptor *desc, const uint8_t *int_descr, size_t len) {
86 uint8_t *ptr = malloc(len);
87 if (!ptr) {
88 return ERR_NO_MEMORY;
89 }
90
91 // create a temporary copy of the interface
92 memcpy(ptr, int_descr, len);
93
94 // find the last interface used
95 int interface_num = usb_get_current_iface_num(desc); // current interface
96
97 // patch our interface descriptor with the new id
98 ptr[2] = interface_num;
99
100 // append it to our config descriptor
101 append_desc_data(desc, ptr, len);
102 free(ptr);
103
104 // patch the total length of the config descriptor and set the number of interfaces
105 ((uint16_t *)desc->desc)[1] += len;
106 interface_num++;
107 ((uint8_t *)desc->desc)[4] = interface_num;
108
109 DEBUG_ASSERT(interface_num > 0);
110 return interface_num - 1;
111 }
112
usb_append_interface_highspeed(const uint8_t * int_descr,size_t len)113 int usb_append_interface_highspeed(const uint8_t *int_descr, size_t len) {
114 return usb_append_interface(&usb.config->highspeed.config, int_descr, len);
115 }
116
usb_append_interface_lowspeed(const uint8_t * int_descr,size_t len)117 int usb_append_interface_lowspeed(const uint8_t *int_descr, size_t len) {
118 return usb_append_interface(&usb.config->lowspeed.config, int_descr, len);
119 }
120
set_usb_id(uint16_t vendor,uint16_t product)121 static void set_usb_id(uint16_t vendor, uint16_t product) {
122 // patch the current configuration to with the vendor/product id
123 ((uint16_t *)usb.config->lowspeed.device.desc)[4] = vendor;
124 ((uint16_t *)usb.config->lowspeed.device.desc)[5] = product;
125
126 ((uint16_t *)usb.config->highspeed.device.desc)[4] = vendor;
127 ((uint16_t *)usb.config->highspeed.device.desc)[5] = product;
128 }
129
usb_add_string(const char * string,uint8_t id)130 status_t usb_add_string(const char *string, uint8_t id) {
131 uint i;
132 size_t len = strlen(string);
133
134 uint16_t *strbuf = malloc(len * 2 + 2);
135 if (!strbuf) {
136 return ERR_NO_MEMORY;
137 }
138
139 /* build the usb string descriptor */
140 strbuf[0] = 0x300 | (len * 2 + 2);
141 for (i = 0; i < len; i++) {
142 strbuf[i + 1] = (uint16_t)string[i];
143 }
144
145 /* find a slot to put it */
146 for (i = 0; i < MAX_STRINGS; i++) {
147 if (usb.strings[i].id == 0) {
148 usb.strings[i].string.desc = strbuf;
149 usb.strings[i].string.len = len * 2 + 2;
150 usb.strings[i].id = id;
151 return NO_ERROR;
152 }
153 }
154
155 /* couldn't find a spot */
156 free(strbuf);
157 return ERR_NO_MEMORY;
158 }
159
usb_set_active_config(uint8_t config)160 static void usb_set_active_config(uint8_t config) {
161 if (config != usb.active_config) {
162 usb.active_config = config;
163 if (usb.active_config != 0) {
164 printf("usb online\n");
165 usb_do_callbacks(USB_CB_ONLINE, NULL);
166 } else {
167 printf("usb offline\n");
168 usb_do_callbacks(USB_CB_OFFLINE, NULL);
169 }
170 }
171 }
172
usb_register_callback(usb_callback_t cb,void * cookie)173 status_t usb_register_callback(usb_callback_t cb, void *cookie) {
174 DEBUG_ASSERT(cb);
175
176 usb_callback_container_t *c = malloc(sizeof(usb_callback_container_t));
177 if (!c) {
178 return ERR_NO_MEMORY;
179 }
180
181 c->cb = cb;
182 c->cookie = cookie;
183 list_add_tail(&usb.cb_list, &c->node);
184
185 return NO_ERROR;
186 }
187
usb_do_callbacks(usb_callback_op_t op,const union usb_callback_args * args)188 static void usb_do_callbacks(usb_callback_op_t op, const union usb_callback_args *args) {
189 usb_callback_container_t *c;
190 list_for_every_entry(&usb.cb_list, c, usb_callback_container_t, node) {
191 c->cb(c->cookie, op, args);
192 }
193 }
194
usbc_callback(usb_callback_op_t op,const union usb_callback_args * args)195 status_t usbc_callback(usb_callback_op_t op, const union usb_callback_args *args) {
196 LTRACEF("op %d, args %p\n", op, args);
197
198 /* start looking for specific things to handle */
199 if (op == USB_CB_SETUP_MSG) {
200 bool setup_handled = false;
201 const struct usb_setup *setup = args->setup;
202 DEBUG_ASSERT(setup);
203 LTRACEF("SETUP: req_type=%#x req=%#x value=%#x index=%#x len=%#x\n",
204 setup->request_type, setup->request, setup->value, setup->index, setup->length);
205
206 if ((setup->request_type & TYPE_MASK) == TYPE_STANDARD) {
207 switch (setup->request) {
208 case SET_ADDRESS:
209 LTRACEF("SET_ADDRESS 0x%x\n", setup->value);
210 usbc_ep0_ack();
211 usbc_set_address(setup->value);
212 setup_handled = true;
213 break;
214 case SET_FEATURE:
215 case CLEAR_FEATURE:
216 LTRACEF("SET/CLEAR_FEATURE, feature 0x%x\n", setup->value);
217 usbc_ep0_ack();
218 setup_handled = true;
219 break;
220 case SET_DESCRIPTOR:
221 LTRACEF("SET_DESCRIPTOR\n");
222 usbc_ep0_stall();
223 setup_handled = true;
224 break;
225 case GET_DESCRIPTOR: {
226 if ((setup->request_type & RECIP_MASK) == RECIP_DEVICE) {
227 /* handle device descriptor fetches */
228
229 /* Get the right descriptors based on current speed */
230 const struct usb_descriptor_speed *speed;
231 if (usbc_is_highspeed()) {
232 speed = &usb.config->highspeed;
233 } else {
234 speed = &usb.config->lowspeed;
235 }
236
237 switch (setup->value) {
238 case 0x100: /* device */
239 LTRACEF("got GET_DESCRIPTOR, device descriptor\n");
240 usbc_ep0_send(speed->device.desc, speed->device.len,
241 setup->length);
242 break;
243 case 0x200: /* CONFIGURATION */
244 LTRACEF("got GET_DESCRIPTOR, config descriptor\n");
245 usbc_ep0_send(speed->config.desc, speed->config.len,
246 setup->length);
247 break;
248 case 0x300: /* Language ID */
249 LTRACEF("got GET_DESCRIPTOR, language id\n");
250 usbc_ep0_send(usb.config->langid.desc,
251 usb.config->langid.len, setup->length);
252 break;
253 case (0x301)...(0x3ff): {
254 /* string descriptor, search our list for a match */
255 uint i;
256 bool found = false;
257 uint8_t id = setup->value & 0xff;
258 for (i = 0; i < MAX_STRINGS; i++) {
259 if (usb.strings[i].id == id) {
260 usbc_ep0_send(usb.strings[i].string.desc,
261 usb.strings[i].string.len,
262 setup->length);
263 found = true;
264 break;
265 }
266 }
267 if (!found) {
268 /* couldn't find one, stall */
269 usbc_ep0_stall();
270 }
271 break;
272 }
273 case 0x600: /* DEVICE QUALIFIER */
274 LTRACEF("got GET_DESCRIPTOR, device qualifier\n");
275 usbc_ep0_send(speed->device_qual.desc,
276 speed->device_qual.len, setup->length);
277 break;
278 case 0xa00:
279 /* we aint got one of these */
280 LTRACEF("got GET_DESCRIPTOR, debug descriptor\n");
281 usbc_ep0_stall();
282 break;
283 default:
284 LTRACEF("unhandled descriptor %#x\n", setup->value);
285 // stall
286 break;
287 }
288 setup_handled = true;
289 }
290 break;
291 }
292
293 case SET_CONFIGURATION:
294 LTRACEF("SET_CONFIGURATION %d\n", setup->value);
295 usbc_ep0_ack();
296 usb_set_active_config(setup->value);
297 break;
298
299 case GET_CONFIGURATION:
300 LTRACEF("GET_CONFIGURATION\n");
301 usbc_ep0_send(&usb.active_config, 1, setup->length);
302 break;
303
304 case SET_INTERFACE:
305 LTRACEF("SET_INTERFACE %d\n", setup->value);
306 usbc_ep0_ack();
307 break;
308
309 case GET_INTERFACE: {
310 static uint8_t i = 1;
311 LTRACEF("GET_INTERFACE\n");
312 usbc_ep0_send(&i, 1, setup->length);
313 break;
314 }
315
316 case GET_STATUS: {
317 static uint16_t i = 1; // self powered
318 LTRACEF("GET_STATUS\n");
319 usbc_ep0_send(&i, 2, setup->length);
320 break;
321 }
322 default:
323 LTRACEF("unhandled standard request 0x%x\n", setup->request);
324 }
325 }
326
327 if (!setup_handled) {
328 usb_do_callbacks(op, args);
329 }
330 } else if (op == USB_CB_RESET) {
331 usb_do_callbacks(op, args);
332
333 usb.active_config = 0;
334 usb_do_callbacks(USB_CB_OFFLINE, args);
335 } else {
336 // other non setup messages, pass them down to anyone else
337 usb_do_callbacks(op, args);
338 }
339
340 return NO_ERROR;
341 }
342
usb_setup(usb_config * config)343 status_t usb_setup(usb_config *config) {
344 DEBUG_ASSERT(config);
345 DEBUG_ASSERT(usb.active == false);
346
347 usb.config = config;
348
349 return NO_ERROR;
350 }
351
usb_start(void)352 status_t usb_start(void) {
353 DEBUG_ASSERT(usb.config);
354 DEBUG_ASSERT(usb.active == false);
355
356 // go online
357 usbc_set_active(true);
358 usb.active = true;
359
360 return NO_ERROR;
361 }
362
usb_stop(void)363 status_t usb_stop(void) {
364 DEBUG_ASSERT(usb.active == true);
365
366 usb.active = false;
367 usbc_set_active(false);
368
369 return NO_ERROR;
370 }
371
usb_init(uint level)372 static void usb_init(uint level) {
373 list_initialize(&usb.cb_list);
374 }
375
376 LK_INIT_HOOK(usb, usb_init, LK_INIT_LEVEL_THREADING);
377