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 static __xdata uint16_t ao_usb_in_bytes;
24 static __xdata uint16_t ao_usb_out_bytes;
25 static __xdata uint8_t ao_usb_iif;
26 static __xdata uint8_t ao_usb_oif;
28 /* This interrupt is shared with port 2,
29 * so when we hook that up, fix this
32 ao_usb_isr(void) interrupt 6
37 ao_wakeup(&ao_usb_task);
38 if (ao_usb_iif & (1 << AO_USB_IN_EP))
39 ao_wakeup(&ao_usb_in_bytes);
42 if (ao_usb_oif & (1 << AO_USB_OUT_EP))
43 ao_wakeup(&ao_usb_out_bytes);
47 uint8_t dir_type_recip;
52 } __xdata ao_usb_setup;
54 __xdata uint8_t ao_usb_ep0_state;
55 uint8_t * __xdata ao_usb_ep0_in_data;
56 __xdata uint8_t ao_usb_ep0_in_len;
57 __xdata uint8_t ao_usb_ep0_in_buf[2];
58 __xdata uint8_t ao_usb_ep0_out_len;
59 __xdata uint8_t *__data ao_usb_ep0_out_data;
60 __xdata uint8_t ao_usb_configuration;
62 /* Send an IN data packet */
64 ao_usb_ep0_flush(void)
66 __xdata uint8_t this_len;
71 if (cs0 & USBCS0_INPKT_RDY)
74 this_len = ao_usb_ep0_in_len;
75 if (this_len > AO_USB_CONTROL_SIZE)
76 this_len = AO_USB_CONTROL_SIZE;
77 cs0 = USBCS0_INPKT_RDY;
78 if (this_len != AO_USB_CONTROL_SIZE) {
79 cs0 = USBCS0_INPKT_RDY | USBCS0_DATA_END;
80 ao_usb_ep0_state = AO_USB_EP0_IDLE;
82 ao_usb_ep0_in_len -= this_len;
84 USBFIFO[0] = *ao_usb_ep0_in_data++;
89 __xdata static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
91 /* Walk through the list of descriptors and find a match
94 ao_usb_get_descriptor(uint16_t value)
96 const uint8_t *__xdata descriptor;
97 __xdata uint8_t type = value >> 8;
98 __xdata uint8_t index = value;
100 descriptor = ao_usb_descriptors;
101 while (descriptor[0] != 0) {
102 if (descriptor[1] == type && index-- == 0) {
103 if (type == AO_USB_DESC_CONFIGURATION)
104 ao_usb_ep0_in_len = descriptor[2];
106 ao_usb_ep0_in_len = descriptor[0];
107 ao_usb_ep0_in_data = descriptor;
110 descriptor += descriptor[0];
114 /* Read data from the ep0 OUT fifo
117 ao_usb_ep0_fill(void)
123 if (len > ao_usb_ep0_out_len)
124 len = ao_usb_ep0_out_len;
125 ao_usb_ep0_out_len -= len;
127 *ao_usb_ep0_out_data++ = USBFIFO[0];
131 ao_usb_ep0_queue_byte(uint8_t a)
133 ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
137 ao_usb_set_address(uint8_t address)
139 USBADDR = address | 0x80;
140 while (USBADDR & 0x80)
145 ao_usb_set_configuration(void)
147 /* Set the IN max packet size, double buffered */
148 USBINDEX = AO_USB_IN_EP;
149 USBMAXI = AO_USB_IN_SIZE >> 3;
150 USBCSIH |= USBCSIH_IN_DBL_BUF;
152 /* Set the OUT max packet size, double buffered */
153 USBINDEX = AO_USB_OUT_EP;
154 USBMAXO = AO_USB_OUT_SIZE >> 3;
155 USBCSOH = USBCSOH_OUT_DBL_BUF;
159 ao_usb_ep0_setup(void)
161 /* Pull the setup packet out of the fifo */
162 ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup;
163 ao_usb_ep0_out_len = 8;
165 if (ao_usb_ep0_out_len != 0)
168 /* Figure out how to ACK the setup packet */
169 if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) {
170 if (ao_usb_setup.length)
171 ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
173 ao_usb_ep0_state = AO_USB_EP0_IDLE;
175 if (ao_usb_setup.length)
176 ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
178 ao_usb_ep0_state = AO_USB_EP0_IDLE;
181 if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
182 USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
184 USBCS0 = USBCS0_CLR_OUTPKT_RDY;
186 ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
187 ao_usb_ep0_in_len = 0;
188 switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
189 case AO_USB_TYPE_STANDARD:
190 switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
191 case AO_USB_RECIP_DEVICE:
192 switch(ao_usb_setup.request) {
193 case AO_USB_REQ_GET_STATUS:
194 ao_usb_ep0_queue_byte(0);
195 ao_usb_ep0_queue_byte(0);
197 case AO_USB_REQ_SET_ADDRESS:
198 ao_usb_set_address(ao_usb_setup.value);
200 case AO_USB_REQ_GET_DESCRIPTOR:
201 ao_usb_get_descriptor(ao_usb_setup.value);
203 case AO_USB_REQ_GET_CONFIGURATION:
204 ao_usb_ep0_queue_byte(ao_usb_configuration);
206 case AO_USB_REQ_SET_CONFIGURATION:
207 ao_usb_configuration = ao_usb_setup.value;
208 ao_usb_set_configuration();
212 case AO_USB_RECIP_INTERFACE:
213 #pragma disable_warning 110
214 switch(ao_usb_setup.request) {
215 case AO_USB_REQ_GET_STATUS:
216 ao_usb_ep0_queue_byte(0);
217 ao_usb_ep0_queue_byte(0);
219 case AO_USB_REQ_GET_INTERFACE:
220 ao_usb_ep0_queue_byte(0);
222 case AO_USB_REQ_SET_INTERFACE:
226 case AO_USB_RECIP_ENDPOINT:
227 switch(ao_usb_setup.request) {
228 case AO_USB_REQ_GET_STATUS:
229 ao_usb_ep0_queue_byte(0);
230 ao_usb_ep0_queue_byte(0);
236 case AO_USB_TYPE_CLASS:
237 switch (ao_usb_setup.request) {
238 case SET_LINE_CODING:
239 ao_usb_ep0_out_len = 7;
240 ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
242 case GET_LINE_CODING:
243 ao_usb_ep0_in_len = 7;
244 ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
246 case SET_CONTROL_LINE_STATE:
251 if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
252 if (ao_usb_setup.length < ao_usb_ep0_in_len)
253 ao_usb_ep0_in_len = ao_usb_setup.length;
258 /* End point 0 receives all of the control messages. */
264 ao_usb_ep0_state = AO_USB_EP0_IDLE;
266 __critical for (;;) {
267 if (ao_usb_iif & 1) {
271 ao_sleep(&ao_usb_task);
275 if (cs0 & USBCS0_SETUP_END) {
276 ao_usb_ep0_state = AO_USB_EP0_IDLE;
277 USBCS0 = USBCS0_CLR_SETUP_END;
279 if (cs0 & USBCS0_SENT_STALL) {
280 ao_usb_ep0_state = AO_USB_EP0_IDLE;
281 USBCS0 &= ~USBCS0_SENT_STALL;
283 if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN &&
284 (cs0 & USBCS0_INPKT_RDY) == 0)
288 if (cs0 & USBCS0_OUTPKT_RDY) {
289 switch (ao_usb_ep0_state) {
290 case AO_USB_EP0_IDLE:
293 case AO_USB_EP0_DATA_OUT:
295 if (ao_usb_ep0_out_len == 0)
296 ao_usb_ep0_state = AO_USB_EP0_IDLE;
298 if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
299 USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
301 USBCS0 = USBCS0_CLR_OUTPKT_RDY;
309 ao_usb_flush(void) __critical
311 if (ao_usb_in_bytes) {
312 USBINDEX = AO_USB_IN_EP;
313 USBCSIL |= USBCSIL_INPKT_RDY;
319 ao_usb_putchar(char c) __critical
322 USBINDEX = AO_USB_IN_EP;
323 if ((USBCSIL & USBCSIL_INPKT_RDY) == 0)
325 ao_sleep(&ao_usb_in_bytes);
327 USBFIFO[AO_USB_IN_EP << 1] = c;
328 if (++ao_usb_in_bytes == AO_USB_IN_SIZE) {
329 USBINDEX = AO_USB_IN_EP;
330 USBCSIL |= USBCSIL_INPKT_RDY;
336 ao_usb_getchar(void) __critical
339 while (ao_usb_out_bytes == 0) {
341 USBINDEX = AO_USB_OUT_EP;
342 if ((USBCSOL & USBCSOL_OUTPKT_RDY) != 0)
344 ao_sleep(&ao_usb_out_bytes);
346 ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL;
349 c = USBFIFO[AO_USB_OUT_EP << 1];
350 if (ao_usb_out_bytes == 0) {
351 USBINDEX = AO_USB_OUT_EP;
352 USBCSOL &= ~USBCSOL_OUTPKT_RDY;
360 /* Turn on the USB controller */
361 SLEEP |= SLEEP_USB_EN;
363 ao_usb_set_configuration();
365 /* IN interrupts on the control an IN endpoints */
366 USBIIE = (1 << AO_USB_CONTROL_EP) | (1 << AO_USB_IN_EP);
368 /* OUT interrupts on the OUT endpoint */
369 USBOIE = (1 << AO_USB_OUT_EP);
371 /* Ignore control interrupts */
374 /* enable USB interrupts */
377 /* Clear any pending interrupts */
386 /* Disable USB interrupts */
392 /* Clear any pending interrupts */
397 /* Turn off the USB controller */
398 SLEEP &= ~SLEEP_USB_EN;
406 ao_add_task(&ao_usb_task, ao_usb_ep0, "usb");