mirror of https://gerrit.osmocom.org/simtrace2
456 lines
16 KiB
C
456 lines
16 KiB
C
/* ----------------------------------------------------------------------------
|
|
* ATMEL Microcontroller Software Support
|
|
* ----------------------------------------------------------------------------
|
|
* Copyright (c) 2008, Atmel Corporation
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the disclaimer below.
|
|
*
|
|
* Atmel's name may not be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**\file
|
|
* Implementation of the CDCDSerialPort class methods.
|
|
*/
|
|
|
|
/** \addtogroup usbd_cdc
|
|
*@{
|
|
*/
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Headers
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
#include <CDCDSerialPort.h>
|
|
#include <CDCDescriptors.h>
|
|
#include <USBLib_Trace.h>
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Types
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
/** Parse data extention for descriptor parsing */
|
|
typedef struct _CDCDParseData {
|
|
/** Pointer to CDCDSerialPort instance */
|
|
CDCDSerialPort * pCdcd;
|
|
/** Pointer to found interface descriptor */
|
|
USBInterfaceDescriptor * pIfDesc;
|
|
|
|
} CDCDParseData;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Internal variables
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
/** Line coding values */
|
|
static CDCLineCoding lineCoding;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Internal functions
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Parse descriptors: Interface, Bulk IN/OUT, Interrupt IN.
|
|
* \param desc Pointer to descriptor list.
|
|
* \param arg Argument, pointer to AUDDParseData instance.
|
|
*/
|
|
static uint32_t _Interfaces_Parse(USBGenericDescriptor *pDesc,
|
|
CDCDParseData * pArg)
|
|
{
|
|
CDCDSerialPort *pCdcd = pArg->pCdcd;
|
|
|
|
/* Not a valid descriptor */
|
|
if (pDesc->bLength == 0)
|
|
return USBRC_PARAM_ERR;
|
|
|
|
/* Find interface descriptor */
|
|
if (pDesc->bDescriptorType == USBGenericDescriptor_INTERFACE) {
|
|
USBInterfaceDescriptor *pIf = (USBInterfaceDescriptor*)pDesc;
|
|
|
|
/* Obtain interface from descriptor */
|
|
if (pCdcd->bInterfaceNdx == 0xFF) {
|
|
/* First interface is communication */
|
|
if (pIf->bInterfaceClass ==
|
|
CDCCommunicationInterfaceDescriptor_CLASS) {
|
|
pCdcd->bInterfaceNdx = pIf->bInterfaceNumber;
|
|
pCdcd->bNumInterface = 2;
|
|
}
|
|
/* Only data interface */
|
|
else if(pIf->bInterfaceClass == CDCDataInterfaceDescriptor_CLASS) {
|
|
pCdcd->bInterfaceNdx = pIf->bInterfaceNumber;
|
|
pCdcd->bNumInterface = 1;
|
|
}
|
|
pArg->pIfDesc = pIf;
|
|
}
|
|
else if (pCdcd->bInterfaceNdx <= pIf->bInterfaceNumber
|
|
&& pCdcd->bInterfaceNdx + pCdcd->bNumInterface
|
|
> pIf->bInterfaceNumber) {
|
|
pArg->pIfDesc = pIf;
|
|
}
|
|
}
|
|
|
|
/* Parse valid interfaces */
|
|
if (pArg->pIfDesc == 0)
|
|
return 0;
|
|
|
|
/* Find endpoint descriptors */
|
|
if (pDesc->bDescriptorType == USBGenericDescriptor_ENDPOINT) {
|
|
USBEndpointDescriptor *pEp = (USBEndpointDescriptor*)pDesc;
|
|
switch(pEp->bmAttributes & 0x3) {
|
|
case USBEndpointDescriptor_INTERRUPT:
|
|
if (pEp->bEndpointAddress & 0x80)
|
|
pCdcd->bIntInPIPE = pEp->bEndpointAddress & 0x7F;
|
|
break;
|
|
case USBEndpointDescriptor_BULK:
|
|
if (pEp->bEndpointAddress & 0x80)
|
|
pCdcd->bBulkInPIPE = pEp->bEndpointAddress & 0x7F;
|
|
else
|
|
pCdcd->bBulkOutPIPE = pEp->bEndpointAddress;
|
|
}
|
|
}
|
|
|
|
if ( pCdcd->bInterfaceNdx != 0xFF
|
|
&& pCdcd->bBulkInPIPE != 0
|
|
&& pCdcd->bBulkOutPIPE != 0)
|
|
return USBRC_FINISHED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Callback function which should be invoked after the data of a
|
|
* SetLineCoding request has been retrieved. Sends a zero-length packet
|
|
* to the host for acknowledging the request.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
*/
|
|
static void _SetLineCodingCallback(CDCDSerialPort * pCdcd)
|
|
{
|
|
uint32_t exec = 1;
|
|
if (pCdcd->fEventHandler) {
|
|
uint32_t rc = pCdcd->fEventHandler(
|
|
CDCDSerialPortEvent_SETLINECODING,
|
|
(uint32_t)(&lineCoding),
|
|
pCdcd->pArg);
|
|
if (rc == USBD_STATUS_SUCCESS) {
|
|
pCdcd->lineCoding.dwDTERate = lineCoding.dwDTERate;
|
|
pCdcd->lineCoding.bCharFormat = lineCoding.bCharFormat;
|
|
pCdcd->lineCoding.bParityType = lineCoding.bParityType;
|
|
pCdcd->lineCoding.bDataBits = lineCoding.bDataBits;
|
|
}
|
|
else
|
|
exec = 0;
|
|
}
|
|
if (exec) USBD_Write(0, 0, 0, 0, 0);
|
|
else USBD_Stall(0);
|
|
}
|
|
|
|
/**
|
|
* Receives new line coding information from the USB host.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
*/
|
|
static void _SetLineCoding(CDCDSerialPort * pCdcd)
|
|
{
|
|
TRACE_INFO_WP("sLineCoding ");
|
|
|
|
USBD_Read(0,
|
|
(void *) & (lineCoding),
|
|
sizeof(CDCLineCoding),
|
|
(TransferCallback)_SetLineCodingCallback,
|
|
(void*)pCdcd);
|
|
}
|
|
|
|
/**
|
|
* Sends the current line coding information to the host through Control
|
|
* endpoint 0.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
*/
|
|
static void _GetLineCoding(CDCDSerialPort * pCdcd)
|
|
{
|
|
TRACE_INFO_WP("gLineCoding ");
|
|
|
|
USBD_Write(0,
|
|
(void *) &(pCdcd->lineCoding),
|
|
sizeof(CDCLineCoding),
|
|
0,
|
|
0);
|
|
}
|
|
|
|
/**
|
|
* Changes the state of the serial driver according to the information
|
|
* sent by the host via a SetControlLineState request, and acknowledges
|
|
* the request with a zero-length packet.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
* \param request Pointer to a USBGenericRequest instance.
|
|
*/
|
|
static void _SetControlLineState(
|
|
CDCDSerialPort * pCdcd,
|
|
const USBGenericRequest *request)
|
|
{
|
|
uint8_t DTR, RTS;
|
|
|
|
DTR = ((request->wValue & CDCControlLineState_DTR) > 0);
|
|
RTS = ((request->wValue & CDCControlLineState_RTS) > 0);
|
|
TRACE_INFO_WP("sControlLineState(%d, %d) ", DTR, RTS);
|
|
|
|
pCdcd->bControlLineState = (uint8_t)request->wValue;
|
|
USBD_Write(0, 0, 0, 0, 0);
|
|
|
|
if (pCdcd->fEventHandler)
|
|
pCdcd->fEventHandler(CDCDSerialPortEvent_SETCONTROLLINESTATE,
|
|
|
|
(uint32_t)pCdcd->bControlLineState,
|
|
pCdcd->pArg);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------
|
|
* Exported functions
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Initializes the USB Device CDC serial port function.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
* \param pUsbd Pointer to USBDDriver instance.
|
|
* \param fEventHandler Pointer to event handler function.
|
|
* \param firstInterface First interface index for the function
|
|
* (0xFF to parse from descriptors).
|
|
* \param numInterface Number of interfaces for the function.
|
|
*/
|
|
void CDCDSerialPort_Initialize(CDCDSerialPort * pCdcd,
|
|
USBDDriver * pUsbd,
|
|
CDCDSerialPortEventHandler fEventHandler,
|
|
void * pArg,
|
|
uint8_t firstInterface,uint8_t numInterface)
|
|
{
|
|
TRACE_INFO("CDCDSerialPort_Initialize\n\r");
|
|
|
|
/* Initialize event handler */
|
|
pCdcd->fEventHandler = fEventHandler;
|
|
pCdcd->pArg = pArg;
|
|
|
|
/* Initialize USB Device Driver interface */
|
|
pCdcd->pUsbd = pUsbd;
|
|
pCdcd->bInterfaceNdx = firstInterface;
|
|
pCdcd->bNumInterface = numInterface;
|
|
pCdcd->bIntInPIPE = 0;
|
|
pCdcd->bBulkInPIPE = 0;
|
|
pCdcd->bBulkOutPIPE = 0;
|
|
|
|
/* Initialize Abstract Control Model attributes */
|
|
pCdcd->bControlLineState = 0;
|
|
pCdcd->wSerialState = 0;
|
|
CDCLineCoding_Initialize(&(pCdcd->lineCoding),
|
|
115200,
|
|
CDCLineCoding_ONESTOPBIT,
|
|
CDCLineCoding_NOPARITY,
|
|
8);
|
|
}
|
|
|
|
/**
|
|
* Parse CDC Serial Port information for CDCDSerialPort instance.
|
|
* Accepted interfaces:
|
|
* - Communication Interface + Data Interface
|
|
* - Data Interface ONLY
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
* \param pDescriptors Pointer to descriptor list.
|
|
* \param dwLength Descriptor list size in bytes.
|
|
*/
|
|
USBGenericDescriptor *CDCDSerialPort_ParseInterfaces(
|
|
CDCDSerialPort *pCdcd,
|
|
USBGenericDescriptor *pDescriptors,
|
|
uint32_t dwLength)
|
|
{
|
|
CDCDParseData parseData;
|
|
|
|
parseData.pCdcd = pCdcd;
|
|
parseData.pIfDesc = 0;
|
|
|
|
return USBGenericDescriptor_Parse(
|
|
pDescriptors, dwLength,
|
|
(USBDescriptorParseFunction)_Interfaces_Parse,
|
|
&parseData);
|
|
}
|
|
|
|
|
|
/**
|
|
* Handles CDC-specific SETUP requests. Should be called from a
|
|
* re-implementation of USBDCallbacks_RequestReceived() method.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
* \param request Pointer to a USBGenericRequest instance.
|
|
* \return USBRC_SUCCESS if request handled, otherwise error.
|
|
*/
|
|
uint32_t CDCDSerialPort_RequestHandler(
|
|
CDCDSerialPort *pCdcd,
|
|
const USBGenericRequest *request)
|
|
{
|
|
if (USBGenericRequest_GetType(request) != USBGenericRequest_CLASS)
|
|
return USBRC_PARAM_ERR;
|
|
|
|
TRACE_INFO_WP("Cdcs ");
|
|
|
|
/* Validate interface */
|
|
if (request->wIndex >= pCdcd->bInterfaceNdx &&
|
|
request->wIndex < pCdcd->bInterfaceNdx + pCdcd->bNumInterface) {
|
|
}
|
|
else {
|
|
return USBRC_PARAM_ERR;
|
|
}
|
|
|
|
/* Handle the request */
|
|
switch (USBGenericRequest_GetRequest(request)) {
|
|
|
|
case CDCGenericRequest_SETLINECODING:
|
|
|
|
_SetLineCoding(pCdcd);
|
|
break;
|
|
|
|
case CDCGenericRequest_GETLINECODING:
|
|
|
|
_GetLineCoding(pCdcd);
|
|
break;
|
|
|
|
case CDCGenericRequest_SETCONTROLLINESTATE:
|
|
|
|
_SetControlLineState(pCdcd, request);
|
|
break;
|
|
|
|
default:
|
|
|
|
return USBRC_PARAM_ERR;
|
|
}
|
|
|
|
return USBRC_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Receives data from the host through the virtual COM port created by
|
|
* the CDC device serial driver. This function behaves like USBD_Read.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
* \param pData Pointer to the data buffer to put received data.
|
|
* \param dwSize Size of the data buffer in bytes.
|
|
* \param fCallback Optional callback function to invoke when the transfer
|
|
* finishes.
|
|
* \param pArg Optional argument to the callback function.
|
|
* \return USBD_STATUS_SUCCESS if the read operation has been started normally;
|
|
* otherwise, the corresponding error code.
|
|
*/
|
|
uint32_t CDCDSerialPort_Read(const CDCDSerialPort * pCdcd,
|
|
void * pData,uint32_t dwSize,
|
|
TransferCallback fCallback,void * pArg)
|
|
{
|
|
if (pCdcd->bBulkOutPIPE == 0)
|
|
return USBRC_PARAM_ERR;
|
|
|
|
return USBD_Read(pCdcd->bBulkOutPIPE,
|
|
pData, dwSize,
|
|
fCallback, pArg);
|
|
}
|
|
|
|
/**
|
|
* Sends a data buffer through the virtual COM port created by the CDC
|
|
* device serial driver. This function behaves exactly like USBD_Write.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
* \param pData Pointer to the data buffer to send.
|
|
* \param dwSize Size of the data buffer in bytes.
|
|
* \param fCallback Optional callback function to invoke when the transfer
|
|
* finishes.
|
|
* \param pArg Optional argument to the callback function.
|
|
* \return USBD_STATUS_SUCCESS if the read operation has been started normally;
|
|
* otherwise, the corresponding error code.
|
|
*/
|
|
uint32_t CDCDSerialPort_Write(const CDCDSerialPort * pCdcd,
|
|
void * pData, uint32_t dwSize,
|
|
TransferCallback fCallback, void * pArg)
|
|
{
|
|
if (pCdcd->bBulkInPIPE == 0)
|
|
return USBRC_PARAM_ERR;
|
|
|
|
return USBD_Write(pCdcd->bBulkInPIPE,
|
|
pData, dwSize,
|
|
fCallback, pArg);
|
|
}
|
|
|
|
/**
|
|
* Returns the current control line state of the RS-232 line.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
*/
|
|
uint8_t CDCDSerialPort_GetControlLineState(const CDCDSerialPort * pCdcd)
|
|
{
|
|
return pCdcd->bControlLineState;
|
|
}
|
|
|
|
/**
|
|
* Copy current line coding settings to pointered space.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
* \param pLineCoding Pointer to CDCLineCoding instance.
|
|
*/
|
|
void CDCDSerialPort_GetLineCoding(const CDCDSerialPort * pCdcd,
|
|
CDCLineCoding* pLineCoding)
|
|
{
|
|
if (pLineCoding) {
|
|
pLineCoding->dwDTERate = pCdcd->lineCoding.dwDTERate;
|
|
pLineCoding->bCharFormat = pCdcd->lineCoding.bCharFormat;
|
|
pLineCoding->bParityType = pCdcd->lineCoding.bParityType;
|
|
pLineCoding->bDataBits = pCdcd->lineCoding.bDataBits;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the current status of the RS-232 line.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
*/
|
|
uint16_t CDCDSerialPort_GetSerialState(const CDCDSerialPort * pCdcd)
|
|
{
|
|
return pCdcd->wSerialState;
|
|
}
|
|
|
|
/**
|
|
* Sets the current serial state of the device to the given value.
|
|
* \param pCdcd Pointer to CDCDSerialPort instance.
|
|
* \param wSerialState New device state.
|
|
*/
|
|
void CDCDSerialPort_SetSerialState(CDCDSerialPort * pCdcd,
|
|
uint16_t wSerialState)
|
|
{
|
|
if (pCdcd->bIntInPIPE == 0)
|
|
return;
|
|
|
|
/* If new state is different from previous one, send a notification to the
|
|
host */
|
|
if (pCdcd->wSerialState != wSerialState) {
|
|
|
|
pCdcd->wSerialState = wSerialState;
|
|
USBD_Write(pCdcd->bIntInPIPE,
|
|
&(pCdcd->wSerialState),
|
|
2,
|
|
0,
|
|
0);
|
|
|
|
/* Reset one-time flags */
|
|
pCdcd->wSerialState &= ~(CDCSerialState_OVERRUN
|
|
| CDCSerialState_PARITY
|
|
| CDCSerialState_FRAMING
|
|
| CDCSerialState_RINGSIGNAL
|
|
| CDCSerialState_BREAK);
|
|
}
|
|
}
|
|
|
|
/**@}*/
|
|
|