kopia lustrzana https://github.com/OpenRTX/OpenRTX
481 wiersze
16 KiB
C
481 wiersze
16 KiB
C
/***************************************************************************
|
|
* Copyright (C) 2020 - 2023 by Silvano Seva IU2KWO *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 3 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
|
***************************************************************************/
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include "usbd_core.h"
|
|
#include "usb_defines.h"
|
|
#include "usbd_desc.h"
|
|
#include "usbd_cdc_core.h"
|
|
#include "usbd_usr.h"
|
|
#include "usbd_req.h"
|
|
#include "stm32f4xx.h"
|
|
|
|
#include <interfaces/delays.h>
|
|
#include "usb_vcom.h"
|
|
|
|
/* Common USB OTG handle, also defined as 'extern' in other modules */
|
|
USB_OTG_CORE_HANDLE USB_OTG_dev;
|
|
|
|
/* 'Service' variables: command buffer, ... */
|
|
extern uint8_t USBD_DeviceDesc [USB_SIZ_DEVICE_DESC];
|
|
uint8_t CmdBuff[CDC_CMD_PACKET_SZE];
|
|
static uint32_t cdcCmd = 0xFF;
|
|
static uint32_t cdcLen = 0;
|
|
static __IO uint32_t usbd_cdc_AltSet = 0;
|
|
|
|
/* Buffer for OUT endpoint, the one receiving data from host */
|
|
uint8_t outEnpBuffer[CDC_DATA_OUT_PACKET_SIZE];
|
|
|
|
/* Circular buffer for incoming data enqueuement: each packet coming from host
|
|
* is stored here, eventually erasing oldest data.
|
|
* Buffer is statically allocated.
|
|
*/
|
|
struct rb
|
|
{
|
|
uint8_t data[RX_RING_BUF_SIZE];
|
|
size_t readPtr;
|
|
size_t writePtr;
|
|
}
|
|
rxRingBuf;
|
|
|
|
bool txDone; /* Flag for end of data transmission. */
|
|
|
|
/* USB CDC device Configuration Descriptor */
|
|
uint8_t usbd_cdc_CfgDesc[USB_CDC_CONFIG_DESC_SIZ] =
|
|
{
|
|
/*Configuration Descriptor*/
|
|
0x09, /* bLength: Configuration Descriptor size */
|
|
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
|
|
USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */
|
|
0x00,
|
|
0x02, /* bNumInterfaces: 2 interface */
|
|
0x01, /* bConfigurationValue: Configuration value */
|
|
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
|
|
0xC0, /* bmAttributes: self powered */
|
|
0x32, /* MaxPower 0 mA */
|
|
|
|
/*Interface Descriptor */
|
|
0x09, /* bLength: Interface Descriptor size */
|
|
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */
|
|
/* Interface descriptor type */
|
|
0x00, /* bInterfaceNumber: Number of Interface */
|
|
0x00, /* bAlternateSetting: Alternate setting */
|
|
0x01, /* bNumEndpoints: One endpoints used */
|
|
0x02, /* bInterfaceClass: Communication Interface Class */
|
|
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
|
0x01, /* bInterfaceProtocol: Common AT commands */
|
|
0x00, /* iInterface: */
|
|
|
|
/*Header Functional Descriptor*/
|
|
0x05, /* bLength: Endpoint Descriptor size */
|
|
0x24, /* bDescriptorType: CS_INTERFACE */
|
|
0x00, /* bDescriptorSubtype: Header Func Desc */
|
|
0x10, /* bcdCDC: spec release number */
|
|
0x01,
|
|
|
|
/*Call Management Functional Descriptor*/
|
|
0x05, /* bFunctionLength */
|
|
0x24, /* bDescriptorType: CS_INTERFACE */
|
|
0x01, /* bDescriptorSubtype: Call Management Func Desc */
|
|
0x00, /* bmCapabilities: D0+D1 */
|
|
0x01, /* bDataInterface: 1 */
|
|
|
|
/*ACM Functional Descriptor*/
|
|
0x04, /* bFunctionLength */
|
|
0x24, /* bDescriptorType: CS_INTERFACE */
|
|
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
|
|
0x02, /* bmCapabilities */
|
|
|
|
/*Union Functional Descriptor*/
|
|
0x05, /* bFunctionLength */
|
|
0x24, /* bDescriptorType: CS_INTERFACE */
|
|
0x06, /* bDescriptorSubtype: Union func desc */
|
|
0x00, /* bMasterInterface: Communication class interface */
|
|
0x01, /* bSlaveInterface0: Data Class Interface */
|
|
|
|
/*Endpoint 2 Descriptor*/
|
|
0x07, /* bLength: Endpoint Descriptor size */
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
|
|
CDC_CMD_EP, /* bEndpointAddress */
|
|
0x03, /* bmAttributes: Interrupt */
|
|
LOBYTE(CDC_CMD_PACKET_SZE), /* wMaxPacketSize: */
|
|
HIBYTE(CDC_CMD_PACKET_SZE),
|
|
0x10, /* bInterval: */
|
|
|
|
/*Data class interface descriptor*/
|
|
0x09, /* bLength: Endpoint Descriptor size */
|
|
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: */
|
|
0x01, /* bInterfaceNumber: Number of Interface */
|
|
0x00, /* bAlternateSetting: Alternate setting */
|
|
0x02, /* bNumEndpoints: Two endpoints used */
|
|
0x0A, /* bInterfaceClass: CDC */
|
|
0x00, /* bInterfaceSubClass: */
|
|
0x00, /* bInterfaceProtocol: */
|
|
0x00, /* iInterface: */
|
|
|
|
/*Endpoint OUT Descriptor*/
|
|
0x07, /* bLength: Endpoint Descriptor size */
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
|
|
CDC_OUT_EP, /* bEndpointAddress */
|
|
0x02, /* bmAttributes: Bulk */
|
|
LOBYTE(CDC_DATA_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
|
HIBYTE(CDC_DATA_MAX_PACKET_SIZE),
|
|
0x00, /* bInterval: ignore for Bulk transfer */
|
|
|
|
/*Endpoint IN Descriptor*/
|
|
0x07, /* bLength: Endpoint Descriptor size */
|
|
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
|
|
CDC_IN_EP, /* bEndpointAddress */
|
|
0x02, /* bmAttributes: Bulk */
|
|
LOBYTE(CDC_DATA_MAX_PACKET_SIZE), /* wMaxPacketSize: */
|
|
HIBYTE(CDC_DATA_MAX_PACKET_SIZE),
|
|
0x00 /* bInterval: ignore for Bulk transfer */
|
|
};
|
|
|
|
/* The following structures groups all needed parameters to be configured for
|
|
* the ComPort. These parameters can modified on the fly by the host through
|
|
* CDC class command class requests.
|
|
*/
|
|
typedef struct
|
|
{
|
|
uint32_t bitrate;
|
|
uint8_t format;
|
|
uint8_t paritytype;
|
|
uint8_t datatype;
|
|
uint8_t changed;
|
|
}LINE_CODING;
|
|
|
|
/* USB virtual com settings: 115200 baud, 8n1 */
|
|
LINE_CODING linecoding =
|
|
{
|
|
115200, /* baud rate */
|
|
0x00, /* stop bits-1 */
|
|
0x00, /* parity - none */
|
|
0x08, /* nb. of bits 8 */
|
|
1 /* Changed flag */
|
|
};
|
|
|
|
/* USB CDC callbacks */
|
|
static uint8_t usbd_cdc_Init (void *pdev, uint8_t cfgidx);
|
|
static uint8_t usbd_cdc_DeInit (void *pdev, uint8_t cfgidx);
|
|
static uint8_t usbd_cdc_Setup (void *pdev, USB_SETUP_REQ *req);
|
|
static uint8_t usbd_cdc_EP0_RxReady (void *pdev);
|
|
static uint8_t usbd_cdc_DataIn (void *pdev, uint8_t epnum);
|
|
static uint8_t usbd_cdc_DataOut (void *pdev, uint8_t epnum);
|
|
static uint8_t usbd_cdc_SOF (void *pdev);
|
|
static uint8_t *USBD_cdc_GetCfgDesc (uint8_t speed, uint16_t *length);
|
|
|
|
USBD_Class_cb_TypeDef USBD_CDC_cb =
|
|
{
|
|
usbd_cdc_Init,
|
|
usbd_cdc_DeInit,
|
|
usbd_cdc_Setup,
|
|
NULL, /* EP0_TxSent, */
|
|
usbd_cdc_EP0_RxReady,
|
|
usbd_cdc_DataIn,
|
|
usbd_cdc_DataOut,
|
|
usbd_cdc_SOF,
|
|
NULL,
|
|
NULL,
|
|
USBD_cdc_GetCfgDesc
|
|
};
|
|
|
|
/* 'Service' function for CDC callbacks */
|
|
static uint16_t VCP_Ctrl (uint32_t Cmd, uint8_t* Buf, uint32_t Len);
|
|
|
|
/******************************************************************************
|
|
* *
|
|
* Implementation of USB vcom functions *
|
|
* *
|
|
******************************************************************************/
|
|
|
|
int vcom_init()
|
|
{
|
|
rxRingBuf.readPtr = 0;
|
|
rxRingBuf.writePtr = 0;
|
|
|
|
USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_CDC_cb,
|
|
&USR_cb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
ssize_t vcom_writeBlock(const void* buf, size_t len)
|
|
{
|
|
txDone = false;
|
|
DCD_EP_Tx (&USB_OTG_dev, CDC_IN_EP, (uint8_t*) buf, len);
|
|
|
|
uint16_t timeout = 0;
|
|
|
|
while(!txDone)
|
|
{
|
|
delayMs(1);
|
|
timeout++;
|
|
if(timeout > 500)
|
|
{
|
|
DCD_EP_Flush(&USB_OTG_dev, CDC_IN_EP);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
ssize_t vcom_readBlock(void* buf, size_t len)
|
|
{
|
|
uint8_t *b = ((uint8_t *) buf);
|
|
size_t i;
|
|
for(i = 0; i < len; i++)
|
|
{
|
|
/* Terminate if all data available has been popped out */
|
|
if(rxRingBuf.readPtr == rxRingBuf.writePtr) break;
|
|
b[i] = rxRingBuf.data[rxRingBuf.readPtr];
|
|
rxRingBuf.readPtr = (rxRingBuf.readPtr + 1)%RX_RING_BUF_SIZE;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* *
|
|
* Implementation of USB CDC callbacks *
|
|
* *
|
|
******************************************************************************/
|
|
|
|
static uint8_t usbd_cdc_Init (void *pdev, uint8_t cfgidx)
|
|
{
|
|
(void) cfgidx;
|
|
uint8_t *pbuf;
|
|
|
|
/* Open EP IN */
|
|
DCD_EP_Open(pdev, CDC_IN_EP, CDC_DATA_IN_PACKET_SIZE, USB_OTG_EP_BULK);
|
|
|
|
/* Open EP OUT */
|
|
DCD_EP_Open(pdev, CDC_OUT_EP, CDC_DATA_OUT_PACKET_SIZE, USB_OTG_EP_BULK);
|
|
|
|
/* Open Command IN EP */
|
|
DCD_EP_Open(pdev, CDC_CMD_EP, CDC_CMD_PACKET_SZE, USB_OTG_EP_INT);
|
|
|
|
pbuf = (uint8_t *)USBD_DeviceDesc;
|
|
pbuf[4] = DEVICE_CLASS_CDC;
|
|
pbuf[5] = DEVICE_SUBCLASS_CDC;
|
|
|
|
/* Prepare Out endpoint to receive next packet */
|
|
DCD_EP_PrepareRx(pdev, CDC_OUT_EP, outEnpBuffer, CDC_DATA_OUT_PACKET_SIZE);
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
static uint8_t usbd_cdc_DeInit (void *pdev, uint8_t cfgidx)
|
|
{
|
|
(void) cfgidx;
|
|
|
|
/* Open EP IN */
|
|
DCD_EP_Close(pdev, CDC_IN_EP);
|
|
|
|
/* Open EP OUT */
|
|
DCD_EP_Close(pdev, CDC_OUT_EP);
|
|
|
|
/* Open Command IN EP */
|
|
DCD_EP_Close(pdev,CDC_CMD_EP);
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
static uint8_t usbd_cdc_Setup (void *pdev, USB_SETUP_REQ *req)
|
|
{
|
|
uint16_t len=USB_CDC_DESC_SIZ;
|
|
uint8_t *pbuf=usbd_cdc_CfgDesc + 9;
|
|
|
|
switch (req->bmRequest & USB_REQ_TYPE_MASK)
|
|
{
|
|
/* CDC Class Requests -------------------------------*/
|
|
case USB_REQ_TYPE_CLASS :
|
|
/* Check if the request is a data setup packet */
|
|
if (req->wLength)
|
|
{
|
|
/* Check if the request is Device-to-Host */
|
|
if (req->bmRequest & 0x80)
|
|
{
|
|
/* Get the data to be sent to Host from interface layer */
|
|
VCP_Ctrl(req->bRequest, CmdBuff, req->wLength);
|
|
|
|
/* Send the data to the host */
|
|
USBD_CtlSendData (pdev, CmdBuff, req->wLength);
|
|
}
|
|
else /* Host-to-Device requeset */
|
|
{
|
|
/* Set the value of the current command to be processed */
|
|
cdcCmd = req->bRequest;
|
|
cdcLen = req->wLength;
|
|
|
|
/* Prepare the reception of the buffer over EP0
|
|
Next step: the received data will be managed in
|
|
usbd_cdc_EP0_TxSent() function.
|
|
*/
|
|
USBD_CtlPrepareRx (pdev, CmdBuff, req->wLength);
|
|
}
|
|
}
|
|
else /* No Data request */
|
|
{
|
|
/* Transfer the command to the interface layer */
|
|
VCP_Ctrl(req->bRequest, NULL, 0);
|
|
}
|
|
|
|
return USBD_OK;
|
|
|
|
default:
|
|
USBD_CtlError (pdev, req);
|
|
return USBD_FAIL;
|
|
|
|
case USB_REQ_TYPE_STANDARD:
|
|
switch (req->bRequest)
|
|
{
|
|
case USB_REQ_GET_DESCRIPTOR:
|
|
if( (req->wValue >> 8) == CDC_DESCRIPTOR_TYPE)
|
|
{
|
|
pbuf = usbd_cdc_CfgDesc + 9 + (9 * USBD_ITF_MAX_NUM);
|
|
len = MIN(USB_CDC_DESC_SIZ , req->wLength);
|
|
}
|
|
|
|
USBD_CtlSendData (pdev, pbuf, len);
|
|
break;
|
|
|
|
case USB_REQ_GET_INTERFACE:
|
|
USBD_CtlSendData (pdev, (uint8_t *)&usbd_cdc_AltSet, 1);
|
|
break;
|
|
|
|
case USB_REQ_SET_INTERFACE :
|
|
if ((uint8_t)(req->wValue) < USBD_ITF_MAX_NUM)
|
|
{
|
|
usbd_cdc_AltSet = (uint8_t)(req->wValue);
|
|
}
|
|
else
|
|
{
|
|
/* Call the error management function
|
|
* (command will be nacked)
|
|
*/
|
|
USBD_CtlError (pdev, req);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return USBD_OK;
|
|
}
|
|
|
|
static uint8_t usbd_cdc_EP0_RxReady (void *pdev)
|
|
{
|
|
(void) pdev;
|
|
|
|
if (cdcCmd != NO_CMD)
|
|
{
|
|
VCP_Ctrl(cdcCmd, CmdBuff, cdcLen);
|
|
cdcCmd = NO_CMD;
|
|
}
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
static uint8_t usbd_cdc_DataIn (void *pdev, uint8_t epnum)
|
|
{
|
|
(void) pdev;
|
|
(void) epnum;
|
|
|
|
txDone = true;
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
static uint8_t usbd_cdc_DataOut (void *pdev, uint8_t epnum)
|
|
{
|
|
/* Get size of received data */
|
|
size_t len = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count;
|
|
|
|
/* Transfer data to RX ring buffer */
|
|
for(size_t i = 0; i < len; i++)
|
|
{
|
|
rxRingBuf.data[rxRingBuf.writePtr] = outEnpBuffer[i];
|
|
if((rxRingBuf.writePtr + 1) == rxRingBuf.readPtr)
|
|
{
|
|
// Buffer full, pop one byte from tail.
|
|
rxRingBuf.readPtr = (rxRingBuf.readPtr + 1)%RX_RING_BUF_SIZE;
|
|
}
|
|
rxRingBuf.writePtr = (rxRingBuf.writePtr + 1)%RX_RING_BUF_SIZE;
|
|
}
|
|
|
|
/* Prepare Out endpoint to receive next packet */
|
|
DCD_EP_PrepareRx(pdev, CDC_OUT_EP, outEnpBuffer, CDC_DATA_OUT_PACKET_SIZE);
|
|
|
|
return USBD_OK;
|
|
}
|
|
|
|
static uint8_t usbd_cdc_SOF (void *pdev)
|
|
{
|
|
(void) pdev;
|
|
return USBD_OK;
|
|
}
|
|
|
|
static uint8_t *USBD_cdc_GetCfgDesc (uint8_t speed, uint16_t *length)
|
|
{
|
|
(void) speed;
|
|
(void) length;
|
|
|
|
*length = sizeof (usbd_cdc_CfgDesc);
|
|
return usbd_cdc_CfgDesc;
|
|
}
|
|
|
|
static uint16_t VCP_Ctrl (uint32_t Cmd, uint8_t* Buf, uint32_t Len)
|
|
{
|
|
(void) Len;
|
|
|
|
/* NOTE:commands not needed for this driver:
|
|
* SEND_ENCAPSULATED_COMMAND
|
|
* GET_ENCAPSULATED_RESPONSE
|
|
* SET_COMM_FEATURE
|
|
* GET_COMM_FEATURE
|
|
* CLEAR_COMM_FEATURE
|
|
* SET_LINE_CODING
|
|
* GET_LINE_CODING
|
|
* SET_CONTROL_LINE_STATE
|
|
* SEND_BREAK
|
|
*/
|
|
|
|
if(Cmd == SET_LINE_CODING)
|
|
{
|
|
linecoding.bitrate = (uint32_t)(Buf[0] | (Buf[1] << 8)
|
|
| (Buf[2] << 16) | (Buf[3] << 24));
|
|
linecoding.format = Buf[4];
|
|
linecoding.paritytype = Buf[5];
|
|
linecoding.datatype = Buf[6];
|
|
linecoding.changed = 1;
|
|
}
|
|
else if(Cmd == GET_LINE_CODING)
|
|
{
|
|
Buf[0] = (uint8_t)(linecoding.bitrate);
|
|
Buf[1] = (uint8_t)(linecoding.bitrate >> 8);
|
|
Buf[2] = (uint8_t)(linecoding.bitrate >> 16);
|
|
Buf[3] = (uint8_t)(linecoding.bitrate >> 24);
|
|
Buf[4] = linecoding.format;
|
|
Buf[5] = linecoding.paritytype;
|
|
Buf[6] = linecoding.datatype;
|
|
}
|
|
|
|
return USBD_OK;
|
|
}
|