1 /***************************************************************************
2 * Copyright (C) 2011 by Martin Schmoelzer *
3 * <martin.schmoelzer@student.tuwien.ac.at> *
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. *
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. *
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 ***************************************************************************/
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
27 * Therefore, we do not have to support the Set Configuration USB request.
34 /* Also update external declarations in "include/usb.h" if making changes to
36 volatile bool EP2_out = 0;
37 volatile bool EP2_in = 0;
39 volatile __xdata __at 0x7FE8 setup_data_t setup_data;
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
46 * Normally, we would initialize the descriptor structures in C99 style:
48 * __code usb_device_descriptor_t device_descriptor = {
50 * .bDescriptorType = bar,
55 * But SDCC currently does not support this, so we have to do it the
56 * old-fashioned way...
59 __code usb_device_descriptor_t device_descriptor = {
60 /* .bLength = */ sizeof(usb_device_descriptor_t),
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,
72 /* .iSerialNumber = */ 3,
73 /* .bNumConfigurations = */ 1
76 /* WARNING: ALL config, interface and endpoint descriptors MUST be adjacent! */
78 __code usb_config_descriptor_t config_descriptor = {
79 /* .bLength = */ sizeof(usb_config_descriptor_t),
80 /* .bDescriptorType = */ DESCRIPTOR_TYPE_CONFIGURATION,
81 /* .wTotalLength = */ sizeof(usb_config_descriptor_t) +
82 sizeof(usb_interface_descriptor_t) +
83 NUM_ENDPOINTS * sizeof(usb_endpoint_descriptor_t),
84 /* .bNumInterfaces = */ 1,
85 /* .bConfigurationValue = */ 1,
86 /* .iConfiguration = */ 4, /* String describing this configuration */
87 /* .bmAttributes = */ 0x80, /* Only MSB set according to USB spec */
88 /* .MaxPower = */ 50 /* 100 mA */
91 __code usb_interface_descriptor_t interface_descriptor00 = {
92 /* .bLength = */ sizeof(usb_interface_descriptor_t),
93 /* .bDescriptorType = */ DESCRIPTOR_TYPE_INTERFACE,
94 /* .bInterfaceNumber = */ 0,
95 /* .bAlternateSetting = */ 0,
96 /* .bNumEndpoints = */ NUM_ENDPOINTS,
97 /* .bInterfaceClass = */ 0xFF,
98 /* .bInterfaceSubclass = */ 0xFF,
99 /* .bInterfaceProtocol = */ 0xFF,
100 /* .iInterface = */ 0
103 __code usb_endpoint_descriptor_t Bulk_EP2_IN_Endpoint_Descriptor = {
104 /* .bLength = */ sizeof(usb_endpoint_descriptor_t),
105 /* .bDescriptorType = */ 0x05,
106 /* .bEndpointAddress = */ 2 | USB_DIR_IN,
107 /* .bmAttributes = */ 0x02,
108 /* .wMaxPacketSize = */ 64,
112 __code usb_endpoint_descriptor_t Bulk_EP2_OUT_Endpoint_Descriptor = {
113 /* .bLength = */ sizeof(usb_endpoint_descriptor_t),
114 /* .bDescriptorType = */ 0x05,
115 /* .bEndpointAddress = */ 2 | USB_DIR_OUT,
116 /* .bmAttributes = */ 0x02,
117 /* .wMaxPacketSize = */ 64,
121 __code usb_language_descriptor_t language_descriptor = {
123 /* .bDescriptorType = */ DESCRIPTOR_TYPE_STRING,
124 /* .wLANGID = */ {0x0409 /* US English */}
127 __code usb_string_descriptor_t strManufacturer = STR_DESCR(9,'O','p','e','n','U','L','I','N','K');
128 __code usb_string_descriptor_t strProduct = STR_DESCR(9,'O','p','e','n','U','L','I','N','K');
129 __code usb_string_descriptor_t strSerialNumber = STR_DESCR(6, '0','0','0','0','0','1');
130 __code usb_string_descriptor_t strConfigDescr = STR_DESCR(12, 'J','T','A','G',' ','A','d','a','p','t','e','r');
132 /* Table containing pointers to string descriptors */
133 __code usb_string_descriptor_t* __code en_string_descriptors[4] = {
140 void sudav_isr(void) __interrupt SUDAV_ISR
144 usb_handle_setup_data();
150 void sof_isr(void) __interrupt SOF_ISR { }
151 void sutok_isr(void) __interrupt SUTOK_ISR { }
152 void suspend_isr(void) __interrupt SUSPEND_ISR { }
153 void usbreset_isr(void) __interrupt USBRESET_ISR { }
154 void ibn_isr(void) __interrupt IBN_ISR { }
156 void ep0in_isr(void) __interrupt EP0IN_ISR { }
157 void ep0out_isr(void) __interrupt EP0OUT_ISR { }
158 void ep1in_isr(void) __interrupt EP1IN_ISR { }
159 void ep1out_isr(void) __interrupt EP1OUT_ISR { }
162 * EP2 IN: called after the transfer from uC->Host has finished: we sent data
164 void ep2in_isr(void) __interrupt EP2IN_ISR {
168 IN07IRQ = IN2IR; // Clear OUT2 IRQ
172 * EP2 OUT: called after the transfer from Host->uC has finished: we got data
174 void ep2out_isr(void) __interrupt EP2OUT_ISR {
178 OUT07IRQ = OUT2IR; // Clear OUT2 IRQ
181 void ep3in_isr(void) __interrupt EP3IN_ISR { }
182 void ep3out_isr(void) __interrupt EP3OUT_ISR { }
183 void ep4in_isr(void) __interrupt EP4IN_ISR { }
184 void ep4out_isr(void) __interrupt EP4OUT_ISR { }
185 void ep5in_isr(void) __interrupt EP5IN_ISR { }
186 void ep5out_isr(void) __interrupt EP5OUT_ISR { }
187 void ep6in_isr(void) __interrupt EP6IN_ISR { }
188 void ep6out_isr(void) __interrupt EP6OUT_ISR { }
189 void ep7in_isr(void) __interrupt EP7IN_ISR { }
190 void ep7out_isr(void) __interrupt EP7OUT_ISR { }
193 * Return the control/status register for an endpoint
195 * @param ep endpoint address
196 * @return on success: pointer to Control & Status register for endpoint
198 * @return on failure: NULL
200 __xdata u8* usb_get_endpoint_cs_reg(u8 ep)
202 /* Mask direction bit */
203 u8 ep_num = ep & 0x7F;
210 return ep & 0x80 ? &IN1CS : &OUT1CS;
213 return ep & 0x80 ? &IN2CS : &OUT2CS;
216 return ep & 0x80 ? &IN3CS : &OUT3CS;
219 return ep & 0x80 ? &IN4CS : &OUT4CS;
222 return ep & 0x80 ? &IN5CS : &OUT5CS;
225 return ep & 0x80 ? &IN6CS : &OUT6CS;
228 return ep & 0x80 ? &IN7CS : &OUT7CS;
235 void usb_reset_data_toggle(u8 ep)
238 +----+-----+-----+------+-----+-------+-------+-------+
239 | Q | S | R | IO | 0 | EP2 | EP1 | EP0 |
240 +----+-----+-----+------+-----+-------+-------+-------+
242 To reset data toggle bits, we have to write the endpoint direction (IN/OUT)
243 to the IO bit and the endpoint number to the EP2..EP0 bits. Then, in a
244 separate write cycle, the R bit needs to be set.
246 u8 togctl_value = (ep & 0x80 >> 3) | (ep & 0x7);
248 /* First step: Write EP number and direction bit */
249 TOGCTL = togctl_value;
251 /* Second step: Set R bit */
252 togctl_value |= TOG_R;
253 TOGCTL = togctl_value;
257 * Handle GET_STATUS request.
259 * @return on success: true
260 * @return on failure: false
262 bool usb_handle_get_status(void)
266 switch (setup_data.bmRequestType) {
268 /* Two byte response: Byte 0, Bit 0 = self-powered, Bit 1 = remote wakeup.
269 * Byte 1: reserved, reset to zero */
277 /* Always return two zero bytes according to USB 1.1 spec, p. 191 */
285 /* Get stall bit for endpoint specified in low byte of wIndex */
286 ep_cs = usb_get_endpoint_cs_reg(setup_data.wIndex & 0xff);
288 if (*ep_cs & EPSTALL) {
295 /* Second byte sent has to be always zero */
310 * Handle CLEAR_FEATURE request.
312 * @return on success: true
313 * @return on failure: false
315 bool usb_handle_clear_feature(void)
319 switch (setup_data.bmRequestType) {
321 /* Clear remote wakeup not supported: stall EP0 */
325 if (setup_data.wValue == 0) {
326 /* Unstall the endpoint specified in wIndex */
327 ep_cs = usb_get_endpoint_cs_reg(setup_data.wIndex);
334 /* Unsupported feature, stall EP0 */
339 /* Vendor commands... */
346 * Handle SET_FEATURE request.
348 * @return on success: true
349 * @return on failure: false
351 bool usb_handle_set_feature(void)
355 switch (setup_data.bmRequestType) {
357 if (setup_data.wValue == 2) {
362 if (setup_data.wValue == 0) {
363 /* Stall the endpoint specified in wIndex */
364 ep_cs = usb_get_endpoint_cs_reg(setup_data.wIndex);
371 /* Unsupported endpoint feature */
376 /* Vendor commands... */
384 * Handle GET_DESCRIPTOR request.
386 * @return on success: true
387 * @return on failure: false
389 bool usb_handle_get_descriptor(void)
391 __xdata u8 descriptor_type;
392 __xdata u8 descriptor_index;
394 descriptor_type = (setup_data.wValue & 0xff00) >> 8;
395 descriptor_index = setup_data.wValue & 0x00ff;
397 switch (descriptor_type) {
398 case DESCRIPTOR_TYPE_DEVICE:
399 SUDPTRH = HI8(&device_descriptor);
400 SUDPTRL = LO8(&device_descriptor);
402 case DESCRIPTOR_TYPE_CONFIGURATION:
403 SUDPTRH = HI8(&config_descriptor);
404 SUDPTRL = LO8(&config_descriptor);
406 case DESCRIPTOR_TYPE_STRING:
407 if (setup_data.wIndex == 0) {
408 /* Supply language descriptor */
409 SUDPTRH = HI8(&language_descriptor);
410 SUDPTRL = LO8(&language_descriptor);
412 else if (setup_data.wIndex == 0x0409 /* US English */) {
413 /* Supply string descriptor */
414 SUDPTRH = HI8(en_string_descriptors[descriptor_index - 1]);
415 SUDPTRL = LO8(en_string_descriptors[descriptor_index - 1]);
422 /* Unsupported descriptor type */
431 * Handle SET_INTERFACE request.
433 void usb_handle_set_interface(void)
435 /* Reset Data Toggle */
436 usb_reset_data_toggle(USB_DIR_IN | 2);
437 usb_reset_data_toggle(USB_DIR_OUT | 2);
439 /* Unstall & clear busy flag of all valid IN endpoints */
442 /* Unstall all valid OUT endpoints, reset bytecounts */
448 * Handle the arrival of a USB Control Setup Packet.
450 void usb_handle_setup_data(void)
452 switch (setup_data.bRequest) {
454 if (!usb_handle_get_status()) {
459 if (!usb_handle_clear_feature()) {
464 /* Reserved values */
468 if (!usb_handle_set_feature()) {
473 /* Handled by USB core */
476 /* Set Descriptor not supported. */
480 if (!usb_handle_get_descriptor()) {
484 case GET_CONFIGURATION:
485 /* OpenULINK has only one configuration, return its index */
486 IN0BUF[0] = config_descriptor.bConfigurationValue;
489 case SET_CONFIGURATION:
490 /* OpenULINK has only one configuration -> nothing to do */
493 /* OpenULINK only has one interface, return its number */
494 IN0BUF[0] = interface_descriptor00.bInterfaceNumber;
498 usb_handle_set_interface();
501 /* Isochronous endpoints not used -> nothing to do */
504 /* Any other requests: do nothing */
510 * USB initialization. Configures USB interrupts, endpoints and performs
513 void usb_init(void) {
514 /* Mark endpoint 2 IN & OUT as valid */
518 /* Make sure no isochronous endpoints are marked valid */
522 /* Disable isochronous endpoints. This makes the isochronous data buffers
523 * available as 8051 XDATA memory at address 0x2000 - 0x27FF */
526 /* Enable USB Autovectoring */
529 /* Enable SUDAV interrupt */
532 /* Enable EP2 OUT & IN interrupts */
536 /* Enable USB interrupt (EIE register) */
539 /* Perform ReNumeration */
540 USBCS = DISCON | RENUM;
542 USBCS = DISCOE | RENUM;