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@pulkomandy.tk> Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
parent
caf0369a89
commit
e511f0c1cb
@ -17,6 +17,7 @@ KernelAddon usb_serial :
|
||||
Option.cpp
|
||||
Prolific.cpp
|
||||
Silicon.cpp
|
||||
WinChipHead.cpp
|
||||
;
|
||||
|
||||
AddResources usb_serial : usb_serial.rdef ;
|
||||
|
@ -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++) {
|
||||
|
251
src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.cpp
Normal file
251
src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.cpp
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright 2022, Gerasim Troeglazov <3dEyes@gmail.com>
|
||||
* 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;
|
||||
}
|
103
src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.h
Normal file
103
src/add-ons/kernel/drivers/ports/usb_serial/WinChipHead.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2022, Gerasim Troeglazov <3dEyes@gmail.com>
|
||||
* 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_
|
Loading…
x
Reference in New Issue
Block a user