root / arduino-1.0 / hardware / arduino / bootloaders / diskloader / src / USBCore.cpp @ 58d82c77
History | View | Annotate | Download (10.1 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 |
|
21 |
#define CDC_TX CDC_ENDPOINT_IN
|
22 |
#define CDC_RX CDC_ENDPOINT_OUT
|
23 |
|
24 |
#define EP_TYPE_CONTROL 0x00 |
25 |
#define EP_TYPE_BULK_IN 0x81 |
26 |
#define EP_TYPE_BULK_OUT 0x80 |
27 |
#define EP_TYPE_INTERRUPT_IN 0xC1 |
28 |
#define EP_TYPE_INTERRUPT_OUT 0xC0 |
29 |
#define EP_TYPE_ISOCHRONOUS_IN 0x41 |
30 |
#define EP_TYPE_ISOCHRONOUS_OUT 0x40 |
31 |
|
32 |
/** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */
|
33 |
#define TX_RX_LED_PULSE_MS 100 |
34 |
u8 TxLEDPulse; /**< Milliseconds remaining for data Tx LED pulse */
|
35 |
u8 RxLEDPulse; /**< Milliseconds remaining for data Rx LED pulse */
|
36 |
|
37 |
void Reboot();
|
38 |
|
39 |
//==================================================================
|
40 |
//==================================================================
|
41 |
|
42 |
typedef struct |
43 |
{ |
44 |
u32 dwDTERate; |
45 |
u8 bCharFormat; |
46 |
u8 bParityType; |
47 |
u8 bDataBits; |
48 |
u8 lineState; |
49 |
} LineInfo; |
50 |
|
51 |
static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 }; |
52 |
|
53 |
//==================================================================
|
54 |
//==================================================================
|
55 |
|
56 |
// 4 bytes of RAM
|
57 |
volatile u8 _usbConfiguration;
|
58 |
volatile u8 _ejected;
|
59 |
volatile u16 _timeout;
|
60 |
|
61 |
static inline void WaitIN(void) |
62 |
{ |
63 |
while (!(UEINTX & (1<<TXINI))); |
64 |
} |
65 |
|
66 |
static inline void ClearIN(void) |
67 |
{ |
68 |
UEINTX = ~(1<<TXINI);
|
69 |
} |
70 |
|
71 |
static inline void WaitOUT(void) |
72 |
{ |
73 |
while (!(UEINTX & (1<<RXOUTI))) |
74 |
; |
75 |
} |
76 |
|
77 |
static inline u8 WaitForINOrOUT() |
78 |
{ |
79 |
while (!(UEINTX & ((1<<TXINI)|(1<<RXOUTI)))) |
80 |
; |
81 |
return (UEINTX & (1<<RXOUTI)) == 0; |
82 |
} |
83 |
|
84 |
static inline void ClearOUT(void) |
85 |
{ |
86 |
UEINTX = ~(1<<RXOUTI);
|
87 |
} |
88 |
|
89 |
static
|
90 |
void Send(volatile const u8* data, u8 count) |
91 |
{ |
92 |
TXLED1; // light the TX LED
|
93 |
TxLEDPulse = TX_RX_LED_PULSE_MS; |
94 |
while (count--)
|
95 |
UEDATX = *data++; |
96 |
} |
97 |
|
98 |
void Recv(volatile u8* data, u8 count) |
99 |
{ |
100 |
RXLED1; // light the RX LED
|
101 |
RxLEDPulse = TX_RX_LED_PULSE_MS; |
102 |
while (count--)
|
103 |
*data++ = UEDATX; |
104 |
} |
105 |
|
106 |
static inline u8 Recv8() |
107 |
{ |
108 |
RXLED1; // light the RX LED
|
109 |
RxLEDPulse = TX_RX_LED_PULSE_MS; |
110 |
return UEDATX;
|
111 |
} |
112 |
|
113 |
static inline void Send8(u8 d) |
114 |
{ |
115 |
TXLED1; // light the TX LED
|
116 |
TxLEDPulse = TX_RX_LED_PULSE_MS; |
117 |
UEDATX = d; |
118 |
} |
119 |
|
120 |
static inline void SetEP(u8 ep) |
121 |
{ |
122 |
UENUM = ep; |
123 |
} |
124 |
|
125 |
static inline u8 FifoByteCount() |
126 |
{ |
127 |
return UEBCLX;
|
128 |
} |
129 |
|
130 |
static inline u8 ReceivedSetupInt() |
131 |
{ |
132 |
return UEINTX & (1<<RXSTPI); |
133 |
} |
134 |
|
135 |
static inline void ClearSetupInt() |
136 |
{ |
137 |
UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI)); |
138 |
} |
139 |
|
140 |
static inline void Stall() |
141 |
{ |
142 |
UECONX = (1<<STALLRQ) | (1<<EPEN); |
143 |
} |
144 |
|
145 |
static inline u8 ReadWriteAllowed() |
146 |
{ |
147 |
return UEINTX & (1<<RWAL); |
148 |
} |
149 |
|
150 |
static inline u8 Stalled() |
151 |
{ |
152 |
return UEINTX & (1<<STALLEDI); |
153 |
} |
154 |
|
155 |
static inline u8 FifoFree() |
156 |
{ |
157 |
return UEINTX & (1<<FIFOCON); |
158 |
} |
159 |
|
160 |
static inline void ReleaseRX() |
161 |
{ |
162 |
UEINTX = 0x6B; // FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1 |
163 |
} |
164 |
|
165 |
static inline void ReleaseTX() |
166 |
{ |
167 |
UEINTX = 0x3A; // FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0 |
168 |
} |
169 |
|
170 |
static inline u8 FrameNumber() |
171 |
{ |
172 |
return UDFNUML;
|
173 |
} |
174 |
|
175 |
//==================================================================
|
176 |
//==================================================================
|
177 |
|
178 |
#define EP_SINGLE_64 0x32 // EP0 |
179 |
#define EP_DOUBLE_64 0x36 // Other endpoints |
180 |
|
181 |
static void InitEP(u8 index, u8 type, u8 size) |
182 |
{ |
183 |
UENUM = index; |
184 |
UECONX = 1;
|
185 |
UECFG0X = type; |
186 |
UECFG1X = size; |
187 |
} |
188 |
|
189 |
// API
|
190 |
void USBInit(void) |
191 |
{ |
192 |
_timeout = 0;
|
193 |
_usbConfiguration = 0;
|
194 |
_ejected = 0;
|
195 |
|
196 |
UHWCON = 0x01; // power internal reg (don't need this?) |
197 |
USBCON = (1<<USBE)|(1<<FRZCLK); // clock frozen, usb enabled |
198 |
PLLCSR = 0x12; // Need 16 MHz xtal |
199 |
while (!(PLLCSR & (1<<PLOCK))) // wait for lock pll |
200 |
; |
201 |
USBCON = ((1<<USBE)|(1<<OTGPADE)); // start USB clock |
202 |
UDCON = 0; // enable attach resistor |
203 |
} |
204 |
|
205 |
u8 USBGetConfiguration(void)
|
206 |
{ |
207 |
return _usbConfiguration;
|
208 |
} |
209 |
|
210 |
u8 HasData(u8 ep) |
211 |
{ |
212 |
SetEP(ep); |
213 |
return ReadWriteAllowed(); // count in fifo |
214 |
} |
215 |
|
216 |
int USBGetChar();
|
217 |
void Recv(u8 ep, u8* dst, u8 len)
|
218 |
{ |
219 |
SetEP(ep); |
220 |
while (len--)
|
221 |
{ |
222 |
while (!ReadWriteAllowed())
|
223 |
; |
224 |
*dst++ = Recv8(); |
225 |
if (!ReadWriteAllowed()) // release empty buffer |
226 |
ReleaseRX(); |
227 |
} |
228 |
} |
229 |
|
230 |
// Transmit a packet to endpoint
|
231 |
void Transfer(u8 ep, const u8* data, int len) |
232 |
{ |
233 |
u8 zero = ep & TRANSFER_ZERO; |
234 |
SetEP(ep & 7);
|
235 |
while (len--)
|
236 |
{ |
237 |
while (!ReadWriteAllowed())
|
238 |
; // TODO Check for STALL etc
|
239 |
|
240 |
u8 d = (ep & TRANSFER_PGM) ? pgm_read_byte(data) : data[0];
|
241 |
data++; |
242 |
if (zero)
|
243 |
d = 0;
|
244 |
Send8(d); |
245 |
|
246 |
if (!ReadWriteAllowed())
|
247 |
ReleaseTX(); |
248 |
} |
249 |
if (ep & TRANSFER_RELEASE)
|
250 |
ReleaseTX(); |
251 |
} |
252 |
|
253 |
extern const u8 _initEndpoints[] PROGMEM; |
254 |
const u8 _initEndpoints[] =
|
255 |
{ |
256 |
0,
|
257 |
|
258 |
#ifdef CDC_ENABLED
|
259 |
EP_TYPE_INTERRUPT_IN, // CDC_ENDPOINT_ACM
|
260 |
EP_TYPE_BULK_OUT, // CDC_ENDPOINT_OUT
|
261 |
EP_TYPE_BULK_IN, // CDC_ENDPOINT_IN
|
262 |
#endif
|
263 |
|
264 |
EP_TYPE_INTERRUPT_IN, // HID_ENDPOINT_INT
|
265 |
}; |
266 |
|
267 |
static void InitEndpoints() |
268 |
{ |
269 |
for (u8 i = 1; i < sizeof(_initEndpoints); i++) |
270 |
{ |
271 |
UENUM = i; |
272 |
UECONX = 1;
|
273 |
UECFG0X = pgm_read_byte(_initEndpoints+i); |
274 |
UECFG1X = EP_DOUBLE_64; |
275 |
} |
276 |
UERST = 0x7E; // And reset them |
277 |
UERST = 0;
|
278 |
} |
279 |
|
280 |
typedef struct |
281 |
{ |
282 |
u8 bmRequestType; |
283 |
u8 bRequest; |
284 |
u8 wValueL; |
285 |
u8 wValueH; |
286 |
u16 wIndex; |
287 |
u16 wLength; |
288 |
} Setup; |
289 |
Setup _setup; |
290 |
|
291 |
//bool USBHook(Setup& setup)
|
292 |
bool USBHook()
|
293 |
{ |
294 |
Setup& setup = _setup; |
295 |
u8 r = setup.bRequest; |
296 |
|
297 |
// CDC Requests
|
298 |
if (CDC_GET_LINE_CODING == r)
|
299 |
{ |
300 |
Send((const volatile u8*)&_usbLineInfo,7); |
301 |
} |
302 |
|
303 |
else if (CDC_SET_LINE_CODING == r) |
304 |
{ |
305 |
WaitOUT(); |
306 |
Recv((volatile u8*)&_usbLineInfo,7); |
307 |
ClearOUT(); |
308 |
} |
309 |
|
310 |
else if (CDC_SET_CONTROL_LINE_STATE == r) |
311 |
{ |
312 |
_usbLineInfo.lineState = setup.wValueL; |
313 |
} |
314 |
|
315 |
return true; |
316 |
} |
317 |
|
318 |
extern const u8 _rawHID[] PROGMEM; |
319 |
#define LSB(_x) ((_x) & 0xFF) |
320 |
#define MSB(_x) ((_x) >> 8) |
321 |
|
322 |
#define RAWHID_USAGE_PAGE 0xFFC0 |
323 |
#define RAWHID_USAGE 0x0C00 |
324 |
#define RAWHID_TX_SIZE 64 |
325 |
#define RAWHID_RX_SIZE 64 |
326 |
|
327 |
const u8 _rawHID[] =
|
328 |
{ |
329 |
// RAW HID
|
330 |
0x06, LSB(RAWHID_USAGE_PAGE), MSB(RAWHID_USAGE_PAGE), // 30 |
331 |
0x0A, LSB(RAWHID_USAGE), MSB(RAWHID_USAGE),
|
332 |
|
333 |
0xA1, 0x01, // Collection 0x01 |
334 |
0x85, 0x03, // REPORT_ID (3) |
335 |
0x75, 0x08, // report size = 8 bits |
336 |
0x15, 0x00, // logical minimum = 0 |
337 |
0x26, 0xFF, 0x00, // logical maximum = 255 |
338 |
|
339 |
0x95, 64, // report count TX |
340 |
0x09, 0x01, // usage |
341 |
0x81, 0x02, // Input (array) |
342 |
|
343 |
0x95, 64, // report count RX |
344 |
0x09, 0x02, // usage |
345 |
0x91, 0x02, // Output (array) |
346 |
0xC0 // end collection |
347 |
}; |
348 |
|
349 |
u8 _cdcComposite = 0;
|
350 |
|
351 |
bool SendDescriptor()
|
352 |
{ |
353 |
Setup& setup = _setup; |
354 |
u8 desc_length = 0;
|
355 |
const u8* desc_addr = 0; |
356 |
|
357 |
u8 t = setup.wValueH; |
358 |
if (0x22 == t) |
359 |
{ |
360 |
desc_addr = _rawHID; |
361 |
desc_length = sizeof(desc_length);
|
362 |
} else if (USB_DEVICE_DESCRIPTOR_TYPE == t) |
363 |
{ |
364 |
if (setup.wLength == 8) |
365 |
_cdcComposite = 1;
|
366 |
desc_addr = _cdcComposite ? (const u8*)&USB_DeviceDescriptorA : (const u8*)&USB_DeviceDescriptor; |
367 |
} |
368 |
else if (USB_CONFIGURATION_DESCRIPTOR_TYPE == t) |
369 |
{ |
370 |
desc_addr = (const u8*)&USB_ConfigDescriptor;
|
371 |
desc_length = sizeof(USB_ConfigDescriptor);
|
372 |
} |
373 |
else if (USB_STRING_DESCRIPTOR_TYPE == t) |
374 |
{ |
375 |
if (setup.wValueL == 0) |
376 |
desc_addr = (const u8*)&STRING_LANGUAGE;
|
377 |
else if (setup.wValueL == IPRODUCT) |
378 |
desc_addr = (const u8*)&STRING_IPRODUCT;
|
379 |
else if (setup.wValueL == ISERIAL) |
380 |
desc_addr = (const u8*)&STRING_SERIAL;
|
381 |
else if (setup.wValueL == IMANUFACTURER) |
382 |
desc_addr = (const u8*)&STRING_IMANUFACTURER;
|
383 |
else
|
384 |
return false; |
385 |
} else
|
386 |
return false; |
387 |
|
388 |
if (desc_length == 0) |
389 |
desc_length = pgm_read_byte(desc_addr); |
390 |
if ((u8)setup.wLength < desc_length) // bit of a cheat limiting to 255 bytes TODO (saved 8 bytes) |
391 |
desc_length = (u8)setup.wLength; |
392 |
|
393 |
// Send descriptor
|
394 |
// EP0 is 64 bytes long
|
395 |
// RWAL and FIFOCON don't work on EP0
|
396 |
u8 n = 0;
|
397 |
do
|
398 |
{ |
399 |
if (!WaitForINOrOUT())
|
400 |
return false; |
401 |
Send8(pgm_read_byte(&desc_addr[n++])); |
402 |
u8 clr = n & 0x3F;
|
403 |
if (!clr)
|
404 |
ClearIN(); // Fifo is full, release this packet
|
405 |
} while (n < desc_length);
|
406 |
return true; |
407 |
} |
408 |
|
409 |
void USBSetupInterrupt()
|
410 |
{ |
411 |
SetEP(0);
|
412 |
if (!ReceivedSetupInt())
|
413 |
return;
|
414 |
|
415 |
Setup& setup = _setup; // global saves ~30 bytes
|
416 |
Recv((u8*)&setup,8);
|
417 |
ClearSetupInt(); |
418 |
|
419 |
if (setup.bmRequestType & DEVICETOHOST)
|
420 |
WaitIN(); |
421 |
else
|
422 |
ClearIN(); |
423 |
|
424 |
bool ok = true; |
425 |
u8 r = setup.bRequest; |
426 |
if (SET_ADDRESS == r)
|
427 |
{ |
428 |
WaitIN(); |
429 |
UDADDR = setup.wValueL | (1<<ADDEN);
|
430 |
} |
431 |
else if (SET_CONFIGURATION == r) |
432 |
{ |
433 |
_usbConfiguration = setup.wValueL; |
434 |
InitEndpoints(); |
435 |
} |
436 |
else if (GET_CONFIGURATION == r) |
437 |
{ |
438 |
Send8(_usbConfiguration); |
439 |
} |
440 |
else if (GET_STATUS == r) |
441 |
{ |
442 |
Send8(0); // All good as far as I know |
443 |
} |
444 |
else if (GET_DESCRIPTOR == r) |
445 |
{ |
446 |
ok = SendDescriptor(); |
447 |
} |
448 |
else
|
449 |
{ |
450 |
ok = USBHook(); |
451 |
} |
452 |
|
453 |
if (ok)
|
454 |
ClearIN(); |
455 |
else
|
456 |
Stall(); |
457 |
} |
458 |
|
459 |
void USBGeneralInterrupt()
|
460 |
{ |
461 |
u8 udint = UDINT; |
462 |
UDINT = 0;
|
463 |
|
464 |
// End of Reset
|
465 |
if (udint & (1<<EORSTI)) |
466 |
{ |
467 |
InitEP(0,EP_TYPE_CONTROL,EP_SINGLE_64); // init ep0 |
468 |
_usbConfiguration = 0; // not configured yet |
469 |
} |
470 |
|
471 |
// Start of Frame - happens every millisecond so we use it for TX and RX LED one-shot timing, too
|
472 |
if (udint & (1<<SOFI)) |
473 |
{ |
474 |
// check whether the one-shot period has elapsed. if so, turn off the LED
|
475 |
if (TxLEDPulse && !(--TxLEDPulse))
|
476 |
TXLED0; |
477 |
if (RxLEDPulse && !(--RxLEDPulse))
|
478 |
RXLED0; |
479 |
|
480 |
if (!_ejected)
|
481 |
_timeout = 0;
|
482 |
} |
483 |
} |
484 |
|
485 |
void LEDPulse();
|
486 |
int USBGetChar()
|
487 |
{ |
488 |
for(;;)
|
489 |
{ |
490 |
USBSetupInterrupt(); |
491 |
USBGeneralInterrupt(); |
492 |
|
493 |
// Read a char
|
494 |
if (HasData(CDC_RX))
|
495 |
{ |
496 |
u8 c = Recv8(); |
497 |
if (!ReadWriteAllowed())
|
498 |
ReleaseRX(); |
499 |
return c;
|
500 |
} |
501 |
|
502 |
if (!--_timeout) {
|
503 |
Reboot(); // USB not connected, run firmware
|
504 |
} |
505 |
|
506 |
_delay_us(100); // stretch out the bootloader period to about 5 seconds after enumeration |
507 |
LEDPulse(); |
508 |
} |
509 |
return -1; |
510 |
} |