/* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2012-10-02 Yi Qiu first version * 2012-12-12 heyuanjie87 change endpoints and function handler * 2013-06-25 heyuanjie87 remove SOF mechinism * 2013-07-20 Yi Qiu do more test * 2016-02-01 Urey Fix some error * 2021-10-14 mazhiyuan Fix some error */ #include #include #include "drivers/usb_device.h" #include "cdc.h" #ifdef RT_USB_DEVICE_CDC #define DBG_TAG "usbdevice_cdc" #define DBG_LVL DBG_INFO #include #define VCOM_INTF_STR_INDEX 5 #ifdef RT_VCOM_TX_TIMEOUT #define VCOM_TX_TIMEOUT RT_VCOM_TX_TIMEOUT #else /*!RT_VCOM_TX_TIMEOUT*/ #define VCOM_TX_TIMEOUT 1000 #endif /*RT_VCOM_TX_TIMEOUT*/ #ifdef RT_CDC_RX_BUFSIZE #define CDC_RX_BUFSIZE RT_CDC_RX_BUFSIZE #else #define CDC_RX_BUFSIZE 128 #endif #define CDC_MAX_PACKET_SIZE 64 #define VCOM_DEVICE "vcom" #ifdef RT_VCOM_TASK_STK_SIZE #define VCOM_TASK_STK_SIZE RT_VCOM_TASK_STK_SIZE #else /*!RT_VCOM_TASK_STK_SIZE*/ #define VCOM_TASK_STK_SIZE 512 #endif /*RT_VCOM_TASK_STK_SIZE*/ #ifdef RT_VCOM_TX_USE_DMA #define VCOM_TX_USE_DMA #endif /*RT_VCOM_TX_USE_DMA*/ #ifdef RT_VCOM_SERNO #define _SER_NO RT_VCOM_SERNO #else /*!RT_VCOM_SERNO*/ #define _SER_NO "32021919830108" #endif /*RT_VCOM_SERNO*/ #ifdef RT_VCOM_SER_LEN #define _SER_NO_LEN RT_VCOM_SER_LEN #else /*!RT_VCOM_SER_LEN*/ #define _SER_NO_LEN 14 /*rt_strlen("32021919830108")*/ #endif /*RT_VCOM_SER_LEN*/ rt_align(RT_ALIGN_SIZE) static rt_uint8_t vcom_thread_stack[VCOM_TASK_STK_SIZE]; static struct rt_thread vcom_thread; static struct ucdc_line_coding line_coding; #define CDC_TX_BUFSIZE 1024 #define CDC_BULKIN_MAXSIZE (CDC_TX_BUFSIZE / 8) #define CDC_TX_HAS_DATE 0x01 #define CDC_TX_HAS_SPACE 0x02 struct vcom { struct rt_serial_device serial; uep_t ep_out; uep_t ep_in; uep_t ep_cmd; rt_bool_t connected; rt_bool_t in_sending; struct rt_completion wait; rt_uint8_t rx_rbp[CDC_RX_BUFSIZE]; struct rt_ringbuffer rx_ringbuffer; rt_uint8_t tx_rbp[CDC_TX_BUFSIZE]; struct rt_ringbuffer tx_ringbuffer; struct rt_event tx_event; }; struct vcom_tx_msg { struct rt_serial_device * serial; const char *buf; rt_size_t size; }; rt_align(4) static struct udevice_descriptor dev_desc = { USB_DESC_LENGTH_DEVICE, //bLength; USB_DESC_TYPE_DEVICE, //type; USB_BCD_VERSION, //bcdUSB; USB_CLASS_CDC, //bDeviceClass; 0x02, //bDeviceSubClass; 0x00, //bDeviceProtocol; CDC_MAX_PACKET_SIZE, //bMaxPacketSize0; _VENDOR_ID, //idVendor; _PRODUCT_ID, //idProduct; USB_BCD_DEVICE, //bcdDevice; USB_STRING_MANU_INDEX, //iManufacturer; USB_STRING_PRODUCT_INDEX, //iProduct; USB_STRING_SERIAL_INDEX, //iSerialNumber; USB_DYNAMIC, //bNumConfigurations; }; //FS and HS needed rt_align(4) static struct usb_qualifier_descriptor dev_qualifier = { sizeof(dev_qualifier), //bLength USB_DESC_TYPE_DEVICEQUALIFIER, //bDescriptorType 0x0200, //bcdUSB USB_CLASS_CDC, //bDeviceClass 0x02, //bDeviceSubClass 0x00, //bDeviceProtocol 64, //bMaxPacketSize0 0x01, //bNumConfigurations 0, }; /* communcation interface descriptor */ rt_align(4) const static struct ucdc_comm_descriptor _comm_desc = { #ifdef RT_USB_DEVICE_COMPOSITE /* Interface Association Descriptor */ { USB_DESC_LENGTH_IAD, USB_DESC_TYPE_IAD, USB_DYNAMIC, 0x02, USB_CDC_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTOCOL_V25TER, 0x00, }, #endif /* Interface Descriptor */ { USB_DESC_LENGTH_INTERFACE, USB_DESC_TYPE_INTERFACE, USB_DYNAMIC, 0x00, 0x01, USB_CDC_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTOCOL_V25TER, #ifdef RT_USB_DEVICE_COMPOSITE VCOM_INTF_STR_INDEX, #else 0, #endif }, /* Header Functional Descriptor */ { 0x05, USB_CDC_CS_INTERFACE, USB_CDC_SCS_HEADER, 0x0110, }, /* Call Management Functional Descriptor */ { 0x05, USB_CDC_CS_INTERFACE, USB_CDC_SCS_CALL_MGMT, 0x00, USB_DYNAMIC, }, /* Abstract Control Management Functional Descriptor */ { 0x04, USB_CDC_CS_INTERFACE, USB_CDC_SCS_ACM, 0x02, }, /* Union Functional Descriptor */ { 0x05, USB_CDC_CS_INTERFACE, USB_CDC_SCS_UNION, USB_DYNAMIC, USB_DYNAMIC, }, /* Endpoint Descriptor */ { USB_DESC_LENGTH_ENDPOINT, USB_DESC_TYPE_ENDPOINT, USB_DYNAMIC | USB_DIR_IN, USB_EP_ATTR_INT, 0x08, 0xFF, }, }; /* data interface descriptor */ rt_align(4) const static struct ucdc_data_descriptor _data_desc = { /* interface descriptor */ { USB_DESC_LENGTH_INTERFACE, USB_DESC_TYPE_INTERFACE, USB_DYNAMIC, 0x00, 0x02, USB_CDC_CLASS_DATA, 0x00, 0x00, 0x00, }, /* endpoint, bulk out */ { USB_DESC_LENGTH_ENDPOINT, USB_DESC_TYPE_ENDPOINT, USB_DYNAMIC | USB_DIR_OUT, USB_EP_ATTR_BULK, USB_CDC_BUFSIZE, 0x00, }, /* endpoint, bulk in */ { USB_DESC_LENGTH_ENDPOINT, USB_DESC_TYPE_ENDPOINT, USB_DYNAMIC | USB_DIR_IN, USB_EP_ATTR_BULK, USB_CDC_BUFSIZE, 0x00, }, }; rt_align(4) static char serno[_SER_NO_LEN + 1] = {'\0'}; rt_weak rt_err_t vcom_get_stored_serno(char *serno, int size); rt_err_t vcom_get_stored_serno(char *serno, int size) { return -RT_ERROR; } rt_align(4) const static char* _ustring[] = { "Language", "RT-Thread Team.", "RTT Virtual Serial", serno, "Configuration", "Interface", }; static void rt_usb_vcom_init(struct ufunction *func); static void _vcom_reset_state(ufunction_t func) { struct vcom* data; rt_base_t level; RT_ASSERT(func != RT_NULL) data = (struct vcom*)func->user_data; level = rt_hw_interrupt_disable(); data->connected = RT_FALSE; data->in_sending = RT_FALSE; /*rt_kprintf("reset USB serial\n", cnt);*/ rt_hw_interrupt_enable(level); } /** * This function will handle cdc bulk in endpoint request. * * @param func the usb function object. * @param size request size. * * @return RT_EOK. */ static rt_err_t _ep_in_handler(ufunction_t func, rt_size_t size) { struct vcom *data; rt_size_t request_size; RT_ASSERT(func != RT_NULL); data = (struct vcom*)func->user_data; request_size = data->ep_in->request.size; LOG_D("_ep_in_handler %d", request_size); if ((request_size != 0) && ((request_size % EP_MAXPACKET(data->ep_in)) == 0)) { /* don't have data right now. Send a zero-length-packet to * terminate the transaction. * * FIXME: actually, this might not be the right place to send zlp. * Only the rt_device_write could know how much data is sending. */ data->in_sending = RT_TRUE; data->ep_in->request.buffer = RT_NULL; data->ep_in->request.size = 0; data->ep_in->request.req_type = UIO_REQUEST_WRITE; rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); return RT_EOK; } rt_completion_done(&data->wait); return RT_EOK; } /** * This function will handle cdc bulk out endpoint request. * * @param func the usb function object. * @param size request size. * * @return RT_EOK. */ static rt_err_t _ep_out_handler(ufunction_t func, rt_size_t size) { rt_base_t level; struct vcom *data; RT_ASSERT(func != RT_NULL); LOG_D("_ep_out_handler %d", size); data = (struct vcom*)func->user_data; /* ensure serial is active */ if((data->serial.parent.flag & RT_DEVICE_FLAG_ACTIVATED) && (data->serial.parent.open_flag & RT_DEVICE_OFLAG_OPEN)) { /* receive data from USB VCOM */ level = rt_hw_interrupt_disable(); rt_ringbuffer_put(&data->rx_ringbuffer, data->ep_out->buffer, size); rt_hw_interrupt_enable(level); /* notify receive data */ rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_RX_IND); } data->ep_out->request.buffer = data->ep_out->buffer; data->ep_out->request.size = EP_MAXPACKET(data->ep_out); data->ep_out->request.req_type = UIO_REQUEST_READ_BEST; rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); return RT_EOK; } /** * This function will handle cdc interrupt in endpoint request. * * @param device the usb device object. * @param size request size. * * @return RT_EOK. */ static rt_err_t _ep_cmd_handler(ufunction_t func, rt_size_t size) { RT_ASSERT(func != RT_NULL); LOG_D("_ep_cmd_handler"); return RT_EOK; } /** * This function will handle cdc_get_line_coding request. * * @param device the usb device object. * @param setup the setup request. * * @return RT_EOK on successful. */ static rt_err_t _cdc_get_line_coding(udevice_t device, ureq_t setup) { struct ucdc_line_coding data; rt_uint16_t size; RT_ASSERT(device != RT_NULL); RT_ASSERT(setup != RT_NULL); LOG_D("_cdc_get_line_coding"); data.dwDTERate = 115200; data.bCharFormat = 0; data.bDataBits = 8; data.bParityType = 0; size = setup->wLength > 7 ? 7 : setup->wLength; rt_usbd_ep0_write(device, (void*)&data, size); return RT_EOK; } static rt_err_t _cdc_set_line_coding_callback(udevice_t device, rt_size_t size) { LOG_D("_cdc_set_line_coding_callback"); dcd_ep0_send_status(device->dcd); return RT_EOK; } /** * This function will handle cdc_set_line_coding request. * * @param device the usb device object. * @param setup the setup request. * * @return RT_EOK on successful. */ static rt_err_t _cdc_set_line_coding(udevice_t device, ureq_t setup) { RT_ASSERT(device != RT_NULL); RT_ASSERT(setup != RT_NULL); LOG_D("_cdc_set_line_coding"); rt_usbd_ep0_read(device, (void*)&line_coding, sizeof(struct ucdc_line_coding), _cdc_set_line_coding_callback); return RT_EOK; } /** * This function will handle cdc interface request. * * @param device the usb device object. * @param setup the setup request. * * @return RT_EOK on successful. */ static rt_err_t _interface_handler(ufunction_t func, ureq_t setup) { struct vcom *data; RT_ASSERT(func != RT_NULL); RT_ASSERT(func->device != RT_NULL); RT_ASSERT(setup != RT_NULL); data = (struct vcom*)func->user_data; switch(setup->bRequest) { case CDC_SEND_ENCAPSULATED_COMMAND: break; case CDC_GET_ENCAPSULATED_RESPONSE: break; case CDC_SET_COMM_FEATURE: break; case CDC_GET_COMM_FEATURE: break; case CDC_CLEAR_COMM_FEATURE: break; case CDC_SET_LINE_CODING: _cdc_set_line_coding(func->device, setup); break; case CDC_GET_LINE_CODING: _cdc_get_line_coding(func->device, setup); break; case CDC_SET_CONTROL_LINE_STATE: data->connected = (setup->wValue & 0x01) > 0?RT_TRUE:RT_FALSE; LOG_D("vcom state:%d ", data->connected); dcd_ep0_send_status(func->device->dcd); break; case CDC_SEND_BREAK: break; default: rt_kprintf("unknown cdc request\n",setup->request_type); return -RT_ERROR; } return RT_EOK; } /** * This function will run cdc function, it will be called on handle set configuration request. * * @param func the usb function object. * * @return RT_EOK on successful. */ static rt_err_t _function_enable(ufunction_t func) { struct vcom *data; RT_ASSERT(func != RT_NULL); LOG_D("cdc function enable"); _vcom_reset_state(func); data = (struct vcom*)func->user_data; data->ep_out->buffer = rt_malloc(CDC_RX_BUFSIZE); RT_ASSERT(data->ep_out->buffer != RT_NULL); #ifdef RT_USING_SERIAL_V2 data->serial.serial_rx = &data->rx_ringbuffer; #endif data->ep_out->request.buffer = data->ep_out->buffer; data->ep_out->request.size = EP_MAXPACKET(data->ep_out); data->ep_out->request.req_type = UIO_REQUEST_READ_BEST; rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request); return RT_EOK; } /** * This function will stop cdc function, it will be called on handle set configuration request. * * @param func the usb function object. * * @return RT_EOK on successful. */ static rt_err_t _function_disable(ufunction_t func) { struct vcom *data; RT_ASSERT(func != RT_NULL); LOG_D("cdc function disable"); _vcom_reset_state(func); data = (struct vcom*)func->user_data; if(data->ep_out->buffer != RT_NULL) { rt_free(data->ep_out->buffer); data->ep_out->buffer = RT_NULL; } return RT_EOK; } static struct ufunction_ops ops = { _function_enable, _function_disable, RT_NULL, }; /** * This function will configure cdc descriptor. * * @param comm the communication interface number. * @param data the data interface number. * * @return RT_EOK on successful. */ static rt_err_t _cdc_descriptor_config(ucdc_comm_desc_t comm, rt_uint8_t cintf_nr, ucdc_data_desc_t data, rt_uint8_t dintf_nr) { comm->call_mgmt_desc.data_interface = dintf_nr; comm->union_desc.master_interface = cintf_nr; comm->union_desc.slave_interface0 = dintf_nr; #ifdef RT_USB_DEVICE_COMPOSITE comm->iad_desc.bFirstInterface = cintf_nr; #endif return RT_EOK; } /** * This function will create a cdc function instance. * * @param device the usb device object. * * @return RT_EOK on successful. */ ufunction_t rt_usbd_function_cdc_create(udevice_t device) { ufunction_t func; struct vcom* data; uintf_t intf_comm, intf_data; ualtsetting_t comm_setting, data_setting; ucdc_data_desc_t data_desc; ucdc_comm_desc_t comm_desc; /* parameter check */ RT_ASSERT(device != RT_NULL); rt_memset(serno, 0, _SER_NO_LEN + 1); if(vcom_get_stored_serno(serno, _SER_NO_LEN) != RT_EOK) { rt_memset(serno, 0, _SER_NO_LEN + 1); rt_memcpy(serno, _SER_NO, rt_strlen(_SER_NO)); } #ifdef RT_USB_DEVICE_COMPOSITE rt_usbd_device_set_interface_string(device, VCOM_INTF_STR_INDEX, _ustring[2]); #else /* set usb device string description */ rt_usbd_device_set_string(device, _ustring); #endif /* create a cdc function */ func = rt_usbd_function_new(device, &dev_desc, &ops); /* support HS */ rt_usbd_device_set_qualifier(device, &dev_qualifier); /* allocate memory for cdc vcom data */ data = (struct vcom*)rt_malloc(sizeof(struct vcom)); RT_ASSERT(data != RT_NULL); rt_memset(data, 0, sizeof(struct vcom)); func->user_data = (void*)data; /* initilize vcom */ rt_usb_vcom_init(func); /* create a cdc communication interface and a cdc data interface */ intf_comm = rt_usbd_interface_new(device, _interface_handler); intf_data = rt_usbd_interface_new(device, _interface_handler); /* create a communication alternate setting and a data alternate setting */ comm_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_comm_descriptor)); data_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_data_descriptor)); /* config desc in alternate setting */ rt_usbd_altsetting_config_descriptor(comm_setting, &_comm_desc, (rt_off_t)&((ucdc_comm_desc_t)0)->intf_desc); rt_usbd_altsetting_config_descriptor(data_setting, &_data_desc, 0); /* configure the cdc interface descriptor */ _cdc_descriptor_config(comm_setting->desc, intf_comm->intf_num, data_setting->desc, intf_data->intf_num); /* create a command endpoint */ comm_desc = (ucdc_comm_desc_t)comm_setting->desc; data->ep_cmd = rt_usbd_endpoint_new(&comm_desc->ep_desc, _ep_cmd_handler); /* add the command endpoint to the cdc communication interface */ rt_usbd_altsetting_add_endpoint(comm_setting, data->ep_cmd); /* add the communication alternate setting to the communication interface, then set default setting of the interface */ rt_usbd_interface_add_altsetting(intf_comm, comm_setting); rt_usbd_set_altsetting(intf_comm, 0); /* add the communication interface to the cdc function */ rt_usbd_function_add_interface(func, intf_comm); /* create a bulk in and a bulk endpoint */ data_desc = (ucdc_data_desc_t)data_setting->desc; data->ep_out = rt_usbd_endpoint_new(&data_desc->ep_out_desc, _ep_out_handler); data->ep_in = rt_usbd_endpoint_new(&data_desc->ep_in_desc, _ep_in_handler); /* add the bulk out and bulk in endpoints to the data alternate setting */ rt_usbd_altsetting_add_endpoint(data_setting, data->ep_in); rt_usbd_altsetting_add_endpoint(data_setting, data->ep_out); /* add the data alternate setting to the data interface then set default setting of the interface */ rt_usbd_interface_add_altsetting(intf_data, data_setting); rt_usbd_set_altsetting(intf_data, 0); /* add the cdc data interface to cdc function */ rt_usbd_function_add_interface(func, intf_data); return func; } /** * UART device in RT-Thread */ static rt_err_t _vcom_configure(struct rt_serial_device *serial, struct serial_configure *cfg) { return RT_EOK; } static rt_err_t _vcom_control(struct rt_serial_device *serial, int cmd, void *arg) { struct ufunction *func; struct vcom *data; func = (struct ufunction*)serial->parent.user_data; data = (struct vcom*)func->user_data; switch (cmd) { case RT_DEVICE_CTRL_CLR_INT: /* disable rx irq */ break; case RT_DEVICE_CTRL_SET_INT: /* enable rx irq */ break; case RT_USBD_CLASS_CTRL_CONNECTED: (*(rt_bool_t*)arg) = data->connected; break; } return RT_EOK; } static int _vcom_getc(struct rt_serial_device *serial) { int result; rt_uint8_t ch; rt_base_t level; struct ufunction *func; struct vcom *data; func = (struct ufunction*)serial->parent.user_data; data = (struct vcom*)func->user_data; result = -1; level = rt_hw_interrupt_disable(); if(rt_ringbuffer_getchar(&data->rx_ringbuffer, &ch) != 0) { result = ch; } rt_hw_interrupt_enable(level); return result; } static rt_ssize_t _vcom_rb_block_put(struct vcom *data, const rt_uint8_t *buf, rt_size_t size) { rt_base_t level; rt_size_t put_len = 0; rt_size_t w_ptr = 0; rt_uint32_t res; rt_size_t remain_size = size; while (remain_size) { level = rt_hw_interrupt_disable(); put_len = rt_ringbuffer_put(&data->tx_ringbuffer, (const rt_uint8_t *)&buf[w_ptr], remain_size); rt_hw_interrupt_enable(level); w_ptr += put_len; remain_size -= put_len; if (put_len == 0) { rt_event_recv(&data->tx_event, CDC_TX_HAS_SPACE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, VCOM_TX_TIMEOUT, &res); } else { rt_event_send(&data->tx_event, CDC_TX_HAS_DATE); } } return size; } #ifdef RT_USING_SERIAL_V1 static rt_ssize_t _vcom_tx(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction) #else static rt_ssize_t _vcom_tx(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, rt_uint32_t tx_flag) #endif { struct ufunction *func; struct vcom *data; rt_uint32_t send_size = 0; rt_size_t ptr = 0; rt_uint8_t crlf[2] = {'\r', '\n',}; func = (struct ufunction*)serial->parent.user_data; data = (struct vcom*)func->user_data; RT_ASSERT(serial != RT_NULL); RT_ASSERT(buf != RT_NULL); LOG_D("%s",__func__); if (data->connected) { if((serial->parent.open_flag & RT_DEVICE_FLAG_STREAM)) { while(send_size < size) { while(ptr < size && buf[ptr] != '\n') { ptr++; } if(ptr < size) { send_size += _vcom_rb_block_put(data, (const rt_uint8_t *)&buf[send_size], ptr - send_size); _vcom_rb_block_put(data, crlf, 2); send_size++; ptr++; } else if (ptr == size) { send_size += _vcom_rb_block_put(data, (const rt_uint8_t *)&buf[send_size], ptr - send_size); } else { break; } } } else { while (send_size < size) { send_size += _vcom_rb_block_put(data, (rt_uint8_t *)&buf[send_size], size - send_size); } } } else { /* recover dataqueue resources */ rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DMADONE); } return size; } static int _vcom_putc(struct rt_serial_device *serial, char c) { rt_base_t level; struct ufunction *func; struct vcom *data; func = (struct ufunction*)serial->parent.user_data; data = (struct vcom*)func->user_data; RT_ASSERT(serial != RT_NULL); if (data->connected) { if(c == '\n' && (serial->parent.open_flag & RT_DEVICE_FLAG_STREAM)) { level = rt_hw_interrupt_disable(); rt_ringbuffer_putchar_force(&data->tx_ringbuffer, '\r'); rt_hw_interrupt_enable(level); rt_event_send(&data->tx_event, CDC_TX_HAS_DATE); } level = rt_hw_interrupt_disable(); rt_ringbuffer_putchar_force(&data->tx_ringbuffer, c); rt_hw_interrupt_enable(level); rt_event_send(&data->tx_event, CDC_TX_HAS_DATE); } return 1; } static const struct rt_uart_ops usb_vcom_ops = { _vcom_configure, _vcom_control, _vcom_putc, _vcom_getc, _vcom_tx }; /* Vcom Tx Thread */ static void vcom_tx_thread_entry(void* parameter) { rt_base_t level; rt_uint32_t res; struct ufunction *func = (struct ufunction *)parameter; struct vcom *data = (struct vcom*)func->user_data; rt_uint8_t ch[CDC_BULKIN_MAXSIZE]; while (1) { if ( (rt_event_recv(&data->tx_event, CDC_TX_HAS_DATE, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &res) != RT_EOK) || (!(res & CDC_TX_HAS_DATE)) ) { continue; } if(!(res & CDC_TX_HAS_DATE)) { continue; } while(rt_ringbuffer_data_len(&data->tx_ringbuffer)) { level = rt_hw_interrupt_disable(); res = rt_ringbuffer_get(&data->tx_ringbuffer, ch, CDC_BULKIN_MAXSIZE); rt_hw_interrupt_enable(level); if(!res) { continue; } if (!data->connected) { #ifdef RT_USING_SERIAL_V1 #ifndef VCOM_TX_USE_DMA if(data->serial.parent.open_flag & RT_DEVICE_FLAG_INT_TX) #else if(data->serial.parent.open_flag & RT_DEVICE_FLAG_DMA_TX) #endif #endif #ifdef RT_USING_SERIAL_V2 if(data->serial.parent.open_flag & RT_DEVICE_FLAG_TX_BLOCKING) #endif { /* drop msg */ #ifndef VCOM_TX_USE_DMA rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DONE); #else rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DMADONE); #endif } continue; } rt_completion_init(&data->wait); data->ep_in->request.buffer = ch; data->ep_in->request.size = res; data->ep_in->request.req_type = UIO_REQUEST_WRITE; rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request); if (rt_completion_wait(&data->wait, VCOM_TX_TIMEOUT) != RT_EOK) { LOG_D("vcom tx timeout"); } #ifdef RT_USING_SERIAL_V1 #ifndef VCOM_TX_USE_DMA if(data->serial.parent.open_flag & RT_DEVICE_FLAG_INT_TX) #else if(data->serial.parent.open_flag & RT_DEVICE_FLAG_DMA_TX) #endif #endif #ifdef RT_USING_SERIAL_V2 if(data->serial.parent.open_flag & RT_DEVICE_FLAG_TX_BLOCKING) #endif { #ifndef VCOM_TX_USE_DMA rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DONE); #else rt_hw_serial_isr(&data->serial,RT_SERIAL_EVENT_TX_DMADONE); #endif rt_event_send(&data->tx_event, CDC_TX_HAS_SPACE); } } } } static void rt_usb_vcom_init(struct ufunction *func) { rt_err_t result = RT_EOK; struct serial_configure config; struct vcom *data = (struct vcom*)func->user_data; /* initialize ring buffer */ rt_ringbuffer_init(&data->rx_ringbuffer, data->rx_rbp, CDC_RX_BUFSIZE); rt_ringbuffer_init(&data->tx_ringbuffer, data->tx_rbp, CDC_TX_BUFSIZE); rt_event_init(&data->tx_event, "vcom", RT_IPC_FLAG_FIFO); config.baud_rate = BAUD_RATE_115200; config.data_bits = DATA_BITS_8; config.stop_bits = STOP_BITS_1; config.parity = PARITY_NONE; config.bit_order = BIT_ORDER_LSB; config.invert = NRZ_NORMAL; #if defined(RT_USING_SERIAL_V1) config.bufsz = CDC_RX_BUFSIZE; #elif defined(RT_USING_SERIAL_V2) config.rx_bufsz = CDC_RX_BUFSIZE; config.tx_bufsz = CDC_TX_BUFSIZE; #endif data->serial.ops = &usb_vcom_ops; data->serial.serial_rx = RT_NULL; data->serial.config = config; /* register vcom device */ rt_hw_serial_register(&data->serial, VCOM_DEVICE, #ifndef VCOM_TX_USE_DMA RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX, #else RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_DMA_TX, #endif func); /* init usb device thread */ rt_thread_init(&vcom_thread, "vcom", vcom_tx_thread_entry, func, vcom_thread_stack, VCOM_TASK_STK_SIZE, 16, 20); result = rt_thread_startup(&vcom_thread); RT_ASSERT(result == RT_EOK); } struct udclass vcom_class = { .rt_usbd_function_create = rt_usbd_function_cdc_create }; int rt_usbd_vcom_class_register(void) { rt_usbd_class_register(&vcom_class); return 0; } INIT_PREV_EXPORT(rt_usbd_vcom_class_register); #endif