Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1702 | - | 1 | /* Copyright 2009-2011 Oleg Mazurov, Circuits At Home, http://www.circuitsathome.com */ |
2 | /* USB functions */ |
||
3 | |||
4 | #include "Usb.h" |
||
5 | |||
6 | static byte usb_error = 0; |
||
7 | static byte usb_task_state; |
||
8 | DEV_RECORD devtable[ USB_NUMDEVICES + 1 ]; |
||
9 | EP_RECORD dev0ep; //Endpoint data structure used during enumeration for uninitialized device |
||
10 | |||
11 | |||
12 | /* constructor */ |
||
13 | |||
14 | USB::USB () { |
||
15 | usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; //set up state machine |
||
16 | init(); |
||
17 | } |
||
18 | /* Initialize data structures */ |
||
19 | void USB::init() |
||
20 | { |
||
21 | byte i; |
||
22 | for( i = 0; i < ( USB_NUMDEVICES + 1 ); i++ ) { |
||
23 | devtable[ i ].epinfo = NULL; //clear device table |
||
24 | devtable[ i ].devclass = 0; |
||
25 | } |
||
26 | devtable[ 0 ].epinfo = &dev0ep; //set single ep for uninitialized device |
||
27 | // not necessary dev0ep.MaxPktSize = 8; //minimum possible |
||
28 | dev0ep.sndToggle = bmSNDTOG0; //set DATA0/1 toggles to 0 |
||
29 | dev0ep.rcvToggle = bmRCVTOG0; |
||
30 | } |
||
31 | byte USB::getUsbTaskState( void ) |
||
32 | { |
||
33 | return( usb_task_state ); |
||
34 | } |
||
35 | void USB::setUsbTaskState( byte state ) |
||
36 | { |
||
37 | usb_task_state = state; |
||
38 | } |
||
39 | EP_RECORD* USB::getDevTableEntry( byte addr, byte ep ) |
||
40 | { |
||
41 | EP_RECORD* ptr; |
||
42 | ptr = devtable[ addr ].epinfo; |
||
43 | ptr += ep; |
||
44 | return( ptr ); |
||
45 | } |
||
46 | /* set device table entry */ |
||
47 | /* each device is different and has different number of endpoints. This function plugs endpoint record structure, defined in application, to devtable */ |
||
48 | void USB::setDevTableEntry( byte addr, EP_RECORD* eprecord_ptr ) |
||
49 | { |
||
50 | devtable[ addr ].epinfo = eprecord_ptr; |
||
51 | //return(); |
||
52 | } |
||
53 | /* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer, */ |
||
54 | /* depending on request. Actual requests are defined as inlines */ |
||
55 | /* return codes: */ |
||
56 | /* 00 = success */ |
||
57 | /* 01-0f = non-zero HRSLT */ |
||
58 | byte USB::ctrlReq( byte addr, byte ep, byte bmReqType, byte bRequest, byte wValLo, byte wValHi, unsigned int wInd, unsigned int nbytes, char* dataptr, unsigned int nak_limit ) |
||
59 | { |
||
60 | boolean direction = false; //request direction, IN or OUT |
||
61 | byte rcode; |
||
62 | SETUP_PKT setup_pkt; |
||
63 | |||
64 | regWr( rPERADDR, addr ); //set peripheral address |
||
65 | if( bmReqType & 0x80 ) { |
||
66 | direction = true; //determine request direction |
||
67 | } |
||
68 | /* fill in setup packet */ |
||
69 | setup_pkt.ReqType_u.bmRequestType = bmReqType; |
||
70 | setup_pkt.bRequest = bRequest; |
||
71 | setup_pkt.wVal_u.wValueLo = wValLo; |
||
72 | setup_pkt.wVal_u.wValueHi = wValHi; |
||
73 | setup_pkt.wIndex = wInd; |
||
74 | setup_pkt.wLength = nbytes; |
||
75 | bytesWr( rSUDFIFO, 8, ( char *)&setup_pkt ); //transfer to setup packet FIFO |
||
76 | rcode = dispatchPkt( tokSETUP, ep, nak_limit ); //dispatch packet |
||
77 | //Serial.println("Setup packet"); //DEBUG |
||
78 | if( rcode ) { //return HRSLT if not zero |
||
79 | Serial.print("Setup packet error: "); |
||
80 | Serial.print( rcode, HEX ); |
||
81 | return( rcode ); |
||
82 | } |
||
83 | //Serial.println( direction, HEX ); |
||
84 | if( dataptr != NULL ) { //data stage, if present |
||
85 | rcode = ctrlData( addr, ep, nbytes, dataptr, direction ); |
||
86 | } |
||
87 | if( rcode ) { //return error |
||
88 | Serial.print("Data packet error: "); |
||
89 | Serial.print( rcode, HEX ); |
||
90 | return( rcode ); |
||
91 | } |
||
92 | rcode = ctrlStatus( ep, direction ); //status stage |
||
93 | return( rcode ); |
||
94 | } |
||
95 | /* Control transfer with status stage and no data stage */ |
||
96 | /* Assumed peripheral address is already set */ |
||
97 | byte USB::ctrlStatus( byte ep, boolean direction, unsigned int nak_limit ) |
||
98 | { |
||
99 | byte rcode; |
||
100 | if( direction ) { //GET |
||
101 | rcode = dispatchPkt( tokOUTHS, ep, nak_limit ); |
||
102 | } |
||
103 | else { |
||
104 | rcode = dispatchPkt( tokINHS, ep, nak_limit ); |
||
105 | } |
||
106 | return( rcode ); |
||
107 | } |
||
108 | /* Control transfer with data stage. Stages 2 and 3 of control transfer. Assumes preipheral address is set and setup packet has been sent */ |
||
109 | byte USB::ctrlData( byte addr, byte ep, unsigned int nbytes, char* dataptr, boolean direction, unsigned int nak_limit ) |
||
110 | { |
||
111 | byte rcode; |
||
112 | if( direction ) { //IN transfer |
||
113 | devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG1; |
||
114 | rcode = inTransfer( addr, ep, nbytes, dataptr, nak_limit ); |
||
115 | return( rcode ); |
||
116 | } |
||
117 | else { //OUT transfer |
||
118 | devtable[ addr ].epinfo[ ep ].sndToggle = bmSNDTOG1; |
||
119 | rcode = outTransfer( addr, ep, nbytes, dataptr, nak_limit ); |
||
120 | return( rcode ); |
||
121 | } |
||
122 | } |
||
123 | /* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */ |
||
124 | /* Keep sending INs and writes data to memory area pointed by 'data' */ |
||
125 | /* rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error, |
||
126 | fe USB xfer timeout */ |
||
127 | byte USB::inTransfer( byte addr, byte ep, unsigned int nbytes, char* data, unsigned int nak_limit ) |
||
128 | { |
||
129 | byte rcode; |
||
130 | byte pktsize; |
||
131 | byte maxpktsize = devtable[ addr ].epinfo[ ep ].MaxPktSize; |
||
132 | unsigned int xfrlen = 0; |
||
133 | regWr( rHCTL, devtable[ addr ].epinfo[ ep ].rcvToggle ); //set toggle value |
||
134 | while( 1 ) { // use a 'return' to exit this loop |
||
135 | rcode = dispatchPkt( tokIN, ep, nak_limit ); //IN packet to EP-'endpoint'. Function takes care of NAKS. |
||
136 | if( rcode ) { |
||
137 | return( rcode ); //should be 0, indicating ACK. Else return error code. |
||
138 | } |
||
139 | /* check for RCVDAVIRQ and generate error if not present */ |
||
140 | /* the only case when absense of RCVDAVIRQ makes sense is when toggle error occured. Need to add handling for that */ |
||
141 | if(( regRd( rHIRQ ) & bmRCVDAVIRQ ) == 0 ) { |
||
142 | return ( 0xf0 ); //receive error |
||
143 | } |
||
144 | pktsize = regRd( rRCVBC ); //number of received bytes |
||
145 | data = bytesRd( rRCVFIFO, pktsize, data ); |
||
146 | regWr( rHIRQ, bmRCVDAVIRQ ); // Clear the IRQ & free the buffer |
||
147 | xfrlen += pktsize; // add this packet's byte count to total transfer length |
||
148 | /* The transfer is complete under two conditions: */ |
||
149 | /* 1. The device sent a short packet (L.T. maxPacketSize) */ |
||
150 | /* 2. 'nbytes' have been transferred. */ |
||
151 | if (( pktsize < maxpktsize ) || (xfrlen >= nbytes )) { // have we transferred 'nbytes' bytes? |
||
152 | if( regRd( rHRSL ) & bmRCVTOGRD ) { //save toggle value |
||
153 | devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG1; |
||
154 | } |
||
155 | else { |
||
156 | devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG0; |
||
157 | } |
||
158 | return( 0 ); |
||
159 | } |
||
160 | }//while( 1 ) |
||
161 | } |
||
162 | /* OUT transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */ |
||
163 | /* Handles NAK bug per Maxim Application Note 4000 for single buffer transfer */ |
||
164 | /* rcode 0 if no errors. rcode 01-0f is relayed from HRSL */ |
||
165 | /* major part of this function borrowed from code shared by Richard Ibbotson */ |
||
166 | byte USB::outTransfer( byte addr, byte ep, unsigned int nbytes, char* data, unsigned int nak_limit ) |
||
167 | { |
||
168 | byte rcode, retry_count; |
||
169 | char* data_p = data; //local copy of the data pointer |
||
170 | unsigned int bytes_tosend, nak_count; |
||
171 | unsigned int bytes_left = nbytes; |
||
172 | byte maxpktsize = devtable[ addr ].epinfo[ ep ].MaxPktSize; |
||
173 | unsigned long timeout = millis() + USB_XFER_TIMEOUT; |
||
174 | |||
175 | if (!maxpktsize) { //todo: move this check close to epinfo init. Make it 1< pktsize <64 |
||
176 | return 0xFE; |
||
177 | } |
||
178 | |||
179 | regWr( rHCTL, devtable[ addr ].epinfo[ ep ].sndToggle ); //set toggle value |
||
180 | while( bytes_left ) { |
||
181 | retry_count = 0; |
||
182 | nak_count = 0; |
||
183 | bytes_tosend = ( bytes_left >= maxpktsize ) ? maxpktsize : bytes_left; |
||
184 | bytesWr( rSNDFIFO, bytes_tosend, data_p ); //filling output FIFO |
||
185 | regWr( rSNDBC, bytes_tosend ); //set number of bytes |
||
186 | regWr( rHXFR, ( tokOUT | ep )); //dispatch packet |
||
187 | while(!(regRd( rHIRQ ) & bmHXFRDNIRQ )); //wait for the completion IRQ |
||
188 | regWr( rHIRQ, bmHXFRDNIRQ ); //clear IRQ |
||
189 | rcode = ( regRd( rHRSL ) & 0x0f ); |
||
190 | while( rcode && ( timeout > millis())) { |
||
191 | switch( rcode ) { |
||
192 | case hrNAK: |
||
193 | nak_count++; |
||
194 | if( nak_limit && ( nak_count == USB_NAK_LIMIT )) { |
||
195 | return( rcode); //return NAK |
||
196 | } |
||
197 | break; |
||
198 | case hrTIMEOUT: |
||
199 | retry_count++; |
||
200 | if( retry_count == USB_RETRY_LIMIT ) { |
||
201 | return( rcode ); //return TIMEOUT |
||
202 | } |
||
203 | break; |
||
204 | default: |
||
205 | return( rcode ); |
||
206 | }//switch( rcode... |
||
207 | /* process NAK according to Host out NAK bug */ |
||
208 | regWr( rSNDBC, 0 ); |
||
209 | regWr( rSNDFIFO, *data_p ); |
||
210 | regWr( rSNDBC, bytes_tosend ); |
||
211 | regWr( rHXFR, ( tokOUT | ep )); //dispatch packet |
||
212 | while(!(regRd( rHIRQ ) & bmHXFRDNIRQ )); //wait for the completion IRQ |
||
213 | regWr( rHIRQ, bmHXFRDNIRQ ); //clear IRQ |
||
214 | rcode = ( regRd( rHRSL ) & 0x0f ); |
||
215 | }//while( rcode && .... |
||
216 | bytes_left -= bytes_tosend; |
||
217 | data_p += bytes_tosend; |
||
218 | }//while( bytes_left... |
||
219 | devtable[ addr ].epinfo[ ep ].sndToggle = ( regRd( rHRSL ) & bmSNDTOGRD ) ? bmSNDTOG1 : bmSNDTOG0; //update toggle |
||
220 | return( rcode ); //should be 0 in all cases |
||
221 | } |
||
222 | /* dispatch usb packet. Assumes peripheral address is set and relevant buffer is loaded/empty */ |
||
223 | /* If NAK, tries to re-send up to nak_limit times */ |
||
224 | /* If nak_limit == 0, do not count NAKs, exit after timeout */ |
||
225 | /* If bus timeout, re-sends up to USB_RETRY_LIMIT times */ |
||
226 | /* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout */ |
||
227 | byte USB::dispatchPkt( byte token, byte ep, unsigned int nak_limit ) |
||
228 | { |
||
229 | unsigned long timeout = millis() + USB_XFER_TIMEOUT; |
||
230 | byte tmpdata; |
||
231 | byte rcode; |
||
232 | unsigned int nak_count = 0; |
||
233 | char retry_count = 0; |
||
234 | |||
235 | while( timeout > millis() ) { |
||
236 | regWr( rHXFR, ( token|ep )); //launch the transfer |
||
237 | rcode = 0xff; |
||
238 | while( millis() < timeout ) { //wait for transfer completion |
||
239 | tmpdata = regRd( rHIRQ ); |
||
240 | if( tmpdata & bmHXFRDNIRQ ) { |
||
241 | regWr( rHIRQ, bmHXFRDNIRQ ); //clear the interrupt |
||
242 | rcode = 0x00; |
||
243 | break; |
||
244 | }//if( tmpdata & bmHXFRDNIRQ |
||
245 | }//while ( millis() < timeout |
||
246 | if( rcode != 0x00 ) { //exit if timeout |
||
247 | return( rcode ); |
||
248 | } |
||
249 | rcode = ( regRd( rHRSL ) & 0x0f ); //analyze transfer result |
||
250 | switch( rcode ) { |
||
251 | case hrNAK: |
||
252 | nak_count ++; |
||
253 | if( nak_limit && ( nak_count == nak_limit )) { |
||
254 | return( rcode ); |
||
255 | } |
||
256 | break; |
||
257 | case hrTIMEOUT: |
||
258 | retry_count ++; |
||
259 | if( retry_count == USB_RETRY_LIMIT ) { |
||
260 | return( rcode ); |
||
261 | } |
||
262 | break; |
||
263 | default: |
||
264 | return( rcode ); |
||
265 | }//switch( rcode |
||
266 | }//while( timeout > millis() |
||
267 | return( rcode ); |
||
268 | } |
||
269 | /* USB main task. Performs enumeration/cleanup */ |
||
270 | void USB::Task( void ) //USB state machine |
||
271 | { |
||
272 | byte i; |
||
273 | byte rcode; |
||
274 | static byte tmpaddr; |
||
275 | byte tmpdata; |
||
276 | static unsigned long delay = 0; |
||
277 | USB_DEVICE_DESCRIPTOR buf; |
||
278 | tmpdata = getVbusState(); |
||
279 | /* modify USB task state if Vbus changed */ |
||
280 | |||
281 | switch( tmpdata ) { |
||
282 | case SE1: //illegal state |
||
283 | usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL; |
||
284 | break; |
||
285 | case SE0: //disconnected |
||
286 | if(( usb_task_state & USB_STATE_MASK ) != USB_STATE_DETACHED ) { |
||
287 | usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; |
||
288 | } |
||
289 | break; |
||
290 | case FSHOST: //attached |
||
291 | case LSHOST: |
||
292 | if(( usb_task_state & USB_STATE_MASK ) == USB_STATE_DETACHED ) { |
||
293 | delay = millis() + USB_SETTLE_DELAY; |
||
294 | usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; |
||
295 | } |
||
296 | break; |
||
297 | }// switch( tmpdata |
||
298 | //Serial.print("USB task state: "); |
||
299 | //Serial.println( usb_task_state, HEX ); |
||
300 | switch( usb_task_state ) { |
||
301 | case USB_DETACHED_SUBSTATE_INITIALIZE: |
||
302 | init(); |
||
303 | usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE; |
||
304 | break; |
||
305 | case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here |
||
306 | break; |
||
307 | case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here |
||
308 | break; |
||
309 | case USB_ATTACHED_SUBSTATE_SETTLE: //setlle time for just attached device |
||
310 | if( delay < millis() ) { |
||
311 | usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE; |
||
312 | } |
||
313 | break; |
||
314 | case USB_ATTACHED_SUBSTATE_RESET_DEVICE: |
||
315 | regWr( rHCTL, bmBUSRST ); //issue bus reset |
||
316 | usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE; |
||
317 | break; |
||
318 | case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE: |
||
319 | if(( regRd( rHCTL ) & bmBUSRST ) == 0 ) { |
||
320 | tmpdata = regRd( rMODE ) | bmSOFKAENAB; //start SOF generation |
||
321 | regWr( rMODE, tmpdata ); |
||
322 | // regWr( rMODE, bmSOFKAENAB ); |
||
323 | usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF; |
||
324 | delay = millis() + 20; //20ms wait after reset per USB spec |
||
325 | } |
||
326 | break; |
||
327 | case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order |
||
328 | if( regRd( rHIRQ ) & bmFRAMEIRQ ) { //when first SOF received we can continue |
||
329 | if( delay < millis() ) { //20ms passed |
||
330 | usb_task_state = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE; |
||
331 | } |
||
332 | } |
||
333 | break; |
||
334 | case USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE: |
||
335 | // toggle( BPNT_0 ); |
||
336 | devtable[ 0 ].epinfo->MaxPktSize = 8; //set max.packet size to min.allowed |
||
337 | rcode = getDevDescr( 0, 0, 8, ( char* )&buf ); |
||
338 | if( rcode == 0 ) { |
||
339 | devtable[ 0 ].epinfo->MaxPktSize = buf.bMaxPacketSize0; |
||
340 | usb_task_state = USB_STATE_ADDRESSING; |
||
341 | } |
||
342 | else { |
||
343 | usb_error = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE; |
||
344 | usb_task_state = USB_STATE_ERROR; |
||
345 | } |
||
346 | break; |
||
347 | case USB_STATE_ADDRESSING: |
||
348 | for( i = 1; i < USB_NUMDEVICES; i++ ) { |
||
349 | if( devtable[ i ].epinfo == NULL ) { |
||
350 | devtable[ i ].epinfo = devtable[ 0 ].epinfo; //set correct MaxPktSize |
||
351 | //temporary record |
||
352 | //until plugged with real device endpoint structure |
||
353 | rcode = setAddr( 0, 0, i ); |
||
354 | if( rcode == 0 ) { |
||
355 | tmpaddr = i; |
||
356 | usb_task_state = USB_STATE_CONFIGURING; |
||
357 | } |
||
358 | else { |
||
359 | usb_error = USB_STATE_ADDRESSING; //set address error |
||
360 | usb_task_state = USB_STATE_ERROR; |
||
361 | } |
||
362 | break; //break if address assigned or error occured during address assignment attempt |
||
363 | } |
||
364 | }//for( i = 1; i < USB_NUMDEVICES; i++ |
||
365 | if( usb_task_state == USB_STATE_ADDRESSING ) { //no vacant place in devtable |
||
366 | usb_error = 0xfe; |
||
367 | usb_task_state = USB_STATE_ERROR; |
||
368 | } |
||
369 | break; |
||
370 | case USB_STATE_CONFIGURING: |
||
371 | break; |
||
372 | case USB_STATE_RUNNING: |
||
373 | break; |
||
374 | case USB_STATE_ERROR: |
||
375 | break; |
||
376 | }// switch( usb_task_state |
||
377 | } |
||
378 |