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