KT24-1110_65E-HA-651B/apps/common/usb/device/cdc.c
2024-11-10 18:44:17 +08:00

487 lines
16 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "usb/device/usb_stack.h"
#include "usb/usb_config.h"
#include "usb/device/cdc.h"
#include "app_config.h"
#include "os/os_api.h"
#include "cdc_defs.h" //need redefine __u8, __u16, __u32
#define LOG_TAG_CONST USB
#define LOG_TAG "[USB]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
/* #define LOG_DUMP_ENABLE */
#define LOG_CLI_ENABLE
#include "debug.h"
#if TCFG_USB_SLAVE_CDC_ENABLE
struct usb_cdc_gadget {
u8 *cdc_buffer;
u8 *bulk_ep_out_buffer;
u8 *bulk_ep_in_buffer;
void *priv;
int (*output)(void *priv, u8 *obuf, u32 olen);
void (*wakeup_handler)(struct usb_device_t *usb_device);
OS_MUTEX mutex_data;
#if CDC_INTR_EP_ENABLE
OS_MUTEX mutex_intr;
u8 *intr_ep_in_buffer;
#endif
u8 bmTransceiver;
u8 subtype_data[8];
};
static struct usb_cdc_gadget *cdc_hdl;
#if USB_MALLOC_ENABLE
#else
static u8 _cdc_buffer[MAXP_SIZE_CDC_BULKOUT] SEC(.cdc_var) __attribute__((aligned(4)));
static struct usb_cdc_gadget _cdc_hdl SEC(.cdc_var);
#endif
static const u8 cdc_virtual_comport_desc[] = {
//IAD Descriptor
0x08, //bLength
0x0b, //bDescriptorType
0x00, //bFirstInterface
0x02, //bInterfaceCount
0x02, //bFunctionClass, Comunication and CDC control
0x02, //bFunctionSubClass
0x01, //bFunctionProtocol
0x00, //iFunction
//Interface 0, Alt 0
0x09, //Length
0x04, //DescriptorType:Interface
0x00, //InterfaceNum
0x00, //AlternateSetting
0x01, //NumEndpoint
0x02, //InterfaceClass, Communation and CDC control
0x02, //InterfaceSubClass, Abstract Control Model
0x01, //InterfaceProtocol, AT commands defined by ITU-T V.250 etc
0x00, //Interface String
//CDC Interface Descriptor
0x05, //bLength
0x24, //bDescriptorType
0x00, //bDescriptorSubType, Header Functional Desc
0x10, 0x01, //bcdCDC, version 1.10
//CDC Interface Descriptor
0x05, //bLength
0x24, //bDescriptorType
0x01, //bDescriptorSubType, Call Management Functional Descriptor
0x03, //bmCapabilities, D7..D2 reversed
// D7..D2 reversed
// D1 sends/receives call management information only over a Data Class interface
// D0 handle call management itself
0x01, //bDataInterface
//CDC Interface Descriptor
0x04, //bLength
0x24, //bDescriptorType
0x02, //bDescriptorSubType, Abstract Control Management Functional Descriptor
0x02, //bmCapabilities, D7..D2 reversed
// D7..D4 reversed
// D3 supports the notification Network_Connection
// D2 not supports the request Send_Break
// D1 supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State
// D0 supports the request combination of Set_Comm_Feature, Clear_Comm_Feature, and Get_Comm_Feature
//CDC Interface Descriptor
0x05, //bLength
0x24, //bDescriptorType
0x06, //bDescriptorSubType, Union Functional Descriptor
0x00, //bControlInterface
0x01, //bSubordinateInterface[0]
//Endpoint In
0x07, //bLength
0x05, //bDescritorType
0x82, //bEndpointAddr
0x03, //bmAttributes, interrupt
0x08, 0x00, //wMaxPacketSize
0x01, //bInterval, 1ms
//Interface 1, Alt 0
0x09, //Length
0x04, //DescriptorType:Interface
0x01, //InterfaceNum
0x00, //AlternateSetting
0x02, //NumEndpoint
0x0a, //InterfaceClass, CDC Data
0x00, //InterfaceSubClass
0x00, //InterfaceProtocol
0x00, //Interface String
//Endpoint Out
0x07, //bLength
0x05, //bDescriptor
0x02, //bEndpointAddr
0x02, //bmAttributes, bulk
0x40, 0x00, //wMaxPacketSize
0x00, //bInterval
//Endpoint In
0x07, //bLength
0x05, //bDescritorType
0x83, //bEndpointAddr
0x02, //bmAttributes, bulk
0x40, 0x00, //wMaxPacketSize
0x00, //bInterval
};
static void cdc_endpoint_init(struct usb_device_t *usb_device, u32 itf);
static u32 cdc_setup_rx(struct usb_device_t *usb_device, struct usb_ctrlrequest *ctrl_req);
static void usb_cdc_line_coding_init(struct usb_cdc_line_coding *lc)
{
lc->dwDTERate = 460800;
lc->bCharFormat = USB_CDC_1_STOP_BITS;
lc->bParityType = USB_CDC_NO_PARITY;
lc->bDataBits = 8;
}
static void dump_line_coding(struct usb_cdc_line_coding *lc)
{
log_debug("dtw rate : %d", lc->dwDTERate);
log_debug("stop bits : %d", lc->bCharFormat);
log_debug("verify bits : %d", lc->bParityType);
log_debug("data bits : %d", lc->bDataBits);
}
static u32 cdc_setup(struct usb_device_t *usb_device, struct usb_ctrlrequest *ctrl_req)
{
const usb_dev usb_id = usb_device2id(usb_device);
int recip_type;
u32 len;
recip_type = ctrl_req->bRequestType & USB_TYPE_MASK;
switch (recip_type) {
case USB_TYPE_CLASS:
switch (ctrl_req->bRequest) {
case USB_CDC_REQ_SET_LINE_CODING:
log_debug("set line coding");
usb_set_setup_recv(usb_device, cdc_setup_rx);
break;
case USB_CDC_REQ_GET_LINE_CODING:
log_debug("get line codling");
len = ctrl_req->wLength < sizeof(struct usb_cdc_line_coding) ?
ctrl_req->wLength : sizeof(struct usb_cdc_line_coding);
if (cdc_hdl == NULL) {
usb_set_setup_phase(usb_device, USB_EP0_SET_STALL);
break;
}
usb_set_data_payload(usb_device, ctrl_req, cdc_hdl->subtype_data, len);
dump_line_coding((struct usb_cdc_line_coding *)cdc_hdl->subtype_data);
break;
case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
log_debug("set control line state - %d", ctrl_req->wValue);
if (cdc_hdl) {
/* if (ctrl_req->wValue & BIT(0)) { //DTR */
cdc_hdl->bmTransceiver |= BIT(0);
/* } else { */
/* usb_slave->cdc->bmTransceiver &= ~BIT(0); */
/* } */
/* if (ctrl_req->wValue & BIT(1)) { //RTS */
cdc_hdl->bmTransceiver |= BIT(1);
/* } else { */
/* usb_slave->cdc->bmTransceiver &= ~BIT(1); */
/* } */
cdc_hdl->bmTransceiver |= BIT(4); //cfg done
}
usb_set_setup_phase(usb_device, USB_EP0_STAGE_SETUP);
//cdc_endpoint_init(usb_device, (ctrl_req->wIndex & USB_RECIP_MASK));
break;
default:
log_error("unsupported class req");
usb_set_setup_phase(usb_device, USB_EP0_SET_STALL);
break;
}
break;
default:
log_error("unsupported req type");
usb_set_setup_phase(usb_device, USB_EP0_SET_STALL);
break;
}
return 0;
}
static u32 cdc_setup_rx(struct usb_device_t *usb_device, struct usb_ctrlrequest *ctrl_req)
{
const usb_dev usb_id = usb_device2id(usb_device);
int recip_type;
struct usb_cdc_line_coding *lc = 0;
u32 len;
u8 read_ep[8];
len = ctrl_req->wLength;
usb_read_ep0(usb_id, read_ep, len);
recip_type = ctrl_req->bRequestType & USB_TYPE_MASK;
switch (recip_type) {
case USB_TYPE_CLASS:
switch (ctrl_req->bRequest) {
case USB_CDC_REQ_SET_LINE_CODING:
log_debug("USB_CDC_REQ_SET_LINE_CODING");
if (cdc_hdl == NULL) {
break;
}
if (len > sizeof(struct usb_cdc_line_coding)) {
len = sizeof(struct usb_cdc_line_coding);
}
memcpy(cdc_hdl->subtype_data, read_ep, len);
lc = (struct usb_cdc_line_coding *)cdc_hdl->subtype_data;
dump_line_coding(lc);
break;
}
break;
}
return USB_EP0_STAGE_SETUP;
}
static void cdc_reset(struct usb_device_t *usb_device, u32 itf)
{
log_error("%s()", __func__);
const usb_dev usb_id = usb_device2id(usb_device);
#if USB_ROOT2
usb_disable_ep(usb_id, CDC_DATA_EP_IN);
#if CDC_INTR_EP_ENABLE
usb_disable_ep(usb_id, CDC_INTR_EP_IN);
#endif
#else
cdc_endpoint_init(usb_device, itf);
#endif
}
u32 cdc_desc_config(const usb_dev usb_id, u8 *ptr, u32 *itf)
{
u8 *tptr;
tptr = ptr;
memcpy(tptr, cdc_virtual_comport_desc, sizeof(cdc_virtual_comport_desc));
//iad interface number
tptr[2] = *itf;
//control interface number
tptr[8 + 2] = *itf;
tptr[8 + 9 + 5 + 4] = *itf + 1;
tptr[8 + 9 + 5 + 5 + 4 + 3] = *itf;
tptr[8 + 9 + 5 + 5 + 4 + 4] = *itf + 1;
//interrupt in ep
tptr[8 + 9 + 5 + 5 + 4 + 5 + 2] = USB_DIR_IN | CDC_INTR_EP_IN;
tptr[8 + 9 + 5 + 5 + 4 + 5 + 4] = MAXP_SIZE_CDC_INTRIN & 0xff;
tptr[8 + 9 + 5 + 5 + 4 + 5 + 5] = (MAXP_SIZE_CDC_INTRIN >> 8) & 0xff;
//data interface number
tptr[8 + 9 + 5 + 5 + 4 + 5 + 7 + 2] = *itf + 1;
//bulk out ep
tptr[8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 2] = CDC_DATA_EP_OUT;
tptr[8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 4] = MAXP_SIZE_CDC_BULKOUT & 0xff;
tptr[8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 5] = (MAXP_SIZE_CDC_BULKOUT >> 8) & 0xff;
//bulk in ep
tptr[8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 2] = USB_DIR_IN | CDC_DATA_EP_IN;
tptr[8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 4] = MAXP_SIZE_CDC_BULKIN & 0xff;
tptr[8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 5] = (MAXP_SIZE_CDC_BULKIN >> 8) & 0xff;
tptr += sizeof(cdc_virtual_comport_desc);
if (usb_set_interface_hander(usb_id, *itf, cdc_setup) != *itf) {
ASSERT(0, "cdc set interface_hander fail");
}
if (usb_set_reset_hander(usb_id, *itf, cdc_reset) != *itf) {
}
*itf += 2;
return (u32)(tptr - ptr);
}
void cdc_set_wakeup_handler(void (*handle)(struct usb_device_t *usb_device))
{
if (cdc_hdl) {
cdc_hdl->wakeup_handler = handle;
}
}
static void cdc_wakeup_handler(struct usb_device_t *usb_device, u32 ep)
{
if (cdc_hdl && cdc_hdl->wakeup_handler) {
cdc_hdl->wakeup_handler(usb_device);
}
}
void cdc_set_output_handle(void *priv, int (*output_handler)(void *priv, u8 *buf, u32 len))
{
if (cdc_hdl) {
cdc_hdl->output = output_handler;
cdc_hdl->priv = priv;
}
}
static void cdc_intrrx(struct usb_device_t *usb_device, u32 ep)
{
const usb_dev usb_id = usb_device2id(usb_device);
if (cdc_hdl == NULL) {
return;
}
u8 *cdc_rx_buf = cdc_hdl->cdc_buffer;
//由于bulk传输使用双缓冲无法用usb_get_ep_buffer()知道是哪一个buffer需要外部buffer接收数据
u32 len = usb_g_bulk_read(usb_id, CDC_DATA_EP_OUT, cdc_rx_buf, MAXP_SIZE_CDC_BULKOUT, 0);
if (cdc_hdl->output) {
cdc_hdl->output(cdc_hdl->priv, cdc_rx_buf, len);
}
}
static void cdc_endpoint_init(struct usb_device_t *usb_device, u32 itf)
{
ASSERT(cdc_hdl, "cdc not register");
const usb_dev usb_id = usb_device2id(usb_device);
usb_g_ep_config(usb_id, CDC_DATA_EP_IN | USB_DIR_IN, USB_ENDPOINT_XFER_BULK,
0, cdc_hdl->bulk_ep_in_buffer, MAXP_SIZE_CDC_BULKIN);
usb_g_ep_config(usb_id, CDC_DATA_EP_OUT | USB_DIR_OUT, USB_ENDPOINT_XFER_BULK,
1, cdc_hdl->bulk_ep_out_buffer, MAXP_SIZE_CDC_BULKOUT);
/* usb_g_set_intr_hander(usb_id, CDC_DATA_EP_OUT | USB_DIR_OUT, cdc_intrrx); */
usb_g_set_intr_hander(usb_id, CDC_DATA_EP_OUT | USB_DIR_OUT, cdc_wakeup_handler);
usb_enable_ep(usb_id, CDC_DATA_EP_IN);
#if CDC_INTR_EP_ENABLE
usb_g_ep_config(usb_id, CDC_INTR_EP_IN | USB_DIR_IN, USB_ENDPOINT_XFER_INT,
0, cdc_hdl->intr_ep_in_buffer, MAXP_SIZE_CDC_INTRIN);
usb_enable_ep(usb_id, CDC_INTR_EP_IN);
#endif
}
u32 cdc_read_data(const usb_dev usb_id, u8 *buf, u32 len)
{
u32 rxlen;
if (cdc_hdl == NULL) {
return 0;
}
u8 *cdc_rx_buf = cdc_hdl->cdc_buffer;
os_mutex_pend(&cdc_hdl->mutex_data, 0);
//由于bulk传输使用双缓冲无法用usb_get_ep_buffer()知道是哪一个buffer需要外部buffer接收数据
rxlen = usb_g_bulk_read(usb_id, CDC_DATA_EP_OUT, cdc_rx_buf, MAXP_SIZE_CDC_BULKOUT, 0);
rxlen = rxlen > len ? len : rxlen;
if (rxlen > 0) {
memcpy(buf, cdc_rx_buf, rxlen);
}
os_mutex_post(&cdc_hdl->mutex_data);
return rxlen;
}
u32 cdc_write_data(const usb_dev usb_id, u8 *buf, u32 len)
{
u32 txlen, offset;
if (cdc_hdl == NULL) {
return 0;
}
if ((cdc_hdl->bmTransceiver & (BIT(1) | BIT(4))) != (BIT(1) | BIT(4))) {
return 0;
}
offset = 0;
os_mutex_pend(&cdc_hdl->mutex_data, 0);
while (offset < len) {
txlen = len - offset > MAXP_SIZE_CDC_BULKIN ?
MAXP_SIZE_CDC_BULKIN : len - offset;
txlen = usb_g_bulk_write(usb_id, CDC_DATA_EP_IN, buf + offset, txlen);
if (txlen == 0) {
break;
}
if ((cdc_hdl->bmTransceiver & (BIT(1) | BIT(4))) != (BIT(1) | BIT(4))) {
break;
}
offset += txlen;
}
if (txlen == MAXP_SIZE_CDC_BULKIN) {
txlen = usb_g_bulk_write(usb_id, CDC_DATA_EP_IN, NULL, 0);
}
os_mutex_post(&cdc_hdl->mutex_data);
return offset;
}
u32 cdc_write_inir(const usb_dev usb_id, u8 *buf, u32 len)
{
#if CDC_INTR_EP_ENABLE
u32 txlen, offset;
if (cdc_hdl == NULL) {
return 0;
}
if ((cdc_hdl->bmTransceiver & BIT(4)) == 0) {
return 0;
}
offset = 0;
os_mutex_pend(&cdc_hdl->mutex_intr, 0);
while (offset < len) {
txlen = len - offset > MAXP_SIZE_CDC_INTRIN ?
MAXP_SIZE_CDC_INTRIN : len - offset;
txlen = usb_g_intr_write(usb_id, CDC_INTR_EP_IN, buf + offset, txlen);
if (txlen == 0) {
break;
}
if ((cdc_hdl->bmTransceiver & BIT(4)) == 0) {
break;
}
offset += txlen;
}
os_mutex_post(&cdc_hdl->mutex_intr);
return offset;
#else
return 0;
#endif
}
void cdc_register(const usb_dev usb_id)
{
struct usb_cdc_line_coding *lc;
/* log_info("%s() %d", __func__, __LINE__); */
if (!cdc_hdl) {
#if USB_MALLOC_ENABLE
cdc_hdl = zalloc(sizeof(struct usb_cdc_gadget));
if (!cdc_hdl) {
log_error("cdc_register err 1");
return;
}
cdc_hdl->cdc_buffer = malloc(MAXP_SIZE_CDC_BULKOUT);
if (!cdc_hdl->cdc_buf) {
log_error("cdc_register err 2");
goto __exit_err;
}
#else
memset(&_cdc_hdl, 0, sizeof(struct usb_cdc_gadget));
cdc_hdl = &_cdc_hdl;
cdc_hdl->cdc_buffer = _cdc_buffer;
#endif
lc = (struct usb_cdc_line_coding *)cdc_hdl->subtype_data;
usb_cdc_line_coding_init(lc);
os_mutex_create(&cdc_hdl->mutex_data);
cdc_hdl->bulk_ep_in_buffer = usb_alloc_ep_dmabuffer(usb_id, CDC_DATA_EP_IN | USB_DIR_IN, MAXP_SIZE_CDC_BULKIN);
cdc_hdl->bulk_ep_out_buffer = usb_alloc_ep_dmabuffer(usb_id, CDC_DATA_EP_OUT | USB_DIR_OUT, MAXP_SIZE_CDC_BULKOUT);
#if CDC_INTR_EP_ENABLE
os_mutex_create(&cdc_hdl->mutex_intr);
cdc_hdl->intr_ep_in_buffer = usb_alloc_ep_dmabuffer(usb_id, CDC_INTR_EP_IN | USB_DIR_IN, MAXP_SIZE_CDC_INTRIN);
#endif
}
return;
__exit_err:
#if USB_MALLOC_ENABLE
if (cdc_hdl->cdc_buffer) {
free(cdc_hdl->cdc_buffer);
}
if (cdc_hdl) {
free(cdc_hdl);
}
#endif
cdc_hdl = NULL;
}
void cdc_release(const usb_dev usb_id)
{
/* log_info("%s() %d", __func__, __LINE__); */
if (cdc_hdl) {
#if USB_MALLOC_ENABLE
free(cdc_hdl->cdc_buffer);
free(cdc_hdl);
#endif
cdc_hdl = NULL;
}
}
#endif