211 lines
5.5 KiB
C++
211 lines
5.5 KiB
C++
|
/*
|
||
|
* 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;
|
||
|
}
|
||
|
}
|