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