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