diff mbox series

Add Proliic new chip: PL2303TB & PL2303N(G)

Message ID 20181226122810.1716-1-charlesyeh522@gmail.com (mailing list archive)
State New, archived
Headers show
Series Add Proliic new chip: PL2303TB & PL2303N(G) | expand

Commit Message

Charles Yeh Dec. 26, 2018, 12:28 p.m. UTC
Add new PID to support PL2303TB (TYPE_HX)
Add new PID to support PL2303(N)GC/GB/GS/GT/GL/GE (TYPE_HXN)
Add new Flow Control to support PL2303(N)GC/GB/GS/GT/GL/GE (TYPE_HXN)
Add new Pull-Up Mode to support PL2303HXD (TYPE_HX)
Fixed warning:restricted__le16 degrades to integer

Signed-off-by: Charles Yeh <charlesyeh522@gmail.com>
---
 drivers/usb/serial/pl2303.c | 116 +++++++++++++++++++++++++++++-------
 drivers/usb/serial/pl2303.h |  11 ++++
 2 files changed, 106 insertions(+), 21 deletions(-)

Comments

Johan Hovold Jan. 8, 2019, 5:01 p.m. UTC | #1
On Wed, Dec 26, 2018 at 08:28:10PM +0800, Charles Yeh wrote:

> Add new PID to support PL2303TB (TYPE_HX)
> Add new PID to support PL2303(N)GC/GB/GS/GT/GL/GE (TYPE_HXN)
> Add new Flow Control to support PL2303(N)GC/GB/GS/GT/GL/GE (TYPE_HXN)
> Add new Pull-Up Mode to support PL2303HXD (TYPE_HX)

So the above should go in three separate patches as we already discussed
(PL2303TB PID, TYPE_HXN support + PIDs, "pull-up mode").

> Fixed warning:restricted__le16 degrades to integer

When you update and resend a patch, it's good to include changelog
information like this, but please put it below the cut-off line (---)
below so that it doesn't end up in the commit log.

Also include a patch revision in the Subject of the mail. Let's call
this one v2, so next time use the following Subject prefix:

	[PATCH v3]: USB: serial: pl2303: add ...

(also after breaking the current patch into a series of three or more
patches).

> Signed-off-by: Charles Yeh <charlesyeh522@gmail.com>
> ---
>  drivers/usb/serial/pl2303.c | 116 +++++++++++++++++++++++++++++-------
>  drivers/usb/serial/pl2303.h |  11 ++++
>  2 files changed, 106 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
> index a4e0d13fc121..8c71612e1811 100644
> --- a/drivers/usb/serial/pl2303.c
> +++ b/drivers/usb/serial/pl2303.c
> @@ -46,6 +46,13 @@ static const struct usb_device_id id_table[] = {
>  	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) },
>  	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) },
>  	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) },
> +	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_TB) },
> +	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GC) },
> +	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GB) },
> +	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GT) },
> +	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GL) },
> +	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GE) },
> +	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GS) },
>  	{ 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),
> @@ -123,9 +130,11 @@ MODULE_DEVICE_TABLE(usb, id_table);
>  
>  #define VENDOR_WRITE_REQUEST_TYPE	0x40
>  #define VENDOR_WRITE_REQUEST		0x01
> +#define VENDOR_WRITE_NREQUEST		0x80
>  
>  #define VENDOR_READ_REQUEST_TYPE	0xc0
>  #define VENDOR_READ_REQUEST		0x01
> +#define VENDOR_READ_NREQUEST		0x81
>  
>  #define UART_STATE_INDEX		8
>  #define UART_STATE_MSR_MASK		0x8b
> @@ -139,11 +148,21 @@ MODULE_DEVICE_TABLE(usb, id_table);
>  #define UART_OVERRUN_ERROR		0x40
>  #define UART_CTS			0x80
>  
> +#define TYPE_HX_READ_OTP_STATUS_REGISTER	0x8484
> +#define TYPE_HX_EXTERNAL_PULLUP_MODE	0x08
> +#define TYPE_HX_PULLUP_MODE_REG		0x09
> +#define TYPE_HXN_UART_FLOWCONTROL	0x0A
> +#define TYPE_HXN_HARDWAREFLOW		0xFA
> +#define TYPE_HXN_SOFTWAREFLOW		0xEE
> +#define TYPE_HXN_NOFLOW			0xFF
> +#define TYPE_HXN_RESET_DOWN_UPSTREAM	0x07
> +
>  static void pl2303_set_break(struct usb_serial_port *port, bool enable);
>  
>  enum pl2303_type {
>  	TYPE_01,	/* Type 0 and 1 (difference unknown) */
>  	TYPE_HX,	/* HX version of the pl2303 chip */
> +	TYPE_HXN,	/* HXN version of the pl2303 chip */
>  	TYPE_COUNT
>  };
>  
> @@ -173,16 +192,26 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
>  	[TYPE_HX] = {
>  		.max_baud_rate =	12000000,
>  	},
> +	[TYPE_HXN] = {
> +		.max_baud_rate =	12000000,
> +	},
>  };
>  
>  static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
>  							unsigned char buf[1])
>  {
>  	struct device *dev = &serial->interface->dev;
> +	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
>  	int res;
> +	u8 request;
> +
> +	if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +		request = VENDOR_READ_NREQUEST;
> +	else
> +		request = VENDOR_READ_REQUEST;
>  
>  	res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
> -			VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
> +			request, VENDOR_READ_REQUEST_TYPE,
>  			value, 0, buf, 1, 100);
>  	if (res != 1) {
>  		dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__,
> @@ -201,12 +230,19 @@ static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
>  static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
>  {
>  	struct device *dev = &serial->interface->dev;
> +	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
>  	int res;
> +	u8 request;
>  
>  	dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index);
>  
> +	if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +		request = VENDOR_WRITE_NREQUEST;
> +	else
> +		request = VENDOR_WRITE_REQUEST;
> +
>  	res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
> -			VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
> +			request, VENDOR_WRITE_REQUEST_TYPE,
>  			value, index, NULL, 0, 100);
>  	if (res) {
>  		dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__,
> @@ -286,6 +322,7 @@ static int pl2303_startup(struct usb_serial *serial)
>  	struct pl2303_serial_private *spriv;
>  	enum pl2303_type type = TYPE_01;
>  	unsigned char *buf;
> +	int res;
>  
>  	spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
>  	if (!spriv)
> @@ -307,26 +344,36 @@ static int pl2303_startup(struct usb_serial *serial)
>  		type = TYPE_01;		/* type 1 */
>  	dev_dbg(&serial->interface->dev, "device type: %d\n", type);
>  
> +	if (type == TYPE_HX) {
> +		res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
> +			VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
> +			TYPE_HX_READ_OTP_STATUS_REGISTER, 0, buf, 1, 100);
> +		if (res != 1)
> +			type = TYPE_HXN;
> +	}

