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