diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index 4c42387..df8d71d 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -21,6 +21,10 @@ jobs: usb/usb_host_msc; usb/usb_host_uvc; usb/esp_modem_usb_dte; + usb/usb_host_vcp; + usb/usb_host_ch34x_vcp; + usb/usb_host_cp210x_vcp; + usb/usb_host_ftdi_vcp; fmt; namespace: "espressif" api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/usb/test_app/CMakeLists.txt b/usb/test_app/CMakeLists.txt index 1a33263..c61c261 100644 --- a/usb/test_app/CMakeLists.txt +++ b/usb/test_app/CMakeLists.txt @@ -5,6 +5,10 @@ cmake_minimum_required(VERSION 3.16) set(EXTRA_COMPONENT_DIRS ../usb_host_cdc_acm ../usb_host_msc ../usb_host_uvc + ../usb_host_ch34x_vcp + ../usb_host_cp210x_vcp + ../usb_host_ftdi_vcp + ../usb_host_vcp ../esp_modem_usb_dte ) diff --git a/usb/test_app/sdkconfig.defaults b/usb/test_app/sdkconfig.defaults index bab404d..a7c0cac 100644 --- a/usb/test_app/sdkconfig.defaults +++ b/usb/test_app/sdkconfig.defaults @@ -14,3 +14,5 @@ CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y CONFIG_COMPILER_STACK_CHECK=y CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y + +CONFIG_COMPILER_CXX_EXCEPTIONS=y diff --git a/usb/usb_host_ch34x_vcp/CMakeLists.txt b/usb/usb_host_ch34x_vcp/CMakeLists.txt new file mode 100644 index 0000000..f7c14ae --- /dev/null +++ b/usb/usb_host_ch34x_vcp/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "usb_host_ch34x_vcp.cpp" + INCLUDE_DIRS "include") diff --git a/usb/usb_host_ch34x_vcp/LICENSE b/usb/usb_host_ch34x_vcp/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/usb/usb_host_ch34x_vcp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/usb/usb_host_ch34x_vcp/README.md b/usb/usb_host_ch34x_vcp/README.md new file mode 100644 index 0000000..4673a69 --- /dev/null +++ b/usb/usb_host_ch34x_vcp/README.md @@ -0,0 +1,6 @@ +# CH34x USB-UART converter driver + +Limited implementation only. The vendor does not provide full specification. + +* CH340 and CH341 supported +* [Datasheet](http://www.wch-ic.com/downloads/CH341DS1_PDF.html) diff --git a/usb/usb_host_ch34x_vcp/idf_component.yml b/usb/usb_host_ch34x_vcp/idf_component.yml new file mode 100644 index 0000000..c86599d --- /dev/null +++ b/usb/usb_host_ch34x_vcp/idf_component.yml @@ -0,0 +1,9 @@ +## IDF Component Manager Manifest File +version: "1.0.0" +description: USB Host driver for CH34x series of chips +url: https://github.com/espressif/idf-extra-components/tree/master/usb/usb_host_ch34x_vcp +dependencies: + espressif/usb_host_cdc_acm: + version: "^1.0.4" + public: true + idf: ">=4.4" diff --git a/usb/usb_host_ch34x_vcp/include/usb/vcp_ch34x.hpp b/usb/usb_host_ch34x_vcp/include/usb/vcp_ch34x.hpp new file mode 100644 index 0000000..acf0f89 --- /dev/null +++ b/usb/usb_host_ch34x_vcp/include/usb/vcp_ch34x.hpp @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021 WCH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "usb/cdc_acm_host.h" + +#define NANJING_QINHENG_MICROE_VID (0x1A86) +#define CH340_PID (0x7522) +#define CH340_PID_1 (0x7523) +#define CH341_PID (0x5523) + +namespace esp_usb { +class CH34x : public CdcAcmDevice { +public: + /** + * @brief Constructor for this CH34x driver + * + * @note USB Host library and CDC-ACM driver must be already installed + * + * @param[in] pid PID eg. CH340_PID + * @param[in] dev_config CDC device configuration + * @param[in] interface_idx Interface number + * @return CdcAcmDevice Pointer to created and opened CH34x device + */ + CH34x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx = 0); + + /** + * @brief Set Line Coding method + * + * @note Overrides default implementation in CDC-ACM driver + * @param[in] line_coding Line Coding structure + * @return esp_err_t + */ + esp_err_t line_coding_set(cdc_acm_line_coding_t *line_coding); + + /** + * @brief Set Control Line State method + * + * @note Overrides default implementation in CDC-ACM driver + * @note Both signals are active low + * @param[in] dtr Indicates to DCE if DTE is present or not. This signal corresponds to V.24 signal 108/2 and RS-232 signal Data Terminal Ready. + * @param[in] rts Carrier control for half duplex modems. This signal corresponds to V.24 signal 105 and RS-232 signal Request To Send. + * @return esp_err_t + */ + esp_err_t set_control_line_state(bool dtr, bool rts); + + // List of supported VIDs and PIDs + static constexpr uint16_t vid = NANJING_QINHENG_MICROE_VID; + static constexpr std::array pids = {CH340_PID, CH340_PID_1, CH341_PID}; + +private: + const uint8_t intf; + + // Make open functions from CdcAcmDevice class private + using CdcAcmDevice::open; + using CdcAcmDevice::open_vendor_specific; + using CdcAcmDevice::send_break; // Break is not supported by CH34x + using CdcAcmDevice::line_coding_get; // Manufacturer doesn't provide enough information to implement this + + // This function comes from official Linux driver + static int calculate_baud_divisor(unsigned int baud_rate, unsigned char *factor, unsigned char *divisor); +}; +} // namespace esp_usb diff --git a/usb/usb_host_ch34x_vcp/usb_host_ch34x_vcp.cpp b/usb/usb_host_ch34x_vcp/usb_host_ch34x_vcp.cpp new file mode 100644 index 0000000..f0d9b4f --- /dev/null +++ b/usb/usb_host_ch34x_vcp/usb_host_ch34x_vcp.cpp @@ -0,0 +1,210 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021 WCH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "usb/vcp_ch34x.hpp" +#include "usb/usb_types_ch9.h" +#include "esp_log.h" +#include "esp_check.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" + +#ifndef CONFIG_COMPILER_CXX_EXCEPTIONS +#error This component requires C++ exceptions +#endif + +#define CH34X_READ_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_RECIP_DEVICE | USB_BM_REQUEST_TYPE_DIR_IN) +#define CH34X_WRITE_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_RECIP_DEVICE | USB_BM_REQUEST_TYPE_DIR_OUT) + +#define CH34X_CMD_READ_TYPE 0xC0 +#define CH34X_CMD_READ 0x95 +#define CH34X_CMD_WRITE 0x9A +#define CH34X_CMD_SERIAL_INIT 0xA1 +#define CH34X_CMD_MODEM_OUT 0xA4 +#define CH34X_CMD_VERSION 0x5F + +// For CMD 0xA4 +#define CH34X_UART_CTS 0x01 +#define CH34X_UART_DSR 0x02 +#define CH34X_UART_RING 0x04 +#define CH34X_UART_DCD 0x08 +#define CH34X_CONTROL_OUT 0x10 +#define CH34X_CONTROL_DTR 0x20 +#define CH34X_CONTROL_RTS 0x40 + +// Uart state +#define CH34X_UART_STATE 0x00 +#define CH34X_UART_OVERRUN_ERROR 0x01 +#define CH34X_UART_BREAK_ERROR // no define +#define CH34X_UART_PARITY_ERROR 0x02 +#define CH34X_UART_FRAME_ERROR 0x06 +#define CH34X_UART_RECV_ERROR 0x02 +#define CH34X_UART_STATE_TRANSIENT_MASK 0x07 + +//CH34x Baud Rate +#define CH34x_BAUDRATE_FACTOR 1532620800 +#define CH34x_BAUDRATE_DIVMAX 3 + +// Line Coding Register (LCR) +#define CH34x_REG_LCR 0x18 +#define CH34x_LCR_ENABLE_RX 0x80 +#define CH34x_LCR_ENABLE_TX 0x40 +#define CH34x_LCR_MARK_SPACE 0x20 +#define CH34x_LCR_PAR_EVEN 0x10 +#define CH34x_LCR_ENABLE_PAR 0x08 +#define CH34x_LCR_STOP_BITS_2 0x04 +#define CH34x_LCR_CS8 0x03 +#define CH34x_LCR_CS7 0x02 +#define CH34x_LCR_CS6 0x01 +#define CH34x_LCR_CS5 0x00 + +static const char *TAG = "CH34X"; + +namespace esp_usb { +CH34x::CH34x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) + : intf(interface_idx) +{ + const esp_err_t err = this->open_vendor_specific(vid, pid, this->intf, dev_config); + if (err != ESP_OK) { + throw (err); + } +}; + +esp_err_t CH34x::line_coding_set(cdc_acm_line_coding_t *line_coding) +{ + assert(line_coding); + + // Baudrate + if (line_coding->dwDTERate != 0) { + uint8_t factor, divisor; + if (calculate_baud_divisor(line_coding->dwDTERate, &factor, &divisor) != 0) { + return ESP_ERR_INVALID_ARG; + } + uint16_t baud_reg_val = (factor << 8) | divisor; + baud_reg_val |= BIT7; + ESP_RETURN_ON_ERROR(this->send_custom_request(CH34X_WRITE_REQ, CH34X_CMD_WRITE, 0x1312, baud_reg_val, 0, NULL), TAG, "Set baudrate failed"); + } + + // Line coding + if (line_coding->bDataBits != 0) { + uint8_t lcr = CH34x_LCR_ENABLE_RX | CH34x_LCR_ENABLE_TX; + + switch (line_coding->bDataBits) { + case 5: + lcr |= CH34x_LCR_CS5; + break; + case 6: + lcr |= CH34x_LCR_CS6; + break; + case 7: + lcr |= CH34x_LCR_CS7; + break; + case 8: + lcr |= CH34x_LCR_CS8; + break; + default: + return ESP_ERR_INVALID_ARG; + } + + switch (line_coding->bParityType) { + case 0: + break; + case 1: + lcr |= CH34x_LCR_ENABLE_PAR; + break; + case 2: + lcr |= CH34x_LCR_ENABLE_PAR | CH34x_LCR_PAR_EVEN; + break; + case 3: // Mark + case 4: + lcr |= CH34x_LCR_ENABLE_PAR | CH34x_LCR_MARK_SPACE; + break; + default: + return ESP_ERR_INVALID_ARG; + } + + switch (line_coding->bCharFormat) { + case 0: + break; // 1 Stop bit + case 2: + lcr |= CH34x_LCR_STOP_BITS_2; + break; + default: + return ESP_ERR_INVALID_ARG; // 1.5 stop bits not supported + } + + ESP_RETURN_ON_ERROR(this->send_custom_request(CH34X_WRITE_REQ, CH34X_CMD_WRITE, 0x2518, lcr, 0, NULL), TAG, + "Set line coding failed"); + } + + return ESP_OK; +} + +esp_err_t CH34x::set_control_line_state(bool dtr, bool rts) +{ + uint16_t wValue = 0; + if (dtr) { + wValue |= CH34X_CONTROL_DTR; + } + if (rts) { + wValue |= CH34X_CONTROL_RTS; + } + return this->send_custom_request(CH34X_WRITE_REQ, CH34X_CMD_MODEM_OUT, wValue, this->intf, 0, NULL); +} + +int CH34x::calculate_baud_divisor(unsigned int baud_rate, unsigned char *factor, unsigned char *divisor) +{ + unsigned char a; + unsigned char b; + unsigned long c; + + assert(factor); + assert(divisor); + + switch (baud_rate) { + case 921600: + a = 0xf3; + b = 7; + break; + case 307200: + a = 0xd9; + b = 7; + break; + default: + if (baud_rate > 6000000 / 255) { + b = 3; + c = 6000000; + } else if (baud_rate > 750000 / 255) { + b = 2; + c = 750000; + } else if (baud_rate > 93750 / 255) { + b = 1; + c = 93750; + } else { + b = 0; + c = 11719; + } + + a = (unsigned char)(c / baud_rate); + if (a == 0 || a == 0xFF) { + return -1; // Can't set required baud rate + } + // Deal with integer division + const int delta_0 = c / a - baud_rate; + const int delta_1 = baud_rate - c / (a + 1); + if (delta_0 > delta_1) { + a++; + } + a = 256 - a; + break; + } + + *factor = a; + *divisor = b; + return 0; +} +} diff --git a/usb/usb_host_cp210x_vcp/CMakeLists.txt b/usb/usb_host_cp210x_vcp/CMakeLists.txt new file mode 100644 index 0000000..c710373 --- /dev/null +++ b/usb/usb_host_cp210x_vcp/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "usb_host_cp210x_vcp.cpp" + INCLUDE_DIRS "include") diff --git a/usb/usb_host_cp210x_vcp/LICENSE b/usb/usb_host_cp210x_vcp/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/usb/usb_host_cp210x_vcp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/usb/usb_host_cp210x_vcp/README.md b/usb/usb_host_cp210x_vcp/README.md new file mode 100644 index 0000000..0b68101 --- /dev/null +++ b/usb/usb_host_cp210x_vcp/README.md @@ -0,0 +1,4 @@ +# Silicon Labs CP210x USB-UART converter driver + +* [Datasheet](https://www.silabs.com/documents/public/data-sheets/CP2102-9.pdf) +* [Application note](https://www.silabs.com/documents/public/application-notes/an197.pdf) diff --git a/usb/usb_host_cp210x_vcp/idf_component.yml b/usb/usb_host_cp210x_vcp/idf_component.yml new file mode 100644 index 0000000..73dae32 --- /dev/null +++ b/usb/usb_host_cp210x_vcp/idf_component.yml @@ -0,0 +1,9 @@ +## IDF Component Manager Manifest File +version: "1.0.0" +description: USB Host driver for CP210x series of chips +url: https://github.com/espressif/idf-extra-components/tree/master/usb/usb_host_cp210x_vcp +dependencies: + espressif/usb_host_cdc_acm: + version: "^1.0.4" + public: true + idf: ">=4.4" diff --git a/usb/usb_host_cp210x_vcp/include/usb/vcp_cp210x.hpp b/usb/usb_host_cp210x_vcp/include/usb/vcp_cp210x.hpp new file mode 100644 index 0000000..8ae3c07 --- /dev/null +++ b/usb/usb_host_cp210x_vcp/include/usb/vcp_cp210x.hpp @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "usb/cdc_acm_host.h" + +#define SILICON_LABS_VID (0x10C4) +#define CP210X_PID (0xEA60) // Single i.e. CP2101 - CP2104 +#define CP2105_PID (0xEA70) // Dual +#define CP2108_PID (0xEA71) // Quad + +// @see AN571: CP210x Virtual COM Port Interface, chapter 5 +#define CP210X_CMD_IFC_ENABLE (0x00) // Enable or disable the interface +#define CP210X_CMD_SET_BAUDDIV (0x01) // Set the baud rate divisor +#define CP210X_CMD_GET_BAUDDIV (0x02) // Get the baud rate divisor +#define CP210X_CMD_SET_LINE_CTL (0x03) // Set the line control +#define CP210X_CMD_GET_LINE_CTL (0x04) // Get the line control +#define CP210X_CMD_SET_BREAK (0x05) // Set a BREAK +#define CP210X_CMD_IMM_CHAR (0x06) // Send character out of order +#define CP210X_CMD_SET_MHS (0x07) // Set modem handshaking +#define CP210X_CMD_GET_MDMSTS (0x08) // Get modem status +#define CP210X_CMD_SET_XON (0x09) // Emulate XON +#define CP210X_CMD_SET_XOFF (0x0A) // Emulate XOFF +#define CP210X_CMD_SET_EVENTMASK (0x0B) // Set the event mask +#define CP210X_CMD_GET_EVENTMASK (0x0C) // Get the event mask +#define CP210X_CMD_GET_EVENTSTATE (0x16) // Get the event state +#define CP210X_CMD_SET_RECEIVE (0x17) // Set receiver max timeout +#define CP210X_CMD_GET_RECEIVE (0x18) // Get receiver max timeout +#define CP210X_CMD_SET_CHAR (0x0D) // Set special character individually +#define CP210X_CMD_GET_CHARS (0x0E) // Get special characters +#define CP210X_CMD_GET_PROPS (0x0F) // Get properties +#define CP210X_CMD_GET_COMM_STATUS (0x10) // Get the serial status +#define CP210X_CMD_RESET (0x11) // Reset +#define CP210X_CMD_PURGE (0x12) // Purge +#define CP210X_CMD_SET_FLOW (0x13) // Set flow control +#define CP210X_CMD_GET_FLOW (0x14) // Get flow control +#define CP210X_CMD_EMBED_EVENTS (0x15) // Control embedding of events in the data stream +#define CP210X_CMD_GET_BAUDRATE (0x1D) // Get the baud rate +#define CP210X_CMD_SET_BAUDRATE (0x1E) // Set the baud rate +#define CP210X_CMD_SET_CHARS (0x19) // Set special characters +#define CP210X_CMD_VENDOR_SPECIFIC (0xFF) // Read/write latch values + +namespace esp_usb { +class CP210x : public CdcAcmDevice { +public: + /** + * @brief Constructor for this CP210x driver + * + * @note USB Host library and CDC-ACM driver must be already installed + * + * @param[in] pid PID eg. CP210X_PID + * @param[in] dev_config CDC device configuration + * @param[in] interface_idx Interface number + * @return CdcAcmDevice Pointer to created and opened CP210x device + */ + CP210x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx = 0); + + /** + * @brief Get Line Coding method + * + * @see AN571: CP210x Virtual COM Port Interface chapters 5.6 and 5.8 + * @note Overrides default implementation in CDC-ACM driver + * @param[out] line_coding Line Coding structure + * @return esp_err_t + */ + esp_err_t line_coding_get(cdc_acm_line_coding_t *line_coding); + + /** + * @brief Set Line Coding method + * + * @see AN571: CP210x Virtual COM Port Interface chapters 5.5 and 5.7 + * @note Overrides default implementation in CDC-ACM driver + * @param[in] line_coding Line Coding structure + * @return esp_err_t + */ + esp_err_t line_coding_set(cdc_acm_line_coding_t *line_coding); + + /** + * @brief Set Control Line State method + * + * @see AN571: CP210x Virtual COM Port Interface chapter 5.9 + * @note Overrides default implementation in CDC-ACM driver + * @note Both signals are active low + * @param[in] dtr Indicates to DCE if DTE is present or not. This signal corresponds to V.24 signal 108/2 and RS-232 signal Data Terminal Ready. + * @param[in] rts Carrier control for half duplex modems. This signal corresponds to V.24 signal 105 and RS-232 signal Request To Send. + * @return esp_err_t + */ + esp_err_t set_control_line_state(bool dtr, bool rts); + + /** + * @brief Send Break method + * + * @see AN571: CP210x Virtual COM Port Interface chapter 5.20 + * @note Overrides default implementation in CDC-ACM driver + * @param[in] duration_ms Duration of the break condition in [ms] + * @return esp_err_t + */ + esp_err_t send_break(uint16_t duration_ms); + + // List of supported VIDs and PIDs + static constexpr uint16_t vid = SILICON_LABS_VID; + static constexpr std::array pids = {CP210X_PID, CP2105_PID, CP2108_PID}; + +private: + const uint8_t intf; + + // Make open functions from CdcAcmDevice class private + using CdcAcmDevice::open; + using CdcAcmDevice::open_vendor_specific; +}; +} // namespace esp_usb diff --git a/usb/usb_host_cp210x_vcp/usb_host_cp210x_vcp.cpp b/usb/usb_host_cp210x_vcp/usb_host_cp210x_vcp.cpp new file mode 100644 index 0000000..4e466f2 --- /dev/null +++ b/usb/usb_host_cp210x_vcp/usb_host_cp210x_vcp.cpp @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "usb/vcp_cp210x.hpp" +#include "usb/usb_types_ch9.h" +#include "esp_log.h" +#include "esp_check.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" + +#ifndef CONFIG_COMPILER_CXX_EXCEPTIONS +#error This component requires C++ exceptions +#endif + +#define CP210X_READ_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_RECIP_INTERFACE | USB_BM_REQUEST_TYPE_DIR_IN) +#define CP210X_WRITE_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_RECIP_INTERFACE | USB_BM_REQUEST_TYPE_DIR_OUT) + +namespace esp_usb { +CP210x::CP210x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) + : intf(interface_idx) +{ + esp_err_t err; + err = this->open_vendor_specific(vid, pid, this->intf, dev_config); + if (err != ESP_OK) { + throw (err); + } + + // CP210X interfaces must be explicitly enabled + err = this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_IFC_ENABLE, 1, this->intf, 0, NULL); + if (err != ESP_OK) { + throw (err); + } +}; + +esp_err_t CP210x::line_coding_get(cdc_acm_line_coding_t *line_coding) +{ + assert(line_coding); + + ESP_RETURN_ON_ERROR(this->send_custom_request(CP210X_READ_REQ, CP210X_CMD_GET_BAUDRATE, 0, this->intf, sizeof(line_coding->dwDTERate), (uint8_t *)&line_coding->dwDTERate), "CP210X",); + + uint8_t temp_data[2]; + ESP_RETURN_ON_ERROR(this->send_custom_request(CP210X_READ_REQ, CP210X_CMD_GET_LINE_CTL, 0, this->intf, 2, temp_data), "CP210X",); + line_coding->bCharFormat = temp_data[0] & 0x0F; + line_coding->bParityType = (temp_data[0] & 0xF0) >> 4; + line_coding->bDataBits = temp_data[1]; + + return ESP_OK; +} + +esp_err_t CP210x::line_coding_set(cdc_acm_line_coding_t *line_coding) +{ + assert(line_coding); + + if (line_coding->dwDTERate != 0) { + ESP_RETURN_ON_ERROR(this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_BAUDRATE, 0, this->intf, sizeof(line_coding->dwDTERate), (uint8_t *)&line_coding->dwDTERate), "CP210X",); + } + + if (line_coding->bDataBits != 0) { + const uint16_t wValue = line_coding->bCharFormat | (line_coding->bParityType << 4) | (line_coding->bDataBits << 8); + return this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_LINE_CTL, wValue, this->intf, 0, NULL); + } + return ESP_OK; +} + +esp_err_t CP210x::set_control_line_state(bool dtr, bool rts) +{ + const uint16_t wValue = (uint16_t)dtr | ((uint16_t)rts << 1) | 0x0300; + return this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_MHS, wValue, this->intf, 0, NULL); +} + +esp_err_t CP210x::send_break(uint16_t duration_ms) +{ + ESP_RETURN_ON_ERROR(this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_BREAK, 1, this->intf, 0, NULL), "CP210x",); + vTaskDelay(pdMS_TO_TICKS(duration_ms)); + return this->send_custom_request(CP210X_WRITE_REQ, CP210X_CMD_SET_BREAK, 0, this->intf, 0, NULL); +} +} diff --git a/usb/usb_host_ftdi_vcp/CMakeLists.txt b/usb/usb_host_ftdi_vcp/CMakeLists.txt new file mode 100644 index 0000000..02f1169 --- /dev/null +++ b/usb/usb_host_ftdi_vcp/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "usb_host_ftdi_vcp.cpp" + INCLUDE_DIRS "include") diff --git a/usb/usb_host_ftdi_vcp/LICENSE b/usb/usb_host_ftdi_vcp/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/usb/usb_host_ftdi_vcp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/usb/usb_host_ftdi_vcp/README.md b/usb/usb_host_ftdi_vcp/README.md new file mode 100644 index 0000000..56d5e3c --- /dev/null +++ b/usb/usb_host_ftdi_vcp/README.md @@ -0,0 +1,5 @@ +# FTDI UART-USB converters driver + +Supported devices: +* FT231 +* FT232 diff --git a/usb/usb_host_ftdi_vcp/idf_component.yml b/usb/usb_host_ftdi_vcp/idf_component.yml new file mode 100644 index 0000000..e368bce --- /dev/null +++ b/usb/usb_host_ftdi_vcp/idf_component.yml @@ -0,0 +1,9 @@ +## IDF Component Manager Manifest File +version: "1.0.0" +description: USB Host driver for FTDI USB<->UART converters series of chips +url: https://github.com/espressif/idf-extra-components/tree/master/usb/usb_host_ftdi_vcp +dependencies: + espressif/usb_host_cdc_acm: + version: "^1.0.4" + public: true + idf: ">=4.4" diff --git a/usb/usb_host_ftdi_vcp/include/usb/vcp_ftdi.hpp b/usb/usb_host_ftdi_vcp/include/usb/vcp_ftdi.hpp new file mode 100644 index 0000000..17f7189 --- /dev/null +++ b/usb/usb_host_ftdi_vcp/include/usb/vcp_ftdi.hpp @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "usb/cdc_acm_host.h" + +#define FTDI_VID (0x0403) +#define FT232_PID (0x6001) +#define FT231_PID (0x6015) + +#define FTDI_CMD_RESET (0x00) +#define FTDI_CMD_SET_FLOW (0x01) +#define FTDI_CMD_SET_MHS (0x02) // Modem handshaking +#define FTDI_CMD_SET_BAUDRATE (0x03) +#define FTDI_CMD_SET_LINE_CTL (0x04) +#define FTDI_CMD_GET_MDMSTS (0x05) // Modem status + +namespace esp_usb { +class FT23x : public CdcAcmDevice { +public: + /** + * @brief Constructor for this FTDI driver + * + * @note USB Host library and CDC-ACM driver must be already installed + * + * @param[in] pid PID eg. FTDI_FT232_PID + * @param[in] dev_config CDC device configuration + * @param[in] interface_idx Interface number + * @return CdcAcmDevice Pointer to created and opened FTDI device + */ + FT23x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx = 0); + + /** + * @brief Set Line Coding method + * + * @note Overrides default implementation in CDC-ACM driver + * @param[in] line_coding Line Coding structure + * @return esp_err_t + */ + esp_err_t line_coding_set(cdc_acm_line_coding_t *line_coding); + + /** + * @brief Set Control Line State method + * + * @note Overrides default implementation in CDC-ACM driver + * @note Both signals are active low + * @param[in] dtr Indicates to DCE if DTE is present or not. This signal corresponds to V.24 signal 108/2 and RS-232 signal Data Terminal Ready. + * @param[in] rts Carrier control for half duplex modems. This signal corresponds to V.24 signal 105 and RS-232 signal Request To Send. + * @return esp_err_t + */ + esp_err_t set_control_line_state(bool dtr, bool rts); + + // List of supported VIDs and PIDs + static constexpr uint16_t vid = FTDI_VID; + static constexpr std::array pids = {FT232_PID, FT231_PID}; + +private: + const uint8_t intf; + const cdc_acm_data_callback_t user_data_cb; + const cdc_acm_host_dev_callback_t user_event_cb; + void *user_arg; + uint16_t uart_state; + + /** + * @brief FT23x's RX data handler + * + * First two bytes are status bytes, the RX data start at data[2]. + * Coding of status bytes: + * Byte 0: + * Bit 0: Full Speed packet + * Bit 1: High Speed packet + * Bit 4: CTS + * Bit 5: DSR + * Bit 6: RI + * Bit 7: DCD + * Byte 1: + * Bit 1: RX overflow + * Bit 2: Parity error + * Bit 3: Framing error + * Bit 4: Break received + * Bit 5: Transmitter holding register empty + * Bit 6: Transmitter empty + * + * @todo When CTS is asserted, this driver should stop sending data. + * + * @param[in] data Received data + * @param[in] data_len Received data length + * @param[in] user_arg Pointer to FT23x class + */ + static void ftdi_rx(uint8_t *data, size_t data_len, void *user_arg); + + // Just a wrapper to recover user's argument + static void ftdi_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx); + + /** + * @brief Construct a new calculate baudrate object + * + * A Baud rate for the FT232R, FT2232 (UART mode) or FT232B is generated using the chips + * internal 48MHz clock. This is input to Baud rate generator circuitry where it is then divided by 16 + * and fed into a prescaler as a 3MHz reference clock. This 3MHz reference clock is then divided + * down to provide the required Baud rate for the device's on chip UART. The value of the Baud rate + * divisor is an integer plus a sub-integer prescaler. + * Allowed values for the Baud rate divisor are: + * Divisor = n + 0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875; where n is an integer between 2 and + * 16384 (214). + * + * Note: Divisor = 1 and Divisor = 0 are special cases. A divisor of 0 will give 3 MBaud, and a divisor + * of 1 will give 2 MBaud. Sub-integer divisors between 0 and 2 are not allowed. + * Therefore the value of the divisor needed for a given Baud rate is found by dividing 3000000 by the + * required Baud rate. + * + * @see FTDI AN232B-05 Configuring FT232R, FT2232 and FT232B Baud Rates + * @param[in] baudrate + * @param[out] wValue + * @param[out] wIndex + */ + static int calculate_baudrate(uint32_t baudrate, uint16_t *wValue, uint16_t *wIndex); + + // Make open functions from CdcAcmDevice class private + using CdcAcmDevice::open; + using CdcAcmDevice::open_vendor_specific; + using CdcAcmDevice::line_coding_get; // Not implemented + using CdcAcmDevice::send_break; // Not implemented +}; +} // namespace esp_usb diff --git a/usb/usb_host_ftdi_vcp/usb_host_ftdi_vcp.cpp b/usb/usb_host_ftdi_vcp/usb_host_ftdi_vcp.cpp new file mode 100644 index 0000000..6e8aba4 --- /dev/null +++ b/usb/usb_host_ftdi_vcp/usb_host_ftdi_vcp.cpp @@ -0,0 +1,175 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "usb/vcp_ftdi.hpp" +#include "usb/usb_types_ch9.h" +#include "esp_log.h" +#include "esp_check.h" +#include "sdkconfig.h" + +#ifndef CONFIG_COMPILER_CXX_EXCEPTIONS +#error This component requires C++ exceptions +#endif + +#define FTDI_READ_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_DIR_IN) +#define FTDI_WRITE_REQ (USB_BM_REQUEST_TYPE_TYPE_VENDOR | USB_BM_REQUEST_TYPE_DIR_OUT) + +namespace esp_usb { +FT23x::FT23x(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) + : intf(interface_idx), user_data_cb(dev_config->data_cb), user_event_cb(dev_config->event_cb), + user_arg(dev_config->user_arg), uart_state(0) +{ + cdc_acm_host_device_config_t ftdi_config; + memcpy(&ftdi_config, dev_config, sizeof(cdc_acm_host_device_config_t)); + // FT23x reports modem status in first two bytes of RX data + // so here we override the RX handler with our own + + if (dev_config->data_cb) { + ftdi_config.data_cb = ftdi_rx; + ftdi_config.user_arg = this; + } + + if (dev_config->event_cb) { + ftdi_config.event_cb = ftdi_event; + ftdi_config.user_arg = this; + } + + esp_err_t err; + err = this->open_vendor_specific(vid, pid, this->intf, &ftdi_config); + if (err != ESP_OK) { + throw (err); + } + + // FT23x interface must be first reset and configured (115200 8N1) + err = this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_RESET, 0, this->intf + 1, 0, NULL); + if (err != ESP_OK) { + throw (err); + } + + cdc_acm_line_coding_t line_coding = { + .dwDTERate = 115200, + .bCharFormat = 0, + .bParityType = 0, + .bDataBits = 8, + }; + err = this->line_coding_set(&line_coding); + if (err != ESP_OK) { + throw (err); + } +}; + +esp_err_t FT23x::line_coding_set(cdc_acm_line_coding_t *line_coding) +{ + assert(line_coding); + + if (line_coding->dwDTERate != 0) { + uint16_t wIndex, wValue; + calculate_baudrate(line_coding->dwDTERate, &wValue, &wIndex); + ESP_RETURN_ON_ERROR(this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_SET_BAUDRATE, wValue, wIndex, 0, NULL), "FT23x",); + } + + if (line_coding->bDataBits != 0) { + const uint16_t wValue = (line_coding->bDataBits) | (line_coding->bParityType << 8) | (line_coding->bCharFormat << 11); + return this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_SET_LINE_CTL, wValue, this->intf, 0, NULL); + } + return ESP_OK; +} + +esp_err_t FT23x::set_control_line_state(bool dtr, bool rts) +{ + ESP_RETURN_ON_ERROR(this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_SET_MHS, dtr ? 0x11 : 0x10, this->intf, 0, NULL), "FT23x",); // DTR + return this->send_custom_request(FTDI_WRITE_REQ, FTDI_CMD_SET_MHS, rts ? 0x21 : 0x20, this->intf, 0, NULL); // RTS +} + +void FT23x::ftdi_rx(uint8_t *data, size_t data_len, void *user_arg) +{ + FT23x *this_ftdi = (FT23x *)user_arg; + + // Dispatch serial state if it has changed + if (this_ftdi->user_event_cb) { + cdc_acm_uart_state_t new_state; + new_state.val = 0; + new_state.bRxCarrier = data[0] & 0x80; // DCD + new_state.bTxCarrier = data[0] & 0x20; // DSR + new_state.bBreak = data[1] & 0x10; + new_state.bRingSignal = data[0] & 0x40; + new_state.bFraming = data[1] & 0x08; + new_state.bParity = data[1] & 0x04; + new_state.bOverRun = data[1] & 0x02; + + if (this_ftdi->uart_state != new_state.val) { + cdc_acm_host_dev_event_data_t serial_event; + serial_event.type = CDC_ACM_HOST_SERIAL_STATE; + serial_event.data.serial_state = new_state; + this_ftdi->user_event_cb(&serial_event, this_ftdi->user_arg); + this_ftdi->uart_state = new_state.val; + } + } + + // Dispatch data if any + if (data_len > 2) { + this_ftdi->user_data_cb(&data[2], data_len - 2, this_ftdi->user_arg); + } +} + +void FT23x::ftdi_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx) +{ + FT23x *this_ftdi = (FT23x *)user_ctx; + this_ftdi->user_event_cb(event, this_ftdi->user_arg); +} + +int FT23x::calculate_baudrate(uint32_t baudrate, uint16_t *wValue, uint16_t *wIndex) +{ +#define FTDI_BASE_CLK (3000000) + + int baudrate_real; + if (baudrate > 2000000) { + // set to 3000000 + *wValue = 0; + *wIndex = 0; + baudrate_real = 3000000; + } else if (baudrate >= 1000000) { + // set to 1000000 + *wValue = 1; + *wIndex = 0; + baudrate_real = 1000000; + } else { + const float ftdi_fractal[] = {0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1}; + const uint8_t ftdi_fractal_bits[] = {0, 0x03, 0x02, 0x04, 0x01, 0x05, 0x06, 0x07}; + uint16_t divider_n = FTDI_BASE_CLK / baudrate; // integer value + int ftdi_fractal_idx = 0; + float divider = FTDI_BASE_CLK / (float)baudrate; // float value + float divider_fractal = divider - (float)divider_n; + + // Find closest bigger FT23x fractal divider + for (ftdi_fractal_idx = 0; ftdi_fractal[ftdi_fractal_idx] <= divider_fractal; ftdi_fractal_idx++) {}; + + // Calculate baudrate errors for two closest fractal divisors + int diff1 = baudrate - (int)(FTDI_BASE_CLK / (divider_n + ftdi_fractal[ftdi_fractal_idx])); // Greater than required baudrate + int diff2 = (int)(FTDI_BASE_CLK / (divider_n + ftdi_fractal[ftdi_fractal_idx - 1])) - baudrate; // Lesser than required baudrate + + // Chose divider and fractal divider with smallest error + if (diff2 < diff1) { + ftdi_fractal_idx--; + } else { + if (ftdi_fractal_idx == 8) { + ftdi_fractal_idx = 0; + divider_n++; + } + } + + baudrate_real = FTDI_BASE_CLK / (float)((float)divider_n + ftdi_fractal[ftdi_fractal_idx]); + *wValue = ((0x3FFFF) & divider_n) | (ftdi_fractal_bits[ftdi_fractal_idx] << 14); + *wIndex = ftdi_fractal_bits[ftdi_fractal_idx] >> 2; + } + ESP_LOGD("FT23x", "wValue: 0x%04X wIndex: 0x%04X", *wValue, *wIndex); + ESP_LOGI("FT23x", "Baudrate required: %" PRIu32", set: %d", baudrate, baudrate_real); + + return baudrate_real; +} +} // esp_usb diff --git a/usb/usb_host_vcp/CMakeLists.txt b/usb/usb_host_vcp/CMakeLists.txt new file mode 100644 index 0000000..e719094 --- /dev/null +++ b/usb/usb_host_vcp/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register(SRCS "usb_host_vcp.cpp" + INCLUDE_DIRS "include") + +set_target_properties(${COMPONENT_LIB} PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON +) +target_compile_options(${COMPONENT_LIB} PRIVATE -fconcepts) diff --git a/usb/usb_host_vcp/LICENSE b/usb/usb_host_vcp/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/usb/usb_host_vcp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/usb/usb_host_vcp/README.md b/usb/usb_host_vcp/README.md new file mode 100644 index 0000000..4c2f501 --- /dev/null +++ b/usb/usb_host_vcp/README.md @@ -0,0 +1,7 @@ +# Virtual COM Port Service + +Virtual COM Port (VCP) service manages drivers to connected VCP devices - typically USB <-> UART converters. +In practice, you rarely care about specifics of the devices; you only want uniform interface for them all. + +VCP service does just that, after you register drivers for various VCP devices, you can just call VCP::open +and the service will load proper driver for device that was just plugged into USB port. diff --git a/usb/usb_host_vcp/idf_component.yml b/usb/usb_host_vcp/idf_component.yml new file mode 100644 index 0000000..5419f50 --- /dev/null +++ b/usb/usb_host_vcp/idf_component.yml @@ -0,0 +1,9 @@ +## IDF Component Manager Manifest File +version: "1.0.0" +description: USB Host Virtual COM Port Service +url: https://github.com/espressif/idf-extra-components/tree/master/usb/usb_host_vcp +dependencies: + espressif/usb_host_cdc_acm: + version: "^1.0.4" + public: true + idf: ">=4.4" diff --git a/usb/usb_host_vcp/include/usb/vcp.hpp b/usb/usb_host_vcp/include/usb/vcp.hpp new file mode 100644 index 0000000..ece97f5 --- /dev/null +++ b/usb/usb_host_vcp/include/usb/vcp.hpp @@ -0,0 +1,116 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "usb/cdc_acm_host.h" + +namespace esp_usb { +/** + * @brief Virtual COM Port Service Class + * + * Virtual COM Port (VCP) service manages drivers to connected VCP devices - typically USB <-> UART converters. + * In practice, you rarely care about specifics of the devices; you only want uniform interface for them all. + * VCP service does just that, after you register drivers for various VCP devices, you can just call VCP::open + * and the service will load proper driver for device that was just plugged into USB port. + * + * Example usage: + * \code{.cpp} + * VCP::register_driver(); + * VCP::register_driver(); + * VCP::register_driver(); + * auto vcp = VCP::open(&dev_config); + * \endcode + * + * The example code assumes that you have USB Host Lib already installed. + */ +class VCP { +public: + /** + * @brief Register VCP driver to VCP service + * + * To fully leverage from VCP service functionalities, you must register VCP drivers first. + * The driver must contain the following public members/methods; + * #. vid: Supported VID + * #. pids: Array of supported PIDs + * # Constructor with (uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) input parameters + * + * @tparam T VCP driver type + */ + template static void + register_driver(void) + { + static_assert(T::pids.begin() != nullptr, "Every VCP driver must contain array of supported PIDs in 'pids' array"); + static_assert(T::vid != 0, "Every VCP driver must contain supported VID in'vid' integer"); + std::vector pids(T::pids.begin(), T::pids.end()); // Convert array to vector + vcp_driver new_driver = vcp_driver([](uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) { + return static_cast (new T(pid, dev_config, interface_idx)); // Lambda function: Open factory method + }, T::vid, pids); + drivers.push_back(new_driver); + } + + /** + * @brief VCP factory with VID and PID + * + * Use this function if you know VID and PID of the device. + * The VCP service will look for correct (already registered) driver and load it. + * + * @attention USB Host Library must be installed before calling this function! + * + * @param[in] _vid VID of the device + * @param[in] _pid PID of the device + * @param[in] dev_config Configuration of the device + * @param[in] interface_idx USB interface to use + * @return std::shared_ptr + */ + static CdcAcmDevice * + open(uint16_t _vid, uint16_t _pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx = 0); + + /** + * @brief VCP factory + * + * Use this function when you want the VCP service to open any connected VCP device. + * The VCP service will look for correct (already registered) driver and load it. + * + * This function will block until a valid VCP device is found or + * until dev_config->connection_timeout_ms expires. Set timeout to 0 to wait forever. + * + * @note If there are more USB devices connected, the VCP service will return first successfully opened device + * @attention USB Host Library must be installed before calling this function! + * + * @param[in] dev_config Configuration of the device + * @param[in] interface_idx USB interface to use + * @return std::shared_ptr + */ + static CdcAcmDevice * + open(const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx = 0); + +private: + // Default operators + VCP() = delete; // This driver acts as a service, you can't instantiate it + VCP(const VCP &) = delete; + VCP &operator=(VCP &) = delete; + bool operator== (const VCP ¶m) = delete; + bool operator!= (const VCP ¶m) = delete; + + /** + * @brief VCP driver structure + */ + typedef struct vcp_driver { + CdcAcmDevice *(*open)(uint16_t pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx); /*!< Factory method of this driver */ + uint16_t vid; /*!< VID this driver supports */ + std::vector pids; /*!< List of PIDs this driver supports */ + vcp_driver(auto open_func, const uint16_t _vid, const std::vector &_pids): open(open_func), vid(_vid), pids(_pids) {}; + } vcp_driver; + + /** + * @brief List of registered VCP drivers + */ + static std::vector drivers; +}; // VCP class +} // namespace esp_usb diff --git a/usb/usb_host_vcp/usb_host_vcp.cpp b/usb/usb_host_vcp/usb_host_vcp.cpp new file mode 100644 index 0000000..9eed2df --- /dev/null +++ b/usb/usb_host_vcp/usb_host_vcp.cpp @@ -0,0 +1,91 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "usb/vcp.hpp" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "VCP service"; + +namespace esp_usb { +std::vector VCP::drivers; +CdcAcmDevice *VCP::open(uint16_t _vid, uint16_t _pid, const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) +{ + // In case user didn't install CDC-ACM driver, we try to install it here. + const esp_err_t err = cdc_acm_host_install(NULL); + switch (err) { + case ESP_OK: ESP_LOGD(TAG, "CDC-ACM driver installed"); break; + case ESP_ERR_INVALID_STATE: ESP_LOGD(TAG, "CDC-ACM driver already installed"); break; + default: ESP_LOGE(TAG, "Failed to install CDC-ACM driver"); return nullptr; + } + + for (vcp_driver drv : drivers) { + if (drv.vid == _vid) { + for (uint16_t p : drv.pids) { + if (p == _pid) { + try { + return drv.open(_pid, dev_config, interface_idx); + } catch (esp_err_t &e) { + switch (e) { + case ESP_ERR_NO_MEM: throw std::bad_alloc(); + case ESP_ERR_NOT_FOUND: // fallthrough + default: return nullptr; + } + } + } + } + } + } + return nullptr; +} + +CdcAcmDevice *VCP::open(const cdc_acm_host_device_config_t *dev_config, uint8_t interface_idx) +{ + // Setup this function timeout + TickType_t timeout_ticks = (dev_config->connection_timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(dev_config->connection_timeout_ms); + TimeOut_t connection_timeout; + vTaskSetTimeOutState(&connection_timeout); + + // In case user didn't install CDC-ACM driver, we try to install it here. + esp_err_t err = cdc_acm_host_install(NULL); + switch (err) { + case ESP_OK: ESP_LOGD(TAG, "CDC-ACM driver installed"); break; + case ESP_ERR_INVALID_STATE: ESP_LOGD(TAG, "CDC-ACM driver already installed"); break; + default: ESP_LOGE(TAG, "Failed to install CDC-ACM driver"); return nullptr; + } + + // dev_config->connection_timeout_ms is normally meant for 1 device, + // but here it is a timeout for the whole function call + cdc_acm_host_device_config_t _config = { + .connection_timeout_ms = 1, + .out_buffer_size = dev_config->out_buffer_size, + .event_cb = dev_config->event_cb, + .data_cb = dev_config->data_cb, + .user_arg = dev_config->user_arg, + }; + + // Try opening all registered devices, return on first success + do { + for (vcp_driver drv : drivers) { + for (uint16_t pid : drv.pids) { + try { + return drv.open(pid, &_config, interface_idx); + } catch (esp_err_t &e) { + switch (e) { + case ESP_ERR_NOT_FOUND: break; + case ESP_ERR_NO_MEM: throw std::bad_alloc(); + default: return nullptr; + } + } + } + } + vTaskDelay(pdMS_TO_TICKS(50)); + } while (xTaskCheckForTimeOut(&connection_timeout, &timeout_ticks) == pdFALSE); + return nullptr; +} +} // namespace esp_usb