blob: 1b1661d82899a361de387247c9995ed0c16c5ad2 [file] [log] [blame]
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -07001/*
2 * Copyright (c) 2008 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>
Travis Geiselbrecht89fcb142008-10-10 03:18:10 -070024#include <err.h>
25#include <stdlib.h>
26#include <string.h>
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -070027#include <dev/usbc.h>
Travis Geiselbrecht89fcb142008-10-10 03:18:10 -070028#include <dev/usb.h>
29
Travis Geiselbrecht345adb52009-01-01 05:25:50 -080030#define LOCAL_TRACE 0
Travis Geiselbrecht89fcb142008-10-10 03:18:10 -070031
Travis Geiselbrecht345adb52009-01-01 05:25:50 -080032#define MAX_STRINGS 8
33static usb_string strings[MAX_STRINGS];
Travis Geiselbrecht89fcb142008-10-10 03:18:10 -070034
35static usb_config *config;
36
37static uint8_t active_config;
38static bool usb_active = false;
39
40static void append_desc_data(usb_descriptor *desc, const void *dat, size_t len)
41{
42 uint8_t *ptr = malloc(desc->len + len);
43
44 memcpy(ptr, desc->desc, desc->len);
45 memcpy(ptr + desc->len, dat, len);
46 free(desc->desc);
47 desc->desc = ptr;
48 desc->len += len;
49}
50
51/* returns the interface number assigned */
52static int usb_append_interface(usb_descriptor *desc, const uint8_t *int_descr, size_t len)
53{
54 uint8_t *ptr = malloc(len);
55 int interface_num;
56
57 // create a temporary copy of the interface
58 memcpy(ptr, int_descr, len);
59
60 // find the last interface used
61 interface_num = ((uint8_t *)desc->desc)[4]; // current interface
62
63 // patch our interface descriptor with the new id
64 ptr[2] = interface_num;
65
66 // append it to our config desriptor
67 append_desc_data(desc, ptr, len);
68 free(ptr);
69
70 // patch the total length of the config descriptor and set the number of interfaces
71 ((uint16_t *)desc->desc)[1] += len;
72 interface_num++;
73 ((uint8_t *)desc->desc)[4] = interface_num;
74
75 return interface_num - 1;
76}
77
78int usb_append_interface_highspeed(const uint8_t *int_descr, size_t len)
79{
80 return usb_append_interface(&config->highspeed.config, int_descr, len);
81}
82
83int usb_append_interface_lowspeed(const uint8_t *int_descr, size_t len)
84{
85 return usb_append_interface(&config->lowspeed.config, int_descr, len);
86}
87
88void usb_set_string_descriptor(usb_descriptor *desc, const char *string)
89{
90 int len = strlen(string);
91 ushort *data;
92 int datalen = len * 2 + 2;
93
94 data = malloc(datalen);
95
96 /* write length field */
97 data[0] = 0x0300 + datalen;
98
99 /* copy the string into the uint16_t based usb string */
100 int i;
101 for (i = 0; i < len; i++) {
102 data[i + 1] = string[i];
103 }
104
105 desc->desc = (void *)data;
106 desc->len = datalen;
107}
108
109static void set_usb_id(uint16_t vendor, uint16_t product)
110{
111 // patch the current configuration to with the vendor/product id
112 ((uint16_t *)config->lowspeed.device.desc)[4] = vendor;
113 ((uint16_t *)config->lowspeed.device.desc)[5] = product;
114
115 ((uint16_t *)config->highspeed.device.desc)[4] = vendor;
116 ((uint16_t *)config->highspeed.device.desc)[5] = product;
117}
118
Travis Geiselbrecht345adb52009-01-01 05:25:50 -0800119void usb_add_string(const char *string, uint8_t id)
120{
121 uint i;
122 size_t len = strlen(string);
123 uint16_t *strbuf = malloc(len * 2 + 2);
124
125 /* build the usb string descriptor */
126 strbuf[0] = 0x300 | (len * 2 + 2);
127 for (i = 0; i < len; i++) {
128 strbuf[i + 1] = (uint16_t)string[i];
129 }
130
131 /* find a slot to put it */
132 for (i = 0; i < MAX_STRINGS; i++) {
133 if (strings[i].id == 0) {
134 strings[i].string.desc = strbuf;
135 strings[i].string.len = len * 2 + 2;
136 strings[i].id = id;
137 break;
138 }
139 }
140}
141
Travis Geiselbrecht89fcb142008-10-10 03:18:10 -0700142static int default_usb_callback(usbc_callback_op_t op, const union usb_callback_args *args)
143{
144 LTRACEF("op %d, args %p\n", op, args);
145
146 /* start looking for specific things to handle */
147 if (op == CB_SETUP_MSG) {
148 const struct usb_setup *setup = args->setup;
149 DEBUG_ASSERT(setup);
150 LTRACEF("SETUP: req_type=%#x req=%#x value=%#x index=%#x len=%#x\n", setup->request_type, setup->request, setup->value, setup->index, setup->length);
151
152 if ((setup->request_type & TYPE_MASK) == TYPE_STANDARD) {
153 switch (setup->request) {
154 case SET_ADDRESS:
155 LTRACEF("SET_ADDRESS 0x%x\n", setup->value);
156 usbc_ep0_ack();
157 break;
158 case SET_FEATURE:
159 case CLEAR_FEATURE:
160 // OTAY
161 LTRACEF("SET/CLEAR_FEATURE, feature 0x%x\n", setup->value);
162 usbc_ep0_ack();
163 break;
164 case SET_DESCRIPTOR:
165 LTRACEF("SET_DESCRIPTOR\n");
166 usbc_ep0_stall();
167 break;
168 case GET_DESCRIPTOR: {
169 /* Get the right descriptors based on current speed */
170 const struct usb_descriptor_speed *speed;
171 if (usbc_is_highspeed()) {
172 speed = &config->highspeed;
173 } else {
174 speed = &config->lowspeed;
175 }
176
177 if ((setup->request_type & RECIP_MASK) == RECIP_DEVICE) {
178 switch (setup->value) {
179 case 0x100: /* device */
180 LTRACEF("got GET_DESCRIPTOR, device descriptor\n");
181 usbc_ep0_send(speed->device.desc, speed->device.len,
182 setup->length);
183 break;
184 case 0x200: /* CONFIGURATION */
185 LTRACEF("got GET_DESCRIPTOR, config descriptor\n");
186 usbc_ep0_send(speed->config.desc, speed->config.len,
187 setup->length);
188 break;
189 case 0x300: /* Language ID */
190 LTRACEF("got GET_DESCRIPTOR, language id\n");
191 usbc_ep0_send(config->langid.desc,
192 config->langid.len, setup->length);
193 break;
Travis Geiselbrecht345adb52009-01-01 05:25:50 -0800194 case (0x301)...(0x3ff): {
195 /* string descriptor, search our list for a match */
196 uint i;
197 bool found = false;
198 uint8_t id = setup->value & 0xff;
199 for (i = 0; i < MAX_STRINGS; i++) {
200 if (strings[i].id == id) {
201 usbc_ep0_send(strings[i].string.desc,
202 strings[i].string.len,
Travis Geiselbrecht89fcb142008-10-10 03:18:10 -0700203 setup->length);
Travis Geiselbrecht345adb52009-01-01 05:25:50 -0800204 found = true;
205 break;
206 }
207 }
208 if (!found) {
209 /* couldn't find one, stall */
Travis Geiselbrecht89fcb142008-10-10 03:18:10 -0700210 usbc_ep0_stall();
211 }
212 break;
Travis Geiselbrecht345adb52009-01-01 05:25:50 -0800213 }
Travis Geiselbrecht89fcb142008-10-10 03:18:10 -0700214 case 0x600: /* DEVICE QUALIFIER */
215 LTRACEF("got GET_DESCRIPTOR, device qualifier\n");
216 usbc_ep0_send(speed->device_qual.desc,
217 speed->device_qual.len, setup->length);
218 break;
219 case 0xa00:
220 /* we aint got one of these */
221 LTRACEF("got GET_DESCRIPTOR, debug descriptor\n");
222 usbc_ep0_stall();
223 break;
224 default:
225 LTRACEF("unhandled descriptor %#x\n", setup->value);
226 // stall
227 break;
228 }
229 } else {
230 // interface/endpoint descriptors? let someone else handle it
231 // STALL
232 }
233 break;
234 }
235
236 case SET_CONFIGURATION:
237 LTRACEF("SET_CONFIGURATION %d\n", setup->value);
238 active_config = setup->value;
239 usbc_ep0_ack();
240 break;
241
242 case GET_CONFIGURATION:
243 LTRACEF("GET_CONFIGURATION\n");
244 usbc_ep0_send(&active_config, 1, setup->length);
245 break;
246
247 case SET_INTERFACE:
248 LTRACEF("SET_INTERFACE %d\n", setup->value);
249 usbc_ep0_ack();
250 break;
251
252 case GET_INTERFACE: {
253 static uint8_t i = 1;
254 LTRACEF("GET_INTERFACE\n");
255 usbc_ep0_send(&i, 1, setup->length);
256 break;
257 }
258
259 case GET_STATUS: {
260 static uint16_t i = 1; // self powered
261 LTRACEF("GET_STATUS\n");
262 usbc_ep0_send(&i, 2, setup->length);
263 break;
264 }
265 default:
266 LTRACEF("unhandled standard request 0x%x\n", setup->request);
267 }
268 }
269 }
270
271 return 0;
272}
273
274void usb_setup(usb_config *_config)
275{
276 ASSERT(_config);
277
278 config = _config;
279
280 ASSERT(usb_active == false);
281
282 // set the default usb control callback handler
283 usbc_set_callback(&default_usb_callback);
284}
285
286void usb_start(void)
287{
288 ASSERT(config);
289 ASSERT(usb_active == false);
290
291 // go online
292 usbc_set_active(true);
293 usb_active = true;
294}
295
296void usb_stop(void)
297{
298 ASSERT(usb_active == true);
299
300 usb_active = false;
301 usbc_set_active(false);
302}
Travis Geiselbrecht2c691e82008-09-04 02:41:01 -0700303
304void usb_init(void)
305{
306}
307