]> git.sur5r.net Git - freertos/blob - Demo/lwIP_Demo_Rowley_ARM7/USB/USB-CDC.c
3a57c2feb18cc5a4d5b1499182c9e26942f6418c
[freertos] / Demo / lwIP_Demo_Rowley_ARM7 / USB / USB-CDC.c
1 /*\r
2         FreeRTOS.org V4.0.2 - copyright (C) 2003-2006 Richard Barry.\r
3 \r
4         This file is part of the FreeRTOS.org distribution.\r
5 \r
6         FreeRTOS.org is free software; you can redistribute it and/or modify\r
7         it under the terms of the GNU General Public License as published by\r
8         the Free Software Foundation; either version 2 of the License, or\r
9         (at your option) any later version.\r
10 \r
11         FreeRTOS.org is distributed in the hope that it will be useful,\r
12         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14         GNU General Public License for more details.\r
15 \r
16         You should have received a copy of the GNU General Public License\r
17         along with FreeRTOS.org; if not, write to the Free Software\r
18         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 \r
20         A special exception to the GPL can be applied should you wish to distribute\r
21         a combined work that includes FreeRTOS.org, without being obliged to provide\r
22         the source code for any proprietary components.  See the licensing section\r
23         of http://www.FreeRTOS.org for full details of how and when the exception\r
24         can be applied.\r
25 \r
26         ***************************************************************************\r
27         See http://www.FreeRTOS.org for documentation, latest information, license\r
28         and contact details.  Please ensure to read the configuration and relevant\r
29         port sections of the online documentation.\r
30         ***************************************************************************\r
31 */\r
32 \r
33 /*\r
34         USB Communications Device Class driver.\r
35         Implements task vUSBCDCTask and provides an Abstract Control Model serial \r
36         interface.  Control is through endpoint 0, device-to-host notification is \r
37         provided by interrupt-in endpoint 3, and raw data is transferred through \r
38         bulk endpoints 1 and 2.\r
39 \r
40         - developed from original FreeRTOS HID example by Scott Miller\r
41         - modified to support 3.2 GCC by najay\r
42 */\r
43 \r
44 /* Standard includes. */\r
45 #include <string.h>\r
46 #include <stdio.h>\r
47 \r
48 /* Demo board includes. */\r
49 #include "Board.h"\r
50 \r
51 /* Scheduler includes. */\r
52 #include "FreeRTOS.h"\r
53 #include "task.h"\r
54 #include "queue.h"\r
55 \r
56 /* Demo app includes. */\r
57 #include "USB-CDC.h"\r
58 #include "descriptors.h"\r
59 \r
60 #define usbNO_BLOCK ( ( portTickType ) 0 )\r
61 \r
62 /* Reset all endpoints */\r
63 static void prvResetEndPoints( void );\r
64 \r
65 /* Clear pull up resistor to detach device from host */\r
66 static void vDetachUSBInterface( void );\r
67 \r
68 /* Set up interface and initialize variables */\r
69 static void vInitUSBInterface( void );\r
70 \r
71 /* Handle control endpoint events. */\r
72 static void prvProcessEndPoint0Interrupt( xISRStatus *pxMessage );\r
73 \r
74 /* Handle standard device requests. */\r
75 static void prvHandleStandardDeviceRequest( xUSB_REQUEST *pxRequest );\r
76 \r
77 /* Handle standard interface requests. */\r
78 static void prvHandleStandardInterfaceRequest( xUSB_REQUEST *pxRequest );\r
79 \r
80 /* Handle endpoint requests. */\r
81 static void prvHandleStandardEndPointRequest( xUSB_REQUEST *pxRequest );\r
82 \r
83 /* Handle class interface requests. */\r
84 static void prvHandleClassInterfaceRequest( xUSB_REQUEST *pxRequest );\r
85 \r
86 /* Prepare control data transfer.  prvSendNextSegment starts transfer. */\r
87 static void prvSendControlData( unsigned portCHAR *pucData, unsigned portSHORT usRequestedLength, unsigned portLONG ulLengthLeftToSend, portLONG lSendingDescriptor );\r
88 \r
89 /* Send next segment of data for the control transfer */\r
90 static void prvSendNextSegment( void );\r
91 \r
92 /* Send stall - used to respond to unsupported requests */\r
93 static void prvSendStall( void );\r
94 \r
95 /* Send a zero-length (null) packet */\r
96 static void prvSendZLP( void );\r
97 \r
98 /* Handle requests for standard interface descriptors */\r
99 static void prvGetStandardInterfaceDescriptor( xUSB_REQUEST *pxRequest );\r
100 \r
101 /*------------------------------------------------------------*/\r
102 \r
103 /* File scope static variables */\r
104 static unsigned portCHAR ucUSBConfig = ( unsigned portCHAR ) 0;\r
105 static unsigned portLONG ulReceivedAddress = ( unsigned portLONG ) 0;\r
106 static eDRIVER_STATE eDriverState = eNOTHING;\r
107 \r
108 /* Incoming and outgoing control data structures */\r
109 static xCONTROL_MESSAGE pxControlTx;\r
110 static xCONTROL_MESSAGE pxControlRx;\r
111 \r
112 /* Queue holding pointers to pending messages */\r
113 xQueueHandle xUSBInterruptQueue; \r
114 \r
115 /* Queues used to hold received characters, and characters waiting to be\r
116 transmitted.  Rx queue must be larger than FIFO size. */\r
117 static xQueueHandle xRxCDC; \r
118 static xQueueHandle xTxCDC; \r
119 \r
120 /* Line coding - 115,200 baud, N-8-1 */\r
121 static const unsigned portCHAR pxLineCoding[] = { 0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08 };\r
122 \r
123 /* Status variables. */\r
124 static unsigned portCHAR ucControlState;\r
125 static unsigned int uiCurrentBank;\r
126 \r
127 \r
128 /*------------------------------------------------------------*/\r
129 \r
130 \r
131 void vUSBCDCTask( void *pvParameters )\r
132 {\r
133 xISRStatus *pxMessage;\r
134 unsigned portLONG ulStatus;\r
135 unsigned portLONG ulRxBytes;\r
136 unsigned portCHAR ucByte;\r
137 portBASE_TYPE xByte;\r
138 \r
139         ( void ) pvParameters;\r
140 \r
141         /* Disconnect USB device from hub.  For debugging - causes host to register reset */\r
142         portENTER_CRITICAL();\r
143                  vDetachUSBInterface();\r
144         portEXIT_CRITICAL();\r
145         \r
146         vTaskDelay( portTICK_RATE_MS * 60 );\r
147 \r
148         /* Init USB interface */\r
149         portENTER_CRITICAL();\r
150                 vInitUSBInterface();\r
151         portEXIT_CRITICAL();\r
152         \r
153         /* Main task loop.  Process incoming endpoint 0 interrupts, handle data transfers. */\r
154          \r
155         for( ;; )\r
156         {\r
157                 /* Look for data coming from the ISR. */\r
158                 if( xQueueReceive( xUSBInterruptQueue, &pxMessage, usbSHORTEST_DELAY ) )\r
159                 {\r
160                         if( pxMessage->ulISR & AT91C_UDP_EPINT0 )\r
161                         {\r
162                                 /* All endpoint 0 interrupts are handled here. */\r
163                                 prvProcessEndPoint0Interrupt( pxMessage );\r
164                         }\r
165 \r
166                         if( pxMessage->ulISR & AT91C_UDP_ENDBUSRES )\r
167                         {\r
168                                 /* End of bus reset - reset the endpoints and de-configure. */\r
169                                 prvResetEndPoints();            \r
170                         }\r
171                 }\r
172                 \r
173                 /* See if we're ready to send and receive data. */\r
174                 if( eDriverState == eREADY_TO_SEND && ucControlState ) \r
175                 {\r
176                         if( ( !(AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_2 ] & AT91C_UDP_TXPKTRDY) ) && uxQueueMessagesWaiting( xTxCDC ) )\r
177                         {\r
178                                 for( xByte = 0; xByte < 64; xByte++ )\r
179                                 {                                  \r
180                                         if( !xQueueReceive( xTxCDC, &ucByte, 0 ) ) \r
181                                         {\r
182                                                 /* No data buffered to transmit. */\r
183                                                 break;\r
184                                         }\r
185 \r
186                                         /* Got a byte to transmit. */\r
187                                         AT91C_BASE_UDP->UDP_FDR[ usbEND_POINT_2 ] = ucByte;\r
188                                 } \r
189                                 AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_2 ] |= AT91C_UDP_TXPKTRDY;\r
190                         }\r
191 \r
192                         /* Check for incoming data (host-to-device) on endpoint 1. */\r
193                         while( AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_1 ] & (AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1) )\r
194                         {\r
195                                 ulRxBytes = (AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_1 ] >> 16) & usbRX_COUNT_MASK;\r
196 \r
197                                 /* Only process FIFO if there's room to store it in the queue */\r
198                                 if( ulRxBytes < ( USB_CDC_QUEUE_SIZE - uxQueueMessagesWaiting( xRxCDC ) ) )\r
199                                 {\r
200                                         while( ulRxBytes-- )\r
201                                         {\r
202                                                 ucByte = AT91C_BASE_UDP->UDP_FDR[ usbEND_POINT_1 ];\r
203                                                 xQueueSend( xRxCDC, &ucByte, 0 );\r
204                                         }\r
205 \r
206                                         /* Release the FIFO */\r
207                                         portENTER_CRITICAL();\r
208                                         {\r
209                                                 ulStatus = AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_1 ];\r
210                                                 usbCSR_CLEAR_BIT( &ulStatus, uiCurrentBank );\r
211                                                 AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_1 ] = ulStatus;\r
212                                         }\r
213                                         portEXIT_CRITICAL();\r
214 \r
215                                         /* Re-enable endpoint 1's interrupts */\r
216                                         AT91C_BASE_UDP->UDP_IER = AT91C_UDP_EPINT1;\r
217                                 \r
218                                         /* Update the current bank in use */\r
219                                         if( uiCurrentBank == AT91C_UDP_RX_DATA_BK0 ) \r
220                                         {\r
221                                                 uiCurrentBank = AT91C_UDP_RX_DATA_BK1;\r
222                                         }\r
223                                         else \r
224                                         {\r
225                                                 uiCurrentBank = AT91C_UDP_RX_DATA_BK0;\r
226                                         }\r
227 \r
228                                 }\r
229                                 else \r
230                                 {\r
231                                         break;\r
232                                 }\r
233                         }\r
234                 }\r
235         }\r
236 }\r
237 /*------------------------------------------------------------*/\r
238 \r
239 void vUSBSendByte( portCHAR cByte )\r
240 {\r
241         /* Queue the byte to be sent.  The USB task will send it. */\r
242         xQueueSend( xTxCDC, &cByte, usbNO_BLOCK );\r
243 }\r
244 /*------------------------------------------------------------*/\r
245 \r
246 static void prvSendZLP( void )\r
247 {\r
248 unsigned portLONG ulStatus;\r
249 \r
250         /* Wait until the FIFO is free - even though we are not going to use it.\r
251         THERE IS NO TIMEOUT HERE! */\r
252         while( AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ] & AT91C_UDP_TXPKTRDY )\r
253         {\r
254                 vTaskDelay( usbSHORTEST_DELAY );\r
255         }\r
256 \r
257         portENTER_CRITICAL();\r
258         {\r
259                 /* Cancel any further pending data */\r
260                 pxControlTx.ulTotalDataLength = pxControlTx.ulNextCharIndex;\r
261 \r
262                 /* Set the TXPKTRDY bit to cause a transmission with no data. */\r
263                 ulStatus = AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ];\r
264                 usbCSR_SET_BIT( &ulStatus, AT91C_UDP_TXPKTRDY );\r
265                 AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ] = ulStatus;\r
266         }\r
267         portEXIT_CRITICAL();\r
268 }\r
269 /*------------------------------------------------------------*/\r
270 \r
271 static void prvSendStall( void )\r
272 {\r
273         unsigned portLONG ulStatus;\r
274 \r
275         portENTER_CRITICAL();\r
276         {\r
277                 /* Force a stall by simply setting the FORCESTALL bit in the CSR. */\r
278                 ulStatus = AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ];\r
279                 usbCSR_SET_BIT( &ulStatus, AT91C_UDP_FORCESTALL );\r
280                 AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ] = ulStatus;\r
281         }\r
282         portEXIT_CRITICAL();\r
283 }\r
284 /*------------------------------------------------------------*/\r
285 \r
286 static void prvResetEndPoints( void )\r
287 {\r
288 unsigned portLONG ulTemp;\r
289 \r
290         eDriverState = eJUST_RESET;\r
291         ucControlState = 0;\r
292 \r
293         /* Reset all the end points. */\r
294         AT91C_BASE_UDP->UDP_RSTEP  = usbEND_POINT_RESET_MASK;\r
295         AT91C_BASE_UDP->UDP_RSTEP  = ( unsigned portLONG ) 0x00;\r
296 \r
297         /* Enable data to be sent and received. */\r
298         AT91C_BASE_UDP->UDP_FADDR = AT91C_UDP_FEN;\r
299 \r
300         /* Repair the configuration end point. */\r
301         portENTER_CRITICAL();\r
302         {\r
303                 ulTemp = AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ];\r
304                 usbCSR_SET_BIT( &ulTemp, ( ( unsigned portLONG ) ( AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL ) ) );\r
305                 AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ] = ulTemp;\r
306                 AT91C_BASE_UDP->UDP_IER = AT91C_UDP_EPINT0;\r
307         }\r
308         portEXIT_CRITICAL();\r
309         uiCurrentBank = AT91C_UDP_RX_DATA_BK0;\r
310 }\r
311 /*------------------------------------------------------------*/\r
312 \r
313 static void prvProcessEndPoint0Interrupt( xISRStatus *pxMessage )\r
314 {\r
315 static xUSB_REQUEST xRequest;\r
316 unsigned portLONG ulRxBytes;\r
317 \r
318         /* Get number of bytes received, if any */\r
319         ulRxBytes = pxMessage->ulCSR0 >> 16;\r
320         ulRxBytes &= usbRX_COUNT_MASK;\r
321 \r
322         if( pxMessage->ulCSR0 & AT91C_UDP_TXCOMP )\r
323         {\r
324                 /* We received a TX complete interrupt.  What we do depends on\r
325                 what we sent to get this interrupt. */\r
326 \r
327                 if( eDriverState == eJUST_GOT_CONFIG )\r
328                 {\r
329                         /* We sent an acknowledgement of a SET_CONFIG request.  We\r
330                         are now at the end of the enumeration.\r
331                         \r
332                         TODO: Config 0 sets unconfigured state, should enter Address state.\r
333                         Request for unsupported config should stall. */\r
334                         AT91C_BASE_UDP->UDP_GLBSTATE = AT91C_UDP_CONFG;\r
335                         \r
336                         /* Set up endpoints */\r
337                         portENTER_CRITICAL();\r
338                         {\r
339                                 unsigned portLONG ulTemp;\r
340 \r
341                                 /* Set endpoint 1 to bulk-out */\r
342                                 ulTemp = AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_1 ];                                     \r
343                                 usbCSR_SET_BIT( &ulTemp, AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT );\r
344                                 AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_1 ] = ulTemp;             \r
345                                 AT91C_BASE_UDP->UDP_IER = AT91C_UDP_EPINT1;\r
346                                 /* Set endpoint 2 to bulk-in */\r
347                                 ulTemp = AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_2 ];                                     \r
348                                 usbCSR_SET_BIT( &ulTemp, AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN );\r
349                                 AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_2 ] = ulTemp;             \r
350                                 AT91C_BASE_UDP->UDP_IER = AT91C_UDP_EPINT2;\r
351                                         /* Set endpoint 3 to interrupt-in, enable it, and enable interrupts */\r
352                                 ulTemp = AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_3 ];                                     \r
353                                 usbCSR_SET_BIT( &ulTemp, AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN );\r
354                                 AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_3 ] = ulTemp;             \r
355                                 /*AT91F_UDP_EnableIt( AT91C_BASE_UDP, AT91C_UDP_EPINT3 );                                */\r
356                         }\r
357                         portEXIT_CRITICAL();\r
358 \r
359                         eDriverState = eREADY_TO_SEND;\r
360                 }               \r
361                 else if( eDriverState == eJUST_GOT_ADDRESS )\r
362                 {\r
363                         /* We sent an acknowledgement of a SET_ADDRESS request.  Move\r
364                         to the addressed state. */\r
365                         if( ulReceivedAddress != ( unsigned portLONG ) 0 )\r
366                         {                       \r
367                                 AT91C_BASE_UDP->UDP_GLBSTATE = AT91C_UDP_FADDEN;\r
368                         }\r
369                         else\r
370                         {\r
371                                 AT91C_BASE_UDP->UDP_GLBSTATE = 0;\r
372                         }                       \r
373 \r
374                         AT91C_BASE_UDP->UDP_FADDR = ( AT91C_UDP_FEN | ulReceivedAddress );              \r
375                         eDriverState = eNOTHING;\r
376                 }\r
377                 else\r
378                 {               \r
379                         /* The TXCOMP was not for any special type of transmission.  See\r
380                         if there is any more data to send. */\r
381                         prvSendNextSegment();\r
382                 }\r
383         }\r
384 \r
385         if( pxMessage->ulCSR0 & AT91C_UDP_RX_DATA_BK0 )\r
386         {\r
387                 /* Received a control data packet.  May be a 0-length ACK or a data stage. */\r
388                 unsigned portCHAR ucBytesToGet;\r
389          \r
390                 /* Got data.  Cancel any outgoing data. */\r
391                 pxControlTx.ulNextCharIndex = pxControlTx.ulTotalDataLength;\r
392                 \r
393                  /* Determine how many bytes we need to receive. */\r
394                 ucBytesToGet = pxControlRx.ulTotalDataLength - pxControlRx.ulNextCharIndex;\r
395                 if( ucBytesToGet > ulRxBytes ) \r
396                 {       \r
397                         ucBytesToGet = ulRxBytes;\r
398                 }\r
399 \r
400                 /* If we're not expecting any data, it's an ack - just quit now. */\r
401                 if( !ucBytesToGet )\r
402                 {\r
403                          return;\r
404                 }\r
405 \r
406                 /* Get the required data and update the index. */\r
407                 memcpy( pxControlRx.ucBuffer, pxMessage->ucFifoData, ucBytesToGet );\r
408                 pxControlRx.ulNextCharIndex += ucBytesToGet;    \r
409         }\r
410 \r
411         if( pxMessage->ulCSR0 & AT91C_UDP_RXSETUP )\r
412         {\r
413                 /* Received a SETUP packet.  May be followed by data packets. */\r
414 \r
415                 if( ulRxBytes >= usbEXPECTED_NUMBER_OF_BYTES )\r
416                 {                               \r
417                         /* Create an xUSB_REQUEST variable from the raw bytes array. */\r
418 \r
419                         xRequest.ucReqType = pxMessage->ucFifoData[ usbREQUEST_TYPE_INDEX ];\r
420                         xRequest.ucRequest = pxMessage->ucFifoData[ usbREQUEST_INDEX ];\r
421 \r
422                         xRequest.usValue = pxMessage->ucFifoData[ usbVALUE_HIGH_BYTE ];\r
423                         xRequest.usValue <<= 8;\r
424                         xRequest.usValue |= pxMessage->ucFifoData[ usbVALUE_LOW_BYTE ];\r
425                                                 \r
426                         xRequest.usIndex = pxMessage->ucFifoData[ usbINDEX_HIGH_BYTE ];\r
427                         xRequest.usIndex <<= 8;\r
428                         xRequest.usIndex |= pxMessage->ucFifoData[ usbINDEX_LOW_BYTE ];\r
429                         \r
430                         xRequest.usLength = pxMessage->ucFifoData[ usbLENGTH_HIGH_BYTE ];\r
431                         xRequest.usLength <<= 8;\r
432                         xRequest.usLength |= pxMessage->ucFifoData[ usbLENGTH_LOW_BYTE ];\r
433 \r
434                         pxControlRx.ulNextCharIndex = 0;\r
435                         if( ! (xRequest.ucReqType & 0x80) ) /* Host-to-Device transfer, may need to get data first */\r
436                         {\r
437                                 if( xRequest.usLength > usbMAX_CONTROL_MESSAGE_SIZE )\r
438                                 {       \r
439                                         /* Too big!  No space for control data, stall and abort. */\r
440                                         prvSendStall();\r
441                                         return;\r
442                                 }\r
443 \r
444                                 pxControlRx.ulTotalDataLength = xRequest.usLength;\r
445                         }\r
446                         else\r
447                         {\r
448                                 /* We're sending the data, don't wait for any. */\r
449                                 pxControlRx.ulTotalDataLength = 0; \r
450                         }\r
451                 }\r
452         }\r
453 \r
454         /* See if we've got a pending request and all its associated data ready */\r
455         if( ( pxMessage->ulCSR0 & ( AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RXSETUP ) ) \r
456                 && ( pxControlRx.ulNextCharIndex >= pxControlRx.ulTotalDataLength ) )\r
457         {\r
458                 unsigned portCHAR ucRequest;\r
459 \r
460                 /* Manipulate the ucRequestType and the ucRequest parameters to \r
461                 generate a zero based request selection.  This is just done to \r
462                 break up the requests into subsections for clarity.  The \r
463                 alternative would be to have more huge switch statement that would\r
464                 be difficult to optimise. */\r
465                 ucRequest = ( ( xRequest.ucReqType & 0x60 ) >> 3 );\r
466                 ucRequest |= ( xRequest.ucReqType & 0x03 );\r
467                         \r
468                 switch( ucRequest )\r
469                 {\r
470                         case usbSTANDARD_DEVICE_REQUEST:        \r
471                                 /* Standard Device request */\r
472                                 prvHandleStandardDeviceRequest( &xRequest );\r
473                                 break;\r
474 \r
475                         case usbSTANDARD_INTERFACE_REQUEST:     \r
476                                 /* Standard Interface request */\r
477                                 prvHandleStandardInterfaceRequest( &xRequest );\r
478                                 break;\r
479 \r
480                         case usbSTANDARD_END_POINT_REQUEST:     \r
481                                 /* Standard Endpoint request */\r
482                                 prvHandleStandardEndPointRequest( &xRequest );\r
483                                 break;\r
484 \r
485                         case usbCLASS_INTERFACE_REQUEST:        \r
486                                 /* Class Interface request */\r
487                                 prvHandleClassInterfaceRequest( &xRequest );\r
488                                 break;\r
489 \r
490                         default:        /* This is not something we want to respond to. */\r
491                                 prvSendStall(); \r
492                 }\r
493         }\r
494 }\r
495 /*------------------------------------------------------------*/\r
496 \r
497 static void prvGetStandardDeviceDescriptor( xUSB_REQUEST *pxRequest )\r
498 {\r
499         /* The type is in the high byte.  Return whatever has been requested. */\r
500         switch( ( pxRequest->usValue & 0xff00 ) >> 8 )\r
501         {\r
502                 case usbDESCRIPTOR_TYPE_DEVICE:\r
503                         prvSendControlData( ( unsigned portCHAR * ) &pxDeviceDescriptor, pxRequest->usLength, sizeof( pxDeviceDescriptor ), pdTRUE );\r
504                         break;\r
505 \r
506                 case usbDESCRIPTOR_TYPE_CONFIGURATION:\r
507                         prvSendControlData( ( unsigned portCHAR * ) &( pxConfigDescriptor ), pxRequest->usLength, sizeof( pxConfigDescriptor ), pdTRUE );\r
508                         break;\r
509 \r
510                 case usbDESCRIPTOR_TYPE_STRING:\r
511 \r
512                         /* The index to the string descriptor is the lower byte. */\r
513                         switch( pxRequest->usValue & 0xff )\r
514                         {                       \r
515                                 case usbLANGUAGE_STRING:\r
516                                         prvSendControlData( ( unsigned portCHAR * ) &pxLanguageStringDescriptor, pxRequest->usLength, sizeof(pxLanguageStringDescriptor), pdTRUE );\r
517                                         break;\r
518 \r
519                                 case usbMANUFACTURER_STRING:\r
520                                         prvSendControlData( ( unsigned portCHAR * ) &pxManufacturerStringDescriptor, pxRequest->usLength, sizeof( pxManufacturerStringDescriptor ), pdTRUE );\r
521                                         break;\r
522 \r
523                                 case usbPRODUCT_STRING:\r
524                                         prvSendControlData( ( unsigned portCHAR * ) &pxProductStringDescriptor, pxRequest->usLength, sizeof( pxProductStringDescriptor ), pdTRUE );\r
525                                         break;\r
526 \r
527                                 case usbCONFIGURATION_STRING:\r
528                                         prvSendControlData( ( unsigned portCHAR * ) &pxConfigurationStringDescriptor, pxRequest->usLength, sizeof( pxConfigurationStringDescriptor ), pdTRUE );\r
529                                         break;\r
530 \r
531                                 case usbINTERFACE_STRING:\r
532                                         prvSendControlData( ( unsigned portCHAR * ) &pxInterfaceStringDescriptor, pxRequest->usLength, sizeof( pxInterfaceStringDescriptor ), pdTRUE );\r
533                                         break;\r
534 \r
535                                 default:\r
536                                         prvSendStall();\r
537                                         break;\r
538                         }\r
539                         break;\r
540 \r
541                 default:\r
542                         prvSendStall();\r
543                         break;\r
544         }\r
545 }\r
546 /*------------------------------------------------------------*/\r
547 \r
548 static void prvHandleStandardDeviceRequest( xUSB_REQUEST *pxRequest )\r
549 {\r
550 unsigned portSHORT usStatus = 0;\r
551 \r
552         switch( pxRequest->ucRequest )\r
553         {\r
554                 case usbGET_STATUS_REQUEST:\r
555                         /* Just send two byte dummy status. */\r
556                         prvSendControlData( ( unsigned portCHAR * ) &usStatus, sizeof( usStatus ), sizeof( usStatus ), pdFALSE );\r
557                         break;\r
558 \r
559                 case usbGET_DESCRIPTOR_REQUEST:\r
560                         /* Send device descriptor */\r
561                         prvGetStandardDeviceDescriptor( pxRequest );\r
562                         break;\r
563 \r
564                 case usbGET_CONFIGURATION_REQUEST:\r
565                         /* Send selected device configuration */\r
566                         prvSendControlData( ( unsigned portCHAR * ) &ucUSBConfig, sizeof( ucUSBConfig ), sizeof( ucUSBConfig ), pdFALSE );\r
567                         break;\r
568 \r
569                 case usbSET_FEATURE_REQUEST:\r
570                         prvSendZLP();\r
571                         break;\r
572 \r
573                 case usbSET_ADDRESS_REQUEST:                    \r
574                         /* Get assigned address and send ack, but don't implement new address until we get a TXCOMP */\r
575                         prvSendZLP();                   \r
576                         eDriverState = eJUST_GOT_ADDRESS;                       \r
577                         ulReceivedAddress = ( unsigned portLONG ) pxRequest->usValue;\r
578                         break;\r
579 \r
580                 case usbSET_CONFIGURATION_REQUEST:\r
581                         /* Ack SET_CONFIGURATION request, but don't implement until TXCOMP */\r
582                         ucUSBConfig = ( unsigned portCHAR ) ( pxRequest->usValue & 0xff );\r
583                         eDriverState = eJUST_GOT_CONFIG;\r
584                         prvSendZLP();\r
585                         break;\r
586 \r
587                 default:\r
588                         /* Any unsupported request results in a STALL response. */\r
589                         prvSendStall();\r
590                         break;\r
591         }\r
592 }\r
593 /*------------------------------------------------------------*/\r
594 \r
595 static void prvHandleClassInterfaceRequest( xUSB_REQUEST *pxRequest )\r
596 {\r
597         switch( pxRequest->ucRequest )\r
598         {\r
599                 case usbSEND_ENCAPSULATED_COMMAND:\r
600                         prvSendStall();\r
601                         break;\r
602 \r
603                 case usbGET_ENCAPSULATED_RESPONSE:\r
604                         prvSendStall();\r
605                         break;\r
606 \r
607                 case usbSET_LINE_CODING:\r
608                         /* Set line coding - baud rate, data bits, parity, stop bits */\r
609                         prvSendZLP();\r
610                         memcpy( ( void * ) pxLineCoding, pxControlRx.ucBuffer, sizeof( pxLineCoding ) );\r
611                         break;\r
612 \r
613                 case usbGET_LINE_CODING:\r
614                         /* Get line coding */\r
615                         prvSendControlData( (unsigned portCHAR *) &pxLineCoding, pxRequest->usLength, sizeof( pxLineCoding ), pdFALSE );\r
616                         break;\r
617 \r
618                 case usbSET_CONTROL_LINE_STATE:\r
619                         /* D0: 1=DTR, 0=No DTR,  D1: 1=Activate Carrier, 0=Deactivate carrier (RTS, half-duplex) */\r
620                         prvSendZLP();\r
621                         ucControlState = pxRequest->usValue;\r
622                         break;\r
623 \r
624                 default:\r
625                         prvSendStall();\r
626                         break;\r
627         }\r
628 }\r
629 /*------------------------------------------------------------*/\r
630 \r
631 static void prvGetStandardInterfaceDescriptor( xUSB_REQUEST *pxRequest )\r
632 {\r
633         switch( ( pxRequest->usValue & ( unsigned portSHORT ) 0xff00 ) >> 8 )\r
634         {\r
635                 default:\r
636                         prvSendStall();\r
637                         break;\r
638         }\r
639 }\r
640 /*-----------------------------------------------------------*/\r
641 \r
642 static void prvHandleStandardInterfaceRequest( xUSB_REQUEST *pxRequest )\r
643 {\r
644 unsigned portSHORT usStatus = 0;\r
645 \r
646         switch( pxRequest->ucRequest )\r
647         {\r
648                 case usbGET_STATUS_REQUEST:\r
649                         /* Send dummy 2 bytes. */\r
650                         prvSendControlData( ( unsigned portCHAR * ) &usStatus, sizeof( usStatus ), sizeof( usStatus ), pdFALSE );\r
651                         break;\r
652 \r
653                 case usbGET_DESCRIPTOR_REQUEST:\r
654                         prvGetStandardInterfaceDescriptor( pxRequest ); \r
655                         break;\r
656 \r
657                 /* This minimal implementation does not respond to these. */\r
658                 case usbGET_INTERFACE_REQUEST:\r
659                 case usbSET_FEATURE_REQUEST:\r
660                 case usbSET_INTERFACE_REQUEST:  \r
661 \r
662                 default:\r
663                         prvSendStall();\r
664                         break;\r
665         }\r
666 }\r
667 /*-----------------------------------------------------------*/\r
668 \r
669 static void prvHandleStandardEndPointRequest( xUSB_REQUEST *pxRequest )\r
670 {\r
671         switch( pxRequest->ucRequest )\r
672         {\r
673                 /* This minimal implementation does not expect to respond to these. */\r
674                 case usbGET_STATUS_REQUEST:\r
675                 case usbCLEAR_FEATURE_REQUEST: \r
676                 case usbSET_FEATURE_REQUEST:\r
677 \r
678                 default:                        \r
679                         prvSendStall();\r
680                         break;\r
681         }\r
682 }\r
683 /*-----------------------------------------------------------*/\r
684 \r
685 static void vDetachUSBInterface( void)\r
686 {\r
687         /* Setup the PIO for the USB pull up resistor. */\r
688         AT91C_BASE_PIOA->PIO_PER = AT91C_PIO_PA16;\r
689         AT91C_BASE_PIOA->PIO_OER = AT91C_PIO_PA16;\r
690 \r
691 \r
692         /* Disable pull up */\r
693         AT91C_BASE_PIOA->PIO_SODR = AT91C_PIO_PA16;\r
694\r
695 /*-----------------------------------------------------------*/\r
696 \r
697 static void vInitUSBInterface( void )\r
698 {\r
699 extern void ( vUSB_ISR )( void );\r
700 \r
701         /* Create the queue used to communicate between the USB ISR and task. */\r
702         xUSBInterruptQueue = xQueueCreate( usbQUEUE_LENGTH + 1, sizeof( xISRStatus * ) );\r
703         \r
704         /* Create the queues used to hold Rx and Tx characters. */\r
705         xRxCDC = xQueueCreate( USB_CDC_QUEUE_SIZE, ( unsigned portCHAR ) sizeof( signed portCHAR ) );\r
706         xTxCDC = xQueueCreate( USB_CDC_QUEUE_SIZE + 1, ( unsigned portCHAR ) sizeof( signed portCHAR ) );\r
707 \r
708         if( (!xUSBInterruptQueue) || (!xRxCDC) || (!xTxCDC) )\r
709         {       \r
710                 /* Not enough RAM to create queues!. */\r
711                 return;\r
712         }\r
713         \r
714         /* Initialise a few state variables. */\r
715         pxControlTx.ulNextCharIndex = ( unsigned portLONG ) 0;\r
716         pxControlRx.ulNextCharIndex = ( unsigned portLONG ) 0;\r
717         ucUSBConfig = ( unsigned portCHAR ) 0;\r
718         eDriverState = eNOTHING;\r
719         ucControlState = 0;\r
720         uiCurrentBank = AT91C_UDP_RX_DATA_BK0;\r
721 \r
722 \r
723         /* HARDWARE SETUP */\r
724 \r
725         /* Set the PLL USB Divider */\r
726         AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1;\r
727 \r
728         /* Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock. */\r
729         AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP;\r
730         AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP);\r
731 \r
732         /* Setup the PIO for the USB pull up resistor. */\r
733         AT91C_BASE_PIOA->PIO_PER = AT91C_PIO_PA16;\r
734         AT91C_BASE_PIOA->PIO_OER = AT91C_PIO_PA16;\r
735 \r
736 \r
737         /* Start without the pullup - this will get set at the end of this \r
738         function. */\r
739         AT91C_BASE_PIOA->PIO_SODR = AT91C_PIO_PA16;\r
740 \r
741 \r
742         /* When using the USB debugger the peripheral registers do not always get\r
743         set to the correct default values.  To make sure set the relevant registers\r
744         manually here. */\r
745         AT91C_BASE_UDP->UDP_IDR = ( unsigned portLONG ) 0xffffffff;\r
746         AT91C_BASE_UDP->UDP_ICR = ( unsigned portLONG ) 0xffffffff;\r
747         AT91C_BASE_UDP->UDP_CSR[ 0 ] = ( unsigned portLONG ) 0x00;\r
748         AT91C_BASE_UDP->UDP_CSR[ 1 ] = ( unsigned portLONG ) 0x00;\r
749         AT91C_BASE_UDP->UDP_CSR[ 2 ] = ( unsigned portLONG ) 0x00;\r
750         AT91C_BASE_UDP->UDP_CSR[ 3 ] = ( unsigned portLONG ) 0x00;\r
751         AT91C_BASE_UDP->UDP_GLBSTATE = 0;\r
752         AT91C_BASE_UDP->UDP_FADDR = 0;\r
753 \r
754         /* Enable the transceiver. */\r
755         AT91C_UDP_TRANSCEIVER_ENABLE = 0;\r
756 \r
757         /* Enable the USB interrupts - other interrupts get enabled as the \r
758         enumeration process progresses. */\r
759         AT91F_AIC_ConfigureIt( AT91C_ID_UDP, usbINTERRUPT_PRIORITY, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, ( void (*)( void ) ) vUSB_ISR );\r
760         AT91C_BASE_AIC->AIC_IECR = 0x1 << AT91C_ID_UDP;\r
761 \r
762 \r
763         /* Wait a short while before making our presence known. */\r
764         vTaskDelay( usbINIT_DELAY );\r
765         AT91C_BASE_PIOA->PIO_CODR = AT91C_PIO_PA16;\r
766 }\r
767 /*-----------------------------------------------------------*/\r
768 \r
769 static void prvSendControlData( unsigned portCHAR *pucData, unsigned portSHORT usRequestedLength, unsigned portLONG ulLengthToSend, portLONG lSendingDescriptor )\r
770 {\r
771         if( ( ( unsigned portLONG ) usRequestedLength < ulLengthToSend ) )\r
772         {\r
773                 /* Cap the data length to that requested. */\r
774                 ulLengthToSend = ( unsigned portSHORT ) usRequestedLength;\r
775         }\r
776         else if( ( ulLengthToSend < ( unsigned portLONG ) usRequestedLength ) && lSendingDescriptor )\r
777         {\r
778                 /* We are sending a descriptor.  If the descriptor is an exact \r
779                 multiple of the FIFO length then it will have to be terminated\r
780                 with a NULL packet.  Set the state to indicate this if\r
781                 necessary. */\r
782                 if( ( ulLengthToSend % usbFIFO_LENGTH ) == 0 )\r
783                 {\r
784                         eDriverState = eSENDING_EVEN_DESCRIPTOR;\r
785                 }\r
786         }\r
787 \r
788         /* Here we assume that the previous message has been sent.  THERE IS NO\r
789         BUFFER OVERFLOW PROTECTION HERE.\r
790 \r
791         Copy the data to send into the buffer as we cannot send it all at once\r
792         (if it is greater than 8 bytes in length). */\r
793         memcpy( pxControlTx.ucBuffer, pucData, ulLengthToSend );\r
794 \r
795         /* Reinitialise the buffer index so we start sending from the start of \r
796         the data. */\r
797         pxControlTx.ulTotalDataLength = ulLengthToSend;\r
798         pxControlTx.ulNextCharIndex = ( unsigned portLONG ) 0;\r
799 \r
800         /* Send the first 8 bytes now.  The rest will get sent in response to \r
801         TXCOMP interrupts. */\r
802         prvSendNextSegment();\r
803 }\r
804 /*-----------------------------------------------------------*/\r
805 \r
806 static void prvSendNextSegment( void )\r
807 {\r
808 volatile unsigned portLONG ulNextLength, ulStatus, ulLengthLeftToSend;\r
809 \r
810         /* Is there any data to send? */\r
811         if( pxControlTx.ulTotalDataLength > pxControlTx.ulNextCharIndex )\r
812         {\r
813                 ulLengthLeftToSend = pxControlTx.ulTotalDataLength - pxControlTx.ulNextCharIndex;\r
814         \r
815                 /* We can only send 8 bytes to the fifo at a time. */\r
816                 if( ulLengthLeftToSend > usbFIFO_LENGTH )\r
817                 {\r
818                         ulNextLength = usbFIFO_LENGTH;\r
819                 }\r
820                 else\r
821                 {\r
822                         ulNextLength = ulLengthLeftToSend;\r
823                 }\r
824 \r
825                 /* Wait until we can place data in the fifo.  THERE IS NO TIMEOUT\r
826                 HERE! */\r
827                 while( AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ] & AT91C_UDP_TXPKTRDY )\r
828                 {\r
829                         vTaskDelay( usbSHORTEST_DELAY );\r
830                 }\r
831 \r
832                 /* Write the data to the FIFO. */\r
833                 while( ulNextLength > ( unsigned portLONG ) 0 )\r
834                 {\r
835                         AT91C_BASE_UDP->UDP_FDR[ usbEND_POINT_0 ] = pxControlTx.ucBuffer[ pxControlTx.ulNextCharIndex ];\r
836         \r
837                         ulNextLength--;\r
838                         pxControlTx.ulNextCharIndex++;\r
839                 }\r
840         \r
841                 /* Start the transmission. */\r
842                 portENTER_CRITICAL();\r
843                 {\r
844                         ulStatus = AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ];\r
845                         usbCSR_SET_BIT( &ulStatus, ( ( unsigned portLONG ) 0x10 ) );\r
846                         AT91C_BASE_UDP->UDP_CSR[ usbEND_POINT_0 ] = ulStatus;\r
847                 }\r
848                 portEXIT_CRITICAL();\r
849         }\r
850         else\r
851         {\r
852                 /* There is no data to send.  If we were sending a descriptor and the \r
853                 descriptor was an exact multiple of the max packet size then we need\r
854                 to send a null to terminate the transmission. */\r
855                 if( eDriverState == eSENDING_EVEN_DESCRIPTOR )\r
856                 {\r
857                         prvSendZLP();\r
858                         eDriverState = eNOTHING;\r
859                 }\r
860         }\r
861 }\r
862 \r
863 \r