So HXN looks like an HX, but responds with an error when trying to read
any (?) register using VENDOR_READ_REQUEST? Isn't there any more direct
way of determining the device type?

My old HXD device report bcdUSB as 0x0110, and you tested you also
tested against 0x0200 in a previous version of this patch. How does
bcdUSB relate to the various types?

Note that you should use le16_to_cpu() to access bcdUSB safely also on
big-endian systems (which the kbuild test robot somewhat cryptically
reported).

> +
>  	spriv->type = &pl2303_type_data[type];
>  	spriv->quirks = (unsigned long)usb_get_serial_data(serial);
>  	spriv->quirks |= spriv->type->quirks;
>  
>  	usb_set_serial_data(serial, spriv);
>  
> -	pl2303_vendor_read(serial, 0x8484, buf);
> -	pl2303_vendor_write(serial, 0x0404, 0);
> -	pl2303_vendor_read(serial, 0x8484, buf);
> -	pl2303_vendor_read(serial, 0x8383, buf);
> -	pl2303_vendor_read(serial, 0x8484, buf);
> -	pl2303_vendor_write(serial, 0x0404, 1);
> -	pl2303_vendor_read(serial, 0x8484, buf);
> -	pl2303_vendor_read(serial, 0x8383, buf);
> -	pl2303_vendor_write(serial, 0, 1);
> -	pl2303_vendor_write(serial, 1, 0);
> -	if (spriv->quirks & PL2303_QUIRK_LEGACY)
> -		pl2303_vendor_write(serial, 2, 0x24);
> -	else
> -		pl2303_vendor_write(serial, 2, 0x44);
> +	if (type != TYPE_HXN) {
> +		pl2303_vendor_read(serial, 0x8484, buf);
> +		pl2303_vendor_write(serial, 0x0404, 0);
> +		pl2303_vendor_read(serial, 0x8484, buf);
> +		pl2303_vendor_read(serial, 0x8383, buf);
> +		pl2303_vendor_read(serial, 0x8484, buf);
> +		pl2303_vendor_write(serial, 0x0404, 1);
> +		pl2303_vendor_read(serial, 0x8484, buf);
> +		pl2303_vendor_read(serial, 0x8383, buf);
> +		pl2303_vendor_write(serial, 0, 1);
> +		pl2303_vendor_write(serial, 1, 0);
> +		if (spriv->quirks & PL2303_QUIRK_LEGACY)
> +			pl2303_vendor_write(serial, 2, 0x24);
> +		else
> +			pl2303_vendor_write(serial, 2, 0x44);
> +	}

