2 * Copyright © 2009 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 struct ao_task __xdata ao_usb_task;
23 #define AO_USB_CONTROL_EP 0
24 #define AO_USB_INT_EP 1
25 #define AO_USB_OUT_EP 4
26 #define AO_USB_IN_EP 5
27 #define AO_USB_CONTROL_SIZE 32
29 * Double buffer IN and OUT EPs, so each
30 * gets half of the available space
32 #define AO_USB_IN_SIZE 256
33 #define AO_USB_OUT_SIZE 128
35 static uint16_t ao_usb_in_bytes;
36 static uint16_t ao_usb_out_bytes;
37 static __data uint8_t ao_usb_iif;
38 static __data uint8_t ao_usb_oif;
40 /* This interrupt is shared with port 2,
41 * so when we hook that up, fix this
44 ao_usb_isr(void) interrupt 6
49 ao_wakeup(&ao_usb_task);
50 if (ao_usb_iif & (1 << AO_USB_IN_EP))
51 ao_wakeup(&ao_usb_in_bytes);
54 if (ao_usb_oif & (1 << AO_USB_OUT_EP))
55 ao_wakeup(&ao_usb_out_bytes);
58 #define AO_USB_EP0_IDLE 0
59 #define AO_USB_EP0_DATA_IN 1
60 #define AO_USB_EP0_DATA_OUT 2
63 uint8_t dir_type_recip;
70 __data uint8_t ao_usb_ep0_state;
71 uint8_t * __data ao_usb_ep0_in_data;
72 __data uint8_t ao_usb_ep0_in_len;
73 __xdata uint8_t ao_usb_ep0_in_buf[2];
74 __data uint8_t ao_usb_ep0_out_len;
75 __xdata uint8_t *__data ao_usb_ep0_out_data;
76 __data uint8_t ao_usb_configuration;
78 /* Send an IN data packet */
80 ao_usb_ep0_flush(void)
87 if (cs0 & USBCS0_INPKT_RDY)
90 this_len = ao_usb_ep0_in_len;
91 if (this_len > AO_USB_CONTROL_SIZE)
92 this_len = AO_USB_CONTROL_SIZE;
93 cs0 = USBCS0_INPKT_RDY;
94 if (this_len != AO_USB_CONTROL_SIZE) {
95 cs0 = USBCS0_INPKT_RDY | USBCS0_DATA_END;
96 ao_usb_ep0_state = AO_USB_EP0_IDLE;
98 ao_usb_ep0_in_len -= this_len;
100 USBFIFO[0] = *ao_usb_ep0_in_data++;
105 #define LE_WORD(x) ((x)&0xFF),((x)>>8)
107 /* CDC definitions */
108 #define CS_INTERFACE 0x24
109 #define CS_ENDPOINT 0x25
111 #define SET_LINE_CODING 0x20
112 #define GET_LINE_CODING 0x21
113 #define SET_CONTROL_LINE_STATE 0x22
115 /* Data structure for GET_LINE_CODING / SET_LINE_CODING class requests */
116 struct ao_usb_line_coding {
123 static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
125 /* USB descriptors in one giant block of bytes */
126 static const uint8_t ao_usb_descriptors [] =
128 /* Device descriptor */
131 LE_WORD(0x0110), /* bcdUSB */
132 0x02, /* bDeviceClass */
133 0x00, /* bDeviceSubClass */
134 0x00, /* bDeviceProtocol */
135 AO_USB_CONTROL_SIZE, /* bMaxPacketSize */
136 LE_WORD(0xFFFE), /* idVendor */
137 LE_WORD(0x000A), /* idProduct */
138 LE_WORD(0x0100), /* bcdDevice */
139 0x01, /* iManufacturer */
141 0x03, /* iSerialNumber */
142 0x01, /* bNumConfigurations */
144 /* Configuration descriptor */
146 AO_USB_DESC_CONFIGURATION,
147 LE_WORD(67), /* wTotalLength */
148 0x02, /* bNumInterfaces */
149 0x01, /* bConfigurationValue */
150 0x00, /* iConfiguration */
151 0xC0, /* bmAttributes */
152 0x32, /* bMaxPower */
154 /* Control class interface */
156 AO_USB_DESC_INTERFACE,
157 0x00, /* bInterfaceNumber */
158 0x00, /* bAlternateSetting */
159 0x01, /* bNumEndPoints */
160 0x02, /* bInterfaceClass */
161 0x02, /* bInterfaceSubClass */
162 0x01, /* bInterfaceProtocol, linux requires value of 1 for the cdc_acm module */
163 0x00, /* iInterface */
165 /* Header functional descriptor */
168 0x00, /* bDescriptor SubType Header */
169 LE_WORD(0x0110), /* CDC version 1.1 */
171 /* Call management functional descriptor */
174 0x01, /* bDescriptor SubType Call Management */
175 0x01, /* bmCapabilities = device handles call management */
176 0x01, /* bDataInterface call management interface number */
178 /* ACM functional descriptor */
181 0x02, /* bDescriptor SubType Abstract Control Management */
182 0x02, /* bmCapabilities = D1 (Set_line_Coding, Set_Control_Line_State, Get_Line_Coding and Serial_State) */
184 /* Union functional descriptor */
187 0x06, /* bDescriptor SubType Union Functional descriptor */
188 0x00, /* bMasterInterface */
189 0x01, /* bSlaveInterface0 */
191 /* Notification EP */
193 AO_USB_DESC_ENDPOINT,
194 AO_USB_INT_EP|0x80, /* bEndpointAddress */
195 0x03, /* bmAttributes = intr */
196 LE_WORD(8), /* wMaxPacketSize */
197 0x0A, /* bInterval */
199 /* Data class interface descriptor */
201 AO_USB_DESC_INTERFACE,
202 0x01, /* bInterfaceNumber */
203 0x00, /* bAlternateSetting */
204 0x02, /* bNumEndPoints */
205 0x0A, /* bInterfaceClass = data */
206 0x00, /* bInterfaceSubClass */
207 0x00, /* bInterfaceProtocol */
208 0x00, /* iInterface */
212 AO_USB_DESC_ENDPOINT,
213 AO_USB_OUT_EP, /* bEndpointAddress */
214 0x02, /* bmAttributes = bulk */
215 LE_WORD(AO_USB_OUT_SIZE),/* wMaxPacketSize */
216 0x00, /* bInterval */
220 AO_USB_DESC_ENDPOINT,
221 AO_USB_IN_EP|0x80, /* bEndpointAddress */
222 0x02, /* bmAttributes = bulk */
223 LE_WORD(AO_USB_IN_SIZE),/* wMaxPacketSize */
224 0x00, /* bInterval */
226 /* String descriptors */
234 'a', 0, 'l', 0, 't', 0, 'u', 0, 's', 0, 'm', 0, 'e', 0, 't', 0, 'r', 0, 'u', 0, 'm', 0, '.', 0, 'o', 0, 'r', 0, 'g', 0,
239 'T', 0, 'e', 0, 'l', 0, 'e', 0, 'M', 0, 'e', 0, 't', 0, 'r', 0, 'u', 0, 'm', 0,
244 't', 0, 'e', 0, 'l', 0, 'e', 0, '-', 0, '0', 0,
246 /* Terminating zero */
250 /* Walk through the list of descriptors and find a match
253 ao_usb_get_descriptor(uint16_t value)
255 const uint8_t *descriptor;
256 uint8_t type = value >> 8;
257 uint8_t index = value;
259 descriptor = ao_usb_descriptors;
260 while (descriptor[0] != 0) {
261 if (descriptor[1] == type && index-- == 0) {
262 if (type == AO_USB_DESC_CONFIGURATION)
263 ao_usb_ep0_in_len = descriptor[2];
265 ao_usb_ep0_in_len = descriptor[0];
266 ao_usb_ep0_in_data = descriptor;
269 descriptor += descriptor[0];
273 /* Read data from the ep0 OUT fifo
276 ao_usb_ep0_fill(void)
282 if (len > ao_usb_ep0_out_len)
283 len = ao_usb_ep0_out_len;
284 ao_usb_ep0_out_len -= len;
286 *ao_usb_ep0_out_data++ = USBFIFO[0];
290 ao_usb_ep0_queue_byte(uint8_t a)
292 ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
296 ao_usb_set_address(uint8_t address)
298 USBADDR = address | 0x80;
299 while (USBADDR & 0x80)
304 ao_usb_set_configuration(void)
308 /* Set the IN max packet size, double buffered */
309 USBINDEX = AO_USB_IN_EP;
310 size = AO_USB_IN_SIZE >> 3;
312 // USBCSIH |= USBCSIH_IN_DBL_BUF;
314 /* Set the OUT max packet size, double buffered */
315 USBINDEX = AO_USB_OUT_EP;
316 size = AO_USB_OUT_SIZE >> 3;
318 // USBCSOH = USBCSOH_OUT_DBL_BUF;
322 ao_usb_ep0_setup(void)
324 /* Pull the setup packet out of the fifo */
325 ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup;
326 ao_usb_ep0_out_len = 8;
328 if (ao_usb_ep0_out_len != 0)
331 /* Figure out how to ACK the setup packet */
332 if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) {
333 if (ao_usb_setup.length)
334 ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
336 ao_usb_ep0_state = AO_USB_EP0_IDLE;
338 if (ao_usb_setup.length)
339 ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
341 ao_usb_ep0_state = AO_USB_EP0_IDLE;
344 if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
345 USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
347 USBCS0 = USBCS0_CLR_OUTPKT_RDY;
349 ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
350 ao_usb_ep0_in_len = 0;
351 switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
352 case AO_USB_TYPE_STANDARD:
353 switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
354 case AO_USB_RECIP_DEVICE:
355 switch(ao_usb_setup.request) {
356 case AO_USB_REQ_GET_STATUS:
357 ao_usb_ep0_queue_byte(0);
358 ao_usb_ep0_queue_byte(0);
360 case AO_USB_REQ_SET_ADDRESS:
361 ao_usb_set_address(ao_usb_setup.value);
363 case AO_USB_REQ_GET_DESCRIPTOR:
364 ao_usb_get_descriptor(ao_usb_setup.value);
366 case AO_USB_REQ_GET_CONFIGURATION:
367 ao_usb_ep0_queue_byte(ao_usb_configuration);
369 case AO_USB_REQ_SET_CONFIGURATION:
370 ao_usb_configuration = ao_usb_setup.value;
371 ao_usb_set_configuration();
375 case AO_USB_RECIP_INTERFACE:
376 switch(ao_usb_setup.request) {
377 case AO_USB_REQ_GET_STATUS:
378 ao_usb_ep0_queue_byte(0);
379 ao_usb_ep0_queue_byte(0);
381 case AO_USB_REQ_GET_INTERFACE:
382 ao_usb_ep0_queue_byte(0);
384 case AO_USB_REQ_SET_INTERFACE:
388 case AO_USB_RECIP_ENDPOINT:
389 switch(ao_usb_setup.request) {
390 case AO_USB_REQ_GET_STATUS:
391 ao_usb_ep0_queue_byte(0);
392 ao_usb_ep0_queue_byte(0);
398 case AO_USB_TYPE_CLASS:
399 switch (ao_usb_setup.request) {
400 case SET_LINE_CODING:
401 ao_usb_ep0_out_len = 7;
402 ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
404 case GET_LINE_CODING:
405 ao_usb_ep0_in_len = 7;
406 ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
408 case SET_CONTROL_LINE_STATE:
413 if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
414 if (ao_usb_setup.length < ao_usb_ep0_in_len)
415 ao_usb_ep0_in_len = ao_usb_setup.length;
420 /* End point 0 receives all of the control messages. */
426 ao_usb_ep0_state = AO_USB_EP0_IDLE;
428 ao_interrupt_disable();
430 if (ao_usb_iif & 1) {
434 ao_sleep(&ao_usb_task);
436 ao_interrupt_enable();
439 if (cs0 & USBCS0_SETUP_END) {
440 ao_usb_ep0_state = AO_USB_EP0_IDLE;
441 USBCS0 = USBCS0_CLR_SETUP_END;
443 if (cs0 & USBCS0_SENT_STALL) {
444 ao_usb_ep0_state = AO_USB_EP0_IDLE;
445 USBCS0 &= ~USBCS0_SENT_STALL;
447 if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN &&
448 (cs0 & USBCS0_INPKT_RDY) == 0)
452 if (cs0 & USBCS0_OUTPKT_RDY) {
453 switch (ao_usb_ep0_state) {
454 case AO_USB_EP0_IDLE:
457 case AO_USB_EP0_DATA_OUT:
459 if (ao_usb_ep0_out_len == 0)
460 ao_usb_ep0_state = AO_USB_EP0_IDLE;
462 if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
463 USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
465 USBCS0 = USBCS0_CLR_OUTPKT_RDY;
475 ao_interrupt_disable();
476 if (ao_usb_in_bytes) {
477 USBINDEX = AO_USB_IN_EP;
478 USBCSIL |= USBCSIL_INPKT_RDY;
481 ao_interrupt_enable();
485 ao_usb_putchar(uint8_t c)
487 ao_interrupt_disable();
489 USBINDEX = AO_USB_IN_EP;
490 if ((USBCSIL & USBCSIL_INPKT_RDY) == 0)
492 ao_sleep(&ao_usb_in_bytes);
494 USBFIFO[AO_USB_IN_EP << 1] = c;
495 if (++ao_usb_in_bytes == AO_USB_IN_SIZE)
497 ao_interrupt_enable();
504 ao_interrupt_disable();
505 while (ao_usb_out_bytes == 0) {
507 USBINDEX = AO_USB_OUT_EP;
508 if ((USBCSOL & USBCSOL_OUTPKT_RDY) != 0)
510 ao_sleep(&ao_usb_out_bytes);
512 ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL;
515 c = USBFIFO[AO_USB_OUT_EP << 1];
516 if (ao_usb_out_bytes == 0) {
517 USBINDEX = AO_USB_OUT_EP;
518 USBCSOL &= ~USBCSOL_OUTPKT_RDY;
520 ao_interrupt_enable();
527 /* Turn on the USB controller */
528 SLEEP |= SLEEP_USB_EN;
530 ao_usb_set_configuration();
532 /* IN interrupts on the control an IN endpoints */
533 USBIIE = (1 << AO_USB_CONTROL_EP) | (1 << AO_USB_IN_EP);
535 /* OUT interrupts on the OUT endpoint */
536 USBOIE = (1 << AO_USB_OUT_EP);
538 /* Ignore control interrupts */
541 /* enable USB interrupts */
544 /* Clear any pending interrupts */
549 ao_add_task(&ao_usb_task, ao_usb_ep0);