]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_A5_SAMA5D3x_Xplained_IAR/CDCCommandConsole.c
19f19f951de3bc5180e1a4d1cf44f4e48accb5dd
[freertos] / FreeRTOS / Demo / CORTEX_A5_SAMA5D3x_Xplained_IAR / CDCCommandConsole.c
1 /*\r
2  * FreeRTOS Kernel V10.2.1\r
3  * Copyright (C) 2019 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  * NOTE:  This file uses a third party USB CDC driver.\r
30  */\r
31 \r
32 /* Standard includes. */\r
33 #include "string.h"\r
34 #include "stdio.h"\r
35 \r
36 /* FreeRTOS includes. */\r
37 #include "FreeRTOS.h"\r
38 #include "task.h"\r
39 #include "event_groups.h"\r
40 \r
41 /* Example includes. */\r
42 #include "FreeRTOS_CLI.h"\r
43 \r
44 /* Library includes. */\r
45 #include "board.h"\r
46 #include "chip.h"\r
47 #include "USBD.h"\r
48 #include "CDCDSerialDriver.h"\r
49 \r
50 /*-----------------------------------------------------------*/\r
51 \r
52 /* Dimensions the buffer into which input characters are placed. */\r
53 #define cmdMAX_INPUT_SIZE               50\r
54 \r
55 /* DEL acts as a backspace. */\r
56 #define cmdASCII_DEL            ( 0x7F )\r
57 \r
58 /* The bits in the event group used to signal USB interrupt events to this\r
59 task. */\r
60 #define cmdRX_COMPLETE_BIT      ( 0x01 )\r
61 #define cmdTX_COMPLETE_BIT      ( 0x02 )\r
62 /*-----------------------------------------------------------*/\r
63 \r
64 /*\r
65  * The task that implements the command console processing.\r
66  */\r
67 static void prvCDCCommandConsoleTask( void *pvParameters );\r
68 \r
69 /*\r
70  * Initialise the USB hardware and driver.\r
71  */\r
72 static void prvCDCInit( void );\r
73 \r
74 /*\r
75  * Handler installed on the VBUS pin to detect connect() and disconnect()\r
76  * events.\r
77  */\r
78 static void prvVBusISRHandler( const Pin *pxPin );\r
79 \r
80 /*\r
81  * USB handler defined by the driver, installed after the CDC driver has been\r
82  * initialised.\r
83  */\r
84 extern void USBD_IrqHandler( void );\r
85 \r
86 /*\r
87  * The function that creates the CLI task.\r
88  */\r
89 void vUSBCommandConsoleStart( uint16_t usStackSize, UBaseType_t uxPriority );\r
90 \r
91 /*\r
92  * Send xDataLength bytes from pcData to the CDC port.\r
93  */\r
94 static void prvCDCSend( const char *pcData, size_t xDataLenth );\r
95 \r
96 /*\r
97  * Initiate a receive into the Rx buffer from the CDC port, then wait for a\r
98  * period for characters to be received.\r
99  */\r
100 static void prvCDCGetChar( void );\r
101 \r
102 /*\r
103  * Configure VBus pins and interrupts, and check for connection.\r
104  */\r
105 static void prvConfigureVBus( void );\r
106 \r
107 /*\r
108  * Callback which is invoked when a CDC read completes.  This callback is\r
109  * passed as a parameter to the CDC receive function.\r
110  */\r
111 static void prvCDCDataReceivedCallback( uint32_t ulUnused, uint8_t ucStatus, uint32_t ulBytesReceived, uint32_t ulBytesRemaining );\r
112 \r
113 /*\r
114  * Callback which is invoked when a CDC write completes.  This callback is\r
115  * passed as a parameter to the CDC send function.\r
116  */\r
117 static void prvCDCDataTransmittedCallback( uint32_t ulUnused, uint8_t ucStatus, uint32_t ulBytesSent, uint32_t ulBytesRemaining );\r
118 \r
119 /*\r
120  * Keep trying to initiate an Rx until it is started successfully\r
121  */\r
122 static void prvStartRx( void );\r
123 \r
124 /*-----------------------------------------------------------*/\r
125 \r
126 /* Const messages output by the command console. */\r
127 static const char * const pcWelcomeMessage = "FreeRTOS command server.\r\nType Help to view a list of registered commands.\r\n\r\n>";\r
128 static const char * const pcEndOfOutputMessage = "\r\n[Press ENTER to execute the previous command again]\r\n>";\r
129 static const char * const pcNewLine = "\r\n";\r
130 \r
131 /* Buffer into which received characters are placed. */\r
132 static char pcRxBuffer[ cmdMAX_INPUT_SIZE ];\r
133 \r
134 /* The number of bytes in pcRxBuffer that have not yet been read. */\r
135 static uint32_t ulBytesAvailable = 0;\r
136 \r
137 /* Used to unblock the task when bytes are received and when bytes have\r
138 completed sending. */\r
139 static EventGroupHandle_t xCDCEventBits;\r
140 \r
141 /*-----------------------------------------------------------*/\r
142 \r
143 void vUSBCommandConsoleStart( uint16_t usStackSize, UBaseType_t uxPriority )\r
144 {\r
145         /* Event group used to indicate that bytes are available in the Rx buffer\r
146         or that bytes have finished sending. */\r
147         xCDCEventBits = xEventGroupCreate();\r
148         configASSERT( xCDCEventBits );\r
149 \r
150         /* Create the task that handles the console itself. */\r
151         xTaskCreate(    prvCDCCommandConsoleTask,       /* The task that implements the command console. */\r
152                                         "CLI",                                          /* Text name assigned to the task.  This is just to assist debugging.  The kernel does not use this name itself. */\r
153                                         usStackSize,                            /* The size of the stack allocated to the task. */\r
154                                         NULL,                                           /* The parameter is not used, so NULL is passed. */\r
155                                         uxPriority,                                     /* The priority allocated to the task. */\r
156                                         NULL );                                         /* A handle is not required, so just pass NULL. */\r
157 }\r
158 /*-----------------------------------------------------------*/\r
159 \r
160 static void prvCDCCommandConsoleTask( void *pvParameters )\r
161 {\r
162 uint8_t ucInputIndex = 0;\r
163 char *pcOutputString, cRxedChar;\r
164 static char cInputString[ cmdMAX_INPUT_SIZE ], cLastInputString[ cmdMAX_INPUT_SIZE ];\r
165 BaseType_t xReturned;\r
166 uint32_t ulBufferIndex = 0;\r
167 \r
168         ( void ) pvParameters;\r
169 \r
170         /* Obtain the address of the output buffer.  Note there is no mutual\r
171         exclusion on this buffer as it is assumed only one command console interface\r
172         will be used at any one time. */\r
173         pcOutputString = FreeRTOS_CLIGetOutputBuffer();\r
174 \r
175         /* Initialise the CDC driver. */\r
176         prvCDCInit();\r
177 \r
178         /* Start receiving into the buffer. */\r
179         prvStartRx();\r
180 \r
181         /* Send the welcome message. */\r
182         prvCDCSend( pcWelcomeMessage, strlen( pcWelcomeMessage ) );\r
183 \r
184         for( ;; )\r
185         {\r
186                 /* Wait for my characters to be available. */\r
187                 prvCDCGetChar();\r
188 \r
189                 /* Process the bytes char for char on the assumption that as input comes\r
190                 from typing it is unlikely that more than a single byte will be received\r
191                 at a time anyway. */\r
192                 while( ulBytesAvailable > 0 )\r
193                 {\r
194                         /* Read next byte from the rx buffer. */\r
195                         cRxedChar = pcRxBuffer[ ulBufferIndex ];\r
196 \r
197                         taskENTER_CRITICAL();\r
198                         {\r
199                                 ulBytesAvailable--;\r
200                         }\r
201                         taskEXIT_CRITICAL();\r
202 \r
203                         /* Echo the character back. */\r
204                         prvCDCSend( &cRxedChar, sizeof( cRxedChar ) );\r
205 \r
206                         /* Was it the end of the line? */\r
207                         if( cRxedChar == '\n' || cRxedChar == '\r' )\r
208                         {\r
209                                 /* Just to space the output from the input. */\r
210                                 prvCDCSend( pcNewLine, strlen( pcNewLine ) );\r
211 \r
212                                 /* See if the command is empty, indicating that the last command\r
213                                 is to be executed again. */\r
214                                 if( ucInputIndex == 0 )\r
215                                 {\r
216                                         /* Copy the last command back into the input string. */\r
217                                         strcpy( cInputString, cLastInputString );\r
218                                 }\r
219 \r
220                                 /* Pass the received command to the command interpreter.  The\r
221                                 command interpreter is called repeatedly until it returns\r
222                                 pdFALSE (indicating there is no more output) as it might\r
223                                 generate more than one string. */\r
224                                 do\r
225                                 {\r
226                                         /* Get the next output string from the command interpreter. */\r
227                                         xReturned = FreeRTOS_CLIProcessCommand( cInputString, pcOutputString, configCOMMAND_INT_MAX_OUTPUT_SIZE );\r
228 \r
229                                         /* Write the generated string to the UART. */\r
230                                         prvCDCSend( pcOutputString, strlen( pcOutputString ) );\r
231 \r
232                                 } while( xReturned != pdFALSE );\r
233 \r
234                                 /* All the strings generated by the input command have been\r
235                                 sent.  Clear the input string ready to receive the next command.\r
236                                 Remember the command that was just processed first in case it is\r
237                                 to be processed again. */\r
238                                 strcpy( cLastInputString, cInputString );\r
239                                 ucInputIndex = 0;\r
240                                 memset( cInputString, 0x00, cmdMAX_INPUT_SIZE );\r
241 \r
242                                 prvCDCSend( pcEndOfOutputMessage, strlen( pcEndOfOutputMessage ) );\r
243                         }\r
244                         else\r
245                         {\r
246                                 if( cRxedChar == '\r' )\r
247                                 {\r
248                                         /* Ignore the character. */\r
249                                 }\r
250                                 else if( ( cRxedChar == '\b' ) || ( cRxedChar == cmdASCII_DEL ) )\r
251                                 {\r
252                                         /* Backspace was pressed.  Erase the last character in the\r
253                                         string - if any. */\r
254                                         if( ucInputIndex > 0 )\r
255                                         {\r
256                                                 ucInputIndex--;\r
257                                                 cInputString[ ucInputIndex ] = '\0';\r
258                                         }\r
259                                 }\r
260                                 else\r
261                                 {\r
262                                         /* A character was entered.  Add it to the string entered so\r
263                                         far.  When a \n is entered the complete string will be\r
264                                         passed to the command interpreter. */\r
265                                         if( ( cRxedChar >= ' ' ) && ( cRxedChar <= '~' ) )\r
266                                         {\r
267                                                 if( ucInputIndex < cmdMAX_INPUT_SIZE )\r
268                                                 {\r
269                                                         cInputString[ ucInputIndex ] = cRxedChar;\r
270                                                         ucInputIndex++;\r
271                                                 }\r
272                                         }\r
273                                 }\r
274                         }\r
275 \r
276                         /* Move onto the next byte the next time around. */\r
277                         ulBufferIndex++;\r
278                         if( ulBufferIndex >= cmdMAX_INPUT_SIZE )\r
279                         {\r
280                                 ulBufferIndex = 0;\r
281                         }\r
282                 }\r
283         }\r
284 }\r
285 /*-----------------------------------------------------------*/\r
286 \r
287 static void prvCDCInit( void )\r
288 {\r
289 extern WEAK const USBDDriverDescriptors cdcdSerialDriverDescriptors;\r
290 \r
291         /* If they are present, configure Vbus & Wake-up pins */\r
292         PIO_InitializeInterrupts( 0 );\r
293 \r
294         /* CDC serial driver initialization */\r
295         CDCDSerialDriver_Initialize( &cdcdSerialDriverDescriptors );\r
296 \r
297         /* Configure VBus pins and interrupts, and check for connection. */\r
298         prvConfigureVBus();\r
299 }\r
300 /*-----------------------------------------------------------*/\r
301 \r
302 static void prvCDCSend( const char *pcData, size_t xDataLength )\r
303 {\r
304 const TickType_t xTransferCompleteDelay = pdMS_TO_TICKS( 500UL );\r
305 \r
306         ( void ) pcData;\r
307         ( void ) xDataLength;\r
308 \r
309         if( xDataLength > 0 )\r
310         {\r
311                 if( CDCDSerialDriver_Write( ( void * ) pcData, xDataLength, ( TransferCallback ) prvCDCDataTransmittedCallback, 0 ) == USBD_STATUS_SUCCESS )\r
312                 {\r
313                         /* Wait for the transfer to complete. */\r
314                         xEventGroupWaitBits(    xCDCEventBits,\r
315                                                                         cmdTX_COMPLETE_BIT, /* The bit to wait for. */\r
316                                                                         pdTRUE, /* Clear the bit before exiting the function. */\r
317                                                                         pdFALSE, /* Only need to wait for one bit anyway. */\r
318                                                                         xTransferCompleteDelay ); /* The maximum time to wait for the event. */\r
319                 }\r
320         }\r
321 }\r
322 /*-----------------------------------------------------------*/\r
323 \r
324 static void prvStartRx( void )\r
325 {\r
326 const TickType_t xFailedReadDelay = pdMS_TO_TICKS( 150UL );\r
327 \r
328         while( CDCDSerialDriver_Read( pcRxBuffer, cmdMAX_INPUT_SIZE, ( TransferCallback ) prvCDCDataReceivedCallback, 0 ) != USBD_STATUS_SUCCESS )\r
329         {\r
330                 /* Maybe the CDC is not connected. */\r
331                 vTaskDelay( xFailedReadDelay );\r
332         }\r
333 }\r
334 /*-----------------------------------------------------------*/\r
335 \r
336 static void prvCDCGetChar( void )\r
337 {\r
338 const TickType_t xTransferCompleteDelay = pdMS_TO_TICKS( 750UL );\r
339 \r
340         if( ulBytesAvailable == 0 )\r
341         {\r
342                 /* Wait for a transfer to complete. */\r
343                 xEventGroupWaitBits(    xCDCEventBits,\r
344                                                                 cmdRX_COMPLETE_BIT, /* The bit to wait for. */\r
345                                                                 pdTRUE, /* Clear the bit before exiting the function. */\r
346                                                                 pdFALSE, /* Only need to wait for one bit anyway. */\r
347                                                                 xTransferCompleteDelay ); /* The maximum time to wait for the event. */\r
348         }\r
349 }\r
350 /*-----------------------------------------------------------*/\r
351 \r
352 static void prvVBusISRHandler( const Pin *pxPin )\r
353 {\r
354         /* NOTE: As this was written for the XPlained board, which is powered\r
355         through the USB and cannot be on without the USB connected, this function\r
356         has not been exercised. */\r
357 \r
358     /* Check current level on VBus to detect a connect/disconnect. */\r
359     if( PIO_Get( pxPin ) != 0 )\r
360     {\r
361         USBD_Connect();\r
362     }\r
363     else\r
364     {\r
365         USBD_Disconnect();\r
366     }\r
367 }\r
368 /*-----------------------------------------------------------*/\r
369 \r
370 static void prvCDCDataReceivedCallback( uint32_t ulUnused, uint8_t ucStatus, uint32_t ulBytesReceived, uint32_t ulBytesRemaining )\r
371 {\r
372 BaseType_t xHigherPriorityTaskWoken = pdFALSE;\r
373 static uint32_t ulNextRxPosition = 0;\r
374 \r
375         ( void ) ulUnused;\r
376         ( void ) ucStatus;\r
377         ( void ) ulBytesRemaining;\r
378 \r
379         /* If bytes were received then store the number of bytes placed into the Rx\r
380         buffer. */\r
381         if( ucStatus == USBD_STATUS_SUCCESS )\r
382         {\r
383                 ulBytesAvailable += ulBytesReceived;\r
384 \r
385                 /* Restart the Rx position from a buffer position past the newly\r
386                 received data. */\r
387                 ulNextRxPosition += ulBytesReceived;\r
388 \r
389                 if( ulNextRxPosition >= cmdMAX_INPUT_SIZE )\r
390                 {\r
391                         ulNextRxPosition = 0;\r
392                 }\r
393                 CDCDSerialDriver_Read( pcRxBuffer + ulNextRxPosition, cmdMAX_INPUT_SIZE - ulNextRxPosition, ( TransferCallback ) prvCDCDataReceivedCallback, 0 );\r
394 \r
395                 /* Ensure the task knows new data is available. */\r
396                 xEventGroupSetBitsFromISR( xCDCEventBits, cmdRX_COMPLETE_BIT, &xHigherPriorityTaskWoken );\r
397                 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );\r
398         }\r
399 }\r
400 /*-----------------------------------------------------------*/\r
401 \r
402 static void prvCDCDataTransmittedCallback( uint32_t ulUnused, uint8_t ucStatus, uint32_t ulBytesSent, uint32_t ulBytesRemaining )\r
403 {\r
404 BaseType_t xHigherPriorityTaskWoken = pdFALSE;\r
405 \r
406         ( void ) ulUnused;\r
407         ( void ) ucStatus;\r
408         ( void ) ulBytesRemaining;\r
409         ( void ) ulBytesSent;\r
410 \r
411         xEventGroupSetBitsFromISR( xCDCEventBits, cmdTX_COMPLETE_BIT, &xHigherPriorityTaskWoken );\r
412         portYIELD_FROM_ISR( xHigherPriorityTaskWoken );\r
413 }\r
414 /*-----------------------------------------------------------*/\r
415 \r
416 static void prvConfigureVBus( void )\r
417 {\r
418 const Pin xVBusPin = PIN_USB_VBUS;\r
419 \r
420         /* Configure PIO to generate an interrupt on status change. */\r
421         PIO_Configure( &xVBusPin, 1 );\r
422         PIO_ConfigureIt( &xVBusPin, prvVBusISRHandler );\r
423         PIO_EnableIt( &xVBusPin );\r
424 \r
425         /* Check current level on VBus */\r
426         if( PIO_Get( &xVBusPin ) != pdFALSE )\r
427         {\r
428                 /* if VBUS present, force the connect */\r
429                 USBD_Connect();\r
430         }\r
431         else\r
432         {\r
433                 USBD_Disconnect();\r
434         }\r
435 }\r
436 /*-----------------------------------------------------------*/\r
437 \r
438 void USBDCallbacks_Initialized( void )\r
439 {\r
440         /* CDC specific re-implementation of weak callback function.  Invoked after\r
441         the USB driver has been initialised. By default, configures the UDP/UDPHS\r
442         interrupt.  The interrupt priority is set to the highest to ensure the\r
443         interrupt nesting tests interfer as little as possible with the USB. */\r
444         IRQ_ConfigureIT( ID_UDPHS, 7, USBD_IrqHandler );\r
445         IRQ_EnableIT( ID_UDPHS );\r
446 }\r
447 /*-----------------------------------------------------------*/\r
448 \r
449 void USBDDriverCallbacks_ConfigurationChanged( uint8_t ucConfigNumber )\r
450 {\r
451         /* CDC specific re-implementation of weak callback function.  Invoked when\r
452         the configuration of the device changes. Parse used endpoints. */\r
453         CDCDSerialDriver_ConfigurationChangedHandler( ucConfigNumber );\r
454 }\r
455 /*-----------------------------------------------------------*/\r
456 \r
457 void USBDCallbacks_RequestReceived( const USBGenericRequest *pxRequest )\r
458 {\r
459         /* CDC specific re-implementation of weak callback function.  Invoked when\r
460         a new SETUP request is received from the host. */\r
461         CDCDSerialDriver_RequestHandler( pxRequest );\r
462 }\r
463 /*-----------------------------------------------------------*/\r