Is this even needed for pre-HXN devices, or could perhaps some of it
just be removed? Can you explain what it does?

>  	kfree(buf);
>  
> @@ -671,15 +718,37 @@ static void pl2303_set_termios(struct tty_struct *tty,
>  	}
>  
>  	if (C_CRTSCTS(tty)) {
> -		if (spriv->quirks & PL2303_QUIRK_LEGACY)
> +		if (spriv->type == &pl2303_type_data[TYPE_01])
>  			pl2303_vendor_write(serial, 0x0, 0x41);
> +		else if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +			pl2303_vendor_write(serial, TYPE_HXN_UART_FLOWCONTROL,
> +						 TYPE_HXN_HARDWAREFLOW);
>  		else
>  			pl2303_vendor_write(serial, 0x0, 0x61);
>  	} else if (I_IXON(tty) && !I_IXANY(tty) && START_CHAR(tty) == 0x11 &&
>  			STOP_CHAR(tty) == 0x13) {
> -		pl2303_vendor_write(serial, 0x0, 0xc0);
> +		if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +			pl2303_vendor_write(serial, TYPE_HXN_UART_FLOWCONTROL,
> +						 TYPE_HXN_SOFTWAREFLOW);
> +		else
> +			pl2303_vendor_write(serial, 0x0, 0xc0);
>  	} else {
> -		pl2303_vendor_write(serial, 0x0, 0x0);
> +		if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +			pl2303_vendor_write(serial, TYPE_HXN_UART_FLOWCONTROL,
> +						 TYPE_HXN_NOFLOW);
> +		else
> +			pl2303_vendor_write(serial, 0x0, 0x0);
> +	}

So this is becoming a bit hard to read. The idea with the type struct
was to abstract the differences. We could add something like

	u8 uart_flowctrl_reg;
	u8 uart_flowctrl_hw;
	u8 uart_flowctrl_sw;
	u8 uart_flowctrl_none;

to struct pl2303_type_data, and replace the above with just three calls
to pl2303_vendor_write().

If you do this, then do this as a preparatory patch before adding HXN
support.

We should probably do something similar with the read and write requests
instead of adding conditionals in those paths.

