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