OpenRTX/platform/mcu/STM32F4xx/drivers/usb_vcom.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;
}