|
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) */ |