/************************************************************************************ * this module is represents the access layer to USB STACK DRIVER * used to communication over USB protocol * the USB protocol implementation is inside the module no need for another includes * ======== USBCDCD.c ======== ************************************************************************************/ /* XDCtools Header files */ #include "include.h" #include #include #include /* BIOS Header files */ #include #include #include #include #include #include #include /* driverlib Header files */ #include #include #include #include #include #include #include /* usblib Header files */ #include #include #include #include #include #include #include #include "Drivers/USB_Communication/USBCDCD.h" #include #include "Common/Utilities/Utils.h" #include "Communication/CommunicationTask.h" #if defined(TIVAWARE) typedef uint32_t USBCDCDEventType; #else #define eUSBModeForceDevice USB_MODE_FORCE_DEVICE typedef unsigned long USBCDCDEventType; #endif /* Defines */ #define USBBUFFERSIZE 256 //**************************************************************************** // // A buffer into which the composite device can write the combined config // descriptor. // //**************************************************************************** #define DESCRIPTOR_BUFFER_SIZE (COMPOSITE_DDFU_SIZE + COMPOSITE_DCDC_SIZE) extern Semaphore_Handle updateSem; extern Semaphore_Handle ReconnectSem; uint8_t g_pui8DescriptorBuffer[DESCRIPTOR_BUFFER_SIZE]; /* Typedefs */ typedef volatile enum { USBCDCD_STATE_IDLE = 0, USBCDCD_STATE_INIT, USBCDCD_STATE_UNCONFIGURED } USBCDCD_USBState; /* Static variables and handles */ static volatile USBCDCD_USBState state; static unsigned char receiveBuffer[USBBUFFERSIZE]; static unsigned char transmitBuffer[USBBUFFERSIZE]; int expected_message_size; int current_message_size; static volatile uint32_t g_RxCount; //static GateMutex_Handle gateTxSerial; static GateMutex_Handle gateRxSerial; static GateMutex_Handle gateUSBWait; static Semaphore_Handle semTxSerial; //static Semaphore_Handle semRxSerial; static Semaphore_Handle semUSBConnected; extern Semaphore_Handle initConnectionSem; #define FLAG_STATUS_UPDATE 0 #define FLAG_USB_CONFIGURED 1 #define FLAG_SENDING_BREAK 2 static volatile uint32_t g_ui32Flags; /* Function prototypes */ /* static USBCDCDEventType cbRxHandler(void *cbData, USBCDCDEventType event, USBCDCDEventType _eventMsgData, void *eventMsgPtr); static USBCDCDEventType cbSerialHandler(void *cbData, USBCDCDEventType event, USBCDCDEventType _eventMsgData, void *eventMsgPtr); static USBCDCDEventType cbTxHandler(void *cbData, USBCDCDEventType event, USBCDCDEventType _eventMsgData, void *eventMsgPtr); */ uint32_t TxHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue, void *pvMsgData); uint32_t RxHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue,void *pvMsgData); uint32_t ControlHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue, void *pvMsgData); void USBCDCD_hwiHandler(UArg arg0); void USBCDCD_init(void); unsigned int USBCDCD_receiveData(unsigned char *_pBuff, unsigned int _length, unsigned int _timeout); bool USBCDCD_waitForConnect(unsigned int _timeout); /* The languages supported by this device. */ const unsigned char langDescriptor[] = { 4, USB_DTYPE_STRING, USBShort(USB_LANG_EN_US) }; /* The manufacturer string. */ const unsigned char manufacturerString[] = { (17 + 1) * 2, USB_DTYPE_STRING, 'T', 0, 'e', 0, 'x', 0, 'a', 0, 's', 0, ' ', 0, 'I', 0, 'n', 0, 's', 0, 't', 0, 'r', 0, 'u', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 's', 0, }; /* The product string. */ const unsigned char productString[] = { 2 + (15 * 2), USB_DTYPE_STRING, 'T', 0, 'w', 0, 'i', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'a', 0, 'n', 0, 'g', 0, 'o', 0, ' ', 0, 'U', 0, 'S', 0, 'B', 0, }; /* The serial number string. */ const unsigned char serialNumberString[] = { (8 + 1) * 2, USB_DTYPE_STRING, '2', 0, '2', 0, '2', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0 }; /* The interface description string. */ const unsigned char controlInterfaceString[] = { 2 + (21 * 2), USB_DTYPE_STRING, 'J', 0, 'i', 0, 'g', 0, ' ', 0, 'C', 0, '2', 0, 'n', 0, 't', 0, 'r', 0, 'o', 0, 'l', 0, ' ', 0, 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0 }; /* The configuration description string. */ const unsigned char configString[] = { 2 + (26 * 2), USB_DTYPE_STRING, 'S', 0, 'e', 0, 'l', 0, 'f', 0, ' ', 0, 'P', 0, 'o', 0, 'w', 0, 'e', 0, 'r', 0, 'e', 0, 'd', 0, ' ', 0, 'C', 0, 'o', 0, 'n', 0, 'f', 0, 'i', 0, 'g', 0, 'u', 0, 'r', 0, 'a', 0, 't', 0, 'i', 0, 'o', 0, 'n', 0 }; //***************************************************************************** // // The descriptor string table. // //***************************************************************************** const uint8_t * const g_ppui8StringDescriptors[] = { langDescriptor, manufacturerString, productString, serialNumberString, controlInterfaceString, configString }; #define NUM_STRING_DESCRIPTORS (sizeof(g_ppui8StringDescriptors) / \ sizeof(g_ppui8StringDescriptors[0])) /* The descriptor string table. */ const unsigned char * const stringDescriptors[] = { langDescriptor, manufacturerString, productString, serialNumberString, controlInterfaceString, configString }; #define STRINGDESCRIPTORSCOUNT (sizeof(stringDescriptors) / \ sizeof(unsigned char *)) tUSBBuffer txBuffer; tUSBBuffer rxBuffer; static tUSBDCDCDevice g_sCDCDevice; tUSBBuffer rxBuffer = { false, /* This is a receive buffer. */ RxHandler, /* pfnCallback */ (void *)&g_sCDCDevice, /* Callback data is our device pointer. */ USBDCDCPacketRead, /* pfnTransfer */ USBDCDCRxPacketAvailable, /* pfnAvailable */ (void *)&g_sCDCDevice, /* pvHandle */ receiveBuffer, /* pcBuffer */ USBBUFFERSIZE, /* ulBufferSize */ {{0, 0, 0, 0}, 0, 0} /* private data workspace */ }; tUSBBuffer txBuffer = { true, /* This is a transmit buffer. */ TxHandler, /* pfnCallback */ (void *)&g_sCDCDevice, /* Callback data is our device pointer. */ USBDCDCPacketWrite, /* pfnTransfer */ USBDCDCTxPacketAvailable, /* pfnAvailable */ (void *)&g_sCDCDevice, /* pvHandle */ transmitBuffer, /* pcBuffer */ USBBUFFERSIZE, /* ulBufferSize */ {{0, 0, 0, 0}, 0, 0} /* private data workspace */ }; static tUSBDCDCDevice g_sCDCDevice = { USB_VID_TI_1CBE, USB_PID_SERIAL, 0, USB_CONF_ATTR_SELF_PWR, ControlHandler, (void *)&g_sCDCDevice, USBBufferEventCallback, (void *)&rxBuffer, USBBufferEventCallback, (void *)&txBuffer, stringDescriptors, STRINGDESCRIPTORSCOUNT }; //static tLineCoding g_sLineCoding = { // 115200, /* 115200 baud rate. */ // 1, /* 1 Stop Bit. */ // 0, /* No Parity. */ // 8 /* 8 Bits of data. */ //}; //***************************************************************************** // //! Waits for a character from the USB port. //! //! This function gets a character from the USB receive buffer. //! If there are no characters available, this function waits until a //! character is received before returning. //! //! \return Returns the character read from the USB port. // //***************************************************************************** char USBGetChar(void) { uint8_t ucChar; int len = 0; len = USBCDCD_receiveData(&ucChar, 1, BIOS_WAIT_FOREVER); if (len !=1) { Task_sleep(1); len = USBCDCD_receiveData(&ucChar, 1, BIOS_WAIT_FOREVER); } // // Now return the char. // return (ucChar); } void USBFlush(void) { USBBufferFlush(&rxBuffer); } //***************************************************************************** // // Set the state of the RS232 RTS and DTR signals. // //***************************************************************************** void SetControlLineState(uint16_t ui16State) { // // TODO: If configured with GPIOs controlling the handshake lines, // set them appropriately depending upon the flags passed in the wValue // field of the request structure passed. // } //***************************************************************************** // // Get the communication parameters in use on the UART. // //***************************************************************************** void GetLineCoding(tLineCoding *psLineCoding) { psLineCoding->ui32Rate = 9600; psLineCoding->ui8Databits = 8; psLineCoding->ui8Parity = USB_CDC_PARITY_NONE; psLineCoding->ui8Stop = USB_CDC_STOP_BITS_1; } /******************************************************************** * ======== USBCDCD_hwiHandler ======== * This function calls the USB library's device interrupt handler. ********************************************************************/ void USBCDCD_hwiHandler(UArg arg0) { USB0DeviceIntHandler(); } //***************************************************************************** // // This function is called whenever serial data is received from the UART. // It is passed the accumulated error flags from each character received in // this interrupt and determines from them whether or not an interrupt // notification to the host is required. // // If a notification is required and the control interrupt endpoint is idle, // we send the notification immediately. If the endpoint is not idle, we // accumulate the errors in a global variable which will be checked on // completion of the previous notification and used to send a second one // if necessary. // //***************************************************************************** void CheckForSerialStateChange(const tUSBDCDCDevice *psDevice, uint32_t ui32Errors) { uint16_t ui16SerialState; // // Clear our USB serial state. Since we are faking the handshakes, always // set the TXCARRIER (DSR) and RXCARRIER (DCD) bits. // ui16SerialState = USB_CDC_SERIAL_STATE_TXCARRIER | USB_CDC_SERIAL_STATE_RXCARRIER; // // Are any error bits set? // if(ui32Errors) { // // At least one error is being notified so translate from our hardware // error bits into the correct state markers for the USB notification. // if(ui32Errors & UART_DR_OE) { ui16SerialState |= USB_CDC_SERIAL_STATE_OVERRUN; } if(ui32Errors & UART_DR_PE) { ui16SerialState |= USB_CDC_SERIAL_STATE_PARITY; } if(ui32Errors & UART_DR_FE) { ui16SerialState |= USB_CDC_SERIAL_STATE_FRAMING; } if(ui32Errors & UART_DR_BE) { ui16SerialState |= USB_CDC_SERIAL_STATE_BREAK; } // // Call the CDC driver to notify the state change. // USBDCDCSerialStateChange((void *)psDevice, ui16SerialState); } } //***************************************************************************** // // Handles CDC driver notifications related to control and setup of the device. // // \param pvCBData is the client-supplied callback pointer for this channel. // \param ulEvent identifies the event we are being notified about. // \param ulMsgValue is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the CDC driver to perform control-related // operations on behalf of the USB host. These functions include setting // and querying the serial communication parameters, setting handshake line // states and sending break conditions. // // \return The return value is event-specific. // //***************************************************************************** uint32_t ControlHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue, void *pvMsgData) { // // Which event are we being asked to process? // switch(ui32Event) { // // We are connected to a host and communication is now possible. // case USB_EVENT_CONNECTED: { // // Now connected and ready for normal operation. // HWREGBITW(&g_ui32Flags, FLAG_USB_CONFIGURED) = 1; SetCommunicationPath(isUSB); // // Flush our buffers. // USBBufferFlush(&txBuffer); USBBufferFlush(&rxBuffer); //TODO: Notify connection! // // Set the command status update flag. // HWREGBITW(&g_ui32Flags, FLAG_STATUS_UPDATE) = 1; break; } // // The host has disconnected. // case USB_EVENT_DISCONNECTED: { // // No longer connected. // HWREGBITW(&g_ui32Flags, FLAG_USB_CONFIGURED) = 0; //TODO: Notify disconnection! // // Set the command status update flag. // HWREGBITW(&g_ui32Flags, FLAG_STATUS_UPDATE) = 1; break; } // // Return the current serial communication parameters. // case USBD_CDC_EVENT_GET_LINE_CODING: { GetLineCoding(pvMsgData); break; } // // Set the current serial communication parameters. // case USBD_CDC_EVENT_SET_LINE_CODING: { GetLineCoding(pvMsgData); break; } // // Set the current serial communication parameters. // case USBD_CDC_EVENT_SET_CONTROL_LINE_STATE: { SetControlLineState((uint16_t)ui32MsgValue); break; } // // Send a break condition on the serial line. // case USBD_CDC_EVENT_SEND_BREAK: { break; } // // Clear the break condition on the serial line. // case USBD_CDC_EVENT_CLEAR_BREAK: { break; } // // Ignore SUSPEND and RESUME for now. // case USB_EVENT_SUSPEND: case USB_EVENT_RESUME: { break; } // // We don't expect to receive any other events. Ignore any that show // up in a release build or hang in a debug build. // default: { #ifdef DEBUG while(1); #else break; #endif } } return(0); } void handleRx(void) { uint32_t ui32Read; uint8_t ui8Char; uint8_t size[4]; int size_bar = 0; if (expected_message_size == 0) { do { ui32Read = USBBufferRead((tUSBBuffer *)&rxBuffer, &ui8Char, 1); if(ui32Read) { size[size_bar++] = ui8Char; } } while(size_bar < 4); expected_message_size = *(int *)size; initArray( expected_message_size); } do { ui32Read = USBBufferRead((tUSBBuffer *)&rxBuffer, &ui8Char, 1); // Did we get a character? if(ui32Read) { insertArray(ui8Char); current_message_size++; } if (current_message_size == expected_message_size) { g_RxCount += current_message_size; CommunicationTaskMessageReceived(g_RxCount); expected_message_size = 0; current_message_size = 0; break; } } while(ui32Read); } //***************************************************************************** // // Handles CDC driver notifications related to the transmit channel (data to // the USB host). // // \param pvCBData is the client-supplied callback pointer for this channel. // \param ui32Event identifies the event we are being notified about. // \param ui32MsgValue is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the CDC driver to notify us of any events // related to operation of the transmit data channel (the IN channel carrying // data to the USB host). // // \return The return value is event-specific. // //***************************************************************************** uint32_t TxHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue, void *pvMsgData) { // // Which event have we been sent? // switch(ui32Event) { case USB_EVENT_TX_COMPLETE: { // // Since we are using the USBBuffer, we don't need to do anything // here. // break; } // // We don't expect to receive any other events. Ignore any that show // up in a release build or hang in a debug build. // default: { #ifdef DEBUG while(1); #else break; #endif } } return(0); } //***************************************************************************** // // Handles CDC driver notifications related to the receive channel (data from // the USB host). // // \param pvCBData is the client-supplied callback data value for this channel. // \param ui32Event identifies the event we are being notified about. // \param ui32MsgValue is an event-specific value. // \param pvMsgData is an event-specific pointer. // // This function is called by the CDC driver to notify us of any events // related to operation of the receive data channel (the OUT channel carrying // data from the USB host). // // \return The return value is event-specific. // //***************************************************************************** uint32_t RxHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgValue,void *pvMsgData) { // // Which event are we being sent? // switch(ui32Event) { // // A new packet has been received. // case USB_EVENT_RX_AVAILABLE: { // // Feed some characters into the UART TX FIFO and enable the // interrupt so we are told when there is more space. // handleRx(); break; } // // We are being asked how much unprocessed data we have still to // process. We return 0 if the UART is currently idle or 1 if it is // in the process of transmitting something. The actual number of // bytes in the UART FIFO is not important here, merely whether or // not everything previously sent to us has been transmitted. // case USB_EVENT_DATA_REMAINING: { // // Get the number of bytes in the buffer and add 1 if some data // still has to clear the transmitter. return(0); } // // We are being asked to provide a buffer into which the next packet // can be read. We do not support this mode of receiving data so let // the driver know by returning 0. The CDC driver should not be sending // this message but this is included just for illustration and // completeness. // case USB_EVENT_REQUEST_BUFFER: { return(0); } // // We don't expect to receive any other events. Ignore any that show // up in a release build or hang in a debug build. // default: #ifdef DEBUG while(1); #else break; #endif } return(0); } //Execute this function on UART0 Interrupt; void InitUSB(void) { CheckForSerialStateChange(&g_sCDCDevice, 0); } //***************************************************************************** // // Interrupt handler for the UART which we are redirecting via USB. // //***************************************************************************** void USB0Handler(void) { InitUSB(); } void * USBDComposite = NULL; /******************************************************************* * ======== USBCDCD_init ======== *******************************************************************/ void USBCDCD_init(void) { Error_Block eb; Error_init(&eb); /* Install interrupt handler */ Hwi_Handle hwi; hwi = Hwi_create(INT_USB0, USBCDCD_hwiHandler, NULL, &eb); if (hwi == NULL) { System_abort("Can't create USB Hwi"); } /* RTOS primitives */ /*Semaphore_Params_init(&semParams); semParams.mode = Semaphore_Mode_BINARY; semTxSerial = Semaphore_create(0, &semParams, &eb); if (semTxSerial == NULL) { System_abort("Can't create TX semaphore"); } semRxSerial = Semaphore_create(0, &semParams, &eb); if (semRxSerial == NULL) { System_abort("Can't create RX semaphore"); } semUSBConnected = Semaphore_create(0, &semParams, &eb); if (semUSBConnected == NULL) { System_abort("Can't create USB semaphore"); } gateTxSerial = GateMutex_create(NULL, &eb); if (gateTxSerial == NULL) { System_abort("Can't create gate"); } gateRxSerial = GateMutex_create(NULL, &eb); if (gateRxSerial == NULL) { System_abort("Can't create gate"); } gateUSBWait = GateMutex_create(NULL, &eb); if (gateUSBWait == NULL) { System_abort("Could not create USB Wait gate"); }*/ /* State specific variables */ state = USBCDCD_STATE_UNCONFIGURED; /* Set the USB stack mode to Device mode with VBUS monitoring */ USBStackModeSet(0, eUSBModeForceDevice, 0); //should be done here only once for supporting firmware upgrade as composite devise //if done several times in different places doesn't work in the upgrade. USBBufferInit(&txBuffer); USBBufferInit(&rxBuffer); //if (!USBDCDCCompositeInit(0, &g_sCDCDevice, &(g_sCompDevice.psDevices[0]))) if (!USBDCDCInit(0, &g_sCDCDevice)) { System_abort("Error initializing the serial device"); } } //----------------------------------------------------------- void USBCDCD_Reinit(void) { Error_Block eb; Semaphore_Params semParams; Error_init(&eb); /* RTOS primitives */ Semaphore_Params_init(&semParams); semParams.mode = Semaphore_Mode_BINARY; semTxSerial = Semaphore_create(0, &semParams, &eb); if (semTxSerial == NULL) { System_abort("Can't create TX semaphore"); } /* State specific variables */ state = USBCDCD_STATE_UNCONFIGURED; /* Set the USB stack mode to Device mode with VBUS monitoring */ USBStackModeSet(0, eUSBModeForceDevice, 0); if (!USBDCDCInit(0, &g_sCDCDevice)) { System_abort("Error initializing the serial device"); } } //----------------------------------------------------------- void USBCDC_close(void) { // USBDCompositeTerm(USBDComposite); // USBCDCD_Reinit(); } /************************************************************** * ======== USBCDCD_receiveData ======== ************************************************************* */ unsigned int USBCDCD_receiveData(unsigned char *_pBuff, unsigned int _length, unsigned int _timeout) { unsigned int retValue = 0; unsigned int key; switch (state) { case USBCDCD_STATE_UNCONFIGURED: { USBCDCD_waitForConnect(_timeout); break; } case USBCDCD_STATE_INIT: { /* Acquire lock */ key = GateMutex_enter(gateRxSerial); state = USBCDCD_STATE_IDLE; handleRx(); //retValue = rxData(_pBuff, _length, _timeout); /* Release lock */ GateMutex_leave(gateRxSerial, key); break; } case USBCDCD_STATE_IDLE: { /* Acquire lock */ key = GateMutex_enter(gateRxSerial); handleRx(); //retValue = rxData(_pBuff, _length, _timeout); /* Release lock */ GateMutex_leave(gateRxSerial, key); break; } default: break; } return (retValue); } /******************************************************* * ======== USBCDCD_sendData ======== *******************************************************/ unsigned int USBCDCD_sendData(const unsigned char *_pBuff, unsigned int _length, unsigned int _timeout) { uint8_t size[4]; size[3] = (_length>>24) & 0xFF; size[2] = (_length>>16) & 0xFF; size[1] = (_length>>8) & 0xFF; size[0] = _length & 0xFF; USBBufferWrite((tUSBBuffer *)&txBuffer, size, 4); return USBBufferWrite((tUSBBuffer *)&txBuffer, (uint8_t*)_pBuff, _length); } /************************************************ * ======== USBCDCD_waitForConnect ======== *************************************************/ bool USBCDCD_waitForConnect(unsigned int _timeout) { bool ret = true; unsigned int key; // Need exclusive access to prevent a race condition key = GateMutex_enter(gateUSBWait); if (state == USBCDCD_STATE_UNCONFIGURED) { if (!Semaphore_pend(semUSBConnected, _timeout)) { ret = false; } } GateMutex_leave(gateUSBWait, key); return (ret); }