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