123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
-
-
- /* Copyright (c) 2010, Peter Barrett
- **
- ** Permission to use, copy, modify, and/or distribute this software for
- ** any purpose with or without fee is hereby granted, provided that the
- ** above copyright notice and this permission notice appear in all copies.
- **
- ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
- ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
- ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- ** SOFTWARE.
- */
-
- #include "Platform.h"
- #include "USBAPI.h"
- #include "USBDesc.h"
-
- #if defined(USBCON)
-
- #define EP_TYPE_CONTROL 0x00
- #define EP_TYPE_BULK_IN 0x81
- #define EP_TYPE_BULK_OUT 0x80
- #define EP_TYPE_INTERRUPT_IN 0xC1
- #define EP_TYPE_INTERRUPT_OUT 0xC0
- #define EP_TYPE_ISOCHRONOUS_IN 0x41
- #define EP_TYPE_ISOCHRONOUS_OUT 0x40
-
- /** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */
- #define TX_RX_LED_PULSE_MS 100
- volatile u8 TxLEDPulse; /**< Milliseconds remaining for data Tx LED pulse */
- volatile u8 RxLEDPulse; /**< Milliseconds remaining for data Rx LED pulse */
-
- //==================================================================
- //==================================================================
-
- extern const u16 STRING_LANGUAGE[] PROGMEM;
- extern const u16 STRING_IPRODUCT[] PROGMEM;
- extern const u16 STRING_IMANUFACTURER[] PROGMEM;
- extern const DeviceDescriptor USB_DeviceDescriptor PROGMEM;
- extern const DeviceDescriptor USB_DeviceDescriptorA PROGMEM;
-
- const u16 STRING_LANGUAGE[2] = {
- (3<<8) | (2+2),
- 0x0409 // English
- };
-
- const u16 STRING_IPRODUCT[17] = {
- (3<<8) | (2+2*16),
- #if USB_PID == 0x8036
- 'A','r','d','u','i','n','o',' ','L','e','o','n','a','r','d','o'
- #elif USB_PID == 0x8037
- 'A','r','d','u','i','n','o',' ','M','i','c','r','o',' ',' ',' '
- #elif USB_PID == 0x803C
- 'A','r','d','u','i','n','o',' ','E','s','p','l','o','r','a',' '
- #elif USB_PID == 0x9208
- 'L','i','l','y','P','a','d','U','S','B',' ',' ',' ',' ',' ',' '
- #else
- 'U','S','B',' ','I','O',' ','B','o','a','r','d',' ',' ',' ',' '
- #endif
- };
-
- const u16 STRING_IMANUFACTURER[12] = {
- (3<<8) | (2+2*11),
- #if USB_VID == 0x2341
- 'A','r','d','u','i','n','o',' ','L','L','C'
- #elif USB_VID == 0x1b4f
- 'S','p','a','r','k','F','u','n',' ',' ',' '
- #else
- 'U','n','k','n','o','w','n',' ',' ',' ',' '
- #endif
- };
-
- #ifdef CDC_ENABLED
- #define DEVICE_CLASS 0x02
- #else
- #define DEVICE_CLASS 0x00
- #endif
-
- // DEVICE DESCRIPTOR
- const DeviceDescriptor USB_DeviceDescriptor =
- D_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);
-
- const DeviceDescriptor USB_DeviceDescriptorA =
- D_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);
-
- //==================================================================
- //==================================================================
-
- volatile u8 _usbConfiguration = 0;
-
- static inline void WaitIN(void)
- {
- while (!(UEINTX & (1<<TXINI)));
- }
-
- static inline void ClearIN(void)
- {
- UEINTX = ~(1<<TXINI);
- }
-
- static inline void WaitOUT(void)
- {
- while (!(UEINTX & (1<<RXOUTI)))
- ;
- }
-
- static inline u8 WaitForINOrOUT()
- {
- while (!(UEINTX & ((1<<TXINI)|(1<<RXOUTI))))
- ;
- return (UEINTX & (1<<RXOUTI)) == 0;
- }
-
- static inline void ClearOUT(void)
- {
- UEINTX = ~(1<<RXOUTI);
- }
-
- void Recv(volatile u8* data, u8 count)
- {
- while (count--)
- *data++ = UEDATX;
-
- RXLED1; // light the RX LED
- RxLEDPulse = TX_RX_LED_PULSE_MS;
- }
-
- static inline u8 Recv8()
- {
- RXLED1; // light the RX LED
- RxLEDPulse = TX_RX_LED_PULSE_MS;
-
- return UEDATX;
- }
-
- static inline void Send8(u8 d)
- {
- UEDATX = d;
- }
-
- static inline void SetEP(u8 ep)
- {
- UENUM = ep;
- }
-
- static inline u8 FifoByteCount()
- {
- return UEBCLX;
- }
-
- static inline u8 ReceivedSetupInt()
- {
- return UEINTX & (1<<RXSTPI);
- }
-
- static inline void ClearSetupInt()
- {
- UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));
- }
-
- static inline void Stall()
- {
- UECONX = (1<<STALLRQ) | (1<<EPEN);
- }
-
- static inline u8 ReadWriteAllowed()
- {
- return UEINTX & (1<<RWAL);
- }
-
- static inline u8 Stalled()
- {
- return UEINTX & (1<<STALLEDI);
- }
-
- static inline u8 FifoFree()
- {
- return UEINTX & (1<<FIFOCON);
- }
-
- static inline void ReleaseRX()
- {
- UEINTX = 0x6B; // FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1
- }
-
- static inline void ReleaseTX()
- {
- UEINTX = 0x3A; // FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0
- }
-
- static inline u8 FrameNumber()
- {
- return UDFNUML;
- }
-
- //==================================================================
- //==================================================================
-
- u8 USBGetConfiguration(void)
- {
- return _usbConfiguration;
- }
-
- #define USB_RECV_TIMEOUT
- class LockEP
- {
- u8 _sreg;
- public:
- LockEP(u8 ep) : _sreg(SREG)
- {
- cli();
- SetEP(ep & 7);
- }
- ~LockEP()
- {
- SREG = _sreg;
- }
- };
-
- // Number of bytes, assumes a rx endpoint
- u8 USB_Available(u8 ep)
- {
- LockEP lock(ep);
- return FifoByteCount();
- }
-
- // Non Blocking receive
- // Return number of bytes read
- int USB_Recv(u8 ep, void* d, int len)
- {
- if (!_usbConfiguration || len < 0)
- return -1;
-
- LockEP lock(ep);
- u8 n = FifoByteCount();
- len = min(n,len);
- n = len;
- u8* dst = (u8*)d;
- while (n--)
- *dst++ = Recv8();
- if (len && !FifoByteCount()) // release empty buffer
- ReleaseRX();
-
- return len;
- }
-
- // Recv 1 byte if ready
- int USB_Recv(u8 ep)
- {
- u8 c;
- if (USB_Recv(ep,&c,1) != 1)
- return -1;
- return c;
- }
-
- // Space in send EP
- u8 USB_SendSpace(u8 ep)
- {
- LockEP lock(ep);
- if (!ReadWriteAllowed())
- return 0;
- return 64 - FifoByteCount();
- }
-
- // Blocking Send of data to an endpoint
- int USB_Send(u8 ep, const void* d, int len)
- {
- if (!_usbConfiguration)
- return -1;
-
- int r = len;
- const u8* data = (const u8*)d;
- u8 zero = ep & TRANSFER_ZERO;
- u8 timeout = 250; // 250ms timeout on send? TODO
- while (len)
- {
- u8 n = USB_SendSpace(ep);
- if (n == 0)
- {
- if (!(--timeout))
- return -1;
- delay(1);
- continue;
- }
-
- if (n > len)
- n = len;
- len -= n;
- {
- LockEP lock(ep);
- if (ep & TRANSFER_ZERO)
- {
- while (n--)
- Send8(0);
- }
- else if (ep & TRANSFER_PGM)
- {
- while (n--)
- Send8(pgm_read_byte(data++));
- }
- else
- {
- while (n--)
- Send8(*data++);
- }
- if (!ReadWriteAllowed() || ((len == 0) && (ep & TRANSFER_RELEASE))) // Release full buffer
- ReleaseTX();
- }
- }
- TXLED1; // light the TX LED
- TxLEDPulse = TX_RX_LED_PULSE_MS;
- return r;
- }
-
- extern const u8 _initEndpoints[] PROGMEM;
- const u8 _initEndpoints[] =
- {
- 0,
-
- #ifdef CDC_ENABLED
- EP_TYPE_INTERRUPT_IN, // CDC_ENDPOINT_ACM
- EP_TYPE_BULK_OUT, // CDC_ENDPOINT_OUT
- EP_TYPE_BULK_IN, // CDC_ENDPOINT_IN
- #endif
-
- #ifdef HID_ENABLED
- EP_TYPE_INTERRUPT_IN // HID_ENDPOINT_INT
- #endif
- };
-
- #define EP_SINGLE_64 0x32 // EP0
- #define EP_DOUBLE_64 0x36 // Other endpoints
-
- static
- void InitEP(u8 index, u8 type, u8 size)
- {
- UENUM = index;
- UECONX = 1;
- UECFG0X = type;
- UECFG1X = size;
- }
-
- static
- void InitEndpoints()
- {
- for (u8 i = 1; i < sizeof(_initEndpoints); i++)
- {
- UENUM = i;
- UECONX = 1;
- UECFG0X = pgm_read_byte(_initEndpoints+i);
- UECFG1X = EP_DOUBLE_64;
- }
- UERST = 0x7E; // And reset them
- UERST = 0;
- }
-
- // Handle CLASS_INTERFACE requests
- static
- bool ClassInterfaceRequest(Setup& setup)
- {
- u8 i = setup.wIndex;
-
- #ifdef CDC_ENABLED
- if (CDC_ACM_INTERFACE == i)
- return CDC_Setup(setup);
- #endif
-
- #ifdef HID_ENABLED
- if (HID_INTERFACE == i)
- return HID_Setup(setup);
- #endif
- return false;
- }
-
- int _cmark;
- int _cend;
- void InitControl(int end)
- {
- SetEP(0);
- _cmark = 0;
- _cend = end;
- }
-
- static
- bool SendControl(u8 d)
- {
- if (_cmark < _cend)
- {
- if (!WaitForINOrOUT())
- return false;
- Send8(d);
- if (!((_cmark + 1) & 0x3F))
- ClearIN(); // Fifo is full, release this packet
- }
- _cmark++;
- return true;
- };
-
- // Clipped by _cmark/_cend
- int USB_SendControl(u8 flags, const void* d, int len)
- {
- int sent = len;
- const u8* data = (const u8*)d;
- bool pgm = flags & TRANSFER_PGM;
- while (len--)
- {
- u8 c = pgm ? pgm_read_byte(data++) : *data++;
- if (!SendControl(c))
- return -1;
- }
- return sent;
- }
-
- // Does not timeout or cross fifo boundaries
- // Will only work for transfers <= 64 bytes
- // TODO
- int USB_RecvControl(void* d, int len)
- {
- WaitOUT();
- Recv((u8*)d,len);
- ClearOUT();
- return len;
- }
-
- int SendInterfaces()
- {
- int total = 0;
- u8 interfaces = 0;
-
- #ifdef CDC_ENABLED
- total = CDC_GetInterface(&interfaces);
- #endif
-
- #ifdef HID_ENABLED
- total += HID_GetInterface(&interfaces);
- #endif
-
- return interfaces;
- }
-
- // Construct a dynamic configuration descriptor
- // This really needs dynamic endpoint allocation etc
- // TODO
- static
- bool SendConfiguration(int maxlen)
- {
- // Count and measure interfaces
- InitControl(0);
- int interfaces = SendInterfaces();
- ConfigDescriptor config = D_CONFIG(_cmark + sizeof(ConfigDescriptor),interfaces);
-
- // Now send them
- InitControl(maxlen);
- USB_SendControl(0,&config,sizeof(ConfigDescriptor));
- SendInterfaces();
- return true;
- }
-
- u8 _cdcComposite = 0;
-
- static
- bool SendDescriptor(Setup& setup)
- {
- u8 t = setup.wValueH;
- if (USB_CONFIGURATION_DESCRIPTOR_TYPE == t)
- return SendConfiguration(setup.wLength);
-
- InitControl(setup.wLength);
- #ifdef HID_ENABLED
- if (HID_REPORT_DESCRIPTOR_TYPE == t)
- return HID_GetDescriptor(t);
- #endif
-
- u8 desc_length = 0;
- const u8* desc_addr = 0;
- if (USB_DEVICE_DESCRIPTOR_TYPE == t)
- {
- if (setup.wLength == 8)
- _cdcComposite = 1;
- desc_addr = _cdcComposite ? (const u8*)&USB_DeviceDescriptorA : (const u8*)&USB_DeviceDescriptor;
- }
- else if (USB_STRING_DESCRIPTOR_TYPE == t)
- {
- if (setup.wValueL == 0)
- desc_addr = (const u8*)&STRING_LANGUAGE;
- else if (setup.wValueL == IPRODUCT)
- desc_addr = (const u8*)&STRING_IPRODUCT;
- else if (setup.wValueL == IMANUFACTURER)
- desc_addr = (const u8*)&STRING_IMANUFACTURER;
- else
- return false;
- }
-
- if (desc_addr == 0)
- return false;
- if (desc_length == 0)
- desc_length = pgm_read_byte(desc_addr);
-
- USB_SendControl(TRANSFER_PGM,desc_addr,desc_length);
- return true;
- }
-
- // Endpoint 0 interrupt
- ISR(USB_COM_vect)
- {
- SetEP(0);
- if (!ReceivedSetupInt())
- return;
-
- Setup setup;
- Recv((u8*)&setup,8);
- ClearSetupInt();
-
- u8 requestType = setup.bmRequestType;
- if (requestType & REQUEST_DEVICETOHOST)
- WaitIN();
- else
- ClearIN();
-
- bool ok = true;
- if (REQUEST_STANDARD == (requestType & REQUEST_TYPE))
- {
- // Standard Requests
- u8 r = setup.bRequest;
- if (GET_STATUS == r)
- {
- Send8(0); // TODO
- Send8(0);
- }
- else if (CLEAR_FEATURE == r)
- {
- }
- else if (SET_FEATURE == r)
- {
- }
- else if (SET_ADDRESS == r)
- {
- WaitIN();
- UDADDR = setup.wValueL | (1<<ADDEN);
- }
- else if (GET_DESCRIPTOR == r)
- {
- ok = SendDescriptor(setup);
- }
- else if (SET_DESCRIPTOR == r)
- {
- ok = false;
- }
- else if (GET_CONFIGURATION == r)
- {
- Send8(1);
- }
- else if (SET_CONFIGURATION == r)
- {
- if (REQUEST_DEVICE == (requestType & REQUEST_RECIPIENT))
- {
- InitEndpoints();
- _usbConfiguration = setup.wValueL;
- } else
- ok = false;
- }
- else if (GET_INTERFACE == r)
- {
- }
- else if (SET_INTERFACE == r)
- {
- }
- }
- else
- {
- InitControl(setup.wLength); // Max length of transfer
- ok = ClassInterfaceRequest(setup);
- }
-
- if (ok)
- ClearIN();
- else
- {
- Stall();
- }
- }
-
- void USB_Flush(u8 ep)
- {
- SetEP(ep);
- if (FifoByteCount())
- ReleaseTX();
- }
-
- // General interrupt
- ISR(USB_GEN_vect)
- {
- u8 udint = UDINT;
- UDINT = 0;
-
- // End of Reset
- if (udint & (1<<EORSTI))
- {
- InitEP(0,EP_TYPE_CONTROL,EP_SINGLE_64); // init ep0
- _usbConfiguration = 0; // not configured yet
- UEIENX = 1 << RXSTPE; // Enable interrupts for ep0
- }
-
- // Start of Frame - happens every millisecond so we use it for TX and RX LED one-shot timing, too
- if (udint & (1<<SOFI))
- {
- #ifdef CDC_ENABLED
- USB_Flush(CDC_TX); // Send a tx frame if found
- if (USB_Available(CDC_RX)) // Handle received bytes (if any)
- Serial.accept();
- #endif
-
- // check whether the one-shot period has elapsed. if so, turn off the LED
- if (TxLEDPulse && !(--TxLEDPulse))
- TXLED0;
- if (RxLEDPulse && !(--RxLEDPulse))
- RXLED0;
- }
- }
-
- // VBUS or counting frames
- // Any frame counting?
- u8 USBConnected()
- {
- u8 f = UDFNUML;
- delay(3);
- return f != UDFNUML;
- }
-
- //=======================================================================
- //=======================================================================
-
- USBDevice_ USBDevice;
-
- USBDevice_::USBDevice_()
- {
- }
-
- void USBDevice_::attach()
- {
- _usbConfiguration = 0;
- UHWCON = 0x01; // power internal reg
- USBCON = (1<<USBE)|(1<<FRZCLK); // clock frozen, usb enabled
- #if F_CPU == 16000000UL
- PLLCSR = 0x12; // Need 16 MHz xtal
- #elif F_CPU == 8000000UL
- PLLCSR = 0x02; // Need 8 MHz xtal
- #endif
- while (!(PLLCSR & (1<<PLOCK))) // wait for lock pll
- ;
-
- // Some tests on specific versions of macosx (10.7.3), reported some
- // strange behaviuors when the board is reset using the serial
- // port touch at 1200 bps. This delay fixes this behaviour.
- delay(1);
-
- USBCON = ((1<<USBE)|(1<<OTGPADE)); // start USB clock
- UDIEN = (1<<EORSTE)|(1<<SOFE); // Enable interrupts for EOR (End of Reset) and SOF (start of frame)
- UDCON = 0; // enable attach resistor
-
- TX_RX_LED_INIT;
- }
-
- void USBDevice_::detach()
- {
- }
-
- // Check for interrupts
- // TODO: VBUS detection
- bool USBDevice_::configured()
- {
- return _usbConfiguration;
- }
-
- void USBDevice_::poll()
- {
- }
-
- #endif /* if defined(USBCON) */
|