From 0eac26038d3572243dc2c6288111ba3593ddc342 Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Fri, 16 Mar 2018 23:50:03 -0500 Subject: [PATCH] Update USBCDC and USBSerial Update the USBCDC and USBSerial classes to the new API. This patch also updates the blocking behavior to unblock at the appropriate times and to let the processor sleep when blocking rather than busy waiting. --- usb/device/USBSerial/USBCDC.cpp | 641 +++++++++++++++++++++++++++++ usb/device/USBSerial/USBCDC.h | 203 +++++++++ usb/device/USBSerial/USBSerial.cpp | 61 +++ usb/device/USBSerial/USBSerial.h | 205 +++++++++ 4 files changed, 1110 insertions(+) create mode 100644 usb/device/USBSerial/USBCDC.cpp create mode 100644 usb/device/USBSerial/USBCDC.h create mode 100644 usb/device/USBSerial/USBSerial.cpp create mode 100644 usb/device/USBSerial/USBSerial.h diff --git a/usb/device/USBSerial/USBCDC.cpp b/usb/device/USBSerial/USBCDC.cpp new file mode 100644 index 0000000000..519ebe7218 --- /dev/null +++ b/usb/device/USBSerial/USBCDC.cpp @@ -0,0 +1,641 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdint.h" +#include "USBCDC.h" +#include "EndpointResolver.h" +#include "AsyncOp.h" + +static const uint8_t cdc_line_coding_default[7] = {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08}; + +#define DEFAULT_CONFIGURATION (1) + +#define CDC_SET_LINE_CODING 0x20 +#define CDC_GET_LINE_CODING 0x21 +#define CDC_SET_CONTROL_LINE_STATE 0x22 + +// Control Line State bits +#define CLS_DTR (1 << 0) +#define CLS_RTS (1 << 1) + +#define CDC_MAX_PACKET_SIZE 64 + +class USBCDC::AsyncWrite: public AsyncOp { +public: + AsyncWrite(uint8_t *buf, uint32_t size): AsyncOp(NULL), tx_buf(buf), tx_size(size), result(false) + { + + } + uint8_t *tx_buf; + uint32_t tx_size; + bool result; +}; + +class USBCDC::AsyncRead: public AsyncOp { +public: + AsyncRead(uint8_t *buf, uint32_t size, uint32_t *size_read, bool read_all) + : AsyncOp(NULL), rx_buf(buf), rx_size(size), rx_actual(size_read), all(read_all), result(false) + { + + } + uint8_t *rx_buf; + uint32_t rx_size; + uint32_t *rx_actual; + bool all; + bool result; +}; + +USBCDC::USBCDC(uint16_t vendor_id, uint16_t product_id, uint16_t product_release, bool connect_blocking) + : USBDevice(vendor_id, product_id, product_release) + +{ + _init(connect_blocking); +} + +USBCDC::USBCDC(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release, bool connect_blocking) + : USBDevice(phy, vendor_id, product_id, product_release) +{ + _init(connect_blocking); +} + +void USBCDC::_init(bool connect_blocking) +{ + memcpy(_cdc_line_coding, cdc_line_coding_default, sizeof(_cdc_line_coding)); + + EndpointResolver resolver(endpoint_table()); + resolver.endpoint_ctrl(CDC_MAX_PACKET_SIZE); + _bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE); + _bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE); + _int_in = resolver.endpoint_in(USB_EP_TYPE_INT, CDC_MAX_PACKET_SIZE); + MBED_ASSERT(resolver.valid()); + + _terminal_connected = false; + + _tx_in_progress = false; + _tx_buf = _tx_buffer; + _tx_size = 0; + + _rx_in_progress = false; + _rx_buf = _rx_buffer; + _rx_size = 0; + + init(); + + USBDevice::connect(false); + if (connect_blocking) { + wait_connected(); + } +} + +void USBCDC::callback_reset() +{ + assert_locked(); + /* Called in ISR context */ + + _change_terminal_connected(false); +}; + +void USBCDC::callback_state_change(DeviceState new_state) +{ + assert_locked(); + /* Called in ISR context */ + + if (new_state != Configured) { + _change_terminal_connected(false); + } +} + +void USBCDC::callback_request(const setup_packet_t *setup) +{ + assert_locked(); + /* Called in ISR context */ + + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + + /* Only process class-specific requests */ + if (setup->bmRequestType.Type == CLASS_TYPE) { + switch (setup->bRequest) { + case CDC_GET_LINE_CODING: + result = Send; + data = _cdc_line_coding; + size = 7; + break; + case CDC_SET_LINE_CODING: + result = Receive; + data = _cdc_new_line_coding; + size = 7; + break; + case CDC_SET_CONTROL_LINE_STATE: + if (setup->wValue & CLS_DTR) { + _change_terminal_connected(true); + } else { + _change_terminal_connected(false); + } + result = Success; + break; + default: + result = Failure; + break; + } + } + complete_request(result, data, size); +} + + +void USBCDC::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + assert_locked(); + /* Called in ISR context */ + + if (aborted) { + complete_request_xfer_done(false); + return; + } + + bool success = false; + + /* Process class-specific requests */ + if (setup->bmRequestType.Type == CLASS_TYPE) { + if ((setup->bRequest == CDC_SET_LINE_CODING) && (setup->wLength == 7)) { + if (memcmp(_cdc_line_coding, _cdc_new_line_coding, 7)) { + memcpy(_cdc_line_coding, _cdc_new_line_coding, 7); + + const uint8_t *buf = _cdc_line_coding; + int baud = buf[0] + (buf[1] << 8) + + (buf[2] << 16) + (buf[3] << 24); + int stop = buf[4]; + int bits = buf[6]; + int parity = buf[5]; + + line_coding_changed(baud, bits, parity, stop); + } + success = true; + } + if (setup->bRequest == CDC_GET_LINE_CODING) { + success = true; + } + } + + complete_request_xfer_done(success); +} + +void USBCDC::callback_set_configuration(uint8_t configuration) +{ + assert_locked(); + /* Called in ISR context */ + + bool ret = false; + if (configuration == DEFAULT_CONFIGURATION) { + // Configure endpoints > 0 + endpoint_add(_int_in, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_INT); + endpoint_add(_bulk_in, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBCDC::_send_isr); + endpoint_add(_bulk_out, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBCDC::_receive_isr); + + read_start(_bulk_out, _rx_buf, sizeof(_rx_buffer)); + _rx_in_progress = true; + + ret = true; + } + + complete_set_configuration(ret); +} + +void USBCDC::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + assert_locked(); + complete_set_interface(true); +} + +void USBCDC::_change_terminal_connected(bool connected) +{ + assert_locked(); + + if (connected) { + _connect_wake_all(); + } else { + _send_abort_all(); + _receive_abort_all(); + } + _terminal_connected = connected; +} + +void USBCDC::wait_connected() +{ + lock(); + + AsyncOp wait_op(NULL); + wait_op.start(&_connected_list); + if (_terminal_connected) { + wait_op.complete(); + } + + unlock(); + + wait_op.wait(); +} + +void USBCDC::_connect_wake_all() +{ + AsyncOp *wait_op = _connected_list.head(); + while (wait_op != NULL) { + wait_op->complete(); + wait_op = _connected_list.head(); + } +} + +bool USBCDC::send(uint8_t *buffer, uint32_t size) +{ + lock(); + + if (!_terminal_connected) { + unlock(); + return false; + } + AsyncWrite write_op(buffer, size); + write_op.start(&_tx_list); + _send_next(); + + unlock(); + + write_op.wait(); + return write_op.result; +} + +void USBCDC::_send_next() +{ + assert_locked(); + + uint32_t actual_size; + do { + // Set current TX operation or return if there are none left + AsyncWrite *tx_cur = _tx_list.head(); + if (tx_cur == NULL) { + break; + } + + actual_size = 0; + send_nb(tx_cur->tx_buf, tx_cur->tx_size, &actual_size, false); + tx_cur->tx_size -= actual_size; + tx_cur->tx_buf += actual_size; + if (tx_cur->tx_size == 0) { + tx_cur->result = true; + tx_cur->complete(); + } + } while (actual_size > 0); + + // Start transfer if it hasn't been + _send_isr_start(); +} + +void USBCDC::_send_abort_all() +{ + assert_locked(); + + if (_tx_in_progress) { + endpoint_abort(_bulk_in); + _tx_in_progress = false; + } + _tx_buf = _tx_buffer; + _tx_size = 0; + + AsyncWrite *tx_cur = _tx_list.head(); + while (tx_cur != NULL) { + tx_cur->result = false; + tx_cur->complete(); + tx_cur = _tx_list.head(); + } +} + +void USBCDC::send_nb(uint8_t *buffer, uint32_t size, uint32_t *actual, bool now) +{ + lock(); + + *actual = 0; + if (_terminal_connected && !_tx_in_progress) { + uint32_t free = sizeof(_tx_buffer) - _tx_size; + uint32_t write_size = free > size ? size : free; + if (size > 0) { + memcpy(_tx_buf, buffer, write_size); + } + _tx_size += write_size; + *actual = write_size; + if (now) { + _send_isr_start(); + } + } + + unlock(); +} + + +void USBCDC::_send_isr_start() +{ + assert_locked(); + + if (!_tx_in_progress && _tx_size) { + if (USBDevice::write_start(_bulk_in, _tx_buffer, _tx_size)) { + _tx_in_progress = true; + } + } +} + +/* +* Called by when CDC data is sent +* Warning: Called in ISR +*/ +void USBCDC::_send_isr(usb_ep_t endpoint) +{ + assert_locked(); + + write_finish(endpoint); + _tx_buf = _tx_buffer; + _tx_size = 0; + _tx_in_progress = false; + + _send_next(); + if (!_tx_in_progress) { + data_tx(); + } +} + +bool USBCDC::receive(uint8_t *buffer, uint32_t size, uint32_t *size_read) +{ + lock(); + + if (!_terminal_connected) { + unlock(); + return false; + } + bool read_all = size_read == NULL; + uint32_t size_read_dummy; + uint32_t *size_read_ptr = read_all ? &size_read_dummy : size_read; + *size_read_ptr = 0; + AsyncRead read_op(buffer, size, size_read_ptr, read_all); + read_op.start(&_rx_list); + _receive_next(); + + unlock(); + + read_op.wait(); + return read_op.result; +} + +void USBCDC::_receive_next() +{ + assert_locked(); + + uint32_t actual_size; + do { + // Set current RX operation or return if there are none left + AsyncRead *rx_cur = _rx_list.head(); + if (rx_cur == NULL) { + break; + } + + actual_size = 0; + receive_nb(rx_cur->rx_buf, rx_cur->rx_size, &actual_size); + rx_cur->rx_buf += actual_size; + *rx_cur->rx_actual += actual_size; + rx_cur->rx_size -= actual_size; + if ((!rx_cur->all && *rx_cur->rx_actual > 0) || (rx_cur->rx_size == 0)) { + // Wake thread if request is done + rx_cur->result = true; + rx_cur->complete(); + rx_cur = NULL; + } + } while (actual_size > 0); + + _receive_isr_start(); + +} + +void USBCDC::_receive_abort_all() +{ + assert_locked(); + + if (_rx_in_progress) { + endpoint_abort(_bulk_in); + _rx_in_progress = false; + } + _rx_buf = _rx_buffer; + _rx_size = 0; + + AsyncRead *rx_cur = _rx_list.head(); + while (rx_cur != NULL) { + rx_cur->result = false; + rx_cur->complete(); + rx_cur = _rx_list.head(); + } +} + +void USBCDC::receive_nb(uint8_t *buffer, uint32_t size, uint32_t *size_read) +{ + + *size_read = 0; + if (_terminal_connected && !_rx_in_progress) { + // Copy data over + uint32_t copy_size = _rx_size > size ? size : _rx_size; + memcpy(buffer, _rx_buf, copy_size); + *size_read = copy_size; + _rx_buf += copy_size; + _rx_size -= copy_size; + if (_rx_size == 0) { + _receive_isr_start(); + } + } +} + +void USBCDC::_receive_isr_start() +{ + if ((_rx_size == 0) && !_rx_in_progress) { + // Refill the buffer + read_start(_bulk_out, _rx_buffer, sizeof(_rx_buffer)); + _rx_in_progress = true; + } +} + +/* +* Called by when CDC data is received +* Warning: Called in ISR +*/ +void USBCDC::_receive_isr(usb_ep_t endpoint) +{ + assert_locked(); + + MBED_ASSERT(_rx_size == 0); + _rx_buf = _rx_buffer; + _rx_size = read_finish(_bulk_out); + _rx_in_progress = false; + _receive_next(); + if (!_rx_in_progress) { + data_rx(); + } + +} + +const uint8_t *USBCDC::device_desc() +{ + uint8_t ep0_size = endpoint_max_packet_size(0x00); + uint8_t device_descriptor_temp[] = { + 18, // bLength + 1, // bDescriptorType + 0x10, 0x01, // bcdUSB + 2, // bDeviceClass + 0, // bDeviceSubClass + 0, // bDeviceProtocol + ep0_size, // bMaxPacketSize0 + (uint8_t)(LSB(vendor_id)), (uint8_t)(MSB(vendor_id)), // idVendor + (uint8_t)(LSB(product_id)), (uint8_t)(MSB(product_id)),// idProduct + 0x00, 0x01, // bcdDevice + 1, // iManufacturer + 2, // iProduct + 3, // iSerialNumber + 1 // bNumConfigurations + }; + MBED_ASSERT(sizeof(device_descriptor_temp) == sizeof(device_descriptor)); + memcpy(device_descriptor, device_descriptor_temp, sizeof(device_descriptor)); + return device_descriptor; +} + +const uint8_t *USBCDC::string_iinterface_desc() +{ + static const uint8_t stringIinterfaceDescriptor[] = { + 0x08, + STRING_DESCRIPTOR, + 'C', 0, 'D', 0, 'C', 0, + }; + return stringIinterfaceDescriptor; +} + +const uint8_t *USBCDC::string_iproduct_desc() +{ + static const uint8_t stringIproductDescriptor[] = { + 0x16, + STRING_DESCRIPTOR, + 'C', 0, 'D', 0, 'C', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 + }; + return stringIproductDescriptor; +} + + +#define CONFIG1_DESC_SIZE (9+8+9+5+5+4+5+7+9+7+7) + +const uint8_t *USBCDC::configuration_desc() +{ + uint8_t config_descriptor_temp[] = { + // configuration descriptor + 9, // bLength + 2, // bDescriptorType + LSB(CONFIG1_DESC_SIZE), // wTotalLength + MSB(CONFIG1_DESC_SIZE), + 2, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0x80, // bmAttributes + 50, // bMaxPower + + // IAD to associate the two CDC interfaces + 0x08, // bLength + 0x0b, // bDescriptorType + 0x00, // bFirstInterface + 0x02, // bInterfaceCount + 0x02, // bFunctionClass + 0x02, // bFunctionSubClass + 0, // bFunctionProtocol + 0, // iFunction + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + 0, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + + // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x03, // bmCapabilities + 1, // bDataInterface + + // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 + 4, // bFunctionLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x06, // bmCapabilities + + // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + 0, // bMasterInterface + 1, // bSlaveInterface0 + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=intr) + LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB) + MSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (MSB) + 16, // bInterval + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + 1, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _bulk_in, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB) + MSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (MSB) + 0, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + _bulk_out, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB) + MSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (MSB) + 0 // bInterval + }; + + MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor)); + memcpy(_config_descriptor, config_descriptor_temp, sizeof(_config_descriptor)); + return _config_descriptor; +} diff --git a/usb/device/USBSerial/USBCDC.h b/usb/device/USBSerial/USBCDC.h new file mode 100644 index 0000000000..1c3c6dc381 --- /dev/null +++ b/usb/device/USBSerial/USBCDC.h @@ -0,0 +1,203 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBCDC_H +#define USBCDC_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice_Types.h" + +#include "USBDevice.h" +#include "LinkedList.h" + +class AsyncOp; + +class USBCDC: public USBDevice { +public: + + /* + * Constructor + * + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your preoduct_release + * @param connect_blocking define if the connection must be blocked if USB not plugged in + */ + USBCDC(uint16_t vendor_id, uint16_t product_id, uint16_t product_release, bool connect_blocking); + + /* + * Constructor + * + * @param phy USB phy to use + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your preoduct_release + * @param connect_blocking define if the connection must be blocked if USB not plugged in + */ + USBCDC(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release, bool connect_blocking); + + /** + * Block until the terminal is connected + */ + void wait_connected(); + + /* + * Send a buffer + * + * This function blocks until the full contents have been sent. + * + * @param buffer buffer to be sent + * @param size length of the buffer + * @returns true if successful false if interrupted due to a state change + */ + bool send(uint8_t *buffer, uint32_t size); + + /** + * Send what there is room for + * + * @param buffer data to send + * @param size maximum number of bytes to send + * @param actual a pointer to where to store the number of bytes sent + * @param now true to start data transmission, false to wait + */ + void send_nb(uint8_t *buffer, uint32_t size, uint32_t *actual, bool now = true); + + /* + * Read a buffer from a certain endpoint. Warning: blocking + * + * Blocks until at least one byte of data has been read (actual != NULL) or + * until the full size has been read (actual == NULL). + * + * @param buffer buffer where will be stored bytes + * @param size the maximum number of bytes to read + * @param actual A pointer to where to store the number of bytes actually read + * or NULL to read the full size + * @returns true if successful false if interrupted due to a state change + */ + bool receive(uint8_t *buffer, uint32_t size, uint32_t *actual=NULL); + + /** + * Read from the receive buffer + * + * @param buffer buffer to fill with data + * @param size maximum number of bytes read + * @param actual a pointer to where to store the number of bytes actually received + */ + void receive_nb(uint8_t *buffer, uint32_t size, uint32_t *actual); + +protected: + /* + * Get device descriptor. Warning: this method has to store the length of the report descriptor in reportLength. + * + * @returns pointer to the device descriptor + */ + virtual const uint8_t *device_desc(); + + /* + * Get string product descriptor + * + * @returns pointer to the string product descriptor + */ + virtual const uint8_t *string_iproduct_desc(); + + /* + * Get string interface descriptor + * + * @returns pointer to the string interface descriptor + */ + virtual const uint8_t *string_iinterface_desc(); + + /* + * Get configuration descriptor + * + * @returns pointer to the configuration descriptor + */ + virtual const uint8_t *configuration_desc(); + + /* + * Called by USBCallback_requestCompleted when CDC line coding is changed + * Warning: Called in ISR + * + * @param baud The baud rate + * @param bits The number of bits in a word (5-8) + * @param parity The parity + * @param stop The number of stop bits (1 or 2) + */ + virtual void line_coding_changed(int baud, int bits, int parity, int stop) {}; + + /* + * Called when there is data that can be read + */ + virtual void data_rx() {} + + /* + * Called when there is space in the TX buffer + */ + virtual void data_tx() {} + +protected: + + class AsyncWrite; + class AsyncRead; + + virtual void callback_reset(); + virtual void callback_state_change(DeviceState new_state); + virtual void callback_request(const setup_packet_t *setup); + virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); + virtual void callback_set_configuration(uint8_t configuration); + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + + void _init(bool connect_blocking); + + void _change_terminal_connected(bool connected); + void _connect_wake_all(); + + void _send_next(); + void _send_abort_all(); + void _send_isr_start(); + void _send_isr(usb_ep_t endpoint); + + void _receive_next(); + void _receive_abort_all(); + void _receive_isr_start(); + void _receive_isr(usb_ep_t endpoint); + + usb_ep_t _bulk_in; + usb_ep_t _bulk_out; + usb_ep_t _int_in; + + uint8_t _cdc_line_coding[7]; + uint8_t _cdc_new_line_coding[7]; + uint8_t _config_descriptor[75]; + + LinkedList _connected_list; + bool _terminal_connected; + + LinkedList _tx_list; + bool _tx_in_progress; + uint8_t _tx_buffer[64]; + uint8_t *_tx_buf; + uint32_t _tx_size; + + LinkedList _rx_list; + bool _rx_in_progress; + uint8_t _rx_buffer[64]; + uint8_t *_rx_buf; + uint32_t _rx_size; +}; + +#endif diff --git a/usb/device/USBSerial/USBSerial.cpp b/usb/device/USBSerial/USBSerial.cpp new file mode 100644 index 0000000000..376c8b8de2 --- /dev/null +++ b/usb/device/USBSerial/USBSerial.cpp @@ -0,0 +1,61 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdint.h" +#include "USBSerial.h" + +int USBSerial::_putc(int c) +{ + return send((uint8_t *)&c, 1) ? 1 : 0; +} + +int USBSerial::_getc() +{ + uint8_t c = 0; + if (receive(&c, sizeof(c))) { + return c; + } else { + return -1; + } +} + +void USBSerial::data_rx() +{ + assert_locked(); + + //call a potential handler + if (rx) { + rx.call(); + } +} + +uint8_t USBSerial::available() +{ + USBCDC::lock(); + + uint8_t size = 0; + if (!_rx_in_progress) { + size = _rx_size > 0xFF ? 0xFF : _rx_size; + } + + USBCDC::unlock(); + return size; +} + +bool USBSerial::connected() +{ + return _terminal_connected; +} diff --git a/usb/device/USBSerial/USBSerial.h b/usb/device/USBSerial/USBSerial.h new file mode 100644 index 0000000000..b3344b4a6e --- /dev/null +++ b/usb/device/USBSerial/USBSerial.h @@ -0,0 +1,205 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USBSERIAL_H +#define USBSERIAL_H + +#include "USBCDC.h" +#include "Stream.h" +#include "Callback.h" + +/** +* USBSerial example +* +* @code +* #include "mbed.h" +* #include "USBSerial.h" +* +* //Virtual serial port over USB +* USBSerial serial; +* +* int main(void) { +* +* while(1) +* { +* serial.printf("I am a virtual serial port\n"); +* wait(1); +* } +* } +* @endcode +*/ +class USBSerial: public USBCDC, public Stream { +public: + + /** + * Constructor + * + * @param vendor_id Your vendor_id (default: 0x1f00) + * @param product_id Your product_id (default: 0x2012) + * @param product_release Your preoduct_release (default: 0x0001) + * @param connect_blocking define if the connection must be blocked if USB not plugged in + * + */ + USBSerial(uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001, bool connect_blocking = true): USBCDC(vendor_id, product_id, product_release, connect_blocking) + { + _settings_changed_callback = 0; + }; + + /** + * Constructor + * + * @param phy USB phy to use + * @param vendor_id Your vendor_id (default: 0x1f00) + * @param product_id Your product_id (default: 0x2012) + * @param product_release Your preoduct_release (default: 0x0001) + * @param connect_blocking define if the connection must be blocked if USB not plugged in + * + */ + USBSerial(USBPhy *phy, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001, bool connect_blocking = true): USBCDC(phy, vendor_id, product_id, product_release, connect_blocking) + { + _settings_changed_callback = 0; + }; + + /** + * Send a character. You can use puts, printf. + * + * @param c character to be sent + * @returns true if there is no error, false otherwise + */ + virtual int _putc(int c); + + /** + * Read a character: blocking + * + * @returns character read + */ + virtual int _getc(); + + /** + * Check the number of bytes available. + * + * @returns the number of bytes available + */ + uint8_t available(); + + /** + * Check if the terminal is connected. + * + * @returns connection status + */ + bool connected(); + + /** Determine if there is a character available to read + * + * @returns + * 1 if there is a character available to read, + * 0 otherwise + */ + int readable() + { + return available() ? 1 : 0; + } + + /** Determine if there is space available to write a character + * + * @returns + * 1 if there is space to write a character, + * 0 otherwise + */ + int writeable() + { + return 1; // always return 1, for write operation is blocking + } + + /** + * Attach a member function to call when a packet is received. + * + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + */ + template + void attach(T *tptr, void (T::*mptr)(void)) + { + USBCDC::lock(); + + if ((mptr != NULL) && (tptr != NULL)) { + rx = Callback(mptr, tptr); + } + + USBCDC::unlock(); + } + + /** + * Attach a callback called when a packet is received + * + * @param fptr function pointer + */ + void attach(void (*fptr)(void)) + { + USBCDC::lock(); + + if (fptr != NULL) { + rx = Callback(fptr); + } + + USBCDC::unlock(); + } + + /** + * Attach a Callback called when a packet is received + * + * @param cb Callback to attach + */ + void attach(Callback &cb) + { + USBCDC::lock(); + + rx = cb; + + USBCDC::unlock(); + } + + /** + * Attach a callback to call when serial's settings are changed. + * + * @param fptr function pointer + */ + void attach(void (*fptr)(int baud, int bits, int parity, int stop)) + { + USBCDC::lock(); + + _settings_changed_callback = fptr; + + USBCDC::unlock(); + } + +protected: + virtual void data_rx(); + virtual void line_coding_changed(int baud, int bits, int parity, int stop) + { + assert_locked(); + + if (_settings_changed_callback) { + _settings_changed_callback(baud, bits, parity, stop); + } + } + +private: + Callback rx; + void (*_settings_changed_callback)(int baud, int bits, int parity, int stop); +}; + +#endif