2 * FreeRTOS Kernel V10.3.0
\r
3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\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
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\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
22 * http://www.FreeRTOS.org
\r
23 * http://aws.amazon.com/freertos
\r
25 * 1 tab == 4 spaces!
\r
29 * NOTE: This file uses a third party USB CDC driver.
\r
32 /* Standard includes. */
\r
36 /* FreeRTOS includes. */
\r
37 #include "FreeRTOS.h"
\r
39 #include "event_groups.h"
\r
41 /* Example includes. */
\r
42 #include "FreeRTOS_CLI.h"
\r
44 /* Library includes. */
\r
48 #include "CDCDSerialDriver.h"
\r
50 /*-----------------------------------------------------------*/
\r
52 /* Dimensions the buffer into which input characters are placed. */
\r
53 #define cmdMAX_INPUT_SIZE 50
\r
55 /* DEL acts as a backspace. */
\r
56 #define cmdASCII_DEL ( 0x7F )
\r
58 /* The bits in the event group used to signal USB interrupt events to this
\r
60 #define cmdRX_COMPLETE_BIT ( 0x01 )
\r
61 #define cmdTX_COMPLETE_BIT ( 0x02 )
\r
62 /*-----------------------------------------------------------*/
\r
65 * The task that implements the command console processing.
\r
67 static void prvCDCCommandConsoleTask( void *pvParameters );
\r
70 * Initialise the USB hardware and driver.
\r
72 static void prvCDCInit( void );
\r
75 * Handler installed on the VBUS pin to detect connect() and disconnect()
\r
78 static void prvVBusISRHandler( const Pin *pxPin );
\r
81 * USB handler defined by the driver, installed after the CDC driver has been
\r
84 extern void USBD_IrqHandler( void );
\r
87 * The function that creates the CLI task.
\r
89 void vUSBCommandConsoleStart( uint16_t usStackSize, UBaseType_t uxPriority );
\r
92 * Send xDataLength bytes from pcData to the CDC port.
\r
94 static void prvCDCSend( const char *pcData, size_t xDataLenth );
\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
100 static void prvCDCGetChar( void );
\r
103 * Configure VBus pins and interrupts, and check for connection.
\r
105 static void prvConfigureVBus( void );
\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
111 static void prvCDCDataReceivedCallback( uint32_t ulUnused, uint8_t ucStatus, uint32_t ulBytesReceived, uint32_t ulBytesRemaining );
\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
117 static void prvCDCDataTransmittedCallback( uint32_t ulUnused, uint8_t ucStatus, uint32_t ulBytesSent, uint32_t ulBytesRemaining );
\r
120 * Keep trying to initiate an Rx until it is started successfully
\r
122 static void prvStartRx( void );
\r
124 /*-----------------------------------------------------------*/
\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
131 /* Buffer into which received characters are placed. */
\r
132 static char pcRxBuffer[ cmdMAX_INPUT_SIZE ];
\r
134 /* The number of bytes in pcRxBuffer that have not yet been read. */
\r
135 static uint32_t ulBytesAvailable = 0;
\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
141 /*-----------------------------------------------------------*/
\r
143 void vUSBCommandConsoleStart( uint16_t usStackSize, UBaseType_t uxPriority )
\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
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
158 /*-----------------------------------------------------------*/
\r
160 static void prvCDCCommandConsoleTask( void *pvParameters )
\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
168 ( void ) pvParameters;
\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
175 /* Initialise the CDC driver. */
\r
178 /* Start receiving into the buffer. */
\r
181 /* Send the welcome message. */
\r
182 prvCDCSend( pcWelcomeMessage, strlen( pcWelcomeMessage ) );
\r
186 /* Wait for my characters to be available. */
\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
194 /* Read next byte from the rx buffer. */
\r
195 cRxedChar = pcRxBuffer[ ulBufferIndex ];
\r
197 taskENTER_CRITICAL();
\r
199 ulBytesAvailable--;
\r
201 taskEXIT_CRITICAL();
\r
203 /* Echo the character back. */
\r
204 prvCDCSend( &cRxedChar, sizeof( cRxedChar ) );
\r
206 /* Was it the end of the line? */
\r
207 if( cRxedChar == '\n' || cRxedChar == '\r' )
\r
209 /* Just to space the output from the input. */
\r
210 prvCDCSend( pcNewLine, strlen( pcNewLine ) );
\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
216 /* Copy the last command back into the input string. */
\r
217 strcpy( cInputString, cLastInputString );
\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
226 /* Get the next output string from the command interpreter. */
\r
227 xReturned = FreeRTOS_CLIProcessCommand( cInputString, pcOutputString, configCOMMAND_INT_MAX_OUTPUT_SIZE );
\r
229 /* Write the generated string to the UART. */
\r
230 prvCDCSend( pcOutputString, strlen( pcOutputString ) );
\r
232 } while( xReturned != pdFALSE );
\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
240 memset( cInputString, 0x00, cmdMAX_INPUT_SIZE );
\r
242 prvCDCSend( pcEndOfOutputMessage, strlen( pcEndOfOutputMessage ) );
\r
246 if( cRxedChar == '\r' )
\r
248 /* Ignore the character. */
\r
250 else if( ( cRxedChar == '\b' ) || ( cRxedChar == cmdASCII_DEL ) )
\r
252 /* Backspace was pressed. Erase the last character in the
\r
253 string - if any. */
\r
254 if( ucInputIndex > 0 )
\r
257 cInputString[ ucInputIndex ] = '\0';
\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
267 if( ucInputIndex < cmdMAX_INPUT_SIZE )
\r
269 cInputString[ ucInputIndex ] = cRxedChar;
\r
276 /* Move onto the next byte the next time around. */
\r
278 if( ulBufferIndex >= cmdMAX_INPUT_SIZE )
\r
285 /*-----------------------------------------------------------*/
\r
287 static void prvCDCInit( void )
\r
289 extern WEAK const USBDDriverDescriptors cdcdSerialDriverDescriptors;
\r
291 /* If they are present, configure Vbus & Wake-up pins */
\r
292 PIO_InitializeInterrupts( 0 );
\r
294 /* CDC serial driver initialization */
\r
295 CDCDSerialDriver_Initialize( &cdcdSerialDriverDescriptors );
\r
297 /* Configure VBus pins and interrupts, and check for connection. */
\r
298 prvConfigureVBus();
\r
300 /*-----------------------------------------------------------*/
\r
302 static void prvCDCSend( const char *pcData, size_t xDataLength )
\r
304 const TickType_t xTransferCompleteDelay = pdMS_TO_TICKS( 500UL );
\r
307 ( void ) xDataLength;
\r
309 if( xDataLength > 0 )
\r
311 if( CDCDSerialDriver_Write( ( void * ) pcData, xDataLength, ( TransferCallback ) prvCDCDataTransmittedCallback, 0 ) == USBD_STATUS_SUCCESS )
\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
322 /*-----------------------------------------------------------*/
\r
324 static void prvStartRx( void )
\r
326 const TickType_t xFailedReadDelay = pdMS_TO_TICKS( 150UL );
\r
328 while( CDCDSerialDriver_Read( pcRxBuffer, cmdMAX_INPUT_SIZE, ( TransferCallback ) prvCDCDataReceivedCallback, 0 ) != USBD_STATUS_SUCCESS )
\r
330 /* Maybe the CDC is not connected. */
\r
331 vTaskDelay( xFailedReadDelay );
\r
334 /*-----------------------------------------------------------*/
\r
336 static void prvCDCGetChar( void )
\r
338 const TickType_t xTransferCompleteDelay = pdMS_TO_TICKS( 750UL );
\r
340 if( ulBytesAvailable == 0 )
\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
350 /*-----------------------------------------------------------*/
\r
352 static void prvVBusISRHandler( const Pin *pxPin )
\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
358 /* Check current level on VBus to detect a connect/disconnect. */
\r
359 if( PIO_Get( pxPin ) != 0 )
\r
368 /*-----------------------------------------------------------*/
\r
370 static void prvCDCDataReceivedCallback( uint32_t ulUnused, uint8_t ucStatus, uint32_t ulBytesReceived, uint32_t ulBytesRemaining )
\r
372 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
\r
373 static uint32_t ulNextRxPosition = 0;
\r
377 ( void ) ulBytesRemaining;
\r
379 /* If bytes were received then store the number of bytes placed into the Rx
\r
381 if( ucStatus == USBD_STATUS_SUCCESS )
\r
383 ulBytesAvailable += ulBytesReceived;
\r
385 /* Restart the Rx position from a buffer position past the newly
\r
387 ulNextRxPosition += ulBytesReceived;
\r
389 if( ulNextRxPosition >= cmdMAX_INPUT_SIZE )
\r
391 ulNextRxPosition = 0;
\r
393 CDCDSerialDriver_Read( pcRxBuffer + ulNextRxPosition, cmdMAX_INPUT_SIZE - ulNextRxPosition, ( TransferCallback ) prvCDCDataReceivedCallback, 0 );
\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
400 /*-----------------------------------------------------------*/
\r
402 static void prvCDCDataTransmittedCallback( uint32_t ulUnused, uint8_t ucStatus, uint32_t ulBytesSent, uint32_t ulBytesRemaining )
\r
404 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
\r
408 ( void ) ulBytesRemaining;
\r
409 ( void ) ulBytesSent;
\r
411 xEventGroupSetBitsFromISR( xCDCEventBits, cmdTX_COMPLETE_BIT, &xHigherPriorityTaskWoken );
\r
412 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
\r
414 /*-----------------------------------------------------------*/
\r
416 static void prvConfigureVBus( void )
\r
418 const Pin xVBusPin = PIN_USB_VBUS;
\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
425 /* Check current level on VBus */
\r
426 if( PIO_Get( &xVBusPin ) != pdFALSE )
\r
428 /* if VBUS present, force the connect */
\r
436 /*-----------------------------------------------------------*/
\r
438 void USBDCallbacks_Initialized( void )
\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
447 /*-----------------------------------------------------------*/
\r
449 void USBDDriverCallbacks_ConfigurationChanged( uint8_t ucConfigNumber )
\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
455 /*-----------------------------------------------------------*/
\r
457 void USBDCallbacks_RequestReceived( const USBGenericRequest *pxRequest )
\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
463 /*-----------------------------------------------------------*/
\r