> +
> +	if (spriv->type == &pl2303_type_data[TYPE_HX]) {
> +		pl2303_vendor_read(serial, 0x8484, buf);
> +		pl2303_vendor_write(serial, 0x0404, TYPE_HX_PULLUP_MODE_REG);
> +		pl2303_vendor_read(serial, 0x8484, buf);
> +		pl2303_vendor_read(serial, 0x8383, buf);
> +		if ((u16)*buf & TYPE_HX_EXTERNAL_PULLUP_MODE) {
> +			pl2303_vendor_write(serial, 0x0, 0x31);
> +			pl2303_vendor_write(serial, 0x1, 0x01);
> +		}

So this bit needs to go in it's own patch with a commit message
explaining why it is needed. Don't forget to replace the "magic
constants" with descriptive defines.

*buf is u8 and no need to cast to u16, as I also already pointed out in
my comments to an earlier version of the patch.

>  	}
>  
>  	kfree(buf);
> @@ -720,8 +789,13 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
>  		usb_clear_halt(serial->dev, port->read_urb->pipe);
>  	} else {
>  		/* reset upstream data pipes */
> -		pl2303_vendor_write(serial, 8, 0);
> -		pl2303_vendor_write(serial, 9, 0);
> +		if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +			pl2303_vendor_write(serial,
> +					TYPE_HXN_RESET_DOWN_UPSTREAM, 0);
> +		else {
> +			pl2303_vendor_write(serial, 8, 0);
> +			pl2303_vendor_write(serial, 9, 0);
> +		}

So this is a bit harder to abstract, but could be done using function
pointers although it's probably not worth it just yet.

Just make sure to do add bracket ({}) also to the branch you add here.

Thanks,
Johan
Yeh.Charles [葉榮鑫] Jan. 9, 2019, 1:12 a.m. UTC | #2
Hi Johan,
  OK, I will release three separate patches on next week.
  1. PL2303TB PID : [PATCH ]: USB: serial: pl2303: Add new PID to support PL2303TB (TYPE_HX)
  2. pull-up mode: [PATCH ]: USB: serial: pl2303: Add new Pull-Up Mode to support PL2303HXD (TYPE_HX)
  3. TYPE_HXN support + PIDs : [PATCH ]: USB: serial: pl2303: Add new PID & flow Control to support PL2303HXN (TYPE_HXN)

   Thank for you check the patch code..

Charles Yeh(葉榮鑫)
TeL: +886-2-26546363 ext.522
Prolific Technology Inc. (旺玖科技股份有限公司)

-----Original Message-----
From: Johan Hovold [mailto:jhovold@gmail.com] On Behalf Of Johan Hovold
Sent: Wednesday, January 9, 2019 1:01 AM
To: Charles Yeh
Cc: lkp@intel.com; kbuild-all@01.org; gregkh@linuxfoundation.org; johan@kernel.org; linux-usb@vger.kernel.org; Yeh.Charles [葉榮鑫]
Subject: Re: [PATCH] Add Proliic new chip: PL2303TB & PL2303N(G)

On Wed, Dec 26, 2018 at 08:28:10PM +0800, Charles Yeh wrote:

> Add new PID to support PL2303TB (TYPE_HX)
> Add new PID to support PL2303(N)GC/GB/GS/GT/GL/GE (TYPE_HXN)
> Add new Flow Control to support PL2303(N)GC/GB/GS/GT/GL/GE (TYPE_HXN)
> Add new Pull-Up Mode to support PL2303HXD (TYPE_HX)

So the above should go in three separate patches as we already discussed
(PL2303TB PID, TYPE_HXN support + PIDs, "pull-up mode").

> Fixed warning:restricted__le16 degrades to integer

When you update and resend a patch, it's good to include changelog
information like this, but please put it below the cut-off line (---)
below so that it doesn't end up in the commit log.

Also include a patch revision in the Subject of the mail. Let's call
this one v2, so next time use the following Subject prefix:

[PATCH v3]: USB: serial: pl2303: add ...

(also after breaking the current patch into a series of three or more
patches).

> Signed-off-by: Charles Yeh <charlesyeh522@gmail.com>
> ---
>  drivers/usb/serial/pl2303.c | 116 +++++++++++++++++++++++++++++-------
>  drivers/usb/serial/pl2303.h |  11 ++++
>  2 files changed, 106 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
> index a4e0d13fc121..8c71612e1811 100644
> --- a/drivers/usb/serial/pl2303.c
> +++ b/drivers/usb/serial/pl2303.c
> @@ -46,6 +46,13 @@ static const struct usb_device_id id_table[] = {
>  { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) },
>  { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) },
>  { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) },
> +{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_TB) },
> +{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GC) },
> +{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GB) },
> +{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GT) },
> +{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GL) },
> +{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GE) },
> +{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GS) },
>  { 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),
> @@ -123,9 +130,11 @@ MODULE_DEVICE_TABLE(usb, id_table);
>
>  #define VENDOR_WRITE_REQUEST_TYPE0x40
>  #define VENDOR_WRITE_REQUEST0x01
> +#define VENDOR_WRITE_NREQUEST0x80
>
>  #define VENDOR_READ_REQUEST_TYPE0xc0
>  #define VENDOR_READ_REQUEST0x01
> +#define VENDOR_READ_NREQUEST0x81
>
>  #define UART_STATE_INDEX8
>  #define UART_STATE_MSR_MASK0x8b
> @@ -139,11 +148,21 @@ MODULE_DEVICE_TABLE(usb, id_table);
>  #define UART_OVERRUN_ERROR0x40
>  #define UART_CTS0x80
>
> +#define TYPE_HX_READ_OTP_STATUS_REGISTER0x8484
> +#define TYPE_HX_EXTERNAL_PULLUP_MODE0x08
> +#define TYPE_HX_PULLUP_MODE_REG0x09
> +#define TYPE_HXN_UART_FLOWCONTROL0x0A
> +#define TYPE_HXN_HARDWAREFLOW0xFA
> +#define TYPE_HXN_SOFTWAREFLOW0xEE
> +#define TYPE_HXN_NOFLOW0xFF
> +#define TYPE_HXN_RESET_DOWN_UPSTREAM0x07
> +
>  static void pl2303_set_break(struct usb_serial_port *port, bool enable);
>
>  enum pl2303_type {
>  TYPE_01,/* Type 0 and 1 (difference unknown) */
>  TYPE_HX,/* HX version of the pl2303 chip */
> +TYPE_HXN,/* HXN version of the pl2303 chip */
>  TYPE_COUNT
>  };
>
> @@ -173,16 +192,26 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
>  [TYPE_HX] = {
>  .max_baud_rate =12000000,
>  },
> +[TYPE_HXN] = {
> +.max_baud_rate =12000000,
> +},
>  };
>
>  static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
>  unsigned char buf[1])
>  {
>  struct device *dev = &serial->interface->dev;
> +struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
>  int res;
> +u8 request;
> +
> +if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +request = VENDOR_READ_NREQUEST;
> +else
> +request = VENDOR_READ_REQUEST;
>
>  res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
> -VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
> +request, VENDOR_READ_REQUEST_TYPE,
>  value, 0, buf, 1, 100);
>  if (res != 1) {
>  dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__,
> @@ -201,12 +230,19 @@ static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
>  static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
>  {
>  struct device *dev = &serial->interface->dev;
> +struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
>  int res;
> +u8 request;
>
>  dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index);
>
> +if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +request = VENDOR_WRITE_NREQUEST;
> +else
> +request = VENDOR_WRITE_REQUEST;
> +
>  res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
> -VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
> +request, VENDOR_WRITE_REQUEST_TYPE,
>  value, index, NULL, 0, 100);
>  if (res) {
>  dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__,
> @@ -286,6 +322,7 @@ static int pl2303_startup(struct usb_serial *serial)
>  struct pl2303_serial_private *spriv;
>  enum pl2303_type type = TYPE_01;
>  unsigned char *buf;
> +int res;
>
>  spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
>  if (!spriv)
> @@ -307,26 +344,36 @@ static int pl2303_startup(struct usb_serial *serial)
>  type = TYPE_01;/* type 1 */
>  dev_dbg(&serial->interface->dev, "device type: %d\n", type);
>
> +if (type == TYPE_HX) {
> +res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
> +VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
> +TYPE_HX_READ_OTP_STATUS_REGISTER, 0, buf, 1, 100);
> +if (res != 1)
> +type = TYPE_HXN;
> +}

