ULINK driver: Remove typedefs in OpenULINK firmware USB descriptor structures
[openocd.git] / src / jtag / drivers / OpenULINK / src / usb.c
1 /***************************************************************************
2 * Copyright (C) 2011 by Martin Schmoelzer *
3 * <martin.schmoelzer@student.tuwien.ac.at> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21 /**
22 * @file Defines USB descriptors, interrupt routines and helper functions.
23 * To minimize code size, we make the following assumptions:
24 * - The OpenULINK has exactly one configuration
25 * - and exactly one alternate setting
26 *
27 * Therefore, we do not have to support the Set Configuration USB request.
28 */
29
30 #include "usb.h"
31 #include "delay.h"
32 #include "io.h"
33
34 /* Also update external declarations in "include/usb.h" if making changes to
35 * these variables! */
36 volatile bool EP2_out = 0;
37 volatile bool EP2_in = 0;
38
39 volatile __xdata __at 0x7FE8 struct setup_data setup_data;
40
41 /* Define number of endpoints (except Control Endpoint 0) in a central place.
42 * Be sure to include the neccessary endpoint descriptors! */
43 #define NUM_ENDPOINTS 2
44
45 /*
46 * Normally, we would initialize the descriptor structures in C99 style:
47 *
48 * __code usb_device_descriptor_t device_descriptor = {
49 * .bLength = foo,
50 * .bDescriptorType = bar,
51 * .bcdUSB = 0xABCD,
52 * ...
53 * };
54 *
55 * But SDCC currently does not support this, so we have to do it the
56 * old-fashioned way...
57 */
58
59 __code struct usb_device_descriptor device_descriptor = {
60 /* .bLength = */ sizeof(struct usb_device_descriptor),
61 /* .bDescriptorType = */ DESCRIPTOR_TYPE_DEVICE,
62 /* .bcdUSB = */ 0x0110, /* BCD: 01.00 (Version 1.0 USB spec) */
63 /* .bDeviceClass = */ 0xFF, /* 0xFF = vendor-specific */
64 /* .bDeviceSubClass = */ 0xFF,
65 /* .bDeviceProtocol = */ 0xFF,
66 /* .bMaxPacketSize0 = */ 64,
67 /* .idVendor = */ 0xC251,
68 /* .idProduct = */ 0x2710,
69 /* .bcdDevice = */ 0x0100,
70 /* .iManufacturer = */ 1,
71 /* .iProduct = */ 2,
72 /* .iSerialNumber = */ 3,
73 /* .bNumConfigurations = */ 1
74 };
75
76 /* WARNING: ALL config, interface and endpoint descriptors MUST be adjacent! */
77
78 __code struct usb_config_descriptor config_descriptor = {
79 /* .bLength = */ sizeof(struct usb_config_descriptor),
80 /* .bDescriptorType = */ DESCRIPTOR_TYPE_CONFIGURATION,
81 /* .wTotalLength = */ sizeof(struct usb_config_descriptor) +
82 sizeof(struct usb_interface_descriptor) +
83 (NUM_ENDPOINTS *
84 sizeof(struct usb_endpoint_descriptor)),
85 /* .bNumInterfaces = */ 1,
86 /* .bConfigurationValue = */ 1,
87 /* .iConfiguration = */ 4, /* String describing this configuration */
88 /* .bmAttributes = */ 0x80, /* Only MSB set according to USB spec */
89 /* .MaxPower = */ 50 /* 100 mA */
90 };
91
92 __code struct usb_interface_descriptor interface_descriptor00 = {
93 /* .bLength = */ sizeof(struct usb_interface_descriptor),
94 /* .bDescriptorType = */ DESCRIPTOR_TYPE_INTERFACE,
95 /* .bInterfaceNumber = */ 0,
96 /* .bAlternateSetting = */ 0,
97 /* .bNumEndpoints = */ NUM_ENDPOINTS,
98 /* .bInterfaceClass = */ 0xFF,
99 /* .bInterfaceSubclass = */ 0xFF,
100 /* .bInterfaceProtocol = */ 0xFF,
101 /* .iInterface = */ 0
102 };
103
104 __code struct usb_endpoint_descriptor Bulk_EP2_IN_Endpoint_Descriptor = {
105 /* .bLength = */ sizeof(struct usb_endpoint_descriptor),
106 /* .bDescriptorType = */ 0x05,
107 /* .bEndpointAddress = */ 2 | USB_DIR_IN,
108 /* .bmAttributes = */ 0x02,
109 /* .wMaxPacketSize = */ 64,
110 /* .bInterval = */ 0
111 };
112
113 __code struct usb_endpoint_descriptor Bulk_EP2_OUT_Endpoint_Descriptor = {
114 /* .bLength = */ sizeof(struct usb_endpoint_descriptor),
115 /* .bDescriptorType = */ 0x05,
116 /* .bEndpointAddress = */ 2 | USB_DIR_OUT,
117 /* .bmAttributes = */ 0x02,
118 /* .wMaxPacketSize = */ 64,
119 /* .bInterval = */ 0
120 };
121
122 __code struct usb_language_descriptor language_descriptor = {
123 /* .bLength = */ 4,
124 /* .bDescriptorType = */ DESCRIPTOR_TYPE_STRING,
125 /* .wLANGID = */ {0x0409 /* US English */}
126 };
127
128 __code struct usb_string_descriptor strManufacturer =
129 STR_DESCR(9,'O','p','e','n','U','L','I','N','K');
130
131 __code struct usb_string_descriptor strProduct =
132 STR_DESCR(9,'O','p','e','n','U','L','I','N','K');
133
134 __code struct usb_string_descriptor strSerialNumber =
135 STR_DESCR(6, '0','0','0','0','0','1');
136
137 __code struct usb_string_descriptor strConfigDescr =
138 STR_DESCR(12, 'J','T','A','G',' ','A','d','a','p','t','e','r');
139
140 /* Table containing pointers to string descriptors */
141 __code struct usb_string_descriptor* __code en_string_descriptors[4] = {
142 &strManufacturer,
143 &strProduct,
144 &strSerialNumber,
145 &strConfigDescr
146 };
147
148 void sudav_isr(void) __interrupt SUDAV_ISR
149 {
150 CLEAR_IRQ();
151
152 usb_handle_setup_data();
153
154 USBIRQ = SUDAVIR;
155 EP0CS |= HSNAK;
156 }
157
158 void sof_isr(void) __interrupt SOF_ISR { }
159 void sutok_isr(void) __interrupt SUTOK_ISR { }
160 void suspend_isr(void) __interrupt SUSPEND_ISR { }
161 void usbreset_isr(void) __interrupt USBRESET_ISR { }
162 void ibn_isr(void) __interrupt IBN_ISR { }
163
164 void ep0in_isr(void) __interrupt EP0IN_ISR { }
165 void ep0out_isr(void) __interrupt EP0OUT_ISR { }
166 void ep1in_isr(void) __interrupt EP1IN_ISR { }
167 void ep1out_isr(void) __interrupt EP1OUT_ISR { }
168
169 /**
170 * EP2 IN: called after the transfer from uC->Host has finished: we sent data
171 */
172 void ep2in_isr(void) __interrupt EP2IN_ISR {
173 EP2_in = 1;
174
175 CLEAR_IRQ();
176 IN07IRQ = IN2IR; // Clear OUT2 IRQ
177 }
178
179 /**
180 * EP2 OUT: called after the transfer from Host->uC has finished: we got data
181 */
182 void ep2out_isr(void) __interrupt EP2OUT_ISR {
183 EP2_out = 1;
184
185 CLEAR_IRQ();
186 OUT07IRQ = OUT2IR; // Clear OUT2 IRQ
187 }
188
189 void ep3in_isr(void) __interrupt EP3IN_ISR { }
190 void ep3out_isr(void) __interrupt EP3OUT_ISR { }
191 void ep4in_isr(void) __interrupt EP4IN_ISR { }
192 void ep4out_isr(void) __interrupt EP4OUT_ISR { }
193 void ep5in_isr(void) __interrupt EP5IN_ISR { }
194 void ep5out_isr(void) __interrupt EP5OUT_ISR { }
195 void ep6in_isr(void) __interrupt EP6IN_ISR { }
196 void ep6out_isr(void) __interrupt EP6OUT_ISR { }
197 void ep7in_isr(void) __interrupt EP7IN_ISR { }
198 void ep7out_isr(void) __interrupt EP7OUT_ISR { }
199
200 /**
201 * Return the control/status register for an endpoint
202 *
203 * @param ep endpoint address
204 * @return on success: pointer to Control & Status register for endpoint
205 * specified in \a ep
206 * @return on failure: NULL
207 */
208 __xdata u8* usb_get_endpoint_cs_reg(u8 ep)
209 {
210 /* Mask direction bit */
211 u8 ep_num = ep & 0x7F;
212
213 switch (ep_num) {
214 case 0:
215 return &EP0CS;
216 break;
217 case 1:
218 return ep & 0x80 ? &IN1CS : &OUT1CS;
219 break;
220 case 2:
221 return ep & 0x80 ? &IN2CS : &OUT2CS;
222 break;
223 case 3:
224 return ep & 0x80 ? &IN3CS : &OUT3CS;
225 break;
226 case 4:
227 return ep & 0x80 ? &IN4CS : &OUT4CS;
228 break;
229 case 5:
230 return ep & 0x80 ? &IN5CS : &OUT5CS;
231 break;
232 case 6:
233 return ep & 0x80 ? &IN6CS : &OUT6CS;
234 break;
235 case 7:
236 return ep & 0x80 ? &IN7CS : &OUT7CS;
237 break;
238 }
239
240 return NULL;
241 }
242
243 void usb_reset_data_toggle(u8 ep)
244 {
245 /* TOGCTL register:
246 +----+-----+-----+------+-----+-------+-------+-------+
247 | Q | S | R | IO | 0 | EP2 | EP1 | EP0 |
248 +----+-----+-----+------+-----+-------+-------+-------+
249
250 To reset data toggle bits, we have to write the endpoint direction (IN/OUT)
251 to the IO bit and the endpoint number to the EP2..EP0 bits. Then, in a
252 separate write cycle, the R bit needs to be set.
253 */
254 u8 togctl_value = (ep & 0x80 >> 3) | (ep & 0x7);
255
256 /* First step: Write EP number and direction bit */
257 TOGCTL = togctl_value;
258
259 /* Second step: Set R bit */
260 togctl_value |= TOG_R;
261 TOGCTL = togctl_value;
262 }
263
264 /**
265 * Handle GET_STATUS request.
266 *
267 * @return on success: true
268 * @return on failure: false
269 */
270 bool usb_handle_get_status(void)
271 {
272 u8 *ep_cs;
273
274 switch (setup_data.bmRequestType) {
275 case GS_DEVICE:
276 /* Two byte response: Byte 0, Bit 0 = self-powered, Bit 1 = remote wakeup.
277 * Byte 1: reserved, reset to zero */
278 IN0BUF[0] = 0;
279 IN0BUF[1] = 0;
280
281 /* Send response */
282 IN0BC = 2;
283 break;
284 case GS_INTERFACE:
285 /* Always return two zero bytes according to USB 1.1 spec, p. 191 */
286 IN0BUF[0] = 0;
287 IN0BUF[1] = 0;
288
289 /* Send response */
290 IN0BC = 2;
291 break;
292 case GS_ENDPOINT:
293 /* Get stall bit for endpoint specified in low byte of wIndex */
294 ep_cs = usb_get_endpoint_cs_reg(setup_data.wIndex & 0xff);
295
296 if (*ep_cs & EPSTALL) {
297 IN0BUF[0] = 0x01;
298 }
299 else {
300 IN0BUF[0] = 0x00;
301 }
302
303 /* Second byte sent has to be always zero */
304 IN0BUF[1] = 0;
305
306 /* Send response */
307 IN0BC = 2;
308 break;
309 default:
310 return false;
311 break;
312 }
313
314 return true;
315 }
316
317 /**
318 * Handle CLEAR_FEATURE request.
319 *
320 * @return on success: true
321 * @return on failure: false
322 */
323 bool usb_handle_clear_feature(void)
324 {
325 __xdata u8 *ep_cs;
326
327 switch (setup_data.bmRequestType) {
328 case CF_DEVICE:
329 /* Clear remote wakeup not supported: stall EP0 */
330 STALL_EP0();
331 break;
332 case CF_ENDPOINT:
333 if (setup_data.wValue == 0) {
334 /* Unstall the endpoint specified in wIndex */
335 ep_cs = usb_get_endpoint_cs_reg(setup_data.wIndex);
336 if (!ep_cs) {
337 return false;
338 }
339 *ep_cs &= ~EPSTALL;
340 }
341 else {
342 /* Unsupported feature, stall EP0 */
343 STALL_EP0();
344 }
345 break;
346 default:
347 /* Vendor commands... */
348 }
349
350 return true;
351 }
352
353 /**
354 * Handle SET_FEATURE request.
355 *
356 * @return on success: true
357 * @return on failure: false
358 */
359 bool usb_handle_set_feature(void)
360 {
361 __xdata u8 *ep_cs;
362
363 switch (setup_data.bmRequestType) {
364 case SF_DEVICE:
365 if (setup_data.wValue == 2) {
366 return true;
367 }
368 break;
369 case SF_ENDPOINT:
370 if (setup_data.wValue == 0) {
371 /* Stall the endpoint specified in wIndex */
372 ep_cs = usb_get_endpoint_cs_reg(setup_data.wIndex);
373 if (!ep_cs) {
374 return false;
375 }
376 *ep_cs |= EPSTALL;
377 }
378 else {
379 /* Unsupported endpoint feature */
380 return false;
381 }
382 break;
383 default:
384 /* Vendor commands... */
385 break;
386 }
387
388 return true;
389 }
390
391 /**
392 * Handle GET_DESCRIPTOR request.
393 *
394 * @return on success: true
395 * @return on failure: false
396 */
397 bool usb_handle_get_descriptor(void)
398 {
399 __xdata u8 descriptor_type;
400 __xdata u8 descriptor_index;
401
402 descriptor_type = (setup_data.wValue & 0xff00) >> 8;
403 descriptor_index = setup_data.wValue & 0x00ff;
404
405 switch (descriptor_type) {
406 case DESCRIPTOR_TYPE_DEVICE:
407 SUDPTRH = HI8(&device_descriptor);
408 SUDPTRL = LO8(&device_descriptor);
409 break;
410 case DESCRIPTOR_TYPE_CONFIGURATION:
411 SUDPTRH = HI8(&config_descriptor);
412 SUDPTRL = LO8(&config_descriptor);
413 break;
414 case DESCRIPTOR_TYPE_STRING:
415 if (setup_data.wIndex == 0) {
416 /* Supply language descriptor */
417 SUDPTRH = HI8(&language_descriptor);
418 SUDPTRL = LO8(&language_descriptor);
419 }
420 else if (setup_data.wIndex == 0x0409 /* US English */) {
421 /* Supply string descriptor */
422 SUDPTRH = HI8(en_string_descriptors[descriptor_index - 1]);
423 SUDPTRL = LO8(en_string_descriptors[descriptor_index - 1]);
424 }
425 else {
426 return false;
427 }
428 break;
429 default:
430 /* Unsupported descriptor type */
431 return false;
432 break;
433 }
434
435 return true;
436 }
437
438 /**
439 * Handle SET_INTERFACE request.
440 */
441 void usb_handle_set_interface(void)
442 {
443 /* Reset Data Toggle */
444 usb_reset_data_toggle(USB_DIR_IN | 2);
445 usb_reset_data_toggle(USB_DIR_OUT | 2);
446
447 /* Unstall & clear busy flag of all valid IN endpoints */
448 IN2CS = 0 | EPBSY;
449
450 /* Unstall all valid OUT endpoints, reset bytecounts */
451 OUT2CS = 0;
452 OUT2BC = 0;
453 }
454
455 /**
456 * Handle the arrival of a USB Control Setup Packet.
457 */
458 void usb_handle_setup_data(void)
459 {
460 switch (setup_data.bRequest) {
461 case GET_STATUS:
462 if (!usb_handle_get_status()) {
463 STALL_EP0();
464 }
465 break;
466 case CLEAR_FEATURE:
467 if (!usb_handle_clear_feature()) {
468 STALL_EP0();
469 }
470 break;
471 case 2: case 4:
472 /* Reserved values */
473 STALL_EP0();
474 break;
475 case SET_FEATURE:
476 if (!usb_handle_set_feature()) {
477 STALL_EP0();
478 }
479 break;
480 case SET_ADDRESS:
481 /* Handled by USB core */
482 break;
483 case SET_DESCRIPTOR:
484 /* Set Descriptor not supported. */
485 STALL_EP0();
486 break;
487 case GET_DESCRIPTOR:
488 if (!usb_handle_get_descriptor()) {
489 STALL_EP0();
490 }
491 break;
492 case GET_CONFIGURATION:
493 /* OpenULINK has only one configuration, return its index */
494 IN0BUF[0] = config_descriptor.bConfigurationValue;
495 IN0BC = 1;
496 break;
497 case SET_CONFIGURATION:
498 /* OpenULINK has only one configuration -> nothing to do */
499 break;
500 case GET_INTERFACE:
501 /* OpenULINK only has one interface, return its number */
502 IN0BUF[0] = interface_descriptor00.bInterfaceNumber;
503 IN0BC = 1;
504 break;
505 case SET_INTERFACE:
506 usb_handle_set_interface();
507 break;
508 case SYNCH_FRAME:
509 /* Isochronous endpoints not used -> nothing to do */
510 break;
511 default:
512 /* Any other requests: do nothing */
513 break;
514 }
515 }
516
517 /**
518 * USB initialization. Configures USB interrupts, endpoints and performs
519 * ReNumeration.
520 */
521 void usb_init(void) {
522 /* Mark endpoint 2 IN & OUT as valid */
523 IN07VAL = IN2VAL;
524 OUT07VAL = OUT2VAL;
525
526 /* Make sure no isochronous endpoints are marked valid */
527 INISOVAL = 0;
528 OUTISOVAL = 0;
529
530 /* Disable isochronous endpoints. This makes the isochronous data buffers
531 * available as 8051 XDATA memory at address 0x2000 - 0x27FF */
532 ISOCTL = ISODISAB;
533
534 /* Enable USB Autovectoring */
535 USBBAV |= AVEN;
536
537 /* Enable SUDAV interrupt */
538 USBIEN |= SUDAVIE;
539
540 /* Enable EP2 OUT & IN interrupts */
541 OUT07IEN = OUT2IEN;
542 IN07IEN = IN2IEN;
543
544 /* Enable USB interrupt (EIE register) */
545 EUSB = 1;
546
547 /* Perform ReNumeration */
548 USBCS = DISCON | RENUM;
549 delay_ms(200);
550 USBCS = DISCOE | RENUM;
551 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)