blob: 5c8948ad84f830693d715279085d091e2c48a90d [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2008-2015 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <debug.h>
24#include <trace.h>
25#include <err.h>
26#include <stdlib.h>
27#include <string.h>
28#include <assert.h>
29#include <list.h>
30#include <dev/usbc.h>
31#include <dev/usb.h>
32#include <lk/init.h>
33
34#define LOCAL_TRACE 0
35
36#define MAX_STRINGS 8
37static struct {
38 bool active;
39 uint8_t active_config;
40
41 usb_config *config;
42
43 struct list_node cb_list;
44
45 usb_string strings[MAX_STRINGS];
46} usb;
47
48typedef struct {
49 struct list_node node;
50 usb_callback_t cb;
51 void *cookie;
52} usb_callback_container_t;
53
54static void usb_do_callbacks(usb_callback_op_t op, const union usb_callback_args *args);
55
56static void append_desc_data(usb_descriptor *desc, const void *dat, size_t len)
57{
58 uint8_t *ptr = malloc(desc->len + len);
59
60 memcpy(ptr, desc->desc, desc->len);
61 memcpy(ptr + desc->len, dat, len);
62
63 /* free the old buffer if it wasn't marked static */
64 if ((desc->flags & USB_DESC_FLAG_STATIC) == 0)
65 free(desc->desc);
66 desc->flags &= ~USB_DESC_FLAG_STATIC;
67
68 desc->desc = ptr;
69 desc->len += len;
70}
71
72/* returns the interface number assigned */
73static int usb_append_interface(usb_descriptor *desc, const uint8_t *int_descr, size_t len)
74{
75 uint8_t *ptr = malloc(len);
76 int interface_num;
77
78 // create a temporary copy of the interface
79 memcpy(ptr, int_descr, len);
80
81 // find the last interface used
82 interface_num = ((uint8_t *)desc->desc)[4]; // current interface
83
84 // patch our interface descriptor with the new id
85 ptr[2] = interface_num;
86
87 // append it to our config desriptor
88 append_desc_data(desc, ptr, len);
89 free(ptr);
90
91 // patch the total length of the config descriptor and set the number of interfaces
92 ((uint16_t *)desc->desc)[1] += len;
93 interface_num++;
94 ((uint8_t *)desc->desc)[4] = interface_num;
95
96 return interface_num - 1;
97}
98
99int usb_append_interface_highspeed(const uint8_t *int_descr, size_t len)
100{
101 return usb_append_interface(&usb.config->highspeed.config, int_descr, len);
102}
103
104int usb_append_interface_lowspeed(const uint8_t *int_descr, size_t len)
105{
106 return usb_append_interface(&usb.config->lowspeed.config, int_descr, len);
107}
108
109void usb_set_string_descriptor(usb_descriptor *desc, const char *string)
110{
111 int len = strlen(string);
112 ushort *data;
113 int datalen = len * 2 + 2;
114
115 data = malloc(datalen);
116
117 /* write length field */
118 data[0] = 0x0300 + datalen;
119
120 /* copy the string into the uint16_t based usb string */
121 int i;
122 for (i = 0; i < len; i++) {
123 data[i + 1] = string[i];
124 }
125
126 desc->desc = (void *)data;
127 desc->len = datalen;
128}
129
130static void set_usb_id(uint16_t vendor, uint16_t product)
131{
132 // patch the current configuration to with the vendor/product id
133 ((uint16_t *)usb.config->lowspeed.device.desc)[4] = vendor;
134 ((uint16_t *)usb.config->lowspeed.device.desc)[5] = product;
135
136 ((uint16_t *)usb.config->highspeed.device.desc)[4] = vendor;
137 ((uint16_t *)usb.config->highspeed.device.desc)[5] = product;
138}
139
140status_t usb_add_string(const char *string, uint8_t id)
141{
142 uint i;
143 size_t len = strlen(string);
144
145 uint16_t *strbuf = malloc(len * 2 + 2);
146 if (!strbuf)
147 return ERR_NO_MEMORY;
148
149 /* build the usb string descriptor */
150 strbuf[0] = 0x300 | (len * 2 + 2);
151 for (i = 0; i < len; i++) {
152 strbuf[i + 1] = (uint16_t)string[i];
153 }
154
155 /* find a slot to put it */
156 for (i = 0; i < MAX_STRINGS; i++) {
157 if (usb.strings[i].id == 0) {
158 usb.strings[i].string.desc = strbuf;
159 usb.strings[i].string.len = len * 2 + 2;
160 usb.strings[i].id = id;
161 return NO_ERROR;
162 }
163 }
164
165 /* couldn't find a spot */
166 free(strbuf);
167 return ERR_NO_MEMORY;
168}
169
170static void usb_set_active_config(uint8_t config)
171{
172 if (config != usb.active_config) {
173 usb.active_config = config;
174 if (usb.active_config != 0) {
175 printf("usb online\n");
176 usb_do_callbacks(USB_CB_ONLINE, NULL);
177 } else {
178 printf("usb offline\n");
179 usb_do_callbacks(USB_CB_OFFLINE, NULL);
180 }
181 }
182}
183
184status_t usb_register_callback(usb_callback_t cb, void *cookie)
185{
186 DEBUG_ASSERT(cb);
187
188 usb_callback_container_t *c = malloc(sizeof(usb_callback_container_t));
189 if (!c)
190 return ERR_NO_MEMORY;
191
192 c->cb = cb;
193 c->cookie = cookie;
194 list_add_tail(&usb.cb_list, &c->node);
195
196 return NO_ERROR;
197}
198
199static void usb_do_callbacks(usb_callback_op_t op, const union usb_callback_args *args)
200{
201 usb_callback_container_t *c;
202 list_for_every_entry(&usb.cb_list, c, usb_callback_container_t, node) {
203 c->cb(c->cookie, op, args);
204 }
205}
206
207status_t usbc_callback(usb_callback_op_t op, const union usb_callback_args *args)
208{
209 LTRACEF("op %d, args %p\n", op, args);
210
211 /* start looking for specific things to handle */
212 if (op == USB_CB_SETUP_MSG) {
213 bool setup_handled = false;
214 const struct usb_setup *setup = args->setup;
215 DEBUG_ASSERT(setup);
216 LTRACEF("SETUP: req_type=%#x req=%#x value=%#x index=%#x len=%#x\n",
217 setup->request_type, setup->request, setup->value, setup->index, setup->length);
218
219 if ((setup->request_type & TYPE_MASK) == TYPE_STANDARD) {
220 switch (setup->request) {
221 case SET_ADDRESS:
222 LTRACEF("SET_ADDRESS 0x%x\n", setup->value);
223 usbc_ep0_ack();
224 usbc_set_address(setup->value);
225 setup_handled = true;
226 break;
227 case SET_FEATURE:
228 case CLEAR_FEATURE:
229 LTRACEF("SET/CLEAR_FEATURE, feature 0x%x\n", setup->value);
230 usbc_ep0_ack();
231 setup_handled = true;
232 break;
233 case SET_DESCRIPTOR:
234 LTRACEF("SET_DESCRIPTOR\n");
235 usbc_ep0_stall();
236 setup_handled = true;
237 break;
238 case GET_DESCRIPTOR: {
239 if ((setup->request_type & RECIP_MASK) == RECIP_DEVICE) {
240 /* handle device descriptor fetches */
241
242 /* Get the right descriptors based on current speed */
243 const struct usb_descriptor_speed *speed;
244 if (usbc_is_highspeed()) {
245 speed = &usb.config->highspeed;
246 } else {
247 speed = &usb.config->lowspeed;
248 }
249
250 switch (setup->value) {
251 case 0x100: /* device */
252 LTRACEF("got GET_DESCRIPTOR, device descriptor\n");
253 usbc_ep0_send(speed->device.desc, speed->device.len,
254 setup->length);
255 break;
256 case 0x200: /* CONFIGURATION */
257 LTRACEF("got GET_DESCRIPTOR, config descriptor\n");
258 usbc_ep0_send(speed->config.desc, speed->config.len,
259 setup->length);
260 break;
261 case 0x300: /* Language ID */
262 LTRACEF("got GET_DESCRIPTOR, language id\n");
263 usbc_ep0_send(usb.config->langid.desc,
264 usb.config->langid.len, setup->length);
265 break;
266 case (0x301)...(0x3ff): {
267 /* string descriptor, search our list for a match */
268 uint i;
269 bool found = false;
270 uint8_t id = setup->value & 0xff;
271 for (i = 0; i < MAX_STRINGS; i++) {
272 if (usb.strings[i].id == id) {
273 usbc_ep0_send(usb.strings[i].string.desc,
274 usb.strings[i].string.len,
275 setup->length);
276 found = true;
277 break;
278 }
279 }
280 if (!found) {
281 /* couldn't find one, stall */
282 usbc_ep0_stall();
283 }
284 break;
285 }
286 case 0x600: /* DEVICE QUALIFIER */
287 LTRACEF("got GET_DESCRIPTOR, device qualifier\n");
288 usbc_ep0_send(speed->device_qual.desc,
289 speed->device_qual.len, setup->length);
290 break;
291 case 0xa00:
292 /* we aint got one of these */
293 LTRACEF("got GET_DESCRIPTOR, debug descriptor\n");
294 usbc_ep0_stall();
295 break;
296 default:
297 LTRACEF("unhandled descriptor %#x\n", setup->value);
298 // stall
299 break;
300 }
301 setup_handled = true;
302 }
303 break;
304 }
305
306 case SET_CONFIGURATION:
307 LTRACEF("SET_CONFIGURATION %d\n", setup->value);
308 usbc_ep0_ack();
309 usb_set_active_config(setup->value);
310 break;
311
312 case GET_CONFIGURATION:
313 LTRACEF("GET_CONFIGURATION\n");
314 usbc_ep0_send(&usb.active_config, 1, setup->length);
315 break;
316
317 case SET_INTERFACE:
318 LTRACEF("SET_INTERFACE %d\n", setup->value);
319 usbc_ep0_ack();
320 break;
321
322 case GET_INTERFACE: {
323 static uint8_t i = 1;
324 LTRACEF("GET_INTERFACE\n");
325 usbc_ep0_send(&i, 1, setup->length);
326 break;
327 }
328
329 case GET_STATUS: {
330 static uint16_t i = 1; // self powered
331 LTRACEF("GET_STATUS\n");
332 usbc_ep0_send(&i, 2, setup->length);
333 break;
334 }
335 default:
336 LTRACEF("unhandled standard request 0x%x\n", setup->request);
337 }
338 } else {
339 LTRACEF("unhandled nonstandard request 0x%x\n", setup->request);
340 }
341
342 if (!setup_handled) {
343 usb_do_callbacks(op, args);
344 }
345 } else if (op == USB_CB_RESET) {
346 usb_do_callbacks(op, args);
347
348 usb.active_config = 0;
349 usb_do_callbacks(USB_CB_OFFLINE, args);
350 } else {
351 // other non setup messages, pass them down to anyone else
352 usb_do_callbacks(op, args);
353 }
354
355 return NO_ERROR;
356}
357
358status_t usb_setup(usb_config *config)
359{
360 DEBUG_ASSERT(config);
361 DEBUG_ASSERT(usb.active == false);
362
363 usb.config = config;
364
365 return NO_ERROR;
366}
367
368status_t usb_start(void)
369{
370 DEBUG_ASSERT(usb.config);
371 DEBUG_ASSERT(usb.active == false);
372
373 // go online
374 usbc_set_active(true);
375 usb.active = true;
376
377 return NO_ERROR;
378}
379
380status_t usb_stop(void)
381{
382 DEBUG_ASSERT(usb.active == true);
383
384 usb.active = false;
385 usbc_set_active(false);
386
387 return NO_ERROR;
388}
389
390static void usb_init(uint level)
391{
392 list_initialize(&usb.cb_list);
393}
394
395LK_INIT_HOOK(usb, usb_init, LK_INIT_LEVEL_THREADING);