So HXN looks like an HX, but responds with an error when trying to read
any (?) register using VENDOR_READ_REQUEST? Isn't there any more direct
way of determining the device type?

My old HXD device report bcdUSB as 0x0110, and you tested you also
tested against 0x0200 in a previous version of this patch. How does
bcdUSB relate to the various types?

Note that you should use le16_to_cpu() to access bcdUSB safely also on
big-endian systems (which the kbuild test robot somewhat cryptically
reported).

> +
>  spriv->type = &pl2303_type_data[type];
>  spriv->quirks = (unsigned long)usb_get_serial_data(serial);
>  spriv->quirks |= spriv->type->quirks;
>
>  usb_set_serial_data(serial, spriv);
>
> -pl2303_vendor_read(serial, 0x8484, buf);
> -pl2303_vendor_write(serial, 0x0404, 0);
> -pl2303_vendor_read(serial, 0x8484, buf);
> -pl2303_vendor_read(serial, 0x8383, buf);
> -pl2303_vendor_read(serial, 0x8484, buf);
> -pl2303_vendor_write(serial, 0x0404, 1);
> -pl2303_vendor_read(serial, 0x8484, buf);
> -pl2303_vendor_read(serial, 0x8383, buf);
> -pl2303_vendor_write(serial, 0, 1);
> -pl2303_vendor_write(serial, 1, 0);
> -if (spriv->quirks & PL2303_QUIRK_LEGACY)
> -pl2303_vendor_write(serial, 2, 0x24);
> -else
> -pl2303_vendor_write(serial, 2, 0x44);
> +if (type != TYPE_HXN) {
> +pl2303_vendor_read(serial, 0x8484, buf);
> +pl2303_vendor_write(serial, 0x0404, 0);
> +pl2303_vendor_read(serial, 0x8484, buf);
> +pl2303_vendor_read(serial, 0x8383, buf);
> +pl2303_vendor_read(serial, 0x8484, buf);
> +pl2303_vendor_write(serial, 0x0404, 1);
> +pl2303_vendor_read(serial, 0x8484, buf);
> +pl2303_vendor_read(serial, 0x8383, buf);
> +pl2303_vendor_write(serial, 0, 1);
> +pl2303_vendor_write(serial, 1, 0);
> +if (spriv->quirks & PL2303_QUIRK_LEGACY)
> +pl2303_vendor_write(serial, 2, 0x24);
> +else
> +pl2303_vendor_write(serial, 2, 0x44);
> +}

Is this even needed for pre-HXN devices, or could perhaps some of it
just be removed? Can you explain what it does?

>  kfree(buf);
>
> @@ -671,15 +718,37 @@ static void pl2303_set_termios(struct tty_struct *tty,
>  }
>
>  if (C_CRTSCTS(tty)) {
> -if (spriv->quirks & PL2303_QUIRK_LEGACY)
> +if (spriv->type == &pl2303_type_data[TYPE_01])
>  pl2303_vendor_write(serial, 0x0, 0x41);
> +else if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +pl2303_vendor_write(serial, TYPE_HXN_UART_FLOWCONTROL,
> + TYPE_HXN_HARDWAREFLOW);
>  else
>  pl2303_vendor_write(serial, 0x0, 0x61);
>  } else if (I_IXON(tty) && !I_IXANY(tty) && START_CHAR(tty) == 0x11 &&
>  STOP_CHAR(tty) == 0x13) {
> -pl2303_vendor_write(serial, 0x0, 0xc0);
> +if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +pl2303_vendor_write(serial, TYPE_HXN_UART_FLOWCONTROL,
> + TYPE_HXN_SOFTWAREFLOW);
> +else
> +pl2303_vendor_write(serial, 0x0, 0xc0);
>  } else {
> -pl2303_vendor_write(serial, 0x0, 0x0);
> +if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +pl2303_vendor_write(serial, TYPE_HXN_UART_FLOWCONTROL,
> + TYPE_HXN_NOFLOW);
> +else
> +pl2303_vendor_write(serial, 0x0, 0x0);
> +}

