hrev56218 adds 1 changeset to branch 'master'
old head: caf0369a8937ceab6d173793524d2dd392c5f8a6
new head: e511f0c1cb7fea2809d57e7eb79577248f2687e9
overview:
https://git.haiku-os.org/haiku/log/?qt=range&q=e511f0c1cb7f+%5Ecaf0369a8937
----------------------------------------------------------------------------
e511f0c1cb7f: usb_serial: add WinChipHead (CH340/CH341) support
Fixes #15872.
Change-Id: I3b43094574045464ca0d3e024f8b5aac32ce5210
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5401
Reviewed-by: Adrien Destugues <pulkomandy@xxxxxxxxxxxxx>
Tested-by: Commit checker robot <no-reply+buildbot@xxxxxxxxxxxx>
[ Gerasim Troeglazov <3dEyes@xxxxxxxxx> ]
----------------------------------------------------------------------------
Revision: hrev56218
Commit: e511f0c1cb7fea2809d57e7eb79577248f2687e9
URL: https://git.haiku-os.org/haiku/commit/?id=e511f0c1cb7f
Author: Gerasim Troeglazov <3dEyes@xxxxxxxxx>
Date: Sun Jun 26 13:57:02 2022 UTC
Ticket: https://dev.haiku-os.org/ticket/15872
----------------------------------------------------------------------------
4 files changed, 366 insertions(+)
.../kernel/drivers/ports/usb_serial/Jamfile | 1 +
.../drivers/ports/usb_serial/SerialDevice.cpp | 11 +
.../drivers/ports/usb_serial/WinChipHead.cpp | 251 +++++++++++++++++++
.../drivers/ports/usb_serial/WinChipHead.h | 103 ++++++++
----------------------------------------------------------------------------
diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/Jamfile
b/src/add-ons/kernel/drivers/ports/usb_serial/Jamfile
index 7fea83c129..5511c0859a 100644
--- a/src/add-ons/kernel/drivers/ports/usb_serial/Jamfile
+++ b/src/add-ons/kernel/drivers/ports/usb_serial/Jamfile
@@ -17,6 +17,7 @@ KernelAddon usb_serial :
Option.cpp
Prolific.cpp
Silicon.cpp
+ WinChipHead.cpp
;
AddResources usb_serial : usb_serial.rdef ;
diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp
b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp
index a7100a0a21..e408669950 100644
--- a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp
+++ b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp
@@ -21,6 +21,7 @@
#include "Option.h"
#include "Prolific.h"
#include "Silicon.h"
+#include "WinChipHead.h"
#include <sys/ioctl.h>
@@ -778,6 +779,16 @@ SerialDevice::MakeDevice(usb_device device, uint16
vendorID,
}
}
+ // WinChipHead Serial Device
+ for (uint32 i = 0; i < sizeof(kWCHDevices)
+ / sizeof(kWCHDevices[0]); i++) {
+ if (vendorID == kWCHDevices[i].vendorID
+ && productID == kWCHDevices[i].productID) {
+ return new(std::nothrow) WCHDevice(device, vendorID,
productID,
+ kWCHDevices[i].deviceName);
+ }
+ }
+
// Option Serial Device
for (uint32 i = 0; i < sizeof(kOptionDevices)
/ sizeof(kOptionDevices[0]); i++) {
diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.cpp
b/src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.cpp
new file mode 100644
index 0000000000..5bb8df3794
--- /dev/null
+++ b/src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2022, Gerasim Troeglazov <3dEyes@xxxxxxxxx>
+ * Distributed under the terms of the MIT License.
+ */
+
+#include "WinChipHead.h"
+
+WCHDevice::WCHDevice(usb_device device, uint16 vendorID, uint16 productID,
+ const char *description)
+ : SerialDevice(device, vendorID, productID, description),
+ fChipVersion(0),
+ fStatusMCR(0),
+ fStatusLCR(CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX |
CH34X_LCR_CS8),
+ fDataRate(CH34X_SIO_9600)
+{
+}
+
+
+// Called for each configuration of the device. Return B_OK if the given
+// configuration sounds like it is the usb serial one.
+status_t
+WCHDevice::AddDevice(const usb_configuration_info *config)
+{
+ TRACE_FUNCALLS("> WCHDevice::AddDevice(%08x, %08x)\n", this, config);
+
+ status_t status = ENODEV;
+ if (config->interface_count > 0) {
+ int32 pipesSet = 0;
+ usb_interface_info *interface = config->interface[0].active;
+ for (size_t i = 0; i < interface->endpoint_count; i++) {
+ usb_endpoint_info *endpoint = &interface->endpoint[i];
+ if (endpoint->descr->attributes ==
USB_ENDPOINT_ATTR_BULK) {
+ if (endpoint->descr->endpoint_address &
USB_ENDPOINT_ADDR_DIR_IN) {
+ SetReadPipe(endpoint->handle);
+ if (++pipesSet >= 3)
+ break;
+ } else {
+ if (endpoint->descr->endpoint_address) {
+
SetControlPipe(endpoint->handle);
+ SetWritePipe(endpoint->handle);
+ pipesSet += 2;
+ if (pipesSet >= 3)
+ break;
+ }
+ }
+ }
+ }
+
+ if (pipesSet >= 3) {
+ status = B_OK;
+ }
+ }
+
+ TRACE_FUNCRET("< WCHDevice::AddDevice() returns: 0x%08x\n", status);
+
+ return status;
+}
+
+
+status_t
+WCHDevice::ResetDevice()
+{
+ TRACE_FUNCALLS("> WCHDevice::ResetDevice(0x%08x)\n", this);
+ size_t length = 0;
+
+ uint8 inputBuffer[CH34X_INPUT_BUF_SIZE];
+ status_t status = gUSBModule->send_request(Device(),
+ USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN,
+ CH34X_REQ_READ_VERSION, 0, 0, sizeof(inputBuffer), inputBuffer,
&length);
+
+ if (status == B_OK) {
+ fChipVersion = inputBuffer[0];
+ TRACE_ALWAYS("= WCHDevice::ResetDevice(): Chip version:
0x%02x\n", fChipVersion);
+ } else {
+ TRACE_ALWAYS("= WCHDevice::ResetDevice(): Can't get chip
version: 0x%08x\n",
+ status);
+ return status;
+ }
+
+ status = gUSBModule->send_request(Device(),
+ USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
+ CH34X_REQ_SERIAL_INIT, 0, 0, 0, NULL, &length);
+
+ if (status != B_OK) {
+ TRACE_ALWAYS("= WCHDevice::ResetDevice(): init failed\n");
+ return status;
+ }
+
+ status = WriteConfig(fDataRate, fStatusLCR, fStatusMCR);
+
+ TRACE_FUNCRET("< WCHDevice::ResetDevice() returns:%08x\n", status);
+ return status;
+}
+
+status_t
+WCHDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
+{
+ TRACE_FUNCALLS("> WCHDevice::SetLineCoding(0x%08x, {%d, 0x%02x, 0x%02x,
0x%02x})\n",
+ this, lineCoding->speed, lineCoding->stopbits,
lineCoding->parity,
+ lineCoding->databits);
+
+ fStatusLCR = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
+
+ switch (lineCoding->stopbits) {
+ case USB_CDC_LINE_CODING_1_STOPBIT:
+ break;
+ case USB_CDC_LINE_CODING_2_STOPBITS:
+ fStatusLCR |= CH34X_LCR_STOP_BITS_2;
+ break;
+ default:
+ TRACE_ALWAYS("= WCHDevice::SetLineCoding(): Wrong
stopbits param: %d\n",
+ lineCoding->stopbits);
+ break;
+ }
+
+ switch (lineCoding->parity) {
+ case USB_CDC_LINE_CODING_NO_PARITY:
+ break;
+ case USB_CDC_LINE_CODING_EVEN_PARITY:
+ fStatusLCR |= CH34X_LCR_ENABLE_PAR | CH34X_LCR_PAR_EVEN;
+ break;
+ case USB_CDC_LINE_CODING_ODD_PARITY:
+ fStatusLCR |= CH34X_LCR_ENABLE_PAR;
+ break;
+ default:
+ TRACE_ALWAYS("= WCHDevice::SetLineCoding(): Wrong
parity param: %d\n",
+ lineCoding->parity);
+ break;
+ }
+
+ switch (lineCoding->databits) {
+ case 5:
+ fStatusLCR |= CH34X_LCR_CS5;
+ break;
+ case 6:
+ fStatusLCR |= CH34X_LCR_CS6;
+ break;
+ case 7:
+ fStatusLCR |= CH34X_LCR_CS7;
+ break;
+ case 8:
+ fStatusLCR |= CH34X_LCR_CS8;
+ break;
+ default:
+ fStatusLCR |= CH34X_LCR_CS8;
+ TRACE_ALWAYS("= WCHDevice::SetLineCoding(): Wrong
databits param: %d\n",
+ lineCoding->databits);
+ break;
+ }
+
+ switch (lineCoding->speed) {
+ case 600: fDataRate = CH34X_SIO_600; break;
+ case 1200: fDataRate = CH34X_SIO_1200; break;
+ case 1800: fDataRate = CH34X_SIO_1800; break;
+ case 2400: fDataRate = CH34X_SIO_2400; break;
+ case 4800: fDataRate = CH34X_SIO_4800; break;
+ case 9600: fDataRate = CH34X_SIO_9600; break;
+ case 19200: fDataRate = CH34X_SIO_19200; break;
+ case 31250: fDataRate = CH34X_SIO_31250; break;
+ case 38400: fDataRate = CH34X_SIO_38400; break;
+ case 57600: fDataRate = CH34X_SIO_57600; break;
+ case 115200: fDataRate = CH34X_SIO_115200; break;
+ case 230400: fDataRate = CH34X_SIO_230400; break;
+ default:
+ fDataRate = CH34X_SIO_9600;
+ TRACE_ALWAYS("= WCHDevice::SetLineCoding(): Datarate:
%d is not "
+ "supported by this hardware. Defaulted to %d\n",
+ lineCoding->speed, CH34X_DEFAULT_BAUD_RATE);
+ break;
+ }
+
+ status_t status = WriteConfig(fDataRate, fStatusLCR, fStatusMCR);
+
+ if (status != B_OK)
+ TRACE_ALWAYS("= WCHDevice::SetLineCoding(): WriteConfig
failed\n");
+
+ TRACE_FUNCRET("< WCHDevice::SetLineCoding() returns: 0x%08x\n", status);
+
+ return status;
+}
+
+
+status_t
+WCHDevice::SetControlLineState(uint16 state)
+{
+ TRACE_FUNCALLS("> WCHDevice::SetControlLineState(0x%08x, 0x%04x)\n",
+ this, state);
+
+ fStatusMCR = 0;
+
+ if (state & USB_CDC_CONTROL_SIGNAL_STATE_RTS)
+ fStatusMCR |= CH34X_BIT_RTS;
+
+ if (state & USB_CDC_CONTROL_SIGNAL_STATE_DTR)
+ fStatusMCR |= CH34X_BIT_DTR;
+
+ size_t length = 0;
+ status_t status = 0;
+
+ if (fChipVersion < CH34X_VER_20) {
+ status = gUSBModule->send_request(Device(),
+ USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
+ CH34X_REQ_WRITE_REG, CH34X_REG_STAT1 | (CH34X_REG_STAT1
<< 8),
+ ~fStatusMCR | (~fStatusMCR << 8), 0, NULL, &length);
+ } else {
+ status = gUSBModule->send_request(Device(),
+ USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
+ CH34X_REQ_MODEM_CTRL, ~fStatusMCR, 0, 0, NULL, &length);
+ }
+
+ TRACE_FUNCRET("< WCHDevice::SetControlLineState() returns: 0x%08x\n",
+ status);
+
+ return status;
+}
+
+status_t
+WCHDevice::WriteConfig(uint16 dataRate, uint8 lcr, uint8 mcr)
+{
+ size_t length = 0;
+ status_t status = gUSBModule->send_request(Device(),
+ USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
+ CH34X_REQ_WRITE_REG, CH34X_REG_BPS_PRE | (CH34X_REG_BPS_DIV <<
8),
+ dataRate | CH34X_BPS_PRE_IMM, 0, NULL, &length);
+
+ if (status != B_OK) {
+ TRACE_ALWAYS("= WCHDevice::WriteConfig(): datarate request
failed: 0x%08x\n",
+ status);
+ return status;
+ }
+
+ status = gUSBModule->send_request(Device(),
+ USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
+ CH34X_REQ_WRITE_REG, CH34X_REG_LCR | (CH34X_REG_LCR2 << 8),
+ lcr, 0, NULL, &length);
+
+ if (status != B_OK) {
+ TRACE_ALWAYS("= WCHDevice::WriteConfig(): LCR request failed:
0x%08x\n",
+ status);
+ return status;
+ }
+
+ status = gUSBModule->send_request(Device(),
+ USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT,
+ CH34X_REQ_MODEM_CTRL, ~mcr, 0, 0, NULL, &length);
+
+ if (status != B_OK)
+ TRACE_ALWAYS("= WCHDevice::WriteConfig(): handshake failed:
0x%08x\n", status);
+
+ return status;
+}
diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.h
b/src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.h
new file mode 100644
index 0000000000..8172b1f4d4
--- /dev/null
+++ b/src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2022, Gerasim Troeglazov <3dEyes@xxxxxxxxx>
+ * Distributed under the terms of the MIT License.
+ */
+
+#ifndef _USB_WINCHIPHEAD_H_
+#define _USB_WINCHIPHEAD_H_
+
+#include "SerialDevice.h"
+
+#define CH34X_DEFAULT_BAUD_RATE 9600
+
+#define CH34X_INPUT_BUF_SIZE 8
+
+#define CH34X_VER_20 0x20
+#define CH34X_VER_30 0x30
+
+#define CH34X_BPS_PRE_IMM 0x80
+
+#define CH34X_BIT_CTS 0x01
+#define CH34X_BIT_DSR 0x02
+#define CH34X_BIT_RI 0x04
+#define CH34X_BIT_DCD 0x08
+#define CH34X_BIT_DTR 0x20
+#define CH34X_BIT_RTS 0x40
+#define CH34X_BITS_MODEM_STAT 0x0f
+
+#define CH34X_MULT_STAT 0x04
+
+#define CH34X_REQ_READ_VERSION 0x5F
+#define CH34X_REQ_WRITE_REG 0x9A
+#define CH34X_REQ_READ_REG 0x95
+#define CH34X_REQ_SERIAL_INIT 0xA1
+#define CH34X_REQ_MODEM_CTRL 0xA4
+
+#define CH34X_REG_BREAK 0x05
+#define CH34X_REG_STAT1 0x06
+#define CH34X_REG_STAT2 0x07
+#define CH34X_REG_BPS_PRE 0x12
+#define CH34X_REG_BPS_DIV 0x13
+#define CH34X_REG_LCR 0x18
+#define CH34X_REG_LCR2 0x25
+
+#define CH34X_LCR_CS5 0x00
+#define CH34X_LCR_CS6 0x01
+#define CH34X_LCR_CS7 0x02
+#define CH34X_LCR_CS8 0x03
+#define CH34X_LCR_STOP_BITS_2 0x04
+#define CH34X_LCR_ENABLE_PAR 0x08
+#define CH34X_LCR_PAR_EVEN 0x10
+#define CH34X_LCR_MARK_SPACE 0x20
+#define CH34X_LCR_ENABLE_TX 0x40
+#define CH34X_LCR_ENABLE_RX 0x80
+
+#define CH34X_SIO_600 0x6401
+#define CH34X_SIO_1200 0xB201
+#define CH34X_SIO_1800 0xCC01
+#define CH34X_SIO_2400 0xD901
+#define CH34X_SIO_4800 0x6402
+#define CH34X_SIO_9600 0xB202
+#define CH34X_SIO_19200 0xD902
+#define CH34X_SIO_31250 0x4003
+#define CH34X_SIO_38400 0x6403
+#define CH34X_SIO_57600 0xF302
+#define CH34X_SIO_115200 0xCC03
+#define CH34X_SIO_230400 0xE603
+
+/* supported vendor and product ids */
+#define VENDOR_WCH 0x4348 // WinChipHead
+#define VENDOR_QIN_HENG 0x1a86 // QinHeng Electronics
+#define VENDOR_GW_INSTEK 0x2184 // GW Instek
+
+const usb_serial_device kWCHDevices[] = {
+ {VENDOR_WCH, 0x5523, "CH341 serial converter"},
+ {VENDOR_QIN_HENG, 0x5523, "QinHeng CH341A serial converter"},
+ {VENDOR_QIN_HENG, 0x7522, "QinHeng CH340 serial converter"},
+ {VENDOR_QIN_HENG, 0x7523, "QinHeng CH340 serial converter"},
+ {VENDOR_GW_INSTEK, 0x0057, "GW Instek Oscilloscope"}
+};
+
+class WCHDevice : public SerialDevice {
+public:
+ WCHDevice(usb_device device,
uint16 vendorID,
+ uint16 productID, const
char *description);
+
+ virtual status_t AddDevice(const usb_configuration_info *config);
+
+ virtual status_t ResetDevice();
+
+ virtual status_t SetLineCoding(usb_cdc_line_coding *coding);
+ virtual status_t SetControlLineState(uint16 state);
+
+private:
+ status_t WriteConfig(uint16 dataRate, uint8 lcr,
uint8 mcr);
+
+ uint8 fChipVersion;
+ uint8 fStatusMCR;
+ uint8 fStatusLCR;
+ uint16 fDataRate;
+};
+
+
+#endif //_USB_WINCHIPHEAD_H_