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