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