3 /* Copyright (c) 2010, Peter Barrett
5 ** Permission to use, copy, modify, and/or distribute this software for
6 ** any purpose with or without fee is hereby granted, provided that the
7 ** above copyright notice and this permission notice appear in all copies.
9 ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
12 ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
13 ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
14 ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
15 ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
25 #define EP_TYPE_CONTROL 0x00
26 #define EP_TYPE_BULK_IN 0x81
27 #define EP_TYPE_BULK_OUT 0x80
28 #define EP_TYPE_INTERRUPT_IN 0xC1
29 #define EP_TYPE_INTERRUPT_OUT 0xC0
30 #define EP_TYPE_ISOCHRONOUS_IN 0x41
31 #define EP_TYPE_ISOCHRONOUS_OUT 0x40
33 /** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */
34 #define TX_RX_LED_PULSE_MS 100
35 volatile u8 TxLEDPulse
; /**< Milliseconds remaining for data Tx LED pulse */
36 volatile u8 RxLEDPulse
; /**< Milliseconds remaining for data Rx LED pulse */
38 //==================================================================
39 //==================================================================
41 extern const u16 STRING_LANGUAGE
[] PROGMEM
;
42 extern const u16 STRING_IPRODUCT
[] PROGMEM
;
43 extern const u16 STRING_IMANUFACTURER
[] PROGMEM
;
44 extern const DeviceDescriptor USB_DeviceDescriptor PROGMEM
;
45 extern const DeviceDescriptor USB_DeviceDescriptorA PROGMEM
;
47 const u16 STRING_LANGUAGE
[2] = {
52 const u16 STRING_IPRODUCT
[17] = {
55 'A','r','d','u','i','n','o',' ','L','e','o','n','a','r','d','o'
57 'U','S','B',' ','I','O',' ','B','o','a','r','d',' ',' ',' ',' '
61 const u16 STRING_IMANUFACTURER
[12] = {
64 'A','r','d','u','i','n','o',' ','L','L','C'
66 'U','n','k','n','o','w','n',' ',' ',' ',' '
71 #define DEVICE_CLASS 0x02
73 #define DEVICE_CLASS 0x00
77 const DeviceDescriptor USB_DeviceDescriptor
=
78 D_DEVICE(0x00,0x00,0x00,64,USB_VID
,USB_PID
,0x100,IMANUFACTURER
,IPRODUCT
,0,1);
80 const DeviceDescriptor USB_DeviceDescriptorA
=
81 D_DEVICE(DEVICE_CLASS
,0x00,0x00,64,USB_VID
,USB_PID
,0x100,IMANUFACTURER
,IPRODUCT
,0,1);
83 //==================================================================
84 //==================================================================
86 volatile u8 _usbConfiguration
= 0;
88 static inline void WaitIN(void)
90 while (!(UEINTX
& (1<<TXINI
)));
93 static inline void ClearIN(void)
98 static inline void WaitOUT(void)
100 while (!(UEINTX
& (1<<RXOUTI
)))
104 static inline u8
WaitForINOrOUT()
106 while (!(UEINTX
& ((1<<TXINI
)|(1<<RXOUTI
))))
108 return (UEINTX
& (1<<RXOUTI
)) == 0;
111 static inline void ClearOUT(void)
113 UEINTX
= ~(1<<RXOUTI
);
116 void Recv(volatile u8
* data
, u8 count
)
121 RXLED1
; // light the RX LED
122 RxLEDPulse
= TX_RX_LED_PULSE_MS
;
125 static inline u8
Recv8()
127 RXLED1
; // light the RX LED
128 RxLEDPulse
= TX_RX_LED_PULSE_MS
;
133 static inline void Send8(u8 d
)
138 static inline void SetEP(u8 ep
)
143 static inline u8
FifoByteCount()
148 static inline u8
ReceivedSetupInt()
150 return UEINTX
& (1<<RXSTPI
);
153 static inline void ClearSetupInt()
155 UEINTX
= ~((1<<RXSTPI
) | (1<<RXOUTI
) | (1<<TXINI
));
158 static inline void Stall()
160 UECONX
= (1<<STALLRQ
) | (1<<EPEN
);
163 static inline u8
ReadWriteAllowed()
165 return UEINTX
& (1<<RWAL
);
168 static inline u8
Stalled()
170 return UEINTX
& (1<<STALLEDI
);
173 static inline u8
FifoFree()
175 return UEINTX
& (1<<FIFOCON
);
178 static inline void ReleaseRX()
180 UEINTX
= 0x6B; // FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1
183 static inline void ReleaseTX()
185 UEINTX
= 0x3A; // FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0
188 static inline u8
FrameNumber()
193 //==================================================================
194 //==================================================================
196 u8
USBGetConfiguration(void)
198 return _usbConfiguration
;
201 #define USB_RECV_TIMEOUT
206 LockEP(u8 ep
) : _sreg(SREG
)
217 // Number of bytes, assumes a rx endpoint
218 u8
USB_Available(u8 ep
)
221 return FifoByteCount();
224 // Non Blocking receive
225 // Return number of bytes read
226 int USB_Recv(u8 ep
, void* d
, int len
)
228 if (!_usbConfiguration
|| len
< 0)
232 u8 n
= FifoByteCount();
238 if (len
&& !FifoByteCount()) // release empty buffer
244 // Recv 1 byte if ready
248 if (USB_Recv(ep
,&c
,1) != 1)
254 u8
USB_SendSpace(u8 ep
)
257 if (!ReadWriteAllowed())
259 return 64 - FifoByteCount();
262 // Blocking Send of data to an endpoint
263 int USB_Send(u8 ep
, const void* d
, int len
)
265 if (!_usbConfiguration
)
269 const u8
* data
= (const u8
*)d
;
270 u8 zero
= ep
& TRANSFER_ZERO
;
271 u8 timeout
= 250; // 250ms timeout on send? TODO
274 u8 n
= USB_SendSpace(ep
);
288 if (ep
& TRANSFER_ZERO
)
293 else if (ep
& TRANSFER_PGM
)
296 Send8(pgm_read_byte(data
++));
303 if (!ReadWriteAllowed() || ((len
== 0) && (ep
& TRANSFER_RELEASE
))) // Release full buffer
307 TXLED1
; // light the TX LED
308 TxLEDPulse
= TX_RX_LED_PULSE_MS
;
312 extern const u8 _initEndpoints
[] PROGMEM
;
313 const u8 _initEndpoints
[] =
318 EP_TYPE_INTERRUPT_IN
, // CDC_ENDPOINT_ACM
319 EP_TYPE_BULK_OUT
, // CDC_ENDPOINT_OUT
320 EP_TYPE_BULK_IN
, // CDC_ENDPOINT_IN
324 EP_TYPE_INTERRUPT_IN
// HID_ENDPOINT_INT
328 #define EP_SINGLE_64 0x32 // EP0
329 #define EP_DOUBLE_64 0x36 // Other endpoints
332 void InitEP(u8 index
, u8 type
, u8 size
)
343 for (u8 i
= 1; i
< sizeof(_initEndpoints
); i
++)
347 UECFG0X
= pgm_read_byte(_initEndpoints
+i
);
348 UECFG1X
= EP_DOUBLE_64
;
350 UERST
= 0x7E; // And reset them
354 // Handle CLASS_INTERFACE requests
356 bool ClassInterfaceRequest(Setup
& setup
)
361 if (CDC_ACM_INTERFACE
== i
)
362 return CDC_Setup(setup
);
366 if (HID_INTERFACE
== i
)
367 return HID_Setup(setup
);
374 void InitControl(int end
)
382 bool SendControl(u8 d
)
386 if (!WaitForINOrOUT())
389 if (!((_cmark
+ 1) & 0x3F))
390 ClearIN(); // Fifo is full, release this packet
396 // Clipped by _cmark/_cend
397 int USB_SendControl(u8 flags
, const void* d
, int len
)
400 const u8
* data
= (const u8
*)d
;
401 bool pgm
= flags
& TRANSFER_PGM
;
404 u8 c
= pgm
? pgm_read_byte(data
++) : *data
++;
411 // Does not timeout or cross fifo boundaries
412 // Will only work for transfers <= 64 bytes
414 int USB_RecvControl(void* d
, int len
)
428 total
= CDC_GetInterface(&interfaces
);
432 total
+= HID_GetInterface(&interfaces
);
438 // Construct a dynamic configuration descriptor
439 // This really needs dynamic endpoint allocation etc
442 bool SendConfiguration(int maxlen
)
444 // Count and measure interfaces
446 int interfaces
= SendInterfaces();
447 ConfigDescriptor config
= D_CONFIG(_cmark
+ sizeof(ConfigDescriptor
),interfaces
);
451 USB_SendControl(0,&config
,sizeof(ConfigDescriptor
));
456 u8 _cdcComposite
= 0;
459 bool SendDescriptor(Setup
& setup
)
461 u8 t
= setup
.wValueH
;
462 if (USB_CONFIGURATION_DESCRIPTOR_TYPE
== t
)
463 return SendConfiguration(setup
.wLength
);
465 InitControl(setup
.wLength
);
467 if (HID_REPORT_DESCRIPTOR_TYPE
== t
)
468 return HID_GetDescriptor(t
);
472 const u8
* desc_addr
= 0;
473 if (USB_DEVICE_DESCRIPTOR_TYPE
== t
)
475 if (setup
.wLength
== 8)
477 desc_addr
= _cdcComposite
? (const u8
*)&USB_DeviceDescriptorA
: (const u8
*)&USB_DeviceDescriptor
;
479 else if (USB_STRING_DESCRIPTOR_TYPE
== t
)
481 if (setup
.wValueL
== 0)
482 desc_addr
= (const u8
*)&STRING_LANGUAGE
;
483 else if (setup
.wValueL
== IPRODUCT
)
484 desc_addr
= (const u8
*)&STRING_IPRODUCT
;
485 else if (setup
.wValueL
== IMANUFACTURER
)
486 desc_addr
= (const u8
*)&STRING_IMANUFACTURER
;
493 if (desc_length
== 0)
494 desc_length
= pgm_read_byte(desc_addr
);
496 USB_SendControl(TRANSFER_PGM
,desc_addr
,desc_length
);
500 // Endpoint 0 interrupt
504 if (!ReceivedSetupInt())
511 u8 requestType
= setup
.bmRequestType
;
512 if (requestType
& REQUEST_DEVICETOHOST
)
518 if (REQUEST_STANDARD
== (requestType
& REQUEST_TYPE
))
521 u8 r
= setup
.bRequest
;
527 else if (CLEAR_FEATURE
== r
)
530 else if (SET_FEATURE
== r
)
533 else if (SET_ADDRESS
== r
)
536 UDADDR
= setup
.wValueL
| (1<<ADDEN
);
538 else if (GET_DESCRIPTOR
== r
)
540 ok
= SendDescriptor(setup
);
542 else if (SET_DESCRIPTOR
== r
)
546 else if (GET_CONFIGURATION
== r
)
550 else if (SET_CONFIGURATION
== r
)
552 if (REQUEST_DEVICE
== (requestType
& REQUEST_RECIPIENT
))
555 _usbConfiguration
= setup
.wValueL
;
559 else if (GET_INTERFACE
== r
)
562 else if (SET_INTERFACE
== r
)
568 InitControl(setup
.wLength
); // Max length of transfer
569 ok
= ClassInterfaceRequest(setup
);
580 void USB_Flush(u8 ep
)
594 if (udint
& (1<<EORSTI
))
596 InitEP(0,EP_TYPE_CONTROL
,EP_SINGLE_64
); // init ep0
597 _usbConfiguration
= 0; // not configured yet
598 UEIENX
= 1 << RXSTPE
; // Enable interrupts for ep0
601 // Start of Frame - happens every millisecond so we use it for TX and RX LED one-shot timing, too
602 if (udint
& (1<<SOFI
))
605 USB_Flush(CDC_TX
); // Send a tx frame if found
606 while (USB_Available(CDC_RX
)) // Handle received bytes (if any)
610 // check whether the one-shot period has elapsed. if so, turn off the LED
611 if (TxLEDPulse
&& !(--TxLEDPulse
))
613 if (RxLEDPulse
&& !(--RxLEDPulse
))
618 // VBUS or counting frames
619 // Any frame counting?
627 //=======================================================================
628 //=======================================================================
630 USBDevice_ USBDevice
;
632 USBDevice_::USBDevice_()
636 void USBDevice_::attach()
638 _usbConfiguration
= 0;
639 UHWCON
= 0x01; // power internal reg
640 USBCON
= (1<<USBE
)|(1<<FRZCLK
); // clock frozen, usb enabled
641 PLLCSR
= 0x12; // Need 16 MHz xtal
642 while (!(PLLCSR
& (1<<PLOCK
))) // wait for lock pll
645 // Some tests on specific versions of macosx (10.7.3), reported some
646 // strange behaviuors when the board is reset using the serial
647 // port touch at 1200 bps. This delay fixes this behaviour.
650 USBCON
= ((1<<USBE
)|(1<<OTGPADE
)); // start USB clock
651 UDIEN
= (1<<EORSTE
)|(1<<SOFE
); // Enable interrupts for EOR (End of Reset) and SOF (start of frame)
652 UDCON
= 0; // enable attach resistor
657 void USBDevice_::detach()
661 // Check for interrupts
662 // TODO: VBUS detection
663 bool USBDevice_::configured()
665 return _usbConfiguration
;
668 void USBDevice_::poll()
672 #endif /* if defined(USBCON) */