[haiku-commits] Change in haiku[master]: usb_serial: add WinChipHead (CH340/CH341) support #15872

  • From: Gerrit <review@xxxxxxxxxxxxxxxxxxx>
  • To: waddlesplash <waddlesplash@xxxxxxxxx>, haiku-commits@xxxxxxxxxxxxx
  • Date: Sun, 26 Jun 2022 13:59:08 +0000

From Gerasim Troeglazov <3dEyes@xxxxxxxxx>:

Gerasim Troeglazov has uploaded this change for review. ( 
https://review.haiku-os.org/c/haiku/+/5401 ;)


Change subject: usb_serial: add WinChipHead (CH340/CH341) support #15872
......................................................................

usb_serial: add WinChipHead (CH340/CH341) support #15872
---
M src/add-ons/kernel/drivers/ports/usb_serial/Jamfile
M src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp
A src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.cpp
A src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.h
4 files changed, 366 insertions(+), 0 deletions(-)



  git pull ssh://git.haiku-os.org:22/haiku refs/changes/01/5401/1

diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/Jamfile 
b/src/add-ons/kernel/drivers/ports/usb_serial/Jamfile
index 7fea83c..5511c08 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 @@
        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 a7100a0..e408669 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 @@
                }
        }

+       // 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 0000000..5bb8df3
--- /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 0000000..8172b1f
--- /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_

--
To view, visit https://review.haiku-os.org/c/haiku/+/5401
To unsubscribe, or for help writing mail filters, visit 
https://review.haiku-os.org/settings

Gerrit-Project: haiku
Gerrit-Branch: master
Gerrit-Change-Id: I3b43094574045464ca0d3e024f8b5aac32ce5210
Gerrit-Change-Number: 5401
Gerrit-PatchSet: 1
Gerrit-Owner: Gerasim Troeglazov <3dEyes@xxxxxxxxx>
Gerrit-MessageType: newchange

Other related posts:

  • » [haiku-commits] Change in haiku[master]: usb_serial: add WinChipHead (CH340/CH341) support #15872 - Gerrit