From 106d43a3fa5b1fc5278eaefd879739d917bdecf7 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 15 May 2011 21:55:27 -0700 Subject: [PATCH] altos-avr: Start writing USB driver. Adapt ao_usb to new environment. Signed-off-by: Keith Packard --- ChangeLog | 4 + src-avr/ao_pins.h | 14 ++ src-avr/ao_timer.c | 2 +- src-avr/ao_usb_avr.c | 512 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 531 insertions(+), 1 deletion(-) create mode 100644 src-avr/ao_usb_avr.c diff --git a/ChangeLog b/ChangeLog index f98de6d6..7cd16387 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2011-05-15 Keith Packard + + * src-avr/ao_usb_avr.c (ao_usb_set_configuration): o + commit 0630e7d6d8cf6abf0fe07f9a6df40ee472cce1ef Author: Bdale Garbee Date: Fri Feb 18 19:54:18 2011 -0700 diff --git a/src-avr/ao_pins.h b/src-avr/ao_pins.h index d99592d4..11f12ef7 100644 --- a/src-avr/ao_pins.h +++ b/src-avr/ao_pins.h @@ -250,6 +250,20 @@ #define HAS_USB 0 #define PACKET_HAS_SLAVE 0 #define HAS_SERIAL_1 1 + #define TEENSY 1 + #define AVR_VCC_5V 1 + #define AVR_VCC_3V3 0 + #define AVR_CLOCK 16000000UL +#endif + +#ifdef TELESCIENCE + #define AO_LED_RED (1<<7) + #define LEDS_AVAILABLE (AO_LED_RED) + #define HAS_USB 1 + #define TEENSY 0 + #define AVR_VCC_5V 0 + #define AVR_VCC_3V3 1 + #define AVR_CLOCK 8000000UL #endif #ifndef AVR diff --git a/src-avr/ao_timer.c b/src-avr/ao_timer.c index 01bf8e0c..9142a497 100644 --- a/src-avr/ao_timer.c +++ b/src-avr/ao_timer.c @@ -149,7 +149,7 @@ ao_clock_init(void) (0x4 << PDIV0)); /* 48MHz PLL clock */ /* Set the frequency of the crystal */ -#if TEENSY +#if AVR_CLOCK > 12000000UL PLLCSR |= (1 << PINDIV); /* For 16MHz crystal on Teensy board */ #else PLLCSR &= ~(1 << PINDIV); /* For 8MHz crystal on TeleScience board */ diff --git a/src-avr/ao_usb_avr.c b/src-avr/ao_usb_avr.c new file mode 100644 index 00000000..ba9361ee --- /dev/null +++ b/src-avr/ao_usb_avr.c @@ -0,0 +1,512 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +struct ao_task __xdata ao_usb_task; + +static __xdata uint16_t ao_usb_in_bytes; +static __xdata uint16_t ao_usb_in_bytes_last; +static __xdata uint16_t ao_usb_out_bytes; +static __xdata uint8_t ao_usb_iif; +static __xdata uint8_t ao_usb_running; + +static void +ao_usb_set_interrupts(void) +{ + /* IN interrupts on the control an IN endpoints */ + USBIIE = (1 << AO_USB_CONTROL_EP) | (1 << AO_USB_IN_EP); + + /* OUT interrupts on the OUT endpoint */ + USBOIE = (1 << AO_USB_OUT_EP); + + /* Only care about reset */ + USBCIE = USBCIE_RSTIE; +} + +void +ao_usb_set_address(uint8_t address) +{ + UDADD = (0 << ADDEN) | address; + ao_usb_running = 1; +} + +#define EP_SIZE(s) ((s) == 64 ? 0x30 : \ + ((s) == 32 ? 0x20 : \ + ((s) == 16 ? 0x10 : \ + 0x00))) + +static void +ao_usb_set_ep0(void) +{ + /* Set the CONTROL max packet size, single buffered */ + UENUM = 0; + UECONX = (1 << EPEN); /* Enable */ + + UECFG0X = ((0 << EPTYPE0) | /* Control */ + (0 << EPDIR)); /* Out (ish) */ + + UECFG1X = ((EP_SIZE(AO_USB_CONTROL_SIZE) << EPSIZE0) | /* Size */ + (0 << EPBK)); /* Single bank */ + + UEIENX = ((1 << RXSTPE) | /* Enable SETUP interrupt */ + (1 << RXOUTE) | /* Enable OUT interrupt */ + (1 << TXINE)); /* Enable IN complete interrupt */ +} + +static void +ao_usb_set_configuration(void) +{ + /* Set the IN max packet size, double buffered */ + UENUM = AO_USB_IN_EP; + UECONX = (1 << EPEN); /* Enable */ + + UECFG0X = ((2 << EPTYPE0) | /* Bulk */ + (1 << EPDIR)); /* In */ + + UECFG1X = ((EP_SIZE(AO_USB_IN_SIZE) << EPSIZE0) | /* Size */ + (1 << EPBK) | /* Double bank */ + (1 << ALLOC)); /* Allocate */ + + /* Set the OUT max packet size, double buffered */ + UENUM = AO_USB_OUT_EP; + UECONX |= (1 << EPEN); /* Enable */ + + UECFG0X = ((2 << EPTYPE0) | /* Bulk */ + (0 << EPDIR)); /* Out */ + + UECFG1X = ((EP_SIZE(AO_USB_OUT_SIZE) << EPSIZE0) | /* Size */ + (1 << EPBK) | /* Double bank */ + (1 << ALLOC)); /* Allocate */ +} + +ISR(USB_GEN_vect) +{ + uint8_t intbits; + + intbits = UDINT; + UDINT = 0; + if (intbits & (1< AO_USB_CONTROL_SIZE) + this_len = AO_USB_CONTROL_SIZE; + + ao_usb_ep0_in_len -= this_len; + while (this_len--) + UEDATX = *ao_usb_ep0_in_data++; + + /* Clear the TXINI bit to send the packet */ + UEINTX &= ~(1 << TXINI); +} + +__xdata static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8}; + +/* Walk through the list of descriptors and find a match + */ +static void +ao_usb_get_descriptor(uint16_t value) +{ + const uint8_t *__xdata descriptor; + __xdata uint8_t type = value >> 8; + __xdata uint8_t index = value; + + descriptor = ao_usb_descriptors; + while (descriptor[0] != 0) { + if (descriptor[1] == type && index-- == 0) { + if (type == AO_USB_DESC_CONFIGURATION) + ao_usb_ep0_in_len = descriptor[2]; + else + ao_usb_ep0_in_len = descriptor[0]; + ao_usb_ep0_in_data = descriptor; + break; + } + descriptor += descriptor[0]; + } +} + +/* Read data from the ep0 OUT fifo + */ +static void +ao_usb_ep0_fill(void) +{ + __xdata uint8_t len; + + UENUM = 0; + len = UEBCLX; /* read length */ + if (len > ao_usb_ep0_out_len) + len = ao_usb_ep0_out_len; + ao_usb_ep0_out_len -= len; + + /* Pull all of the data out of the packet */ + while (len--) + *ao_usb_ep0_out_data++ = UEDATX; +} + +void +ao_usb_ep0_queue_byte(uint8_t a) +{ + ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a; +} + +static void +ao_usb_ep0_setup(void) +{ + /* Pull the setup packet out of the fifo */ + ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup; + ao_usb_ep0_out_len = 8; + ao_usb_ep0_fill(); + UENUM = 0; + UEINTX &= ~(1 << RXSTPI); + 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; + } + UENUM = 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) { + case AO_USB_TYPE_STANDARD: + switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) { + case AO_USB_RECIP_DEVICE: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_queue_byte(0); + ao_usb_ep0_queue_byte(0); + break; + case AO_USB_REQ_SET_ADDRESS: + ao_usb_set_address(ao_usb_setup.value); + break; + case AO_USB_REQ_GET_DESCRIPTOR: + ao_usb_get_descriptor(ao_usb_setup.value); + break; + case AO_USB_REQ_GET_CONFIGURATION: + ao_usb_ep0_queue_byte(ao_usb_configuration); + break; + case AO_USB_REQ_SET_CONFIGURATION: + ao_usb_configuration = ao_usb_setup.value; + ao_usb_set_configuration(); + break; + } + break; + case AO_USB_RECIP_INTERFACE: + #pragma disable_warning 110 + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_queue_byte(0); + ao_usb_ep0_queue_byte(0); + break; + case AO_USB_REQ_GET_INTERFACE: + ao_usb_ep0_queue_byte(0); + break; + case AO_USB_REQ_SET_INTERFACE: + break; + } + break; + case AO_USB_RECIP_ENDPOINT: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_queue_byte(0); + ao_usb_ep0_queue_byte(0); + break; + } + break; + } + break; + case AO_USB_TYPE_CLASS: + switch (ao_usb_setup.request) { + case SET_LINE_CODING: + ao_usb_ep0_out_len = 7; + ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding; + break; + case GET_LINE_CODING: + ao_usb_ep0_in_len = 7; + ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding; + break; + case SET_CONTROL_LINE_STATE: + break; + } + break; + } + if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) { + if (ao_usb_setup.length < ao_usb_ep0_in_len) + ao_usb_ep0_in_len = ao_usb_setup.length; + ao_usb_ep0_flush(); + } +} + +/* End point 0 receives all of the control messages. */ +static void +ao_usb_ep0(void) +{ + __xdata uint8_t intx; + + ao_usb_ep0_state = AO_USB_EP0_IDLE; + for (;;) { + cli(); + for (;;) { + UENUM = 0; + intx = UEINTX; + if (intx & ((1 << RXOUTI) | (1 <