So this is becoming a bit hard to read. The idea with the type struct
was to abstract the differences. We could add something like

u8 uart_flowctrl_reg;
u8 uart_flowctrl_hw;
u8 uart_flowctrl_sw;
u8 uart_flowctrl_none;

to struct pl2303_type_data, and replace the above with just three calls
to pl2303_vendor_write().

If you do this, then do this as a preparatory patch before adding HXN
support.

We should probably do something similar with the read and write requests
instead of adding conditionals in those paths.

> +
> +if (spriv->type == &pl2303_type_data[TYPE_HX]) {
> +pl2303_vendor_read(serial, 0x8484, buf);
> +pl2303_vendor_write(serial, 0x0404, TYPE_HX_PULLUP_MODE_REG);
> +pl2303_vendor_read(serial, 0x8484, buf);
> +pl2303_vendor_read(serial, 0x8383, buf);
> +if ((u16)*buf & TYPE_HX_EXTERNAL_PULLUP_MODE) {
> +pl2303_vendor_write(serial, 0x0, 0x31);
> +pl2303_vendor_write(serial, 0x1, 0x01);
> +}

So this bit needs to go in it's own patch with a commit message
explaining why it is needed. Don't forget to replace the "magic
constants" with descriptive defines.

*buf is u8 and no need to cast to u16, as I also already pointed out in
my comments to an earlier version of the patch.

>  }
>
>  kfree(buf);
> @@ -720,8 +789,13 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
>  usb_clear_halt(serial->dev, port->read_urb->pipe);
>  } else {
>  /* reset upstream data pipes */
> -pl2303_vendor_write(serial, 8, 0);
> -pl2303_vendor_write(serial, 9, 0);
> +if (spriv->type == &pl2303_type_data[TYPE_HXN])
> +pl2303_vendor_write(serial,
> +TYPE_HXN_RESET_DOWN_UPSTREAM, 0);
> +else {
> +pl2303_vendor_write(serial, 8, 0);
> +pl2303_vendor_write(serial, 9, 0);
> +}

So this is a bit harder to abstract, but could be done using function
pointers although it's probably not worth it just yet.

Just make sure to do add bracket ({}) also to the branch you add here.

Thanks,
Johan
保密警語: 本電子郵件內容及其附加檔案均視為機密資料,受保密合約保護或依法不得洩漏。其內容僅供指定收件人按限定範圍或特殊目的合法使用,未經授權者收到此信息均無權閱讀、使用、複製、洩漏或散佈。若您並非本郵件之指定收件人,請即刻回覆郵件並永久刪除此郵件及其附件和銷毀所有複印文件。電子郵件的傳輸可能遭攔截、損毀、遺失、破壞、遲到或不完整、或包含病毒,無法保證其安全或無誤。寄件人不承擔因本電子郵件的錯誤或遺漏所產生的任何損害賠償責任。 Confidentiality Notice: This e-mail message together with any attachments thereto (if any) is confidential, protected under an enforceable non-disclosure agreement, intended only for the use of the named recipient(s) above and may contain information that is privileged, belonging to professional work products or exempt from disclosure under applicable laws. Any unauthorized review, use, copying, disclosure, or distribution of any information contained in or attached to this transmission is strictly prohibited and may be against the laws. If you have received this message in error, or are not the intended recipient(s), please immediately notify the sender by e-mail and delete this e-mail message, all copies, and any attached documentation from your computer. E-mail transmission cannot be guaranteed to be secure or error-free as information could be intercepted, corrupted, lost, destroyed, arrive late or incomplete, or contain viruses. The sender therefore does not accept liability for any damage caused by any errors or omissions in the contents of this email.
diff mbox series

Patch

diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index a4e0d13fc121..8c71612e1811 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -46,6 +46,13 @@  static const struct usb_device_id id_table[] = {
 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) },
 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) },
 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_TB) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GC) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GB) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GT) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GL) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GE) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303G_PRODUCT_ID_GS) },
 	{ 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),
@@ -123,9 +130,11 @@  MODULE_DEVICE_TABLE(usb, id_table);
 
 #define VENDOR_WRITE_REQUEST_TYPE	0x40
 #define VENDOR_WRITE_REQUEST		0x01
+#define VENDOR_WRITE_NREQUEST		0x80
 
 #define VENDOR_READ_REQUEST_TYPE	0xc0
 #define VENDOR_READ_REQUEST		0x01
