diff options
Diffstat (limited to 'lib/usbhost/USB_Host_Shield_2.0/Usb.cpp')
-rw-r--r-- | lib/usbhost/USB_Host_Shield_2.0/Usb.cpp | 812 |
1 files changed, 812 insertions, 0 deletions
diff --git a/lib/usbhost/USB_Host_Shield_2.0/Usb.cpp b/lib/usbhost/USB_Host_Shield_2.0/Usb.cpp new file mode 100644 index 000000000..14272588a --- /dev/null +++ b/lib/usbhost/USB_Host_Shield_2.0/Usb.cpp | |||
@@ -0,0 +1,812 @@ | |||
1 | /* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. | ||
2 | |||
3 | This software may be distributed and modified under the terms of the GNU | ||
4 | General Public License version 2 (GPL2) as published by the Free Software | ||
5 | Foundation and appearing in the file GPL2.TXT included in the packaging of | ||
6 | this file. Please note that GPL2 Section 2[b] requires that all works based | ||
7 | on this software must also be made publicly available under the terms of | ||
8 | the GPL2 ("Copyleft"). | ||
9 | |||
10 | Contact information | ||
11 | ------------------- | ||
12 | |||
13 | Circuits At Home, LTD | ||
14 | Web : http://www.circuitsathome.com | ||
15 | e-mail : support@circuitsathome.com | ||
16 | */ | ||
17 | /* USB functions */ | ||
18 | |||
19 | #include "Usb.h" | ||
20 | |||
21 | static uint8_t usb_error = 0; | ||
22 | static uint8_t usb_task_state; | ||
23 | |||
24 | /* constructor */ | ||
25 | USB::USB() : bmHubPre(0) { | ||
26 | usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; //set up state machine | ||
27 | init(); | ||
28 | } | ||
29 | |||
30 | /* Initialize data structures */ | ||
31 | void USB::init() { | ||
32 | //devConfigIndex = 0; | ||
33 | bmHubPre = 0; | ||
34 | } | ||
35 | |||
36 | uint8_t USB::getUsbTaskState(void) { | ||
37 | return ( usb_task_state); | ||
38 | } | ||
39 | |||
40 | void USB::setUsbTaskState(uint8_t state) { | ||
41 | usb_task_state = state; | ||
42 | } | ||
43 | |||
44 | EpInfo* USB::getEpInfoEntry(uint8_t addr, uint8_t ep) { | ||
45 | UsbDevice *p = addrPool.GetUsbDevicePtr(addr); | ||
46 | |||
47 | if(!p || !p->epinfo) | ||
48 | return NULL; | ||
49 | |||
50 | EpInfo *pep = p->epinfo; | ||
51 | |||
52 | for(uint8_t i = 0; i < p->epcount; i++) { | ||
53 | if((pep)->epAddr == ep) | ||
54 | return pep; | ||
55 | |||
56 | pep++; | ||
57 | } | ||
58 | return NULL; | ||
59 | } | ||
60 | |||
61 | /* set device table entry */ | ||
62 | |||
63 | /* each device is different and has different number of endpoints. This function plugs endpoint record structure, defined in application, to devtable */ | ||
64 | uint8_t USB::setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr) { | ||
65 | if(!eprecord_ptr) | ||
66 | return USB_ERROR_INVALID_ARGUMENT; | ||
67 | |||
68 | UsbDevice *p = addrPool.GetUsbDevicePtr(addr); | ||
69 | |||
70 | if(!p) | ||
71 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; | ||
72 | |||
73 | p->address.devAddress = addr; | ||
74 | p->epinfo = eprecord_ptr; | ||
75 | p->epcount = epcount; | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | uint8_t USB::SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit) { | ||
81 | UsbDevice *p = addrPool.GetUsbDevicePtr(addr); | ||
82 | |||
83 | if(!p) | ||
84 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; | ||
85 | |||
86 | if(!p->epinfo) | ||
87 | return USB_ERROR_EPINFO_IS_NULL; | ||
88 | |||
89 | *ppep = getEpInfoEntry(addr, ep); | ||
90 | |||
91 | if(!*ppep) | ||
92 | return USB_ERROR_EP_NOT_FOUND_IN_TBL; | ||
93 | |||
94 | *nak_limit = (0x0001UL << (((*ppep)->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower)); | ||
95 | (*nak_limit)--; | ||
96 | /* | ||
97 | USBTRACE2("\r\nAddress: ", addr); | ||
98 | USBTRACE2(" EP: ", ep); | ||
99 | USBTRACE2(" NAK Power: ",(*ppep)->bmNakPower); | ||
100 | USBTRACE2(" NAK Limit: ", nak_limit); | ||
101 | USBTRACE("\r\n"); | ||
102 | */ | ||
103 | regWr(rPERADDR, addr); //set peripheral address | ||
104 | |||
105 | uint8_t mode = regRd(rMODE); | ||
106 | |||
107 | //Serial.print("\r\nMode: "); | ||
108 | //Serial.println( mode, HEX); | ||
109 | //Serial.print("\r\nLS: "); | ||
110 | //Serial.println(p->lowspeed, HEX); | ||
111 | |||
112 | |||
113 | |||
114 | // Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise | ||
115 | regWr(rMODE, (p->lowspeed) ? mode | bmLOWSPEED | bmHubPre : mode & ~(bmHUBPRE | bmLOWSPEED)); | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | /* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer, */ | ||
121 | /* depending on request. Actual requests are defined as inlines */ | ||
122 | /* return codes: */ | ||
123 | /* 00 = success */ | ||
124 | |||
125 | /* 01-0f = non-zero HRSLT */ | ||
126 | uint8_t USB::ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi, | ||
127 | uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p) { | ||
128 | bool direction = false; //request direction, IN or OUT | ||
129 | uint8_t rcode; | ||
130 | SETUP_PKT setup_pkt; | ||
131 | |||
132 | EpInfo *pep = NULL; | ||
133 | uint16_t nak_limit = 0; | ||
134 | |||
135 | rcode = SetAddress(addr, ep, &pep, &nak_limit); | ||
136 | |||
137 | if(rcode) | ||
138 | return rcode; | ||
139 | |||
140 | direction = ((bmReqType & 0x80) > 0); | ||
141 | |||
142 | /* fill in setup packet */ | ||
143 | setup_pkt.ReqType_u.bmRequestType = bmReqType; | ||
144 | setup_pkt.bRequest = bRequest; | ||
145 | setup_pkt.wVal_u.wValueLo = wValLo; | ||
146 | setup_pkt.wVal_u.wValueHi = wValHi; | ||
147 | setup_pkt.wIndex = wInd; | ||
148 | setup_pkt.wLength = total; | ||
149 | |||
150 | bytesWr(rSUDFIFO, 8, (uint8_t*) & setup_pkt); //transfer to setup packet FIFO | ||
151 | |||
152 | rcode = dispatchPkt(tokSETUP, ep, nak_limit); //dispatch packet | ||
153 | |||
154 | if(rcode) //return HRSLT if not zero | ||
155 | return ( rcode); | ||
156 | |||
157 | if(dataptr != NULL) //data stage, if present | ||
158 | { | ||
159 | if(direction) //IN transfer | ||
160 | { | ||
161 | uint16_t left = total; | ||
162 | |||
163 | pep->bmRcvToggle = 1; //bmRCVTOG1; | ||
164 | |||
165 | while(left) { | ||
166 | // Bytes read into buffer | ||
167 | uint16_t read = nbytes; | ||
168 | //uint16_t read = (left<nbytes) ? left : nbytes; | ||
169 | |||
170 | rcode = InTransfer(pep, nak_limit, &read, dataptr); | ||
171 | if(rcode == hrTOGERR) { | ||
172 | // yes, we flip it wrong here so that next time it is actually correct! | ||
173 | pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; | ||
174 | continue; | ||
175 | } | ||
176 | |||
177 | if(rcode) | ||
178 | return rcode; | ||
179 | |||
180 | // Invoke callback function if inTransfer completed successfully and callback function pointer is specified | ||
181 | if(!rcode && p) | ||
182 | ((USBReadParser*)p)->Parse(read, dataptr, total - left); | ||
183 | |||
184 | left -= read; | ||
185 | |||
186 | if(read < nbytes) | ||
187 | break; | ||
188 | } | ||
189 | } else //OUT transfer | ||
190 | { | ||
191 | pep->bmSndToggle = 1; //bmSNDTOG1; | ||
192 | rcode = OutTransfer(pep, nak_limit, nbytes, dataptr); | ||
193 | } | ||
194 | if(rcode) //return error | ||
195 | return ( rcode); | ||
196 | } | ||
197 | // Status stage | ||
198 | return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit); //GET if direction | ||
199 | } | ||
200 | |||
201 | /* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */ | ||
202 | /* Keep sending INs and writes data to memory area pointed by 'data' */ | ||
203 | |||
204 | /* rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error, | ||
205 | fe USB xfer timeout */ | ||
206 | uint8_t USB::inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data) { | ||
207 | EpInfo *pep = NULL; | ||
208 | uint16_t nak_limit = 0; | ||
209 | |||
210 | uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit); | ||
211 | |||
212 | if(rcode) { | ||
213 | USBTRACE3("(USB::InTransfer) SetAddress Failed ", rcode, 0x81); | ||
214 | USBTRACE3("(USB::InTransfer) addr requested ", addr, 0x81); | ||
215 | USBTRACE3("(USB::InTransfer) ep requested ", ep, 0x81); | ||
216 | return rcode; | ||
217 | } | ||
218 | return InTransfer(pep, nak_limit, nbytesptr, data); | ||
219 | } | ||
220 | |||
221 | uint8_t USB::InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t* data) { | ||
222 | uint8_t rcode = 0; | ||
223 | uint8_t pktsize; | ||
224 | |||
225 | uint16_t nbytes = *nbytesptr; | ||
226 | //printf("Requesting %i bytes ", nbytes); | ||
227 | uint8_t maxpktsize = pep->maxPktSize; | ||
228 | |||
229 | *nbytesptr = 0; | ||
230 | regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value | ||
231 | |||
232 | // use a 'break' to exit this loop | ||
233 | while(1) { | ||
234 | rcode = dispatchPkt(tokIN, pep->epAddr, nak_limit); //IN packet to EP-'endpoint'. Function takes care of NAKS. | ||
235 | if(rcode == hrTOGERR) { | ||
236 | // yes, we flip it wrong here so that next time it is actually correct! | ||
237 | pep->bmRcvToggle = (regRd(rHRSL) & bmRCVTOGRD) ? 0 : 1; | ||
238 | regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value | ||
239 | continue; | ||
240 | } | ||
241 | if(rcode) { | ||
242 | //printf(">>>>>>>> Problem! dispatchPkt %2.2x\r\n", rcode); | ||
243 | break; //should be 0, indicating ACK. Else return error code. | ||
244 | } | ||
245 | /* check for RCVDAVIRQ and generate error if not present */ | ||
246 | /* the only case when absence of RCVDAVIRQ makes sense is when toggle error occurred. Need to add handling for that */ | ||
247 | if((regRd(rHIRQ) & bmRCVDAVIRQ) == 0) { | ||
248 | //printf(">>>>>>>> Problem! NO RCVDAVIRQ!\r\n"); | ||
249 | rcode = 0xf0; //receive error | ||
250 | break; | ||
251 | } | ||
252 | pktsize = regRd(rRCVBC); //number of received bytes | ||
253 | //printf("Got %i bytes \r\n", pktsize); | ||
254 | // This would be OK, but... | ||
255 | //assert(pktsize <= nbytes); | ||
256 | if(pktsize > nbytes) { | ||
257 | // This can happen. Use of assert on Arduino locks up the Arduino. | ||
258 | // So I will trim the value, and hope for the best. | ||
259 | //printf(">>>>>>>> Problem! Wanted %i bytes but got %i.\r\n", nbytes, pktsize); | ||
260 | pktsize = nbytes; | ||
261 | } | ||
262 | |||
263 | int16_t mem_left = (int16_t)nbytes - *((int16_t*)nbytesptr); | ||
264 | |||
265 | if(mem_left < 0) | ||
266 | mem_left = 0; | ||
267 | |||
268 | data = bytesRd(rRCVFIFO, ((pktsize > mem_left) ? mem_left : pktsize), data); | ||
269 | |||
270 | regWr(rHIRQ, bmRCVDAVIRQ); // Clear the IRQ & free the buffer | ||
271 | *nbytesptr += pktsize; // add this packet's byte count to total transfer length | ||
272 | |||
273 | /* The transfer is complete under two conditions: */ | ||
274 | /* 1. The device sent a short packet (L.T. maxPacketSize) */ | ||
275 | /* 2. 'nbytes' have been transferred. */ | ||
276 | if((pktsize < maxpktsize) || (*nbytesptr >= nbytes)) // have we transferred 'nbytes' bytes? | ||
277 | { | ||
278 | // Save toggle value | ||
279 | pep->bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0; | ||
280 | //printf("\r\n"); | ||
281 | rcode = 0; | ||
282 | break; | ||
283 | } // if | ||
284 | } //while( 1 ) | ||
285 | return ( rcode); | ||
286 | } | ||
287 | |||
288 | /* OUT transfer to arbitrary endpoint. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */ | ||
289 | /* Handles NAK bug per Maxim Application Note 4000 for single buffer transfer */ | ||
290 | |||
291 | /* rcode 0 if no errors. rcode 01-0f is relayed from HRSL */ | ||
292 | uint8_t USB::outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data) { | ||
293 | EpInfo *pep = NULL; | ||
294 | uint16_t nak_limit = 0; | ||
295 | |||
296 | uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit); | ||
297 | |||
298 | if(rcode) | ||
299 | return rcode; | ||
300 | |||
301 | return OutTransfer(pep, nak_limit, nbytes, data); | ||
302 | } | ||
303 | |||
304 | uint8_t USB::OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data) { | ||
305 | uint8_t rcode = hrSUCCESS, retry_count; | ||
306 | uint8_t *data_p = data; //local copy of the data pointer | ||
307 | uint16_t bytes_tosend, nak_count; | ||
308 | uint16_t bytes_left = nbytes; | ||
309 | |||
310 | uint8_t maxpktsize = pep->maxPktSize; | ||
311 | |||
312 | if(maxpktsize < 1 || maxpktsize > 64) | ||
313 | return USB_ERROR_INVALID_MAX_PKT_SIZE; | ||
314 | |||
315 | unsigned long timeout = millis() + USB_XFER_TIMEOUT; | ||
316 | |||
317 | regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value | ||
318 | |||
319 | while(bytes_left) { | ||
320 | retry_count = 0; | ||
321 | nak_count = 0; | ||
322 | bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left; | ||
323 | bytesWr(rSNDFIFO, bytes_tosend, data_p); //filling output FIFO | ||
324 | regWr(rSNDBC, bytes_tosend); //set number of bytes | ||
325 | regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet | ||
326 | while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ | ||
327 | regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ | ||
328 | rcode = (regRd(rHRSL) & 0x0f); | ||
329 | |||
330 | while(rcode && ((long)(millis() - timeout) < 0L)) { | ||
331 | switch(rcode) { | ||
332 | case hrNAK: | ||
333 | nak_count++; | ||
334 | if(nak_limit && (nak_count == nak_limit)) | ||
335 | goto breakout; | ||
336 | //return ( rcode); | ||
337 | break; | ||
338 | case hrTIMEOUT: | ||
339 | retry_count++; | ||
340 | if(retry_count == USB_RETRY_LIMIT) | ||
341 | goto breakout; | ||
342 | //return ( rcode); | ||
343 | break; | ||
344 | case hrTOGERR: | ||
345 | // yes, we flip it wrong here so that next time it is actually correct! | ||
346 | pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; | ||
347 | regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value | ||
348 | break; | ||
349 | default: | ||
350 | goto breakout; | ||
351 | }//switch( rcode | ||
352 | |||
353 | /* process NAK according to Host out NAK bug */ | ||
354 | regWr(rSNDBC, 0); | ||
355 | regWr(rSNDFIFO, *data_p); | ||
356 | regWr(rSNDBC, bytes_tosend); | ||
357 | regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet | ||
358 | while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ | ||
359 | regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ | ||
360 | rcode = (regRd(rHRSL) & 0x0f); | ||
361 | }//while( rcode && .... | ||
362 | bytes_left -= bytes_tosend; | ||
363 | data_p += bytes_tosend; | ||
364 | }//while( bytes_left... | ||
365 | breakout: | ||
366 | |||
367 | pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0; //update toggle | ||
368 | return ( rcode); //should be 0 in all cases | ||
369 | } | ||
370 | /* dispatch USB packet. Assumes peripheral address is set and relevant buffer is loaded/empty */ | ||
371 | /* If NAK, tries to re-send up to nak_limit times */ | ||
372 | /* If nak_limit == 0, do not count NAKs, exit after timeout */ | ||
373 | /* If bus timeout, re-sends up to USB_RETRY_LIMIT times */ | ||
374 | |||
375 | /* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout */ | ||
376 | uint8_t USB::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit) { | ||
377 | unsigned long timeout = millis() + USB_XFER_TIMEOUT; | ||
378 | uint8_t tmpdata; | ||
379 | uint8_t rcode = hrSUCCESS; | ||
380 | uint8_t retry_count = 0; | ||
381 | uint16_t nak_count = 0; | ||
382 | |||
383 | while((long)(millis() - timeout) < 0L) { | ||
384 | regWr(rHXFR, (token | ep)); //launch the transfer | ||
385 | rcode = USB_ERROR_TRANSFER_TIMEOUT; | ||
386 | |||
387 | while((long)(millis() - timeout) < 0L) //wait for transfer completion | ||
388 | { | ||
389 | tmpdata = regRd(rHIRQ); | ||
390 | |||
391 | if(tmpdata & bmHXFRDNIRQ) { | ||
392 | regWr(rHIRQ, bmHXFRDNIRQ); //clear the interrupt | ||
393 | rcode = 0x00; | ||
394 | break; | ||
395 | }//if( tmpdata & bmHXFRDNIRQ | ||
396 | |||
397 | }//while ( millis() < timeout | ||
398 | |||
399 | //if (rcode != 0x00) //exit if timeout | ||
400 | // return ( rcode); | ||
401 | |||
402 | rcode = (regRd(rHRSL) & 0x0f); //analyze transfer result | ||
403 | |||
404 | switch(rcode) { | ||
405 | case hrNAK: | ||
406 | nak_count++; | ||
407 | if(nak_limit && (nak_count == nak_limit)) | ||
408 | return (rcode); | ||
409 | break; | ||
410 | case hrTIMEOUT: | ||
411 | retry_count++; | ||
412 | if(retry_count == USB_RETRY_LIMIT) | ||
413 | return (rcode); | ||
414 | break; | ||
415 | default: | ||
416 | return (rcode); | ||
417 | }//switch( rcode | ||
418 | |||
419 | }//while( timeout > millis() | ||
420 | return ( rcode); | ||
421 | } | ||
422 | |||
423 | /* USB main task. Performs enumeration/cleanup */ | ||
424 | void USB::Task(void) //USB state machine | ||
425 | { | ||
426 | uint8_t rcode; | ||
427 | uint8_t tmpdata; | ||
428 | static unsigned long delay = 0; | ||
429 | //USB_DEVICE_DESCRIPTOR buf; | ||
430 | bool lowspeed = false; | ||
431 | |||
432 | MAX3421E::Task(); | ||
433 | |||
434 | tmpdata = getVbusState(); | ||
435 | |||
436 | /* modify USB task state if Vbus changed */ | ||
437 | switch(tmpdata) { | ||
438 | case SE1: //illegal state | ||
439 | usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL; | ||
440 | lowspeed = false; | ||
441 | break; | ||
442 | case SE0: //disconnected | ||
443 | if((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED) | ||
444 | usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; | ||
445 | lowspeed = false; | ||
446 | break; | ||
447 | case LSHOST: | ||
448 | |||
449 | lowspeed = true; | ||
450 | //intentional fallthrough | ||
451 | case FSHOST: //attached | ||
452 | if((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED) { | ||
453 | delay = millis() + USB_SETTLE_DELAY; | ||
454 | usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; | ||
455 | } | ||
456 | break; | ||
457 | }// switch( tmpdata | ||
458 | |||
459 | for(uint8_t i = 0; i < USB_NUMDEVICES; i++) | ||
460 | if(devConfig[i]) | ||
461 | rcode = devConfig[i]->Poll(); | ||
462 | |||
463 | switch(usb_task_state) { | ||
464 | case USB_DETACHED_SUBSTATE_INITIALIZE: | ||
465 | init(); | ||
466 | |||
467 | for(uint8_t i = 0; i < USB_NUMDEVICES; i++) | ||
468 | if(devConfig[i]) | ||
469 | rcode = devConfig[i]->Release(); | ||
470 | |||
471 | usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE; | ||
472 | break; | ||
473 | case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here | ||
474 | break; | ||
475 | case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here | ||
476 | break; | ||
477 | case USB_ATTACHED_SUBSTATE_SETTLE: //settle time for just attached device | ||
478 | if((long)(millis() - delay) >= 0L) | ||
479 | usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE; | ||
480 | else break; // don't fall through | ||
481 | case USB_ATTACHED_SUBSTATE_RESET_DEVICE: | ||
482 | regWr(rHCTL, bmBUSRST); //issue bus reset | ||
483 | usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE; | ||
484 | break; | ||
485 | case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE: | ||
486 | if((regRd(rHCTL) & bmBUSRST) == 0) { | ||
487 | tmpdata = regRd(rMODE) | bmSOFKAENAB; //start SOF generation | ||
488 | regWr(rMODE, tmpdata); | ||
489 | usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF; | ||
490 | //delay = millis() + 20; //20ms wait after reset per USB spec | ||
491 | } | ||
492 | break; | ||
493 | case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order | ||
494 | if(regRd(rHIRQ) & bmFRAMEIRQ) { | ||
495 | //when first SOF received _and_ 20ms has passed we can continue | ||
496 | /* | ||
497 | if (delay < millis()) //20ms passed | ||
498 | usb_task_state = USB_STATE_CONFIGURING; | ||
499 | */ | ||
500 | usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET; | ||
501 | delay = millis() + 20; | ||
502 | } | ||
503 | break; | ||
504 | case USB_ATTACHED_SUBSTATE_WAIT_RESET: | ||
505 | if((long)(millis() - delay) >= 0L) usb_task_state = USB_STATE_CONFIGURING; | ||
506 | else break; // don't fall through | ||
507 | case USB_STATE_CONFIGURING: | ||
508 | |||
509 | //Serial.print("\r\nConf.LS: "); | ||
510 | //Serial.println(lowspeed, HEX); | ||
511 | |||
512 | rcode = Configuring(0, 0, lowspeed); | ||
513 | |||
514 | if(rcode) { | ||
515 | if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) { | ||
516 | usb_error = rcode; | ||
517 | usb_task_state = USB_STATE_ERROR; | ||
518 | } | ||
519 | } else | ||
520 | usb_task_state = USB_STATE_RUNNING; | ||
521 | break; | ||
522 | case USB_STATE_RUNNING: | ||
523 | break; | ||
524 | case USB_STATE_ERROR: | ||
525 | //MAX3421E::Init(); | ||
526 | break; | ||
527 | } // switch( usb_task_state ) | ||
528 | } | ||
529 | |||
530 | uint8_t USB::DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed) { | ||
531 | //uint8_t buf[12]; | ||
532 | uint8_t rcode; | ||
533 | UsbDevice *p0 = NULL, *p = NULL; | ||
534 | |||
535 | // Get pointer to pseudo device with address 0 assigned | ||
536 | p0 = addrPool.GetUsbDevicePtr(0); | ||
537 | |||
538 | if(!p0) | ||
539 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; | ||
540 | |||
541 | if(!p0->epinfo) | ||
542 | return USB_ERROR_EPINFO_IS_NULL; | ||
543 | |||
544 | p0->lowspeed = (lowspeed) ? true : false; | ||
545 | |||
546 | // Allocate new address according to device class | ||
547 | uint8_t bAddress = addrPool.AllocAddress(parent, false, port); | ||
548 | |||
549 | if(!bAddress) | ||
550 | return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; | ||
551 | |||
552 | p = addrPool.GetUsbDevicePtr(bAddress); | ||
553 | |||
554 | if(!p) | ||
555 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; | ||
556 | |||
557 | p->lowspeed = lowspeed; | ||
558 | |||
559 | // Assign new address to the device | ||
560 | rcode = setAddr(0, 0, bAddress); | ||
561 | |||
562 | if(rcode) { | ||
563 | addrPool.FreeAddress(bAddress); | ||
564 | bAddress = 0; | ||
565 | return rcode; | ||
566 | } | ||
567 | return 0; | ||
568 | }; | ||
569 | |||
570 | uint8_t USB::AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed) { | ||
571 | //printf("AttemptConfig: parent = %i, port = %i\r\n", parent, port); | ||
572 | uint8_t retries = 0; | ||
573 | |||
574 | again: | ||
575 | uint8_t rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed); | ||
576 | if(rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET) { | ||
577 | if(parent == 0) { | ||
578 | // Send a bus reset on the root interface. | ||
579 | regWr(rHCTL, bmBUSRST); //issue bus reset | ||
580 | delay(102); // delay 102ms, compensate for clock inaccuracy. | ||
581 | } else { | ||
582 | // reset parent port | ||
583 | devConfig[parent]->ResetHubPort(port); | ||
584 | } | ||
585 | } else if(rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works | ||
586 | delay(100); | ||
587 | retries++; | ||
588 | goto again; | ||
589 | } else if(rcode) | ||
590 | return rcode; | ||
591 | |||
592 | rcode = devConfig[driver]->Init(parent, port, lowspeed); | ||
593 | if(rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works | ||
594 | delay(100); | ||
595 | retries++; | ||
596 | goto again; | ||
597 | } | ||
598 | if(rcode) { | ||
599 | // Issue a bus reset, because the device may be in a limbo state | ||
600 | if(parent == 0) { | ||
601 | // Send a bus reset on the root interface. | ||
602 | regWr(rHCTL, bmBUSRST); //issue bus reset | ||
603 | delay(102); // delay 102ms, compensate for clock inaccuracy. | ||
604 | } else { | ||
605 | // reset parent port | ||
606 | devConfig[parent]->ResetHubPort(port); | ||
607 | } | ||
608 | } | ||
609 | return rcode; | ||
610 | } | ||
611 | |||
612 | /* | ||
613 | * This is broken. We need to enumerate differently. | ||
614 | * It causes major problems with several devices if detected in an unexpected order. | ||
615 | * | ||
616 | * | ||
617 | * Oleg - I wouldn't do anything before the newly connected device is considered sane. | ||
618 | * i.e.(delays are not indicated for brevity): | ||
619 | * 1. reset | ||
620 | * 2. GetDevDescr(); | ||
621 | * 3a. If ACK, continue with allocating address, addressing, etc. | ||
622 | * 3b. Else reset again, count resets, stop at some number (5?). | ||
623 | * 4. When max.number of resets is reached, toggle power/fail | ||
624 | * If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD() | ||
625 | * it doesn't need to be reset again | ||
626 | * New steps proposal: | ||
627 | * 1: get address pool instance. exit on fail | ||
628 | * 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail. | ||
629 | * 3: bus reset, 100ms delay | ||
630 | * 4: set address | ||
631 | * 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail | ||
632 | * 6: while (configurations) { | ||
633 | * for(each configuration) { | ||
634 | * for (each driver) { | ||
635 | * 6a: Ask device if it likes configuration. Returns 0 on OK. | ||
636 | * If successful, the driver configured device. | ||
637 | * The driver now owns the endpoints, and takes over managing them. | ||
638 | * The following will need codes: | ||
639 | * Everything went well, instance consumed, exit with success. | ||
640 | * Instance already in use, ignore it, try next driver. | ||
641 | * Not a supported device, ignore it, try next driver. | ||
642 | * Not a supported configuration for this device, ignore it, try next driver. | ||
643 | * Could not configure device, fatal, exit with fail. | ||
644 | * } | ||
645 | * } | ||
646 | * } | ||
647 | * 7: for(each driver) { | ||
648 | * 7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID | ||
649 | * 8: if we get here, no driver likes the device plugged in, so exit failure. | ||
650 | * | ||
651 | */ | ||
652 | uint8_t USB::Configuring(uint8_t parent, uint8_t port, bool lowspeed) { | ||
653 | //uint8_t bAddress = 0; | ||
654 | //printf("Configuring: parent = %i, port = %i\r\n", parent, port); | ||
655 | uint8_t devConfigIndex; | ||
656 | uint8_t rcode = 0; | ||
657 | uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)]; | ||
658 | USB_DEVICE_DESCRIPTOR *udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR *>(buf); | ||
659 | UsbDevice *p = NULL; | ||
660 | EpInfo *oldep_ptr = NULL; | ||
661 | EpInfo epInfo; | ||
662 | |||
663 | epInfo.epAddr = 0; | ||
664 | epInfo.maxPktSize = 8; | ||
665 | epInfo.epAttribs = 0; | ||
666 | epInfo.bmNakPower = USB_NAK_MAX_POWER; | ||
667 | |||
668 | //delay(2000); | ||
669 | AddressPool &addrPool = GetAddressPool(); | ||
670 | // Get pointer to pseudo device with address 0 assigned | ||
671 | p = addrPool.GetUsbDevicePtr(0); | ||
672 | if(!p) { | ||
673 | //printf("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\r\n"); | ||
674 | return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; | ||
675 | } | ||
676 | |||
677 | // Save old pointer to EP_RECORD of address 0 | ||
678 | oldep_ptr = p->epinfo; | ||
679 | |||
680 | // Temporary assign new pointer to epInfo to p->epinfo in order to | ||
681 | // avoid toggle inconsistence | ||
682 | |||
683 | p->epinfo = &epInfo; | ||
684 | |||
685 | p->lowspeed = lowspeed; | ||
686 | // Get device descriptor | ||
687 | rcode = getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); | ||
688 | |||
689 | // Restore p->epinfo | ||
690 | p->epinfo = oldep_ptr; | ||
691 | |||
692 | if(rcode) { | ||
693 | //printf("Configuring error: Can't get USB_DEVICE_DESCRIPTOR\r\n"); | ||
694 | return rcode; | ||
695 | } | ||
696 | |||
697 | // to-do? | ||
698 | // Allocate new address according to device class | ||
699 | //bAddress = addrPool.AllocAddress(parent, false, port); | ||
700 | |||
701 | uint16_t vid = udd->idVendor; | ||
702 | uint16_t pid = udd->idProduct; | ||
703 | uint8_t klass = udd->bDeviceClass; | ||
704 | uint8_t subklass = udd->bDeviceSubClass; | ||
705 | // Attempt to configure if VID/PID or device class matches with a driver | ||
706 | // Qualify with subclass too. | ||
707 | // | ||
708 | // VID/PID & class tests default to false for drivers not yet ported | ||
709 | // subclass defaults to true, so you don't have to define it if you don't have to. | ||
710 | // | ||
711 | for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { | ||
712 | if(!devConfig[devConfigIndex]) continue; // no driver | ||
713 | if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed | ||
714 | if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) { | ||
715 | rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed); | ||
716 | if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED) | ||
717 | break; | ||
718 | } | ||
719 | } | ||
720 | |||
721 | if(devConfigIndex < USB_NUMDEVICES) { | ||
722 | return rcode; | ||
723 | } | ||
724 | |||
725 | |||
726 | // blindly attempt to configure | ||
727 | for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) { | ||
728 | if(!devConfig[devConfigIndex]) continue; | ||
729 | if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed | ||
730 | if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) continue; // If this is true it means it must have returned USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED above | ||
731 | rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed); | ||
732 | |||
733 | //printf("ERROR ENUMERATING %2.2x\r\n", rcode); | ||
734 | if(!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)) { | ||
735 | // in case of an error dev_index should be reset to 0 | ||
736 | // in order to start from the very beginning the | ||
737 | // next time the program gets here | ||
738 | //if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) | ||
739 | // devConfigIndex = 0; | ||
740 | return rcode; | ||
741 | } | ||
742 | } | ||
743 | // if we get here that means that the device class is not supported by any of registered classes | ||
744 | rcode = DefaultAddressing(parent, port, lowspeed); | ||
745 | |||
746 | return rcode; | ||
747 | } | ||
748 | |||
749 | uint8_t USB::ReleaseDevice(uint8_t addr) { | ||
750 | if(!addr) | ||
751 | return 0; | ||
752 | |||
753 | for(uint8_t i = 0; i < USB_NUMDEVICES; i++) { | ||
754 | if(!devConfig[i]) continue; | ||
755 | if(devConfig[i]->GetAddress() == addr) | ||
756 | return devConfig[i]->Release(); | ||
757 | } | ||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | #if 1 //!defined(USB_METHODS_INLINE) | ||
762 | //get device descriptor | ||
763 | |||
764 | uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) { | ||
765 | return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, nbytes, dataptr, NULL)); | ||
766 | } | ||
767 | //get configuration descriptor | ||
768 | |||
769 | uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) { | ||
770 | return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, nbytes, dataptr, NULL)); | ||
771 | } | ||
772 | |||
773 | /* Requests Configuration Descriptor. Sends two Get Conf Descr requests. The first one gets the total length of all descriptors, then the second one requests this | ||
774 | total length. The length of the first request can be shorter ( 4 bytes ), however, there are devices which won't work unless this length is set to 9 */ | ||
775 | uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p) { | ||
776 | const uint8_t bufSize = 64; | ||
777 | uint8_t buf[bufSize]; | ||
778 | USB_CONFIGURATION_DESCRIPTOR *ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR *>(buf); | ||
779 | |||
780 | uint8_t ret = getConfDescr(addr, ep, 9, conf, buf); | ||
781 | |||
782 | if(ret) | ||
783 | return ret; | ||
784 | |||
785 | uint16_t total = ucd->wTotalLength; | ||
786 | |||
787 | //USBTRACE2("\r\ntotal conf.size:", total); | ||
788 | |||
789 | return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, total, bufSize, buf, p)); | ||
790 | } | ||
791 | |||
792 | //get string descriptor | ||
793 | |||
794 | uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr) { | ||
795 | return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, ns, ns, dataptr, NULL)); | ||
796 | } | ||
797 | //set address | ||
798 | |||
799 | uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) { | ||
800 | uint8_t rcode = ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL); | ||
801 | //delay(2); //per USB 2.0 sect.9.2.6.3 | ||
802 | delay(300); // Older spec says you should wait at least 200ms | ||
803 | return rcode; | ||
804 | //return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL)); | ||
805 | } | ||
806 | //set configuration | ||
807 | |||
808 | uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) { | ||
809 | return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL)); | ||
810 | } | ||
811 | |||
812 | #endif // defined(USB_METHODS_INLINE) | ||