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