]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/protocol/usb_hid/arduino-1.0.1/cores/arduino/USBCore.cpp
Merge commit 'a074364c3731d66b56d988c8a6c960a83ea0e0a1' as 'tmk_core'
[qmk_firmware.git] / tmk_core / protocol / usb_hid / arduino-1.0.1 / cores / arduino / USBCore.cpp
1
2
3 /* Copyright (c) 2010, Peter Barrett  
4 **  
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.  
8 ** 
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  
16 ** SOFTWARE.  
17 */
18
19 #include "Platform.h"
20 #include "USBAPI.h"
21 #include "USBDesc.h"
22
23 #if defined(USBCON)
24
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
32
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 */
37
38 //==================================================================
39 //==================================================================
40
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;
46
47 const u16 STRING_LANGUAGE[2] = {
48         (3<<8) | (2+2),
49         0x0409  // English
50 };
51
52 const u16 STRING_IPRODUCT[17] = {
53         (3<<8) | (2+2*16),
54 #if USB_PID == 0x8036   
55         'A','r','d','u','i','n','o',' ','L','e','o','n','a','r','d','o'
56 #else
57         'U','S','B',' ','I','O',' ','B','o','a','r','d',' ',' ',' ',' '
58 #endif
59 };
60
61 const u16 STRING_IMANUFACTURER[12] = {
62         (3<<8) | (2+2*11),
63 #if USB_VID == 0x2341
64         'A','r','d','u','i','n','o',' ','L','L','C'
65 #else
66         'U','n','k','n','o','w','n',' ',' ',' ',' '
67 #endif
68 };
69
70 #ifdef CDC_ENABLED
71 #define DEVICE_CLASS 0x02
72 #else
73 #define DEVICE_CLASS 0x00
74 #endif
75
76 //      DEVICE DESCRIPTOR
77 const DeviceDescriptor USB_DeviceDescriptor =
78         D_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);
79
80 const DeviceDescriptor USB_DeviceDescriptorA =
81         D_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);
82
83 //==================================================================
84 //==================================================================
85
86 volatile u8 _usbConfiguration = 0;
87
88 static inline void WaitIN(void)
89 {
90         while (!(UEINTX & (1<<TXINI)));
91 }
92
93 static inline void ClearIN(void)
94 {
95         UEINTX = ~(1<<TXINI);
96 }
97
98 static inline void WaitOUT(void)
99 {
100         while (!(UEINTX & (1<<RXOUTI)))
101                 ;
102 }
103
104 static inline u8 WaitForINOrOUT()
105 {
106         while (!(UEINTX & ((1<<TXINI)|(1<<RXOUTI))))
107                 ;
108         return (UEINTX & (1<<RXOUTI)) == 0;
109 }
110
111 static inline void ClearOUT(void)
112 {
113         UEINTX = ~(1<<RXOUTI);
114 }
115
116 void Recv(volatile u8* data, u8 count)
117 {
118         while (count--)
119                 *data++ = UEDATX;
120         
121         RXLED1;                                 // light the RX LED
122         RxLEDPulse = TX_RX_LED_PULSE_MS;        
123 }
124
125 static inline u8 Recv8()
126 {
127         RXLED1;                                 // light the RX LED
128         RxLEDPulse = TX_RX_LED_PULSE_MS;
129
130         return UEDATX;  
131 }
132
133 static inline void Send8(u8 d)
134 {
135         UEDATX = d;
136 }
137
138 static inline void SetEP(u8 ep)
139 {
140         UENUM = ep;
141 }
142
143 static inline u8 FifoByteCount()
144 {
145         return UEBCLX;
146 }
147
148 static inline u8 ReceivedSetupInt()
149 {
150         return UEINTX & (1<<RXSTPI);
151 }
152
153 static inline void ClearSetupInt()
154 {
155         UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));
156 }
157
158 static inline void Stall()
159 {
160         UECONX = (1<<STALLRQ) | (1<<EPEN);
161 }
162
163 static inline u8 ReadWriteAllowed()
164 {
165         return UEINTX & (1<<RWAL);
166 }
167
168 static inline u8 Stalled()
169 {
170         return UEINTX & (1<<STALLEDI);
171 }
172
173 static inline u8 FifoFree()
174 {
175         return UEINTX & (1<<FIFOCON);
176 }
177
178 static inline void ReleaseRX()
179 {
180         UEINTX = 0x6B;  // FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1
181 }
182
183 static inline void ReleaseTX()
184 {
185         UEINTX = 0x3A;  // FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0
186 }
187
188 static inline u8 FrameNumber()
189 {
190         return UDFNUML;
191 }
192
193 //==================================================================
194 //==================================================================
195
196 u8 USBGetConfiguration(void)
197 {
198         return _usbConfiguration;
199 }
200
201 #define USB_RECV_TIMEOUT
202 class LockEP
203 {
204         u8 _sreg;
205 public:
206         LockEP(u8 ep) : _sreg(SREG)
207         {
208                 cli();
209                 SetEP(ep & 7);
210         }
211         ~LockEP()
212         {
213                 SREG = _sreg;
214         }
215 };
216
217 //      Number of bytes, assumes a rx endpoint
218 u8 USB_Available(u8 ep)
219 {
220         LockEP lock(ep);
221         return FifoByteCount();
222 }
223
224 //      Non Blocking receive
225 //      Return number of bytes read
226 int USB_Recv(u8 ep, void* d, int len)
227 {
228         if (!_usbConfiguration || len < 0)
229                 return -1;
230         
231         LockEP lock(ep);
232         u8 n = FifoByteCount();
233         len = min(n,len);
234         n = len;
235         u8* dst = (u8*)d;
236         while (n--)
237                 *dst++ = Recv8();
238         if (len && !FifoByteCount())    // release empty buffer
239                 ReleaseRX();
240         
241         return len;
242 }
243
244 //      Recv 1 byte if ready
245 int USB_Recv(u8 ep)
246 {
247         u8 c;
248         if (USB_Recv(ep,&c,1) != 1)
249                 return -1;
250         return c;
251 }
252
253 //      Space in send EP
254 u8 USB_SendSpace(u8 ep)
255 {
256         LockEP lock(ep);
257         if (!ReadWriteAllowed())
258                 return 0;
259         return 64 - FifoByteCount();
260 }
261
262 //      Blocking Send of data to an endpoint
263 int USB_Send(u8 ep, const void* d, int len)
264 {
265         if (!_usbConfiguration)
266                 return -1;
267
268         int r = len;
269         const u8* data = (const u8*)d;
270         u8 zero = ep & TRANSFER_ZERO;
271         u8 timeout = 250;               // 250ms timeout on send? TODO
272         while (len)
273         {
274                 u8 n = USB_SendSpace(ep);
275                 if (n == 0)
276                 {
277                         if (!(--timeout))
278                                 return -1;
279                         delay(1);
280                         continue;
281                 }
282
283                 if (n > len)
284                         n = len;
285                 len -= n;
286                 {
287                         LockEP lock(ep);
288                         if (ep & TRANSFER_ZERO)
289                         {
290                                 while (n--)
291                                         Send8(0);
292                         }
293                         else if (ep & TRANSFER_PGM)
294                         {
295                                 while (n--)
296                                         Send8(pgm_read_byte(data++));
297                         }
298                         else
299                         {
300                                 while (n--)
301                                         Send8(*data++);
302                         }
303                         if (!ReadWriteAllowed() || ((len == 0) && (ep & TRANSFER_RELEASE)))     // Release full buffer
304                                 ReleaseTX();
305                 }
306         }
307         TXLED1;                                 // light the TX LED
308         TxLEDPulse = TX_RX_LED_PULSE_MS;
309         return r;
310 }
311
312 extern const u8 _initEndpoints[] PROGMEM;
313 const u8 _initEndpoints[] = 
314 {
315         0,
316         
317 #ifdef CDC_ENABLED
318         EP_TYPE_INTERRUPT_IN,           // CDC_ENDPOINT_ACM
319         EP_TYPE_BULK_OUT,                       // CDC_ENDPOINT_OUT
320         EP_TYPE_BULK_IN,                        // CDC_ENDPOINT_IN
321 #endif
322
323 #ifdef HID_ENABLED
324         EP_TYPE_INTERRUPT_IN            // HID_ENDPOINT_INT
325 #endif
326 };
327
328 #define EP_SINGLE_64 0x32       // EP0
329 #define EP_DOUBLE_64 0x36       // Other endpoints
330
331 static
332 void InitEP(u8 index, u8 type, u8 size)
333 {
334         UENUM = index;
335         UECONX = 1;
336         UECFG0X = type;
337         UECFG1X = size;
338 }
339
340 static
341 void InitEndpoints()
342 {
343         for (u8 i = 1; i < sizeof(_initEndpoints); i++)
344         {
345                 UENUM = i;
346                 UECONX = 1;
347                 UECFG0X = pgm_read_byte(_initEndpoints+i);
348                 UECFG1X = EP_DOUBLE_64;
349         }
350         UERST = 0x7E;   // And reset them
351         UERST = 0;
352 }
353
354 //      Handle CLASS_INTERFACE requests
355 static
356 bool ClassInterfaceRequest(Setup& setup)
357 {
358         u8 i = setup.wIndex;
359
360 #ifdef CDC_ENABLED
361         if (CDC_ACM_INTERFACE == i)
362                 return CDC_Setup(setup);
363 #endif
364
365 #ifdef HID_ENABLED
366         if (HID_INTERFACE == i)
367                 return HID_Setup(setup);
368 #endif
369         return false;
370 }
371
372 int _cmark;
373 int _cend;
374 void InitControl(int end)
375 {
376         SetEP(0);
377         _cmark = 0;
378         _cend = end;
379 }
380
381 static
382 bool SendControl(u8 d)
383 {
384         if (_cmark < _cend)
385         {
386                 if (!WaitForINOrOUT())
387                         return false;
388                 Send8(d);
389                 if (!((_cmark + 1) & 0x3F))
390                         ClearIN();      // Fifo is full, release this packet
391         }
392         _cmark++;
393         return true;
394 };
395
396 //      Clipped by _cmark/_cend
397 int USB_SendControl(u8 flags, const void* d, int len)
398 {
399         int sent = len;
400         const u8* data = (const u8*)d;
401         bool pgm = flags & TRANSFER_PGM;
402         while (len--)
403         {
404                 u8 c = pgm ? pgm_read_byte(data++) : *data++;
405                 if (!SendControl(c))
406                         return -1;
407         }
408         return sent;
409 }
410
411 //      Does not timeout or cross fifo boundaries
412 //      Will only work for transfers <= 64 bytes
413 //      TODO
414 int USB_RecvControl(void* d, int len)
415 {
416         WaitOUT();
417         Recv((u8*)d,len);
418         ClearOUT();
419         return len;
420 }
421
422 int SendInterfaces()
423 {
424         int total = 0;
425         u8 interfaces = 0;
426
427 #ifdef CDC_ENABLED
428         total = CDC_GetInterface(&interfaces);
429 #endif
430
431 #ifdef HID_ENABLED
432         total += HID_GetInterface(&interfaces);
433 #endif
434
435         return interfaces;
436 }
437
438 //      Construct a dynamic configuration descriptor
439 //      This really needs dynamic endpoint allocation etc
440 //      TODO
441 static
442 bool SendConfiguration(int maxlen)
443 {
444         //      Count and measure interfaces
445         InitControl(0); 
446         int interfaces = SendInterfaces();
447         ConfigDescriptor config = D_CONFIG(_cmark + sizeof(ConfigDescriptor),interfaces);
448
449         //      Now send them
450         InitControl(maxlen);
451         USB_SendControl(0,&config,sizeof(ConfigDescriptor));
452         SendInterfaces();
453         return true;
454 }
455
456 u8 _cdcComposite = 0;
457
458 static
459 bool SendDescriptor(Setup& setup)
460 {
461         u8 t = setup.wValueH;
462         if (USB_CONFIGURATION_DESCRIPTOR_TYPE == t)
463                 return SendConfiguration(setup.wLength);
464
465         InitControl(setup.wLength);
466 #ifdef HID_ENABLED
467         if (HID_REPORT_DESCRIPTOR_TYPE == t)
468                 return HID_GetDescriptor(t);
469 #endif
470
471         u8 desc_length = 0;
472         const u8* desc_addr = 0;
473         if (USB_DEVICE_DESCRIPTOR_TYPE == t)
474         {
475                 if (setup.wLength == 8)
476                         _cdcComposite = 1;
477                 desc_addr = _cdcComposite ?  (const u8*)&USB_DeviceDescriptorA : (const u8*)&USB_DeviceDescriptor;
478         }
479         else if (USB_STRING_DESCRIPTOR_TYPE == t)
480         {
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;
487                 else
488                         return false;
489         }
490
491         if (desc_addr == 0)
492                 return false;
493         if (desc_length == 0)
494                 desc_length = pgm_read_byte(desc_addr);
495
496         USB_SendControl(TRANSFER_PGM,desc_addr,desc_length);
497         return true;
498 }
499
500 //      Endpoint 0 interrupt
501 ISR(USB_COM_vect)
502 {
503     SetEP(0);
504         if (!ReceivedSetupInt())
505                 return;
506
507         Setup setup;
508         Recv((u8*)&setup,8);
509         ClearSetupInt();
510
511         u8 requestType = setup.bmRequestType;
512         if (requestType & REQUEST_DEVICETOHOST)
513                 WaitIN();
514         else
515                 ClearIN();
516
517     bool ok = true;
518         if (REQUEST_STANDARD == (requestType & REQUEST_TYPE))
519         {
520                 //      Standard Requests
521                 u8 r = setup.bRequest;
522                 if (GET_STATUS == r)
523                 {
524                         Send8(0);               // TODO
525                         Send8(0);
526                 }
527                 else if (CLEAR_FEATURE == r)
528                 {
529                 }
530                 else if (SET_FEATURE == r)
531                 {
532                 }
533                 else if (SET_ADDRESS == r)
534                 {
535                         WaitIN();
536                         UDADDR = setup.wValueL | (1<<ADDEN);
537                 }
538                 else if (GET_DESCRIPTOR == r)
539                 {
540                         ok = SendDescriptor(setup);
541                 }
542                 else if (SET_DESCRIPTOR == r)
543                 {
544                         ok = false;
545                 }
546                 else if (GET_CONFIGURATION == r)
547                 {
548                         Send8(1);
549                 }
550                 else if (SET_CONFIGURATION == r)
551                 {
552                         if (REQUEST_DEVICE == (requestType & REQUEST_RECIPIENT))
553                         {
554                                 InitEndpoints();
555                                 _usbConfiguration = setup.wValueL;
556                         } else
557                                 ok = false;
558                 }
559                 else if (GET_INTERFACE == r)
560                 {
561                 }
562                 else if (SET_INTERFACE == r)
563                 {
564                 }
565         }
566         else
567         {
568                 InitControl(setup.wLength);             //      Max length of transfer
569                 ok = ClassInterfaceRequest(setup);
570         }
571
572         if (ok)
573                 ClearIN();
574         else
575         {
576                 Stall();
577         }
578 }
579
580 void USB_Flush(u8 ep)
581 {
582         SetEP(ep);
583         if (FifoByteCount())
584                 ReleaseTX();
585 }
586
587 //      General interrupt
588 ISR(USB_GEN_vect)
589 {
590         u8 udint = UDINT;
591         UDINT = 0;
592
593         //      End of Reset
594         if (udint & (1<<EORSTI))
595         {
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
599         }
600
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))
603         {
604 #ifdef CDC_ENABLED
605                 USB_Flush(CDC_TX);                              // Send a tx frame if found
606                 while (USB_Available(CDC_RX))   // Handle received bytes (if any)
607                         Serial.accept();
608 #endif
609                 
610                 // check whether the one-shot period has elapsed.  if so, turn off the LED
611                 if (TxLEDPulse && !(--TxLEDPulse))
612                         TXLED0;
613                 if (RxLEDPulse && !(--RxLEDPulse))
614                         RXLED0;
615         }
616 }
617
618 //      VBUS or counting frames
619 //      Any frame counting?
620 u8 USBConnected()
621 {
622         u8 f = UDFNUML;
623         delay(3);
624         return f != UDFNUML;
625 }
626
627 //=======================================================================
628 //=======================================================================
629
630 USBDevice_ USBDevice;
631
632 USBDevice_::USBDevice_()
633 {
634 }
635
636 void USBDevice_::attach()
637 {
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
643                 ;
644
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.
648         delay(1);
649
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
653         
654         TX_RX_LED_INIT;
655 }
656
657 void USBDevice_::detach()
658 {
659 }
660
661 //      Check for interrupts
662 //      TODO: VBUS detection
663 bool USBDevice_::configured()
664 {
665         return _usbConfiguration;
666 }
667
668 void USBDevice_::poll()
669 {
670 }
671
672 #endif /* if defined(USBCON) */