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