COSA
An Object-Oriented Platform for Arduino Programming
Core.cpp
Go to the documentation of this file.
1 
22 #include "Cosa/USB/Platform.h"
23 
24 #if defined(USBCON)
25 
26 #include "Cosa/CDC.hh"
27 
28 #define min(a,b) ((a)<(b)?(a):(b))
29 #define max(a,b) ((a)>(b)?(a):(b))
30 
31 #if defined(ARDUINO_LEONARDO) || defined(ARDUINO_PRO_MICRO)
32 #define TX_RX_LED_INIT DDRD |= (1<<5), DDRB |= (1<<0)
33 #define TX_LED_OFF PORTD |= (1<<5)
34 #define TX_LED_ON PORTD &= ~(1<<5)
35 #define RX_LED_OFF PORTB |= (1<<0)
36 #define RX_LED_ON PORTB &= ~(1<<0)
37 #else
38 #define TX_RX_LED_INIT {}
39 #define TX_LED_OFF {}
40 #define TX_LED_ON {}
41 #define RX_LED_OFF {}
42 #define RX_LED_ON {}
43 #endif
44 
45 #define EP_TYPE_CONTROL 0x00
46 #define EP_TYPE_BULK_IN 0x81
47 #define EP_TYPE_BULK_OUT 0x80
48 #define EP_TYPE_INTERRUPT_IN 0xC1
49 #define EP_TYPE_INTERRUPT_OUT 0xC0
50 #define EP_TYPE_ISOCHRONOUS_IN 0x41
51 #define EP_TYPE_ISOCHRONOUS_OUT 0x40
52 
57 #define TX_RX_LED_PULSE_MS 100
58 volatile uint8_t TxLEDPulse;
59 volatile uint8_t RxLEDPulse;
60 
61 extern const uint16_t STRING_LANGUAGE[] PROGMEM;
62 extern const uint16_t STRING_IPRODUCT[] PROGMEM;
63 extern const uint16_t STRING_IMANUFACTURER[] PROGMEM;
64 extern const DeviceDescriptor USB_DeviceDescriptor PROGMEM;
65 extern const DeviceDescriptor USB_DeviceDescriptorA PROGMEM;
66 
67 const uint16_t STRING_LANGUAGE[2] = {
68  (3<<8) | (2+2),
69  0x0409
70 };
71 
72 const uint16_t STRING_IPRODUCT[17] = {
73  (3<<8) | (2+2*16),
74 #if USB_PID == 0x8036
75  'A','r','d','u','i','n','o',' ','L','e','o','n','a','r','d','o'
76 #elif USB_PID == 0x8037
77  'A','r','d','u','i','n','o',' ','M','i','c','r','o',' ',' ',' '
78 #elif USB_PID == 0x803C
79  'A','r','d','u','i','n','o',' ','E','s','p','l','o','r','a',' '
80 #elif USB_PID == 0x9208
81  'L','i','l','y','P','a','d','U','S','B',' ',' ',' ',' ',' ',' '
82 #elif USB_PID == 0x0483
83  'U','S','B',' ','S','e','r','i','a','l',' ',' ',' ',' ',' ',' '
84 #else
85  'U','S','B',' ','I','O',' ','B','o','a','r','d',' ',' ',' ',' '
86 #endif
87 };
88 
89 const uint16_t STRING_IMANUFACTURER[12] = {
90  (3<<8) | (2+2*11),
91 #if USB_VID == 0x2341
92  'A','r','d','u','i','n','o',' ','L','L','C'
93 #elif USB_VID == 0x1b4f
94  'S','p','a','r','k','F','u','n',' ',' ',' '
95 #elif USB_VID == 0x16C0
96  'T','e','e','n','s','y','d','u','i','n','o'
97 #else
98  'U','n','k','n','o','w','n',' ',' ',' ',' '
99 #endif
100 };
101 
102 #ifdef CDC_ENABLED
103 #define DEVICE_CLASS 0x02
104 #else
105 #define DEVICE_CLASS 0x00
106 #endif
107 
108 // DEVICE DESCRIPTOR
109 const DeviceDescriptor USB_DeviceDescriptor =
110  D_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);
111 
112 const DeviceDescriptor USB_DeviceDescriptorA =
113  D_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);
114 
115 volatile uint8_t _usbConfiguration = 0;
116 
117 static inline void
118 WaitIN(void)
119 {
120  while (!(UEINTX & (1<<TXINI)))
121  DELAY(1);
122 }
123 
124 static inline void
125 ClearIN(void)
126 {
127  UEINTX = ~(1<<TXINI);
128 }
129 
130 static inline void
131 WaitOUT(void)
132 {
133  while (!(UEINTX & (1<<RXOUTI)))
134  DELAY(1);
135 }
136 
137 static inline uint8_t
138 WaitForINOrOUT()
139 {
140  while (!(UEINTX & ((1<<TXINI)|(1<<RXOUTI))))
141  DELAY(1);
142  return (UEINTX & (1<<RXOUTI)) == 0;
143 }
144 
145 static inline void
146 ClearOUT(void)
147 {
148  UEINTX = ~(1<<RXOUTI);
149 }
150 
151 void
152 Recv(volatile uint8_t* data, uint8_t count)
153 {
154  while (count--) *data++ = UEDATX;
155  RX_LED_ON;
156  RxLEDPulse = TX_RX_LED_PULSE_MS;
157 }
158 
159 static inline uint8_t
160 Recv8()
161 {
162  RX_LED_ON;
163  RxLEDPulse = TX_RX_LED_PULSE_MS;
164  return (UEDATX);
165 }
166 
167 static inline void
168 Send8(uint8_t d)
169 {
170  UEDATX = d;
171 }
172 
173 static inline void
174 SetEP(uint8_t ep)
175 {
176  UENUM = ep;
177 }
178 
179 static inline uint8_t
180 FifoByteCount()
181 {
182  return (UEBCLX);
183 }
184 
185 static inline uint8_t
186 ReceivedSetupInt()
187 {
188  return (UEINTX & (1<<RXSTPI));
189 }
190 
191 static inline void
192 ClearSetupInt()
193 {
194  UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));
195 }
196 
197 static inline void
198 Stall()
199 {
200  UECONX = (1<<STALLRQ) | (1<<EPEN);
201 }
202 
203 static inline uint8_t
204 ReadWriteAllowed()
205 {
206  return (UEINTX & (1<<RWAL));
207 }
208 
209 static inline uint8_t
210 Stalled()
211 {
212  return (UEINTX & (1<<STALLEDI));
213 }
214 
215 static inline uint8_t
216 FifoFree()
217 {
218  return (UEINTX & (1<<FIFOCON));
219 }
220 
221 static inline void
222 ReleaseRX()
223 {
224  // FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1
225  UEINTX = 0x6B;
226 }
227 
228 static inline void
229 ReleaseTX()
230 {
231  // FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0
232  UEINTX = 0x3A;
233 }
234 
235 static inline uint8_t
236 FrameNumber()
237 {
238  return (UDFNUML);
239 }
240 
241 uint8_t USBGetConfiguration(void)
242 {
243  return (_usbConfiguration);
244 }
245 
246 #define USB_RECV_TIMEOUT
247 class LockEP
248 {
249  uint8_t _sreg;
250 public:
251  LockEP(uint8_t ep) : _sreg(SREG)
252  {
253  cli();
254  SetEP(ep & 7);
255  }
256  ~LockEP()
257  {
258  SREG = _sreg;
259  }
260 };
261 
262 uint8_t
263 USB_Available(uint8_t ep)
264 {
265  LockEP lock(ep);
266  return (FifoByteCount());
267 }
268 
269 int
270 USB_Recv(uint8_t ep, void* d, int len)
271 {
272  if (!_usbConfiguration || len < 0) return (-1);
273 
274  LockEP lock(ep);
275  uint8_t n = FifoByteCount();
276  len = min(n,len);
277  n = len;
278  uint8_t* dst = (uint8_t*)d;
279  while (n--)
280  *dst++ = Recv8();
281  if (len && !FifoByteCount())
282  ReleaseRX();
283  return (len);
284 }
285 
286 int
287 USB_Recv(uint8_t ep)
288 {
289  uint8_t c;
290  if (USB_Recv(ep, &c, 1) == 1)
291  return (c);
292  return (-1);
293 }
294 
295 uint8_t
296 USB_SendSpace(uint8_t ep)
297 {
298  LockEP lock(ep);
299  if (!ReadWriteAllowed()) return (0);
300  return (64 - 1 - FifoByteCount());
301 }
302 
303 int
304 USB_Send(uint8_t ep, const void* d, int len)
305 {
306  if (!_usbConfiguration) return (-1);
307 
308  int res = len;
309  const uint8_t* data = (const uint8_t*)d;
310  uint8_t timeout = 250;
311  while (len) {
312  uint8_t n = USB_SendSpace(ep);
313  if (n == 0) {
314  if (!(--timeout)) return (-1);
315  delay(1);
316  continue;
317  }
318  if (n > len) n = len;
319  {
320  LockEP lock(ep);
321  if (!ReadWriteAllowed()) continue;
322  len -= n;
323  if (ep & TRANSFER_ZERO) {
324  while (n--) Send8(0);
325  }
326  else if (ep & TRANSFER_PGM) {
327  while (n--) Send8(pgm_read_byte(data++));
328  }
329  else {
330  while (n--) Send8(*data++);
331  }
332  if (!ReadWriteAllowed() || ((len == 0) && (ep & TRANSFER_RELEASE)))
333  ReleaseTX();
334  }
335  }
336  TX_LED_ON;
337  TxLEDPulse = TX_RX_LED_PULSE_MS;
338  return (res);
339 }
340 
341 extern const uint8_t _initEndpoints[] PROGMEM;
342 const uint8_t _initEndpoints[] =
343  {
344  0,
345 
346 #ifdef CDC_ENABLED
347  EP_TYPE_INTERRUPT_IN, // CDC_ENDPOINT_ACM
348  EP_TYPE_BULK_OUT, // CDC_ENDPOINT_OUT
349  EP_TYPE_BULK_IN, // CDC_ENDPOINT_IN
350 #endif
351 
352 #ifdef HID_ENABLED
353  EP_TYPE_INTERRUPT_IN // HID_ENDPOINT_INT
354 #endif
355  };
356 
357 #define EP_SINGLE_64 0x32
358 #define EP_DOUBLE_64 0x36
359 
360 static void
361 InitEP(uint8_t index, uint8_t type, uint8_t size)
362 {
363  UENUM = index;
364  UECONX = 1;
365  UECFG0X = type;
366  UECFG1X = size;
367 }
368 
369 static void
370 InitEndpoints()
371 {
372  for (uint8_t i = 1; i < sizeof(_initEndpoints); i++) {
373  UENUM = i;
374  UECONX = 1;
375  UECFG0X = pgm_read_byte(_initEndpoints+i);
376  UECFG1X = EP_DOUBLE_64;
377  }
378  UERST = 0x7E;
379  UERST = 0;
380 }
381 
382 static bool
383 ClassInterfaceRequest(Setup& setup)
384 {
385  uint8_t i = setup.wIndex;
386 
387 #ifdef CDC_ENABLED
388  if (CDC_ACM_INTERFACE == i) return (CDC_Setup(setup));
389 #endif
390 
391 #ifdef HID_ENABLED
392  if (HID_INTERFACE == i) return (HID_Setup(setup));
393 #endif
394 
395  return (false);
396 }
397 
398 static int _cmark;
399 static int _cend;
400 
401 void
402 InitControl(int end)
403 {
404  SetEP(0);
405  _cmark = 0;
406  _cend = end;
407 }
408 
409 static bool
410 SendControl(uint8_t d)
411 {
412  if (_cmark < _cend) {
413  if (!WaitForINOrOUT()) return (false);
414  Send8(d);
415  if (!((_cmark + 1) & 0x3F)) ClearIN();
416  }
417  _cmark++;
418  return (true);
419 };
420 
421 int
422 USB_SendControl(uint8_t flags, const void* d, int len)
423 {
424  int sent = len;
425  const uint8_t* data = (const uint8_t*)d;
426  bool pgm = flags & TRANSFER_PGM;
427  while (len--) {
428  uint8_t c = pgm ? pgm_read_byte(data++) : *data++;
429  if (!SendControl(c)) return (-1);
430  }
431  return (sent);
432 }
433 
434 int
435 USB_RecvControl(void* d, int len)
436 {
437  auto length = len;
438  while(length) {
439  // Dont receive more than the USB Control EP has to offer
440  // Use fixed 64 because control EP always have 64 bytes even on 16u2.
441  auto recvLength = length;
442  if(recvLength > 64){
443  recvLength = 64;
444  }
445 
446  // Write data to fit to the end (not the beginning) of the array
447  WaitOUT();
448  Recv((uint8_t*)d + len - length, recvLength);
449  ClearOUT();
450  length -= recvLength;
451  }
452  return len;
453 #if 0
454  WaitOUT();
455  Recv((uint8_t*)d,len);
456  ClearOUT();
457  return (len);
458 #endif
459 }
460 
461 int
462 SendInterfaces()
463 {
464  int total = 0;
465  uint8_t interfaces = 0;
466 
467 #ifdef CDC_ENABLED
468  total = CDC_GetInterface(&interfaces);
469 #endif
470 
471 #ifdef HID_ENABLED
472  total += HID_GetInterface(&interfaces);
473 #endif
474  UNUSED(total);
475  return (interfaces);
476 }
477 
478 static bool
479 SendConfiguration(int maxlen)
480 {
481  InitControl(0);
482  uint8_t interfaces = SendInterfaces();
483  ConfigDescriptor config = D_CONFIG(_cmark + sizeof(ConfigDescriptor), interfaces);
484 
485  InitControl(maxlen);
486  USB_SendControl(0,&config,sizeof(ConfigDescriptor));
487  SendInterfaces();
488  return (true);
489 }
490 
491 static uint8_t _cdcComposite = 0;
492 
493 static bool
494 SendDescriptor(Setup& setup)
495 {
496  uint8_t t = setup.wValueH;
498  return (SendConfiguration(setup.wLength));
499 
500  InitControl(setup.wLength);
501 #ifdef HID_ENABLED
503  return (HID_GetDescriptor(t));
504 #endif
505 
506  uint8_t desc_length = 0;
507  const uint8_t* desc_addr = 0;
508  if (USB_DEVICE_DESCRIPTOR_TYPE == t) {
509  if (setup.wLength == 8)
510  _cdcComposite = 1;
511  desc_addr = _cdcComposite ? (const uint8_t*)&USB_DeviceDescriptorA : (const uint8_t*)&USB_DeviceDescriptor;
512  }
513  else if (USB_STRING_DESCRIPTOR_TYPE == t) {
514  if (setup.wValueL == 0)
515  desc_addr = (const uint8_t*)&STRING_LANGUAGE;
516  else if (setup.wValueL == IPRODUCT)
517  desc_addr = (const uint8_t*)&STRING_IPRODUCT;
518  else if (setup.wValueL == IMANUFACTURER)
519  desc_addr = (const uint8_t*)&STRING_IMANUFACTURER;
520  else
521  return (false);
522  }
523 
524  if (desc_addr == 0) return (false);
525  if (desc_length == 0)
526  desc_length = pgm_read_byte(desc_addr);
527 
528  USB_SendControl(TRANSFER_PGM,desc_addr,desc_length);
529  return (true);
530 }
531 
532 static void
533 Handle_USB_COM(void)
534 {
535  SetEP(0);
536  if (!ReceivedSetupInt()) return;
537 
538  Setup setup;
539  Recv((uint8_t*)&setup,8);
540  ClearSetupInt();
541 
542  uint8_t requestType = setup.bmRequestType;
543  if (requestType & REQUEST_DEVICETOHOST)
544  WaitIN();
545  else
546  ClearIN();
547 
548  bool ok = true;
549  if (REQUEST_STANDARD == (requestType & REQUEST_TYPE)) {
550  uint8_t r = setup.bRequest;
551  switch (r) {
552  case GET_STATUS:
553  Send8(0);
554  Send8(0);
555  break;
556  case SET_ADDRESS:
557  WaitIN();
558  UDADDR = setup.wValueL | (1<<ADDEN);
559  break;
560  case GET_DESCRIPTOR:
561  ok = SendDescriptor(setup);
562  break;
563  case SET_DESCRIPTOR:
564  ok = false;
565  break;
566  case GET_CONFIGURATION:
567  Send8(1);
568  break;
569  case SET_CONFIGURATION:
570  if (REQUEST_DEVICE == (requestType & REQUEST_RECIPIENT)) {
571  InitEndpoints();
572  _usbConfiguration = setup.wValueL;
573  } else
574  ok = false;
575  break;
576  case CLEAR_FEATURE:
577  case SET_FEATURE:
578  case GET_INTERFACE:
579  case SET_INTERFACE:
580  break;
581  }
582  }
583  else {
584  InitControl(setup.wLength);
585  ok = ClassInterfaceRequest(setup);
586  }
587 
588  if (ok)
589  ClearIN();
590  else
591  Stall();
592 }
593 
595 {
596  Handle_USB_COM();
597 }
598 
599 // USB_Keepalive is called from exit implemented in main.cpp
600 void
601 USB_Keepalive(void)
602 {
603  while (_usbConfiguration)
604  Handle_USB_COM();
605 }
606 
607 void
608 USB_Flush(uint8_t ep)
609 {
610  SetEP(ep);
611  if (FifoByteCount())
612  ReleaseTX();
613 }
614 
616 {
617  uint8_t udint = UDINT;
618  UDINT = 0;
619 
620  // End of Reset
621  if (udint & (1<<EORSTI)) {
622  InitEP(0,EP_TYPE_CONTROL,EP_SINGLE_64); // init ep0
623  _usbConfiguration = 0; // not configured yet
624  UEIENX = 1 << RXSTPE; // Enable interrupts for ep0
625  }
626 
627  // Start of Frame - happens every millisecond so we use it for TX
628  // and RX LED one-shot timing, too
629  if (udint & (1<<SOFI)) {
630 #ifdef CDC_ENABLED
631  USB_Flush(CDC_TX); // Send a tx frame if found
632  if (USB_Available(CDC_RX)) // Handle received bytes (if any)
633  cdc.accept();
634 #endif
635 
636  // check whether the one-shot period has elapsed. if so, turn off the LED
637  if (TxLEDPulse && !(--TxLEDPulse))
638  TX_LED_OFF;
639  if (RxLEDPulse && !(--RxLEDPulse))
640  RX_LED_OFF;
641  }
642 }
643 
644 uint8_t
645 USBConnected()
646 {
647  uint8_t f = UDFNUML;
648  delay(3);
649  return (f != UDFNUML);
650 }
651 
652 USBDevice_ USBDevice;
653 
654 USBDevice_::USBDevice_()
655 {
656 }
657 
658 #if defined(__AVR_AT90USB162__)
659 #define HW_CONFIG()
660 #define PLL_CONFIG() (PLLCSR = ((1<<PLLE)|(1<<PLLP0)))
661 #define USB_CONFIG() (USBCON = (1<<USBE))
662 #define USB_UNCONFIG() (USBCON ^= (1<<USBE))
663 #define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
664 #elif defined(__AVR_ATmega32U4__)
665 #define HW_CONFIG() (UHWCON = 0x01)
666 #define PLL_CONFIG() (PLLCSR = ((I_CPU == 16) ? 0x10 : 0x0) | 0x2)
667 #define USB_CONFIG() (USBCON = ((1<<USBE)|(1<<OTGPADE)))
668 #define USB_UNCONFIG() (USBCON ^= (1<<USBE))
669 #define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
670 #elif defined(__AVR_AT90USB646__)
671 #define HW_CONFIG() (UHWCON = 0x81)
672 #define PLL_CONFIG() (PLLCSR = 0x1A)
673 #define USB_CONFIG() (USBCON = ((1<<USBE)|(1<<OTGPADE)))
674 #define USB_UNCONFIG() (USBCON ^= (1<<USBE))
675 #define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
676 #elif defined(__AVR_AT90USB1286__)
677 #define HW_CONFIG() (UHWCON = 0x81)
678 #define PLL_CONFIG() (PLLCSR = 0x16)
679 #define USB_CONFIG() (USBCON = ((1<<USBE)|(1<<OTGPADE)))
680 #define USB_UNCONFIG() (USBCON ^= (1<<USBE))
681 #define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
682 #endif
683 
684 bool
685 USBDevice_::attach()
686 {
687  // Configure hardware and phase-locked-loop and enable
688  _usbConfiguration = 0;
689  HW_CONFIG();
690  USB_FREEZE();
691  PLL_CONFIG();
692 
693  // Wait for the phase-locked-loop to lock on
694  loop_until_bit_is_set(PLLCSR, PLOCK);
695 
696  // Give the host some extra time
697  delay(1);
698 
699  // Start the usb clock and enable interrupt service
700  USB_CONFIG();
701  UDCON = 0;
702  UDINT = 0;
703  UDIEN = _BV(EORSTE) | _BV(SOFE) | _BV(SUSPE);
704 
705  // Wait for usb device to connect to host
706  TX_RX_LED_INIT;
707  for (uint8_t timeout=10;timeout;timeout--) { // 2 seconds
708  if (_usbConfiguration)
709  return (true);
710  delay(200);
711  }
712 
713  // Host didn't connect, disable USB, leave powered
714  UDIEN = _BV(DETACH);
715  USB_UNCONFIG();
716 
717  return (false);
718 }
719 
720 void
721 USBDevice_::detach()
722 {
723 }
724 
725 bool
726 USBDevice_::configured()
727 {
728  return (_usbConfiguration);
729 }
730 
731 void
732 USBDevice_::poll()
733 {
734 }
735 #endif
#define HID_REPORT_DESCRIPTOR_TYPE
Definition: Core.h:132
#define REQUEST_TYPE
Definition: Core.h:47
#define CDC_ACM_INTERFACE
Definition: Desc.h:48
#define IPRODUCT
Definition: Desc.h:71
#define USB_DEVICE_DESCRIPTOR_TYPE
Definition: Core.h:84
#define CDC_RX
Definition: Desc.h:62
#define D_DEVICE(_class, _subClass, _proto, _packetSize0, _vid, _pid, _version, _im, _ip, _is, _configs)
Definition: Core.h:274
#define GET_DESCRIPTOR
Definition: Core.h:32
#define REQUEST_STANDARD
Definition: Core.h:44
ISR(ANALOG_COMP_vect)
#define SET_INTERFACE
Definition: Core.h:37
#define DELAY(us)
Definition: Types.h:280
#define SET_FEATURE
Definition: Core.h:30
#define IMANUFACTURER
Definition: Desc.h:70
uint8_t lock()
Definition: Types.h:319
void(* delay)(uint32_t ms)
#define SET_DESCRIPTOR
Definition: Core.h:33
#define REQUEST_DEVICE
Definition: Core.h:49
#define CDC_TX
Definition: Desc.h:63
#define USB_CONFIGURATION_DESCRIPTOR_TYPE
Definition: Core.h:85
#define REQUEST_RECIPIENT
Definition: Core.h:53
void USB_GEN_vect(void)
#define USB_STRING_DESCRIPTOR_TYPE
Definition: Core.h:86
#define HID_INTERFACE
Definition: Desc.h:55
#define UNUSED(x)
Definition: ATmega328P.hh:31
void USB_COM_vect(void)
#define D_CONFIG(_totalLength, _interfaces)
Definition: Core.h:277
#define CLEAR_FEATURE
Definition: Core.h:29
#define GET_INTERFACE
Definition: Core.h:36
#define SET_ADDRESS
Definition: Core.h:31
#define GET_STATUS
Definition: Core.h:28
#define GET_CONFIGURATION
Definition: Core.h:34
#define SET_CONFIGURATION
Definition: Core.h:35
#define REQUEST_DEVICETOHOST
Definition: Core.h:41