From: Charles Yeh <charlesyeh522@gmail.com>
To: gregkh@linuxfoundation.org, johan@kernel.org, linux-usb@vger.kernel.org
Cc: charles-yeh@prolific.com.tw, Charles Yeh <charlesyeh522@gmail.com>
Subject: [PATCH] USB: serial: pl2303: add new PID to support PL256X (TYPE_MP)
Date: Tue, 24 Mar 2026 20:46:19 +0800 [thread overview]
Message-ID: <20260324124619.843-1-charlesyeh522@gmail.com> (raw)
Prolific has developed a new USB to Multi-UART chip: PL256X (TYPE_MP)
PL256X : PL2543,PL2533,PL2565
The Vendor request used by the PL256X (TYPE_MP) is different from
the existing PL2303 series (TYPE_HXN / TYPE_HX ).
Therefore, different Vendor requests are used to issue related commands.
1. Added a new TYPE_MP type in pl2303_type, and then executes
new Vendor request,new flow control and other related instructions
if TYPE_MP is recognized.
2. Added a interface_num in pl2303_serial_private, and then executes
new USB index request, if TYPE_MP is recognized.
3. Because the new PL256X only accept the new Vendor request,
the old Vendor request cannot be accepted (the error message
will be returned)
So first determine the bcdDevice in pl2303_detect_type.
4. In pl2303_open: Because TYPE_MP is different from the instruction of reset
down/up stream used by TYPE_HXN/TYPE_HX.
Therefore, we will also execute different instructions here.
pl256x_uart_reset
5. In pl2303_set_termios: The UART flow control instructions used by
TYPE_MP/TYPE_HXN/TYPE_HX are different.
Therefore, we will also execute different instructions here.
6. In pl2303_vendor_read & pl2303_vendor_write, since TYPE_MP is different
from the vendor request instruction used by TYPE_HXN/TYPE_HX,
it will also execute different instructions here.
7. In pl2303_update_reg: TYPE_MP used different register for flow control.
Therefore, we will also execute different instructions here.
8. The max baud rate of TYPE_MP is 24M bps, and don't support divisor encoding.
Signed-off-by: Charles Yeh <charlesyeh522@gmail.com>
---
drivers/usb/serial/pl2303.c | 110 +++++++++++++++++++++++++++++++++---
drivers/usb/serial/pl2303.h | 2 +-
2 files changed, 104 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 50dc838e8115..e5bbda1d63b3 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -54,6 +54,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GL) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GE) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GS) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL256X_PRODUCT_ID_4P) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID),
@@ -144,10 +145,15 @@ MODULE_DEVICE_TABLE(usb, id_table);
#define VENDOR_WRITE_REQUEST_TYPE 0x40
#define VENDOR_WRITE_REQUEST 0x01
#define VENDOR_WRITE_NREQUEST 0x80
+#define VENDOR_WRITE_MPREQUEST 0x80
#define VENDOR_READ_REQUEST_TYPE 0xc0
#define VENDOR_READ_REQUEST 0x01
#define VENDOR_READ_NREQUEST 0x81
+#define VENDOR_READ_MPREQUEST 0x80
+
+#define PL256X_RESET_REQUEST_TYPE 0x40
+#define PL256X_RESET_REQUEST 0x96
#define UART_STATE_INDEX 8
#define UART_STATE_MSR_MASK 0x8b
@@ -175,6 +181,16 @@ MODULE_DEVICE_TABLE(usb, id_table);
#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18
#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c
+#define PL256X_PORT_A_FLOWCTRL_REG 0xC005
+#define PL256X_PORT_B_FLOWCTRL_REG 0xD005
+#define PL256X_PORT_C_FLOWCTRL_REG 0xE005
+#define PL256X_PORT_D_FLOWCTRL_REG 0xF005
+
+#define PL256X_FLOWCTRL_MASK 0x43
+#define PL256X_FLOWCTRL_XON_XOFF 0x40
+#define PL256X_FLOWCTRL_RTS_CTS 0x03
+#define PL256X_FLOWCTRL_NONE 0x00
+
static int pl2303_set_break(struct usb_serial_port *port, bool enable);
enum pl2303_type {
@@ -184,6 +200,7 @@ enum pl2303_type {
TYPE_TB,
TYPE_HXD,
TYPE_HXN,
+ TYPE_MP,
TYPE_COUNT
};
@@ -199,6 +216,8 @@ struct pl2303_type_data {
struct pl2303_serial_private {
const struct pl2303_type_data *type;
unsigned long quirks;
+ u16 interface_num;
+ u16 flowctrl_reg;
};
struct pl2303_private {
@@ -239,8 +258,32 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
.max_baud_rate = 12000000,
.no_divisors = true,
},
+ [TYPE_MP] = {
+ .name = "MP",
+ .max_baud_rate = 24000000,
+ .no_divisors = true,
+ },
};
+static int pl256x_uart_reset(struct usb_serial *serial, u16 value, u16 index)
+{
+ struct device *dev = &serial->interface->dev;
+ int res;
+
+ dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index);
+
+ res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ PL256X_RESET_REQUEST, PL256X_RESET_REQUEST_TYPE,
+ value, index, NULL, 0, 100);
+ if (res) {
+ dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__,
+ value, res);
+ return res;
+ }
+
+ return 0;
+}
+
static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
unsigned char buf[1])
{
@@ -251,6 +294,8 @@ static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
if (spriv->type == &pl2303_type_data[TYPE_HXN])
request = VENDOR_READ_NREQUEST;
+ else if (spriv->type == &pl2303_type_data[TYPE_MP])
+ request = VENDOR_READ_MPREQUEST;
else
request = VENDOR_READ_REQUEST;
@@ -282,6 +327,8 @@ static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
if (spriv->type == &pl2303_type_data[TYPE_HXN])
request = VENDOR_WRITE_NREQUEST;
+ else if (spriv->type == &pl2303_type_data[TYPE_MP])
+ request = VENDOR_WRITE_MPREQUEST;
else
request = VENDOR_WRITE_REQUEST;
@@ -300,6 +347,7 @@ static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val)
{
struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
+ struct device *dev = &serial->interface->dev;
int ret = 0;
u8 *buf;
@@ -307,10 +355,12 @@ static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val)
if (!buf)
return -ENOMEM;
- if (spriv->type == &pl2303_type_data[TYPE_HXN])
+ if (spriv->type == &pl2303_type_data[TYPE_HXN] ||
+ spriv->type == &pl2303_type_data[TYPE_MP]) {
ret = pl2303_vendor_read(serial, reg, buf);
- else
+ } else {
ret = pl2303_vendor_read(serial, reg | 0x80, buf);
+ }
if (ret)
goto out_free;
@@ -461,6 +511,14 @@ static int pl2303_detect_type(struct usb_serial *serial)
case 0x905: /* GT-2AB */
case 0x1005: /* GC-Q20 */
return TYPE_HXN;
+ case 0x3302:
+ case 0x3304:
+ case 0x4302:
+ case 0x4304:
+ case 0x6502:
+ case 0x6504:
+ case 0x6506:
+ return TYPE_MP;
}
break;
}
@@ -495,6 +553,7 @@ static int pl2303_startup(struct usb_serial *serial)
enum pl2303_type type;
unsigned char *buf;
int ret;
+ unsigned int ifnum;
ret = pl2303_detect_type(serial);
if (ret < 0)
@@ -503,20 +562,42 @@ static int pl2303_startup(struct usb_serial *serial)
type = ret;
dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name);
+ ifnum = serial->interface->altsetting->desc.bInterfaceNumber;
+
spriv = kzalloc_obj(*spriv);
if (!spriv)
return -ENOMEM;
+ if (type == TYPE_MP) {
+ switch (ifnum) {
+ case 0:
+ spriv->flowctrl_reg = PL256X_PORT_A_FLOWCTRL_REG;
+ break;
+ case 1:
+ spriv->flowctrl_reg = PL256X_PORT_B_FLOWCTRL_REG;
+ break;
+ case 2:
+ spriv->flowctrl_reg = PL256X_PORT_C_FLOWCTRL_REG;
+ break;
+ case 3:
+ spriv->flowctrl_reg = PL256X_PORT_D_FLOWCTRL_REG;
+ break;
+ }
+ }
+
spriv->type = &pl2303_type_data[type];
spriv->quirks = (unsigned long)usb_get_serial_data(serial);
spriv->quirks |= spriv->type->quirks;
+ spriv->interface_num = ifnum;
if (type == TYPE_HXD && pl2303_is_hxd_clone(serial))
spriv->quirks |= PL2303_QUIRK_NO_BREAK_GETLINE;
usb_set_serial_data(serial, spriv);
- if (type != TYPE_HXN) {
+ if (type == TYPE_MP)
+ pl2303_vendor_write(serial, spriv->flowctrl_reg, PL256X_FLOWCTRL_NONE);
+ else if (type != TYPE_HXN) {
buf = kmalloc(1, GFP_KERNEL);
if (!buf) {
kfree(spriv);
@@ -578,13 +659,15 @@ static void pl2303_port_remove(struct usb_serial_port *port)
static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value)
{
struct usb_device *dev = port->serial->dev;
+ struct usb_serial *serial = port->serial;
+ struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
int retval;
dev_dbg(&port->dev, "%s - %02x\n", __func__, value);
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
- value, 0, NULL, 0, 100);
+ value, spriv->interface_num, NULL, 0, 100);
if (retval)
dev_err(&port->dev, "%s - failed: %d\n", __func__, retval);
@@ -764,7 +847,7 @@ static int pl2303_get_line_request(struct usb_serial_port *port,
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
- 0, 0, buf, 7, 100);
+ 0, spriv->interface_num, buf, 7, 100);
if (ret != 7) {
dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
@@ -783,11 +866,13 @@ static int pl2303_set_line_request(struct usb_serial_port *port,
unsigned char buf[7])
{
struct usb_device *udev = port->serial->dev;
+ struct usb_serial *serial = port->serial;
+ struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
int ret;
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
- 0, 0, buf, 7, 100);
+ 0, spriv->interface_num, buf, 7, 100);
if (ret < 0) {
dev_err(&port->dev, "%s - failed: %d\n", __func__, ret);
return ret;
@@ -942,6 +1027,9 @@ static void pl2303_set_termios(struct tty_struct *tty,
pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
PL2303_HXN_FLOWCTRL_MASK,
PL2303_HXN_FLOWCTRL_RTS_CTS);
+ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) {
+ pl2303_vendor_write(serial, spriv->flowctrl_reg,
+ PL256X_FLOWCTRL_RTS_CTS);
} else {
pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60);
}
@@ -950,6 +1038,9 @@ static void pl2303_set_termios(struct tty_struct *tty,
pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
PL2303_HXN_FLOWCTRL_MASK,
PL2303_HXN_FLOWCTRL_XON_XOFF);
+ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) {
+ pl2303_vendor_write(serial, spriv->flowctrl_reg,
+ PL256X_FLOWCTRL_XON_XOFF);
} else {
pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0);
}
@@ -958,6 +1049,9 @@ static void pl2303_set_termios(struct tty_struct *tty,
pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
PL2303_HXN_FLOWCTRL_MASK,
PL2303_HXN_FLOWCTRL_NONE);
+ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) {
+ pl2303_vendor_write(serial, spriv->flowctrl_reg,
+ PL256X_FLOWCTRL_NONE);
} else {
pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0);
}
@@ -1005,6 +1099,8 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
pl2303_vendor_write(serial, PL2303_HXN_RESET_REG,
PL2303_HXN_RESET_UPSTREAM_PIPE |
PL2303_HXN_RESET_DOWNSTREAM_PIPE);
+ } else if (spriv->type == &pl2303_type_data[TYPE_MP]) {
+ pl256x_uart_reset(serial, 0, spriv->interface_num);
} else {
pl2303_vendor_write(serial, 8, 0);
pl2303_vendor_write(serial, 9, 0);
@@ -1115,7 +1211,7 @@ static int pl2303_set_break(struct usb_serial_port *port, bool enable)
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
- 0, NULL, 0, 100);
+ spriv->interface_num, NULL, 0, 100);
if (result) {
dev_err(&port->dev, "error sending break = %d\n", result);
return result;
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index d60eda7f6eda..8eb1e7b5d3ec 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -26,7 +26,7 @@
#define PL2303_PRODUCT_ID_HCR331 0x331a
#define PL2303_PRODUCT_ID_MOTOROLA 0x0307
#define PL2303_PRODUCT_ID_ZTEK 0xe1f1
-
+#define PL256X_PRODUCT_ID_4P 0x2533
#define ATEN_VENDOR_ID 0x0557
#define ATEN_VENDOR_ID2 0x0547
--
2.51.0
next reply other threads:[~2026-03-24 12:46 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-24 12:46 Charles Yeh [this message]
2026-03-24 21:51 ` [PATCH] USB: serial: pl2303: add new PID to support PL256X (TYPE_MP) kernel test robot
2026-03-24 22:15 ` kernel test robot
-- strict thread matches above, loose matches on Subject: below --
2021-06-22 12:09 [PATCH] USB: serial: pl2303: Add " Charles Yeh
2021-07-22 5:16 ` Charles Yeh
2021-07-22 7:40 ` Johan Hovold
[not found] ` <CAAZvQQ6Kr0vbDAnKYum9EaCVxc9Ps69vBV+sg2mov06tB4KqNg@mail.gmail.com>
2021-07-22 5:50 ` Greg KH
2021-08-04 10:54 ` Johan Hovold
2021-11-01 4:53 ` Charles Yeh
2021-11-05 10:02 ` Greg KH
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260324124619.843-1-charlesyeh522@gmail.com \
--to=charlesyeh522@gmail.com \
--cc=charles-yeh@prolific.com.tw \
--cc=gregkh@linuxfoundation.org \
--cc=johan@kernel.org \
--cc=linux-usb@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox