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