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;
38 /* This interrupt is shared with port 2,
39 * so when we hook that up, fix this
42 ao_usb_isr(void) interrupt 6
48 ao_wakeup(&ao_usb_task);
49 if (usb_if & (1 << AO_USB_IN_EP))
50 ao_wakeup(&ao_usb_in_bytes);
53 if (usb_if & (1 << AO_USB_OUT_EP))
54 ao_wakeup(&ao_usb_out_bytes);
57 #define AO_USB_EP0_IDLE 0
58 #define AO_USB_EP0_DATA_IN 1
59 #define AO_USB_EP0_DATA_OUT 2
71 __data uint8_t ao_usb_ep0_state;
72 uint8_t * __data ao_usb_ep0_in_data;
73 __data uint8_t ao_usb_ep0_in_len;
74 __xdata uint8_t ao_usb_ep0_in_buf[2];
75 __data uint8_t ao_usb_ep0_out_len;
76 __xdata uint8_t *__data ao_usb_ep0_out_data;
77 __data uint8_t ao_usb_configuration;
79 /* Send an IN data packet */
81 ao_usb_ep0_flush(void)
86 if (ao_usb_ep0_state != AO_USB_EP0_DATA_IN)
89 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 if (this_len != AO_USB_CONTROL_SIZE) {
94 cs0 = USBCS0_INPKT_RDY | USBCS0_DATA_END;
95 ao_usb_ep0_state = AO_USB_EP0_IDLE;
97 ao_usb_ep0_in_len -= this_len;
99 USBFIFO[0] = *ao_usb_ep0_in_data++;
104 #define LE_WORD(x) ((x)&0xFF),((x)>>8)
106 /* CDC definitions */
107 #define CS_INTERFACE 0x24
108 #define CS_ENDPOINT 0x25
110 #define SET_LINE_CODING 0x20
111 #define GET_LINE_CODING 0x21
112 #define SET_CONTROL_LINE_STATE 0x22
114 /* Data structure for GET_LINE_CODING / SET_LINE_CODING class requests */
115 struct ao_usb_line_coding {
122 static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
124 /* USB descriptors in one giant block of bytes */
125 static const uint8_t ao_usb_descriptors [] =
127 /* Device descriptor */
130 LE_WORD(0x0110), /* bcdUSB */
131 0x02, /* bDeviceClass */
132 0x00, /* bDeviceSubClass */
133 0x00, /* bDeviceProtocol */
134 AO_USB_CONTROL_SIZE, /* bMaxPacketSize */
135 LE_WORD(0xFFFE), /* idVendor */
136 LE_WORD(0x000A), /* idProduct */
137 LE_WORD(0x0100), /* bcdDevice */
138 0x01, /* iManufacturer */
140 0x03, /* iSerialNumber */
141 0x01, /* bNumConfigurations */
143 /* Configuration descriptor */
145 AO_USB_DESC_CONFIGURATION,
146 LE_WORD(67), /* wTotalLength */
147 0x02, /* bNumInterfaces */
148 0x01, /* bConfigurationValue */
149 0x00, /* iConfiguration */
150 0xC0, /* bmAttributes */
151 0x32, /* bMaxPower */
153 /* Control class interface */
155 AO_USB_DESC_INTERFACE,
156 0x00, /* bInterfaceNumber */
157 0x00, /* bAlternateSetting */
158 0x01, /* bNumEndPoints */
159 0x02, /* bInterfaceClass */
160 0x02, /* bInterfaceSubClass */
161 0x01, /* bInterfaceProtocol, linux requires value of 1 for the cdc_acm module */
162 0x00, /* iInterface */
164 /* Header functional descriptor */
167 0x00, /* bDescriptor SubType Header */
168 LE_WORD(0x0110), /* CDC version 1.1 */
170 /* Call management functional descriptor */
173 0x01, /* bDescriptor SubType Call Management */
174 0x01, /* bmCapabilities = device handles call management */
175 0x01, /* bDataInterface call management interface number */
177 /* ACM functional descriptor */
180 0x02, /* bDescriptor SubType Abstract Control Management */
181 0x02, /* bmCapabilities = D1 (Set_line_Coding, Set_Control_Line_State, Get_Line_Coding and Serial_State) */
183 /* Union functional descriptor */
186 0x06, /* bDescriptor SubType Union Functional descriptor */
187 0x00, /* bMasterInterface */
188 0x01, /* bSlaveInterface0 */
190 /* Notification EP */
192 AO_USB_DESC_ENDPOINT,
193 AO_USB_INT_EP|0x80, /* bEndpointAddress */
194 0x03, /* bmAttributes = intr */
195 LE_WORD(8), /* wMaxPacketSize */
196 0x0A, /* bInterval */
198 /* Data class interface descriptor */
200 AO_USB_DESC_INTERFACE,
201 0x01, /* bInterfaceNumber */
202 0x00, /* bAlternateSetting */
203 0x02, /* bNumEndPoints */
204 0x0A, /* bInterfaceClass = data */
205 0x00, /* bInterfaceSubClass */
206 0x00, /* bInterfaceProtocol */
207 0x00, /* iInterface */
211 AO_USB_DESC_ENDPOINT,
212 AO_USB_OUT_EP, /* bEndpointAddress */
213 0x02, /* bmAttributes = bulk */
214 LE_WORD(AO_USB_OUT_SIZE),/* wMaxPacketSize */
215 0x00, /* bInterval */
219 AO_USB_DESC_ENDPOINT,
220 AO_USB_IN_EP|0x80, /* bEndpointAddress */
221 0x02, /* bmAttributes = bulk */
222 LE_WORD(AO_USB_IN_SIZE),/* wMaxPacketSize */
223 0x00, /* bInterval */
225 /* String descriptors */
233 '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,
238 'T', 0, 'e', 0, 'l', 0, 'e', 0, 'M', 0, 'e', 0, 't', 0, 'r', 0, 'u', 0, 'm', 0,
243 't', 0, 'e', 0, 'l', 0, 'e', 0, '-', 0, '0', 0,
245 /* Terminating zero */
250 ao_usb_get_descriptor(uint8_t type, uint8_t id)
252 const uint8_t *descriptor;
254 descriptor = ao_usb_descriptors;
255 ao_usb_ep0_in_len = 0;
256 ao_usb_ep0_in_data = NULL;
257 while (descriptor[0] != 0) {
258 if (descriptor[1] == type && id-- == 0) {
259 if (type == AO_USB_DESC_CONFIGURATION)
260 ao_usb_ep0_in_len = descriptor[2];
262 ao_usb_ep0_in_len = descriptor[0];
263 ao_usb_ep0_in_data = descriptor;
266 descriptor += descriptor[0];
268 ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
272 ao_usb_ep0_fill(void)
278 if (len > ao_usb_ep0_out_len)
279 len = ao_usb_ep0_out_len;
280 ao_usb_ep0_out_len -= len;
282 *ao_usb_ep0_out_data++ = USBFIFO[0];
286 ao_usb_send_two_bytes(uint8_t a, uint8_t b)
288 ao_usb_ep0_in_buf[0] = a;
289 ao_usb_ep0_in_buf[1] = b;
290 ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
291 ao_usb_ep0_in_len = 2;
292 ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
296 ao_usb_send_one_byte(uint8_t a)
298 ao_usb_ep0_in_buf[0] = a;
299 ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
300 ao_usb_ep0_in_len = 1;
301 ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
305 ao_usb_send_zero_bytes(void)
307 ao_usb_ep0_in_len = 0;
308 ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
312 ao_usb_set_address(uint8_t address)
314 USBADDR = address | 0x80;
315 while (USBADDR & 0x80)
320 ao_usb_ep0_setup(void)
322 ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup;
323 ao_usb_ep0_out_len = 8;
325 if (ao_usb_ep0_out_len > 0)
327 switch(ao_usb_setup.type) {
328 case AO_USB_TYPE_STANDARD:
329 switch(ao_usb_setup.recip) {
330 case AO_USB_RECIP_DEVICE:
331 switch(ao_usb_setup.request) {
332 case AO_USB_REQ_GET_STATUS:
333 ao_usb_send_two_bytes(0,0);
335 case AO_USB_REQ_SET_ADDRESS:
336 ao_usb_set_address(ao_usb_setup.value);
338 case AO_USB_REQ_GET_DESCRIPTOR:
339 ao_usb_get_descriptor(ao_usb_setup.value,
342 case AO_USB_REQ_GET_CONFIGURATION:
343 ao_usb_send_one_byte(ao_usb_configuration);
345 case AO_USB_REQ_SET_CONFIGURATION:
346 ao_usb_configuration = ao_usb_setup.value;
350 case AO_USB_RECIP_INTERFACE:
351 switch(ao_usb_setup.request) {
352 case AO_USB_REQ_GET_STATUS:
353 ao_usb_send_two_bytes(0,0);
355 case AO_USB_REQ_GET_INTERFACE:
356 ao_usb_send_one_byte(0);
358 case AO_USB_REQ_SET_INTERFACE:
359 ao_usb_send_zero_bytes();
363 case AO_USB_RECIP_ENDPOINT:
364 switch(ao_usb_setup.request) {
365 case AO_USB_REQ_GET_STATUS:
366 ao_usb_send_two_bytes(0, 0);
372 case AO_USB_TYPE_CLASS:
373 switch (ao_usb_setup.request) {
374 case SET_LINE_CODING:
375 ao_usb_ep0_out_len = 7;
376 ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
377 ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
379 case GET_LINE_CODING:
380 ao_usb_ep0_in_len = 7;
381 ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
382 ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
384 case SET_CONTROL_LINE_STATE:
392 /* End point 0 receives all of the control messages. */
398 ao_usb_ep0_state = AO_USB_EP0_IDLE;
400 ao_sleep(&ao_usb_task);
403 if (cs0 & USBCS0_SETUP_END) {
404 ao_usb_ep0_state = AO_USB_EP0_IDLE;
405 USBCS0 = USBCS0_CLR_SETUP_END;
407 if (cs0 & USBCS0_SENT_STALL) {
408 ao_usb_ep0_state = AO_USB_EP0_IDLE;
409 USBCS0 &= ~USBCS0_SENT_STALL;
411 if (cs0 & USBCS0_INPKT_RDY) {
414 if (cs0 & USBCS0_OUTPKT_RDY) {
415 switch (ao_usb_ep0_state) {
416 case AO_USB_EP0_IDLE:
419 case AO_USB_EP0_DATA_OUT:
421 if (ao_usb_ep0_out_len == 0)
422 ao_usb_ep0_state = AO_USB_EP0_IDLE;
426 if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
427 USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
429 USBCS0 = USBCS0_CLR_OUTPKT_RDY;
437 ao_interrupt_disable();
438 if (ao_usb_in_bytes) {
439 USBINDEX = AO_USB_IN_EP;
440 USBCSIL |= USBCSIL_INPKT_RDY;
443 ao_interrupt_enable();
447 ao_usb_putchar(uint8_t c)
449 ao_interrupt_disable();
451 USBINDEX = AO_USB_IN_EP;
452 if ((USBCSIL & USBCSIL_INPKT_RDY) == 0)
454 ao_sleep(&ao_usb_in_bytes);
456 USBFIFO[AO_USB_IN_EP << 1] = c;
457 if (++ao_usb_in_bytes == AO_USB_IN_SIZE)
459 ao_interrupt_enable();
466 ao_interrupt_disable();
467 while (ao_usb_out_bytes == 0) {
469 USBINDEX = AO_USB_OUT_EP;
470 if ((USBCSOL & USBCSOL_OUTPKT_RDY) != 0)
472 ao_sleep(&ao_usb_out_bytes);
474 ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL;
477 c = USBFIFO[AO_USB_OUT_EP << 1];
478 ao_interrupt_enable();
485 /* Turn on the USB controller */
486 SLEEP |= SLEEP_USB_EN;
488 /* Set the IN max packet size, double buffered */
489 USBINDEX = AO_USB_IN_EP;
490 USBMAXI = AO_USB_IN_SIZE >> 3;
491 USBCSIH |= USBCSIH_IN_DBL_BUF;
493 /* Set the OUT max packet size, double buffered */
494 USBINDEX = AO_USB_OUT_EP;
495 USBMAXO = AO_USB_OUT_SIZE >> 3;
496 USBCSOH = USBCSOH_OUT_DBL_BUF;
498 /* IN interrupts on the control an IN endpoints */
499 USBIIE = (1 << AO_USB_CONTROL_EP) | (1 << AO_USB_IN_EP);
501 /* OUT interrupts on the OUT endpoint */
502 USBOIE = (1 << AO_USB_OUT_EP);
504 /* Ignore control interrupts */
507 /* Clear any pending interrupts */
512 ao_add_task(&ao_usb_task, ao_usb_ep0);