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 __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;
68 } __xdata ao_usb_setup;
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)
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 switch(ao_usb_setup.request) {
373 case AO_USB_REQ_GET_STATUS:
374 ao_usb_ep0_queue_byte(0);
375 ao_usb_ep0_queue_byte(0);
377 case AO_USB_REQ_GET_INTERFACE:
378 ao_usb_ep0_queue_byte(0);
380 case AO_USB_REQ_SET_INTERFACE:
384 case AO_USB_RECIP_ENDPOINT:
385 switch(ao_usb_setup.request) {
386 case AO_USB_REQ_GET_STATUS:
387 ao_usb_ep0_queue_byte(0);
388 ao_usb_ep0_queue_byte(0);
394 case AO_USB_TYPE_CLASS:
395 switch (ao_usb_setup.request) {
396 case SET_LINE_CODING:
397 ao_usb_ep0_out_len = 7;
398 ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
400 case GET_LINE_CODING:
401 ao_usb_ep0_in_len = 7;
402 ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
404 case SET_CONTROL_LINE_STATE:
409 if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
410 if (ao_usb_setup.length < ao_usb_ep0_in_len)
411 ao_usb_ep0_in_len = ao_usb_setup.length;
416 /* End point 0 receives all of the control messages. */
422 ao_usb_ep0_state = AO_USB_EP0_IDLE;
424 __critical for (;;) {
425 if (ao_usb_iif & 1) {
429 ao_sleep(&ao_usb_task);
433 if (cs0 & USBCS0_SETUP_END) {
434 ao_usb_ep0_state = AO_USB_EP0_IDLE;
435 USBCS0 = USBCS0_CLR_SETUP_END;
437 if (cs0 & USBCS0_SENT_STALL) {
438 ao_usb_ep0_state = AO_USB_EP0_IDLE;
439 USBCS0 &= ~USBCS0_SENT_STALL;
441 if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN &&
442 (cs0 & USBCS0_INPKT_RDY) == 0)
446 if (cs0 & USBCS0_OUTPKT_RDY) {
447 switch (ao_usb_ep0_state) {
448 case AO_USB_EP0_IDLE:
451 case AO_USB_EP0_DATA_OUT:
453 if (ao_usb_ep0_out_len == 0)
454 ao_usb_ep0_state = AO_USB_EP0_IDLE;
456 if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
457 USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
459 USBCS0 = USBCS0_CLR_OUTPKT_RDY;
467 ao_usb_flush(void) __critical
469 if (ao_usb_in_bytes) {
470 USBINDEX = AO_USB_IN_EP;
471 USBCSIL |= USBCSIL_INPKT_RDY;
477 ao_usb_putchar(uint8_t c) __critical
480 USBINDEX = AO_USB_IN_EP;
481 if ((USBCSIL & USBCSIL_INPKT_RDY) == 0)
483 ao_sleep(&ao_usb_in_bytes);
485 USBFIFO[AO_USB_IN_EP << 1] = c;
486 if (++ao_usb_in_bytes == AO_USB_IN_SIZE) {
487 USBINDEX = AO_USB_IN_EP;
488 USBCSIL |= USBCSIL_INPKT_RDY;
494 ao_usb_getchar(void) __critical
497 while (ao_usb_out_bytes == 0) {
499 USBINDEX = AO_USB_OUT_EP;
500 if ((USBCSOL & USBCSOL_OUTPKT_RDY) != 0)
502 ao_sleep(&ao_usb_out_bytes);
504 ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL;
507 c = USBFIFO[AO_USB_OUT_EP << 1];
508 if (ao_usb_out_bytes == 0) {
509 USBINDEX = AO_USB_OUT_EP;
510 USBCSOL &= ~USBCSOL_OUTPKT_RDY;
518 /* Turn on the USB controller */
519 SLEEP |= SLEEP_USB_EN;
521 ao_usb_set_configuration();
523 /* IN interrupts on the control an IN endpoints */
524 USBIIE = (1 << AO_USB_CONTROL_EP) | (1 << AO_USB_IN_EP);
526 /* OUT interrupts on the OUT endpoint */
527 USBOIE = (1 << AO_USB_OUT_EP);
529 /* Ignore control interrupts */
532 /* enable USB interrupts */
535 /* Clear any pending interrupts */
540 ao_add_task(&ao_usb_task, ao_usb_ep0);