Fri, 17 Nov 2017 10:13:31 +0100
proper configuration, homing and planner optimization
2 | 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) */ |