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 __xdata uint16_t ao_usb_in_bytes;
36 static __xdata uint16_t ao_usb_out_bytes;
37 static __xdata uint8_t ao_usb_iif;
38 static __xdata 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;
68 } __xdata ao_usb_setup;
70 __xdata uint8_t ao_usb_ep0_state;
71 uint8_t * __xdata ao_usb_ep0_in_data;
72 __xdata uint8_t ao_usb_ep0_in_len;
73 __xdata uint8_t ao_usb_ep0_in_buf[2];
74 __xdata uint8_t ao_usb_ep0_out_len;
75 __xdata uint8_t *__data ao_usb_ep0_out_data;
76 __xdata uint8_t ao_usb_configuration;
78 /* Send an IN data packet */
80 ao_usb_ep0_flush(void)
82 __xdata uint8_t this_len;
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),((uint8_t) (((uint16_t) (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 __xdata 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 '0', 0, '0', 0, '0', 0, '0', 0, '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 *__xdata descriptor;
256 __xdata uint8_t type = value >> 8;
257 __xdata 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)
306 /* Set the IN max packet size, double buffered */
307 USBINDEX = AO_USB_IN_EP;
308 USBMAXI = AO_USB_IN_SIZE >> 3;
309 USBCSIH |= USBCSIH_IN_DBL_BUF;
311 /* Set the OUT max packet size, double buffered */
312 USBINDEX = AO_USB_OUT_EP;
313 USBMAXO = AO_USB_OUT_SIZE >> 3;
314 USBCSOH = USBCSOH_OUT_DBL_BUF;
318 ao_usb_ep0_setup(void)
320 /* Pull the setup packet out of the fifo */
321 ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup;
322 ao_usb_ep0_out_len = 8;
324 if (ao_usb_ep0_out_len != 0)
327 /* Figure out how to ACK the setup packet */
328 if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) {
329 if (ao_usb_setup.length)
330 ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
332 ao_usb_ep0_state = AO_USB_EP0_IDLE;
334 if (ao_usb_setup.length)
335 ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
337 ao_usb_ep0_state = AO_USB_EP0_IDLE;
340 if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
341 USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
343 USBCS0 = USBCS0_CLR_OUTPKT_RDY;
345 ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
346 ao_usb_ep0_in_len = 0;
347 switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
348 case AO_USB_TYPE_STANDARD:
349 switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
350 case AO_USB_RECIP_DEVICE:
351 switch(ao_usb_setup.request) {
352 case AO_USB_REQ_GET_STATUS:
353 ao_usb_ep0_queue_byte(0);
354 ao_usb_ep0_queue_byte(0);
356 case AO_USB_REQ_SET_ADDRESS:
357 ao_usb_set_address(ao_usb_setup.value);
359 case AO_USB_REQ_GET_DESCRIPTOR:
360 ao_usb_get_descriptor(ao_usb_setup.value);
362 case AO_USB_REQ_GET_CONFIGURATION:
363 ao_usb_ep0_queue_byte(ao_usb_configuration);
365 case AO_USB_REQ_SET_CONFIGURATION:
366 ao_usb_configuration = ao_usb_setup.value;
367 ao_usb_set_configuration();
371 case AO_USB_RECIP_INTERFACE:
372 #pragma disable_warning 110
373 switch(ao_usb_setup.request) {
374 case AO_USB_REQ_GET_STATUS:
375 ao_usb_ep0_queue_byte(0);
376 ao_usb_ep0_queue_byte(0);
378 case AO_USB_REQ_GET_INTERFACE:
379 ao_usb_ep0_queue_byte(0);
381 case AO_USB_REQ_SET_INTERFACE:
385 case AO_USB_RECIP_ENDPOINT:
386 switch(ao_usb_setup.request) {
387 case AO_USB_REQ_GET_STATUS:
388 ao_usb_ep0_queue_byte(0);
389 ao_usb_ep0_queue_byte(0);
395 case AO_USB_TYPE_CLASS:
396 switch (ao_usb_setup.request) {
397 case SET_LINE_CODING:
398 ao_usb_ep0_out_len = 7;
399 ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
401 case GET_LINE_CODING:
402 ao_usb_ep0_in_len = 7;
403 ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
405 case SET_CONTROL_LINE_STATE:
410 if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
411 if (ao_usb_setup.length < ao_usb_ep0_in_len)
412 ao_usb_ep0_in_len = ao_usb_setup.length;
417 /* End point 0 receives all of the control messages. */
423 ao_usb_ep0_state = AO_USB_EP0_IDLE;
425 __critical for (;;) {
426 if (ao_usb_iif & 1) {
430 ao_sleep(&ao_usb_task);
434 if (cs0 & USBCS0_SETUP_END) {
435 ao_usb_ep0_state = AO_USB_EP0_IDLE;
436 USBCS0 = USBCS0_CLR_SETUP_END;
438 if (cs0 & USBCS0_SENT_STALL) {
439 ao_usb_ep0_state = AO_USB_EP0_IDLE;
440 USBCS0 &= ~USBCS0_SENT_STALL;
442 if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN &&
443 (cs0 & USBCS0_INPKT_RDY) == 0)
447 if (cs0 & USBCS0_OUTPKT_RDY) {
448 switch (ao_usb_ep0_state) {
449 case AO_USB_EP0_IDLE:
452 case AO_USB_EP0_DATA_OUT:
454 if (ao_usb_ep0_out_len == 0)
455 ao_usb_ep0_state = AO_USB_EP0_IDLE;
457 if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
458 USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
460 USBCS0 = USBCS0_CLR_OUTPKT_RDY;
468 ao_usb_flush(void) __critical
470 if (ao_usb_in_bytes) {
471 USBINDEX = AO_USB_IN_EP;
472 USBCSIL |= USBCSIL_INPKT_RDY;
478 ao_usb_putchar(uint8_t c) __critical
481 USBINDEX = AO_USB_IN_EP;
482 if ((USBCSIL & USBCSIL_INPKT_RDY) == 0)
484 ao_sleep(&ao_usb_in_bytes);
486 USBFIFO[AO_USB_IN_EP << 1] = c;
487 if (++ao_usb_in_bytes == AO_USB_IN_SIZE) {
488 USBINDEX = AO_USB_IN_EP;
489 USBCSIL |= USBCSIL_INPKT_RDY;
495 ao_usb_getchar(void) __critical
498 while (ao_usb_out_bytes == 0) {
500 USBINDEX = AO_USB_OUT_EP;
501 if ((USBCSOL & USBCSOL_OUTPKT_RDY) != 0)
503 ao_sleep(&ao_usb_out_bytes);
505 ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL;
508 c = USBFIFO[AO_USB_OUT_EP << 1];
509 if (ao_usb_out_bytes == 0) {
510 USBINDEX = AO_USB_OUT_EP;
511 USBCSOL &= ~USBCSOL_OUTPKT_RDY;
519 /* Turn on the USB controller */
520 SLEEP |= SLEEP_USB_EN;
522 ao_usb_set_configuration();
524 /* IN interrupts on the control an IN endpoints */
525 USBIIE = (1 << AO_USB_CONTROL_EP) | (1 << AO_USB_IN_EP);
527 /* OUT interrupts on the OUT endpoint */
528 USBOIE = (1 << AO_USB_OUT_EP);
530 /* Ignore control interrupts */
533 /* enable USB interrupts */
536 /* Clear any pending interrupts */
541 ao_add_task(&ao_usb_task, ao_usb_ep0, "usb");