Switch from GPLv2 to GPLv2+
[fw/altos] / src / cc1111 / ao_usb.c
index f66e807cf7bf13e2d9c774a56645cde6fca4923a..7d363c08fe92ef86cdff5eec64745c43bbbf104f 100644 (file)
@@ -3,7 +3,8 @@
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -24,7 +25,7 @@ static __xdata uint16_t       ao_usb_in_bytes;
 static __pdata uint16_t ao_usb_in_bytes_last;
 static __xdata uint16_t        ao_usb_out_bytes;
 static __pdata uint8_t ao_usb_iif;
-static __pdata uint8_t ao_usb_running;
+__pdata uint8_t                ao_usb_running;
 
 static void
 ao_usb_set_interrupts(void)
@@ -152,19 +153,17 @@ ao_usb_ep0_fill(void)
                *ao_usb_ep0_out_data++ = USBFIFO[0];
 }
 
-void
+static void
 ao_usb_ep0_queue_byte(uint8_t a)
 {
        ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
 }
 
-void
+static void
 ao_usb_set_address(uint8_t address)
 {
        ao_usb_running = 1;
-       USBADDR = address | 0x80;
-       while (USBADDR & 0x80)
-               ;
+       USBADDR = address;
 }
 
 static void
@@ -191,24 +190,6 @@ ao_usb_ep0_setup(void)
        if (ao_usb_ep0_out_len != 0)
                return;
 
-       /* Figure out how to ACK the setup packet */
-       if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) {
-               if (ao_usb_setup.length)
-                       ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
-               else
-                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
-       } else {
-               if (ao_usb_setup.length)
-                       ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
-               else
-                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
-       }
-       USBINDEX = 0;
-       if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
-               USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
-       else
-               USBCS0 = USBCS0_CLR_OUTPKT_RDY;
-
        ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
        ao_usb_ep0_in_len = 0;
        switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
@@ -221,6 +202,11 @@ ao_usb_ep0_setup(void)
                                ao_usb_ep0_queue_byte(0);
                                break;
                        case AO_USB_REQ_SET_ADDRESS:
+#if USB_FORCE_FLIGHT_IDLE
+                               /* Go to idle mode if USB is connected
+                                */
+                               ao_flight_force_idle = 1;
+#endif
                                ao_usb_set_address(ao_usb_setup.value);
                                break;
                        case AO_USB_REQ_GET_DESCRIPTOR:
@@ -274,10 +260,39 @@ ao_usb_ep0_setup(void)
                }
                break;
        }
-       if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
+
+       /* Figure out how to ACK the setup packet and the
+        * next state
+        */
+       USBINDEX = 0;
+       if (ao_usb_ep0_in_len) {
+
+               /* Sending data back to the host
+                */
+               ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY;
                if (ao_usb_setup.length < ao_usb_ep0_in_len)
                        ao_usb_ep0_in_len = ao_usb_setup.length;
                ao_usb_ep0_flush();
+       } else if (ao_usb_ep0_out_len) {
+
+               /* Receiving data from the host
+                */
+               ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY;
+       } else if (ao_usb_setup.length) {
+
+               /* Uh-oh, the host expected to send or receive data
+                * and we don't know what to do.
+                */
+               ao_usb_ep0_state = AO_USB_EP0_STALL;
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_SEND_STALL;
+       } else {
+
+               /* Simple setup packet with no data
+                */
+               ao_usb_ep0_state = AO_USB_EP0_IDLE;
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
        }
 }
 
@@ -299,12 +314,12 @@ ao_usb_ep0(void)
                USBINDEX = 0;
                cs0 = USBCS0;
                if (cs0 & USBCS0_SETUP_END) {
-                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
                        USBCS0 = USBCS0_CLR_SETUP_END;
+                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
                }
                if (cs0 & USBCS0_SENT_STALL) {
+                       USBCS0 = 0;
                        ao_usb_ep0_state = AO_USB_EP0_IDLE;
-                       USBCS0 &= ~USBCS0_SENT_STALL;
                }
                if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN &&
                    (cs0 & USBCS0_INPKT_RDY) == 0)
@@ -318,12 +333,11 @@ ao_usb_ep0(void)
                                break;
                        case AO_USB_EP0_DATA_OUT:
                                ao_usb_ep0_fill();
-                               if (ao_usb_ep0_out_len == 0)
-                                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
                                USBINDEX = 0;
-                               if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
+                               if (ao_usb_ep0_out_len == 0) {
+                                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
                                        USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
-                               else
+                               else
                                        USBCS0 = USBCS0_CLR_OUTPKT_RDY;
                                break;
                        }
@@ -383,18 +397,18 @@ ao_usb_putchar(char c) __critical __reentrant
 }
 
 int
-ao_usb_pollchar(void) __critical
+_ao_usb_pollchar(void)
 {
        uint8_t c;
        if (ao_usb_out_bytes == 0) {
                USBINDEX = AO_USB_OUT_EP;
                if ((USBCSOL & USBCSOL_OUTPKT_RDY) == 0)
-                       return -1;
+                       return AO_READ_AGAIN;
                ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL;
                if (ao_usb_out_bytes == 0) {
                        USBINDEX = AO_USB_OUT_EP;
                        USBCSOL &= ~USBCSOL_OUTPKT_RDY;
-                       return -1;
+                       return AO_READ_AGAIN;
                }
        }
        --ao_usb_out_bytes;
@@ -407,12 +421,14 @@ ao_usb_pollchar(void) __critical
 }
 
 char
-ao_usb_getchar(void) __critical
+ao_usb_getchar(void)
 {
        int     c;
 
-       while ((c = ao_usb_pollchar()) == AO_READ_AGAIN)
+       ao_arch_block_interrupts();
+       while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
                ao_sleep(&ao_stdin_ready);
+       ao_arch_release_interrupts();
        return c;
 }
 
@@ -433,6 +449,9 @@ ao_usb_enable(void)
        USBCIF = 0;
        USBOIF = 0;
        USBIIF = 0;
+#if HAS_USB_PULLUP
+       ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, AO_USB_PULLUP, 0);
+#endif
 }
 
 void
@@ -444,6 +463,10 @@ ao_usb_disable(void)
        USBCIE = 0;
        IEN2 &= ~IEN2_USBIE;
 
+#if HAS_USB_PULLUP
+       ao_gpio_set(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, AO_USB_PULLUP, 0);
+#endif
+
        /* Clear any pending interrupts */
        USBCIF = 0;
        USBOIF = 0;
@@ -456,8 +479,11 @@ ao_usb_disable(void)
 void
 ao_usb_init(void)
 {
+#if HAS_USB_PULLUP
+       ao_enable_output(AO_USB_PULLUP_PORT, AO_USB_PULLUP_PIN, AO_USB_PULLUP, 0);
+#endif
        ao_usb_enable();
 
        ao_add_task(&ao_usb_task, ao_usb_ep0, "usb");
-       ao_add_stdio(ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+       ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
 }