+#define VENDOR_READ_NREQUEST		0x81
 
 #define UART_STATE_INDEX		8
 #define UART_STATE_MSR_MASK		0x8b
@@ -139,11 +148,21 @@  MODULE_DEVICE_TABLE(usb, id_table);
 #define UART_OVERRUN_ERROR		0x40
 #define UART_CTS			0x80
 
+#define TYPE_HX_READ_OTP_STATUS_REGISTER	0x8484
+#define TYPE_HX_EXTERNAL_PULLUP_MODE	0x08
+#define TYPE_HX_PULLUP_MODE_REG		0x09
+#define TYPE_HXN_UART_FLOWCONTROL	0x0A
+#define TYPE_HXN_HARDWAREFLOW		0xFA
+#define TYPE_HXN_SOFTWAREFLOW		0xEE
+#define TYPE_HXN_NOFLOW			0xFF
+#define TYPE_HXN_RESET_DOWN_UPSTREAM	0x07
+
 static void pl2303_set_break(struct usb_serial_port *port, bool enable);
 
 enum pl2303_type {
 	TYPE_01,	/* Type 0 and 1 (difference unknown) */
 	TYPE_HX,	/* HX version of the pl2303 chip */
+	TYPE_HXN,	/* HXN version of the pl2303 chip */
 	TYPE_COUNT
 };
 
@@ -173,16 +192,26 @@  static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
 	[TYPE_HX] = {
 		.max_baud_rate =	12000000,
 	},
+	[TYPE_HXN] = {
+		.max_baud_rate =	12000000,
+	},
 };
 
 static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
 							unsigned char buf[1])
 {
 	struct device *dev = &serial->interface->dev;
+	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
 	int res;
+	u8 request;
+
+	if (spriv->type == &pl2303_type_data[TYPE_HXN])
+		request = VENDOR_READ_NREQUEST;
+	else
+		request = VENDOR_READ_REQUEST;
 
 	res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
-			VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
+			request, VENDOR_READ_REQUEST_TYPE,
 			value, 0, buf, 1, 100);
 	if (res != 1) {
 		dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__,
@@ -201,12 +230,19 @@  static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
 static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
 {
 	struct device *dev = &serial->interface->dev;
+	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
 	int res;
+	u8 request;
 
 	dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index);
 
+	if (spriv->type == &pl2303_type_data[TYPE_HXN])
+		request = VENDOR_WRITE_NREQUEST;
+	else
+		request = VENDOR_WRITE_REQUEST;
+
 	res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
-			VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
+			request, VENDOR_WRITE_REQUEST_TYPE,
 			value, index, NULL, 0, 100);
 	if (res) {
 		dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__,
@@ -286,6 +322,7 @@  static int pl2303_startup(struct usb_serial *serial)
 	struct pl2303_serial_private *spriv;
 	enum pl2303_type type = TYPE_01;
 	unsigned char *buf;
+	int res;
 
 	spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
 	if (!spriv)
@@ -307,26 +344,36 @@  static int pl2303_startup(struct usb_serial *serial)
 		type = TYPE_01;		/* type 1 */
 	dev_dbg(&serial->interface->dev, "device type: %d\n", type);
 
+	if (type == TYPE_HX) {
+		res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
+			TYPE_HX_READ_OTP_STATUS_REGISTER, 0, buf, 1, 100);
+		if (res != 1)
+			type = TYPE_HXN;
+	}
+
 	spriv->type = &pl2303_type_data[type];
 	spriv->quirks = (unsigned long)usb_get_serial_data(serial);
 	spriv->quirks |= spriv->type->quirks;
 
 	usb_set_serial_data(serial, spriv);
 
-	pl2303_vendor_read(serial, 0x8484, buf);
-	pl2303_vendor_write(serial, 0x0404, 0);
-	pl2303_vendor_read(serial, 0x8484, buf);
-	pl2303_vendor_read(serial, 0x8383, buf);
-	pl2303_vendor_read(serial, 0x8484, buf);
-	pl2303_vendor_write(serial, 0x0404, 1);
-	pl2303_vendor_read(serial, 0x8484, buf);
-	pl2303_vendor_read(serial, 0x8383, buf);
-	pl2303_vendor_write(serial, 0, 1);
-	pl2303_vendor_write(serial, 1, 0);
-	if (spriv->quirks & PL2303_QUIRK_LEGACY)
-		pl2303_vendor_write(serial, 2, 0x24);
-	else
-		pl2303_vendor_write(serial, 2, 0x44);
+	if (type != TYPE_HXN) {
+		pl2303_vendor_read(serial, 0x8484, buf);
+		pl2303_vendor_write(serial, 0x0404, 0);
+		pl2303_vendor_read(serial, 0x8484, buf);
+		pl2303_vendor_read(serial, 0x8383, buf);
+		pl2303_vendor_read(serial, 0x8484, buf);
+		pl2303_vendor_write(serial, 0x0404, 1);
+		pl2303_vendor_read(serial, 0x8484, buf);
+		pl2303_vendor_read(serial, 0x8383, buf);
+		pl2303_vendor_write(serial, 0, 1);
+		pl2303_vendor_write(serial, 1, 0);
+		if (spriv->quirks & PL2303_QUIRK_LEGACY)
+			pl2303_vendor_write(serial, 2, 0x24);
+		else
+			pl2303_vendor_write(serial, 2, 0x44);
+	}
 
 	kfree(buf);
 
@@ -671,15 +718,37 @@  static void pl2303_set_termios(struct tty_struct *tty,
 	}
 
 	if (C_CRTSCTS(tty)) {
-		if (spriv->quirks & PL2303_QUIRK_LEGACY)
+		if (spriv->type == &pl2303_type_data[TYPE_01])
 			pl2303_vendor_write(serial, 0x0, 0x41);
+		else if (spriv->type == &pl2303_type_data[TYPE_HXN])
+			pl2303_vendor_write(serial, TYPE_HXN_UART_FLOWCONTROL,
+						 TYPE_HXN_HARDWAREFLOW);
 		else
 			pl2303_vendor_write(serial, 0x0, 0x61);
 	} else if (I_IXON(tty) && !I_IXANY(tty) && START_CHAR(tty) == 0x11 &&
 			STOP_CHAR(tty) == 0x13) {
-		pl2303_vendor_write(serial, 0x0, 0xc0);
+		if (spriv->type == &pl2303_type_data[TYPE_HXN])
+			pl2303_vendor_write(serial, TYPE_HXN_UART_FLOWCONTROL,
+						 TYPE_HXN_SOFTWAREFLOW);
+		else
+			pl2303_vendor_write(serial, 0x0, 0xc0);
 	} else {
-		pl2303_vendor_write(serial, 0x0, 0x0);
+		if (spriv->type == &pl2303_type_data[TYPE_HXN])
+			pl2303_vendor_write(serial, TYPE_HXN_UART_FLOWCONTROL,
+						 TYPE_HXN_NOFLOW);
+		else
+			pl2303_vendor_write(serial, 0x0, 0x0);
+	}
+
+	if (spriv->type == &pl2303_type_data[TYPE_HX]) {
+		pl2303_vendor_read(serial, 0x8484, buf);
+		pl2303_vendor_write(serial, 0x0404, TYPE_HX_PULLUP_MODE_REG);
+		pl2303_vendor_read(serial, 0x8484, buf);
+		pl2303_vendor_read(serial, 0x8383, buf);
+		if ((u16)*buf & TYPE_HX_EXTERNAL_PULLUP_MODE) {
+			pl2303_vendor_write(serial, 0x0, 0x31);
+			pl2303_vendor_write(serial, 0x1, 0x01);
+		}
 	}
 
 	kfree(buf);
@@ -720,8 +789,13 @@  static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
 		usb_clear_halt(serial->dev, port->read_urb->pipe);
 	} else {
 		/* reset upstream data pipes */
-		pl2303_vendor_write(serial, 8, 0);
-		pl2303_vendor_write(serial, 9, 0);
+		if (spriv->type == &pl2303_type_data[TYPE_HXN])
+			pl2303_vendor_write(serial,
+					TYPE_HXN_RESET_DOWN_UPSTREAM, 0);
+		else {
+			pl2303_vendor_write(serial, 8, 0);
+			pl2303_vendor_write(serial, 9, 0);
+		}
 	}
 
 	/* Setup termios */
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 26965cc23c17..f7c500622f6b 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -20,6 +20,17 @@ 
 #define PL2303_PRODUCT_ID_MOTOROLA	0x0307
 #define PL2303_PRODUCT_ID_ZTEK		0xe1f1
 
+/* PL2303TB , TYPE_HX */
+#define PL2303_PRODUCT_ID_TB	0x2304
+
+/* PL2303HXN , TYPE_HXN */
+#define PL2303G_PRODUCT_ID_GC	0x23A3
+#define PL2303G_PRODUCT_ID_GB	0x23B3
+#define PL2303G_PRODUCT_ID_GT	0x23C3
+#define PL2303G_PRODUCT_ID_GL	0x23D3
+#define PL2303G_PRODUCT_ID_GE	0x23E3
+#define PL2303G_PRODUCT_ID_GS	0x23F3
+
 #define ATEN_VENDOR_ID		0x0557
 #define ATEN_VENDOR_ID2		0x0547
 #define ATEN_PRODUCT_ID		0x2008