diff options
Diffstat (limited to 'drivers/usb/serial/pl2303.c')
| -rw-r--r-- | drivers/usb/serial/pl2303.c | 659 | 
1 files changed, 304 insertions, 355 deletions
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index e7a84f0f517..b3d5a35c0d4 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -4,11 +4,6 @@   * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com)   * Copyright (C) 2003 IBM Corp.   * - * Copyright (C) 2009, 2013 Frank Schäfer <fschaefer.oss@googlemail.com> - *  - fixes, improvements and documentation for the baud rate encoding methods - * Copyright (C) 2013 Reinhard Max <max@suse.de> - *  - fixes and improvements for the divisor based baud rate encoding method - *   * Original driver for 2.2.x by anonymous   *   *	This program is free software; you can redistribute it and/or @@ -17,12 +12,10 @@   *   * See Documentation/usb/usb-serial.txt for more information on using this   * driver - *   */  #include <linux/kernel.h>  #include <linux/errno.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/tty.h>  #include <linux/tty_driver.h> @@ -37,10 +30,9 @@  #include <asm/unaligned.h>  #include "pl2303.h" -/* - * Version Information - */ -#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver" + +#define PL2303_QUIRK_UART_STATE_IDX0		BIT(0) +#define PL2303_QUIRK_LEGACY			BIT(1)  static const struct usb_device_id id_table[] = {  	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, @@ -69,9 +61,12 @@ static const struct usb_device_id id_table[] = {  	{ USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },  	{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) },  	{ USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) }, -	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1) }, -	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) }, -	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) }, +	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1), +		.driver_info = PL2303_QUIRK_UART_STATE_IDX0 }, +	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65), +		.driver_info = PL2303_QUIRK_UART_STATE_IDX0 }, +	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75), +		.driver_info = PL2303_QUIRK_UART_STATE_IDX0 },  	{ USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) },  	{ USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */  	{ USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) }, @@ -88,6 +83,9 @@ static const struct usb_device_id id_table[] = {  	{ USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) },  	{ USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) },  	{ USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) }, +	{ USB_DEVICE(HP_VENDOR_ID, HP_LD960_PRODUCT_ID) }, +	{ USB_DEVICE(HP_VENDOR_ID, HP_LCM220_PRODUCT_ID) }, +	{ USB_DEVICE(HP_VENDOR_ID, HP_LCM960_PRODUCT_ID) },  	{ USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) },  	{ USB_DEVICE(ZEAGLE_VENDOR_ID, ZEAGLE_N2ITION3_PRODUCT_ID) },  	{ USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) }, @@ -121,7 +119,8 @@ MODULE_DEVICE_TABLE(usb, id_table);  #define VENDOR_READ_REQUEST_TYPE	0xc0  #define VENDOR_READ_REQUEST		0x01 -#define UART_STATE			0x08 +#define UART_STATE_INDEX		8 +#define UART_STATE_MSR_MASK		0x8b  #define UART_STATE_TRANSIENT_MASK	0x74  #define UART_DCD			0x01  #define UART_DSR			0x02 @@ -134,129 +133,142 @@ MODULE_DEVICE_TABLE(usb, id_table);  enum pl2303_type { -	type_0,		/* H version ? */ -	type_1,		/* H version ? */ -	HX_TA,		/* HX(A) / X(A) / TA version  */ /* TODO: improve */ -	HXD_EA_RA_SA,	/* HXD / EA / RA / SA version */ /* TODO: improve */ -	TB,		/* TB version */ +	TYPE_01,	/* Type 0 and 1 (difference unknown) */ +	TYPE_HX,	/* HX version of the pl2303 chip */ +	TYPE_COUNT +}; + +struct pl2303_type_data { +	speed_t max_baud_rate; +	unsigned long quirks;  }; -/* - * NOTE: don't know the difference between type 0 and type 1, - * until someone from Prolific tells us... - * TODO: distinguish between X/HX, TA and HXD, EA, RA, SA variants - */  struct pl2303_serial_private { -	enum pl2303_type type; +	const struct pl2303_type_data *type; +	unsigned long quirks;  };  struct pl2303_private {  	spinlock_t lock;  	u8 line_control;  	u8 line_status; + +	u8 line_settings[7];  }; -static int pl2303_vendor_read(__u16 value, __u16 index, -		struct usb_serial *serial, unsigned char *buf) +static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { +	[TYPE_01] = { +		.max_baud_rate =	1228800, +		.quirks =		PL2303_QUIRK_LEGACY, +	}, +}; + +static int pl2303_vendor_read(struct usb_serial *serial, u16 value, +							unsigned char buf[1])  { -	int res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), +	struct device *dev = &serial->interface->dev; +	int res; + +	res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),  			VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE, -			value, index, buf, 1, 100); -	dev_dbg(&serial->interface->dev, "0x%x:0x%x:0x%x:0x%x  %d - %x\n", -		VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, value, index, -		res, buf[0]); -	return res; +			value, 0, buf, 1, 100); +	if (res != 1) { +		dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__, +								value, res); +		if (res >= 0) +			res = -EIO; + +		return res; +	} + +	dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, buf[0]); + +	return 0;  } -static int pl2303_vendor_write(__u16 value, __u16 index, -		struct usb_serial *serial) +static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)  { -	int res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), +	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),  			VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,  			value, index, NULL, 0, 100); -	dev_dbg(&serial->interface->dev, "0x%x:0x%x:0x%x:0x%x  %d\n", -		VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, value, index, -		res); -	return res; +	if (res) { +		dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__, +								value, res); +		return res; +	} + +	return 0; +} + +static int pl2303_probe(struct usb_serial *serial, +					const struct usb_device_id *id) +{ +	usb_set_serial_data(serial, (void *)id->driver_info); + +	return 0;  }  static int pl2303_startup(struct usb_serial *serial)  {  	struct pl2303_serial_private *spriv; -	enum pl2303_type type = type_0; -	char *type_str = "unknown (treating as type_0)"; +	enum pl2303_type type = TYPE_01;  	unsigned char *buf;  	spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);  	if (!spriv)  		return -ENOMEM; -	buf = kmalloc(10, GFP_KERNEL); +	buf = kmalloc(1, GFP_KERNEL);  	if (!buf) {  		kfree(spriv);  		return -ENOMEM;  	} -	if (serial->dev->descriptor.bDeviceClass == 0x02) { -		type = type_0; -		type_str = "type_0"; -	} else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) { -		/* -		 * NOTE: The bcdDevice version is the only difference between -		 * the device descriptors of the X/HX, HXD, EA, RA, SA, TA, TB -		 */ -		if (le16_to_cpu(serial->dev->descriptor.bcdDevice) == 0x300) { -			type = HX_TA; -			type_str = "X/HX/TA"; -		} else if (le16_to_cpu(serial->dev->descriptor.bcdDevice) -								     == 0x400) { -			type = HXD_EA_RA_SA; -			type_str = "HXD/EA/RA/SA"; -		} else if (le16_to_cpu(serial->dev->descriptor.bcdDevice) -								     == 0x500) { -			type = TB; -			type_str = "TB"; -		} else { -			dev_info(&serial->interface->dev, -					   "unknown/unsupported device type\n"); -			kfree(spriv); -			kfree(buf); -			return -ENODEV; -		} -	} else if (serial->dev->descriptor.bDeviceClass == 0x00 -		   || serial->dev->descriptor.bDeviceClass == 0xFF) { -		type = type_1; -		type_str = "type_1"; -	} -	dev_dbg(&serial->interface->dev, "device type: %s\n", type_str); +	if (serial->dev->descriptor.bDeviceClass == 0x02) +		type = TYPE_01;		/* type 0 */ +	else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) +		type = TYPE_HX; +	else if (serial->dev->descriptor.bDeviceClass == 0x00) +		type = TYPE_01;		/* type 1 */ +	else if (serial->dev->descriptor.bDeviceClass == 0xFF) +		type = TYPE_01;		/* type 1 */ +	dev_dbg(&serial->interface->dev, "device type: %d\n", type); + +	spriv->type = &pl2303_type_data[type]; +	spriv->quirks = (unsigned long)usb_get_serial_data(serial); +	spriv->quirks |= spriv->type->quirks; -	spriv->type = type;  	usb_set_serial_data(serial, spriv); -	pl2303_vendor_read(0x8484, 0, serial, buf); -	pl2303_vendor_write(0x0404, 0, serial); -	pl2303_vendor_read(0x8484, 0, serial, buf); -	pl2303_vendor_read(0x8383, 0, serial, buf); -	pl2303_vendor_read(0x8484, 0, serial, buf); -	pl2303_vendor_write(0x0404, 1, serial); -	pl2303_vendor_read(0x8484, 0, serial, buf); -	pl2303_vendor_read(0x8383, 0, serial, buf); -	pl2303_vendor_write(0, 1, serial); -	pl2303_vendor_write(1, 0, serial); -	if (type == type_0 || type == type_1) -		pl2303_vendor_write(2, 0x24, serial); +	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(2, 0x44, serial); +		pl2303_vendor_write(serial, 2, 0x44);  	kfree(buf); +  	return 0;  }  static void pl2303_release(struct usb_serial *serial)  { -	struct pl2303_serial_private *spriv; +	struct pl2303_serial_private *spriv = usb_get_serial_data(serial); -	spriv = usb_get_serial_data(serial);  	kfree(spriv);  } @@ -279,9 +291,8 @@ static int pl2303_port_probe(struct usb_serial_port *port)  static int pl2303_port_remove(struct usb_serial_port *port)  { -	struct pl2303_private *priv; +	struct pl2303_private *priv = usb_get_serial_port_data(port); -	priv = usb_get_serial_port_data(port);  	kfree(priv);  	return 0; @@ -292,181 +303,157 @@ static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value)  	struct usb_device *dev = port->serial->dev;  	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); -	dev_dbg(&port->dev, "%s - value = %d, retval = %d\n", __func__, -		value, retval); +	if (retval) +		dev_err(&port->dev, "%s - failed: %d\n", __func__, retval); +  	return retval;  } -static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type, -								      u8 buf[4]) +/* + * Returns the nearest supported baud rate that can be set directly without + * using divisors. + */ +static speed_t pl2303_get_supported_baud_rate(speed_t baud)  { -	/* -	 * NOTE: Only the values defined in baud_sup are supported ! -	 *       => if unsupported values are set, the PL2303 seems to -	 *	    use 9600 baud (at least my PL2303X always does) -	 */ -	const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, -				 4800, 7200, 9600, 14400, 19200, 28800, 38400, -				 57600, 115200, 230400, 460800, 614400, 921600, -				 1228800, 2457600, 3000000, 6000000, 12000000 }; -	/* -	 * NOTE: With the exception of type_0/1 devices, the following -	 * additional baud rates are supported (tested with HX rev. 3A only): -	 * 110*, 56000*, 128000, 134400, 161280, 201600, 256000*, 268800, -	 * 403200, 806400.	(*: not HX) -	 * -	 * Maximum values: HXD, TB: 12000000; HX, TA: 6000000; -	 *                 type_0+1: 1228800; RA: 921600; SA: 115200 -	 * -	 * As long as we are not using this encoding method for anything else -	 * than the type_0+1 and HX chips, there is no point in complicating -	 * the code to support them. -	 */ -	int i; +	static const speed_t baud_sup[] = { +		75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, +		14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800, +		614400, 921600, 1228800, 2457600, 3000000, 6000000 +	}; + +	unsigned i; -	/* Set baudrate to nearest supported value */  	for (i = 0; i < ARRAY_SIZE(baud_sup); ++i) {  		if (baud_sup[i] > baud)  			break;  	} +  	if (i == ARRAY_SIZE(baud_sup))  		baud = baud_sup[i - 1];  	else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1]))  		baud = baud_sup[i - 1];  	else  		baud = baud_sup[i]; -	/* Respect the chip type specific baud rate limits */ -	/* -	 * FIXME: as long as we don't know how to distinguish between the -	 * HXD, EA, RA, and SA chip variants, allow the max. value of 12M. -	 */ -	if (type == HX_TA) -		baud = min_t(int, baud, 6000000); -	else if (type == type_0 || type == type_1) -		baud = min_t(int, baud, 1228800); -	/* Direct (standard) baud rate encoding method */ + +	return baud; +} + +/* + * NOTE: If unsupported baud rates are set directly, the PL2303 seems to + *       use 9600 baud. + */ +static speed_t pl2303_encode_baud_rate_direct(unsigned char buf[4], +								speed_t baud) +{  	put_unaligned_le32(baud, buf);  	return baud;  } -static int pl2303_baudrate_encode_divisor(int baud, enum pl2303_type type, -								      u8 buf[4]) +static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4], +								speed_t baud)  { -	/* -	 * Divisor based baud rate encoding method -	 * -	 * NOTE: it's not clear if the type_0/1 chips support this method -	 * -	 * divisor = 12MHz * 32 / baudrate = 2^A * B -	 * -	 * with -	 * -	 * A = buf[1] & 0x0e -	 * B = buf[0]  +  (buf[1] & 0x01) << 8 -	 * -	 * Special cases: -	 * => 8 < B < 16: device seems to work not properly -	 * => B <= 8: device uses the max. value B = 512 instead -	 */ -	unsigned int A, B; +	unsigned int tmp;  	/* -	 * NOTE: The Windows driver allows maximum baud rates of 110% of the -	 * specified maximium value. -	 * Quick tests with early (2004) HX (rev. A) chips suggest, that even -	 * higher baud rates (up to the maximum of 24M baud !) are working fine, -	 * but that should really be tested carefully in "real life" scenarios -	 * before removing the upper limit completely. -	 * Baud rates smaller than the specified 75 baud are definitely working -	 * fine. +	 * Apparently the formula is: +	 * baudrate = 12M * 32 / (2^buf[1]) / buf[0]  	 */ -	if (type == type_0 || type == type_1) -		baud = min_t(int, baud, 1228800 * 1.1); -	else if (type == HX_TA) -		baud = min_t(int, baud, 6000000 * 1.1); -	else if (type == HXD_EA_RA_SA) -		/* HXD, EA: 12Mbps; RA: 1Mbps; SA: 115200 bps */ -		/* -		 * FIXME: as long as we don't know how to distinguish between -		 * these chip variants, allow the max. of these values -		 */ -		baud = min_t(int, baud, 12000000 * 1.1); -	else if (type == TB) -		baud = min_t(int, baud, 12000000 * 1.1); -	/* Determine factors A and B */ -	A = 0; -	B = 12000000 * 32 / baud;  /* 12MHz */ -	B <<= 1; /* Add one bit for rounding */ -	while (B > (512 << 1) && A <= 14) { -		A += 2; -		B >>= 2; -	} -	if (A > 14) { /* max. divisor = min. baudrate reached */ -		A = 14; -		B = 512; -		/* => ~45.78 baud */ -	} else { -		B = (B + 1) >> 1; /* Round the last bit */ -	} -	/* Handle special cases */ -	if (B == 512) -		B = 0; /* also: 1 to 8 */ -	else if (B < 16) -		/* -		 * NOTE: With the current algorithm this happens -		 * only for A=0 and means that the min. divisor -		 * (respectively: the max. baudrate) is reached. -		 */ -		B = 16;		/* => 24 MBaud */ -	/* Encode the baud rate */ -	buf[3] = 0x80;     /* Select divisor encoding method */ +	tmp = 12000000 * 32 / baud; +	buf[3] = 0x80;  	buf[2] = 0; -	buf[1] = (A & 0x0e);		/* A */ -	buf[1] |= ((B & 0x100) >> 8);	/* MSB of B */ -	buf[0] = B & 0xff;		/* 8 LSBs of B */ -	/* Calculate the actual/resulting baud rate */ -	if (B <= 8) -		B = 512; -	baud = 12000000 * 32 / ((1 << A) * B); +	buf[1] = (tmp >= 256); +	while (tmp >= 256) { +		tmp >>= 2; +		buf[1] <<= 1; +	} +	buf[0] = tmp;  	return baud;  } -static void pl2303_encode_baudrate(struct tty_struct *tty, +static void pl2303_encode_baud_rate(struct tty_struct *tty,  					struct usb_serial_port *port, -					enum pl2303_type type,  					u8 buf[4])  { -	int baud; +	struct usb_serial *serial = port->serial; +	struct pl2303_serial_private *spriv = usb_get_serial_data(serial); +	speed_t	baud_sup; +	speed_t baud;  	baud = tty_get_baud_rate(tty); -	dev_dbg(&port->dev, "baud requested = %d\n", baud); +	dev_dbg(&port->dev, "baud requested = %u\n", baud);  	if (!baud)  		return; + +	if (spriv->type->max_baud_rate) +		baud = min_t(speed_t, baud, spriv->type->max_baud_rate);  	/* -	 * There are two methods for setting/encoding the baud rate -	 * 1) Direct method: encodes the baud rate value directly -	 *    => supported by all chip types -	 * 2) Divisor based method: encodes a divisor to a base value (12MHz*32) -	 *    => supported by HX chips (and likely not by type_0/1 chips) +	 * Set baud rate to nearest supported value.  	 * -	 * NOTE: Although the divisor based baud rate encoding method is much -	 * more flexible, some of the standard baud rate values can not be -	 * realized exactly. But the difference is very small (max. 0.2%) and -	 * the device likely uses the same baud rate generator for both methods -	 * so that there is likley no difference. +	 * NOTE: Baud rate 500k can only be set using divisors.  	 */ -	if (type == type_0 || type == type_1) -		baud = pl2303_baudrate_encode_direct(baud, type, buf); +	baud_sup = pl2303_get_supported_baud_rate(baud); + +	if (baud == 500000) +		baud = pl2303_encode_baud_rate_divisor(buf, baud);  	else -		baud = pl2303_baudrate_encode_divisor(baud, type, buf); +		baud = pl2303_encode_baud_rate_direct(buf, baud_sup); +  	/* Save resulting baud rate */  	tty_encode_baud_rate(tty, baud, baud); -	dev_dbg(&port->dev, "baud set = %d\n", baud); +	dev_dbg(&port->dev, "baud set = %u\n", baud); +} + +static int pl2303_get_line_request(struct usb_serial_port *port, +							unsigned char buf[7]) +{ +	struct usb_device *udev = port->serial->dev; +	int ret; + +	ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), +				GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, +				0, 0, buf, 7, 100); +	if (ret != 7) { +		dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); + +		if (ret > 0) +			ret = -EIO; + +		return ret; +	} + +	dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf); + +	return 0; +} + +static int pl2303_set_line_request(struct usb_serial_port *port, +							unsigned char buf[7]) +{ +	struct usb_device *udev = port->serial->dev; +	int ret; + +	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), +				SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, +				0, 0, buf, 7, 100); +	if (ret != 7) { +		dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); + +		if (ret > 0) +			ret = -EIO; + +		return ret; +	} + +	dev_dbg(&port->dev, "%s - %7ph\n", __func__, buf); + +	return 0;  }  static void pl2303_set_termios(struct tty_struct *tty, @@ -477,51 +464,40 @@ static void pl2303_set_termios(struct tty_struct *tty,  	struct pl2303_private *priv = usb_get_serial_port_data(port);  	unsigned long flags;  	unsigned char *buf; -	int i; +	int ret;  	u8 control; -	/* -	 * The PL2303 is reported to lose bytes if you change serial settings -	 * even to the same values as before. Thus we actually need to filter -	 * in this specific case. -	 */  	if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios))  		return;  	buf = kzalloc(7, GFP_KERNEL);  	if (!buf) { -		dev_err(&port->dev, "%s - out of memory.\n", __func__);  		/* Report back no change occurred */  		if (old_termios)  			tty->termios = *old_termios;  		return;  	} -	i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), -			    GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, -			    0, 0, buf, 7, 100); -	dev_dbg(&port->dev, "0xa1:0x21:0:0  %d - %7ph\n", i, buf); +	pl2303_get_line_request(port, buf); -	if (C_CSIZE(tty)) { -		switch (C_CSIZE(tty)) { -		case CS5: -			buf[6] = 5; -			break; -		case CS6: -			buf[6] = 6; -			break; -		case CS7: -			buf[6] = 7; -			break; -		default: -		case CS8: -			buf[6] = 8; -		} -		dev_dbg(&port->dev, "data bits = %d\n", buf[6]); +	switch (C_CSIZE(tty)) { +	case CS5: +		buf[6] = 5; +		break; +	case CS6: +		buf[6] = 6; +		break; +	case CS7: +		buf[6] = 7; +		break; +	default: +	case CS8: +		buf[6] = 8;  	} +	dev_dbg(&port->dev, "data bits = %d\n", buf[6]); -	/* For reference:   buf[0]:buf[3] baud rate value */ -	pl2303_encode_baudrate(tty, port, spriv->type, buf); +	/* For reference buf[0]:buf[3] baud rate value */ +	pl2303_encode_baud_rate(tty, port, &buf[0]);  	/* For reference buf[4]=0 is 1 stop bits */  	/* For reference buf[4]=1 is 1.5 stop bits */ @@ -550,7 +526,7 @@ static void pl2303_set_termios(struct tty_struct *tty,  		/* For reference buf[5]=3 is mark parity */  		/* For reference buf[5]=4 is space parity */  		if (C_PARODD(tty)) { -			if (tty->termios.c_cflag & CMSPAR) { +			if (C_CMSPAR(tty)) {  				buf[5] = 3;  				dev_dbg(&port->dev, "parity = mark\n");  			} else { @@ -558,7 +534,7 @@ static void pl2303_set_termios(struct tty_struct *tty,  				dev_dbg(&port->dev, "parity = odd\n");  			}  		} else { -			if (tty->termios.c_cflag & CMSPAR) { +			if (C_CMSPAR(tty)) {  				buf[5] = 4;  				dev_dbg(&port->dev, "parity = space\n");  			} else { @@ -571,10 +547,23 @@ static void pl2303_set_termios(struct tty_struct *tty,  		dev_dbg(&port->dev, "parity = none\n");  	} -	i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), -			    SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, -			    0, 0, buf, 7, 100); -	dev_dbg(&port->dev, "0x21:0x20:0:0  %d\n", i); +	/* +	 * Some PL2303 are known to lose bytes if you change serial settings +	 * even to the same values as before. Thus we actually need to filter +	 * in this specific case. +	 * +	 * Note that the tty_termios_hw_change check above is not sufficient +	 * as a previously requested baud rate may differ from the one +	 * actually used (and stored in old_termios). +	 * +	 * NOTE: No additional locking needed for line_settings as it is +	 *       only used in set_termios, which is serialised against itself. +	 */ +	if (!old_termios || memcmp(buf, priv->line_settings, 7)) { +		ret = pl2303_set_line_request(port, buf); +		if (!ret) +			memcpy(priv->line_settings, buf, 7); +	}  	/* change control lines if we are switching to or from B0 */  	spin_lock_irqsave(&priv->lock, flags); @@ -591,19 +580,13 @@ static void pl2303_set_termios(struct tty_struct *tty,  		spin_unlock_irqrestore(&priv->lock, flags);  	} -	memset(buf, 0, 7); -	i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), -			    GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, -			    0, 0, buf, 7, 100); -	dev_dbg(&port->dev, "0xa1:0x21:0:0  %d - %7ph\n", i, buf); -  	if (C_CRTSCTS(tty)) { -		if (spriv->type == type_0 || spriv->type == type_1) -			pl2303_vendor_write(0x0, 0x41, serial); +		if (spriv->quirks & PL2303_QUIRK_LEGACY) +			pl2303_vendor_write(serial, 0x0, 0x41);  		else -			pl2303_vendor_write(0x0, 0x61, serial); +			pl2303_vendor_write(serial, 0x0, 0x61);  	} else { -		pl2303_vendor_write(0x0, 0x0, serial); +		pl2303_vendor_write(serial, 0x0, 0x0);  	}  	kfree(buf); @@ -616,13 +599,13 @@ static void pl2303_dtr_rts(struct usb_serial_port *port, int on)  	u8 control;  	spin_lock_irqsave(&priv->lock, flags); -	/* Change DTR and RTS */  	if (on)  		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);  	else  		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);  	control = priv->line_control;  	spin_unlock_irqrestore(&priv->lock, flags); +  	pl2303_set_control_lines(port, control);  } @@ -638,13 +621,13 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)  	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);  	int result; -	if (spriv->type == type_0 || spriv->type == type_1) { +	if (spriv->quirks & PL2303_QUIRK_LEGACY) {  		usb_clear_halt(serial->dev, port->write_urb->pipe);  		usb_clear_halt(serial->dev, port->read_urb->pipe);  	} else {  		/* reset upstream data pipes */ -		pl2303_vendor_write(8, 0, serial); -		pl2303_vendor_write(9, 0, serial); +		pl2303_vendor_write(serial, 8, 0); +		pl2303_vendor_write(serial, 9, 0);  	}  	/* Setup termios */ @@ -653,8 +636,8 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)  	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);  	if (result) { -		dev_err(&port->dev, "%s - failed submitting interrupt urb," -			" error %d\n", __func__, result); +		dev_err(&port->dev, "failed to submit interrupt urb: %d\n", +			result);  		return result;  	} @@ -724,48 +707,10 @@ static int pl2303_tiocmget(struct tty_struct *tty)  static int pl2303_carrier_raised(struct usb_serial_port *port)  {  	struct pl2303_private *priv = usb_get_serial_port_data(port); +  	if (priv->line_status & UART_DCD)  		return 1; -	return 0; -} -static int pl2303_tiocmiwait(struct tty_struct *tty, unsigned long arg) -{ -	struct usb_serial_port *port = tty->driver_data; -	struct pl2303_private *priv = usb_get_serial_port_data(port); -	unsigned long flags; -	unsigned int prevstatus; -	unsigned int status; -	unsigned int changed; - -	spin_lock_irqsave(&priv->lock, flags); -	prevstatus = priv->line_status; -	spin_unlock_irqrestore(&priv->lock, flags); - -	while (1) { -		interruptible_sleep_on(&port->port.delta_msr_wait); -		/* see if a signal did it */ -		if (signal_pending(current)) -			return -ERESTARTSYS; - -		if (port->serial->disconnected) -			return -EIO; - -		spin_lock_irqsave(&priv->lock, flags); -		status = priv->line_status; -		spin_unlock_irqrestore(&priv->lock, flags); - -		changed = prevstatus ^ status; - -		if (((arg & TIOCM_RNG) && (changed & UART_RING)) || -		    ((arg & TIOCM_DSR) && (changed & UART_DSR)) || -		    ((arg & TIOCM_CD)  && (changed & UART_DCD)) || -		    ((arg & TIOCM_CTS) && (changed & UART_CTS))) { -			return 0; -		} -		prevstatus = status; -	} -	/* NOTREACHED */  	return 0;  } @@ -775,8 +720,6 @@ static int pl2303_ioctl(struct tty_struct *tty,  	struct serial_struct ser;  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(&port->dev, "%s cmd = 0x%04x\n", __func__, cmd); -  	switch (cmd) {  	case TIOCGSERIAL:  		memset(&ser, 0, sizeof ser); @@ -790,9 +733,9 @@ static int pl2303_ioctl(struct tty_struct *tty,  		return 0;  	default: -		dev_dbg(&port->dev, "%s not supported = 0x%04x\n", __func__, cmd);  		break;  	} +  	return -ENOIOCTLCMD;  } @@ -807,6 +750,7 @@ static void pl2303_break_ctl(struct tty_struct *tty, int break_state)  		state = BREAK_OFF;  	else  		state = BREAK_ON; +  	dev_dbg(&port->dev, "%s - turning break %s\n", __func__,  			state == BREAK_OFF ? "off" : "on"); @@ -821,48 +765,51 @@ static void pl2303_update_line_status(struct usb_serial_port *port,  				      unsigned char *data,  				      unsigned int actual_length)  { - +	struct usb_serial *serial = port->serial; +	struct pl2303_serial_private *spriv = usb_get_serial_data(serial);  	struct pl2303_private *priv = usb_get_serial_port_data(port);  	struct tty_struct *tty;  	unsigned long flags; -	u8 status_idx = UART_STATE; -	u8 length = UART_STATE + 1; -	u8 prev_line_status; -	u16 idv, idp; - -	idv = le16_to_cpu(port->serial->dev->descriptor.idVendor); -	idp = le16_to_cpu(port->serial->dev->descriptor.idProduct); +	unsigned int status_idx = UART_STATE_INDEX; +	u8 status; +	u8 delta; +	if (spriv->quirks & PL2303_QUIRK_UART_STATE_IDX0) +		status_idx = 0; -	if (idv == SIEMENS_VENDOR_ID) { -		if (idp == SIEMENS_PRODUCT_ID_X65 || -		    idp == SIEMENS_PRODUCT_ID_SX1 || -		    idp == SIEMENS_PRODUCT_ID_X75) { - -			length = 1; -			status_idx = 0; -		} -	} - -	if (actual_length < length) +	if (actual_length < status_idx + 1)  		return; +	status = data[status_idx]; +  	/* Save off the uart status for others to look at */  	spin_lock_irqsave(&priv->lock, flags); -	prev_line_status = priv->line_status; -	priv->line_status = data[status_idx]; +	delta = priv->line_status ^ status; +	priv->line_status = status;  	spin_unlock_irqrestore(&priv->lock, flags); -	if (priv->line_status & UART_BREAK_ERROR) + +	if (status & UART_BREAK_ERROR)  		usb_serial_handle_break(port); -	wake_up_interruptible(&port->port.delta_msr_wait); -	tty = tty_port_tty_get(&port->port); -	if (!tty) -		return; -	if ((priv->line_status ^ prev_line_status) & UART_DCD) -		usb_serial_handle_dcd_change(port, tty, -				priv->line_status & UART_DCD); -	tty_kref_put(tty); +	if (delta & UART_STATE_MSR_MASK) { +		if (delta & UART_CTS) +			port->icount.cts++; +		if (delta & UART_DSR) +			port->icount.dsr++; +		if (delta & UART_RING) +			port->icount.rng++; +		if (delta & UART_DCD) { +			port->icount.dcd++; +			tty = tty_port_tty_get(&port->port); +			if (tty) { +				usb_serial_handle_dcd_change(port, tty, +							status & UART_DCD); +				tty_kref_put(tty); +			} +		} + +		wake_up_interruptible(&port->port.delta_msr_wait); +	}  }  static void pl2303_read_int_callback(struct urb *urb) @@ -897,10 +844,11 @@ static void pl2303_read_int_callback(struct urb *urb)  exit:  	retval = usb_submit_urb(urb, GFP_ATOMIC); -	if (retval) +	if (retval) {  		dev_err(&port->dev,  			"%s - usb_submit_urb failed with result %d\n",  			__func__, retval); +	}  }  static void pl2303_process_read_urb(struct urb *urb) @@ -918,13 +866,14 @@ static void pl2303_process_read_urb(struct urb *urb)  	line_status = priv->line_status;  	priv->line_status &= ~UART_STATE_TRANSIENT_MASK;  	spin_unlock_irqrestore(&priv->lock, flags); -	wake_up_interruptible(&port->port.delta_msr_wait);  	if (!urb->actual_length)  		return; -	/* break takes precedence over parity, */ -	/* which takes precedence over framing errors */ +	/* +	 * Break takes precedence over parity, which takes precedence over +	 * framing errors. +	 */  	if (line_status & UART_BREAK_ERROR)  		tty_flag = TTY_BREAK;  	else if (line_status & UART_PARITY_ERROR) @@ -952,7 +901,6 @@ static void pl2303_process_read_urb(struct urb *urb)  	tty_flip_buffer_push(&port->port);  } -/* All of the device info needed for the PL2303 SIO serial converter */  static struct usb_serial_driver pl2303_device = {  	.driver = {  		.owner =	THIS_MODULE, @@ -964,16 +912,17 @@ static struct usb_serial_driver pl2303_device = {  	.bulk_out_size =	256,  	.open =			pl2303_open,  	.close =		pl2303_close, -	.dtr_rts = 		pl2303_dtr_rts, +	.dtr_rts =		pl2303_dtr_rts,  	.carrier_raised =	pl2303_carrier_raised,  	.ioctl =		pl2303_ioctl,  	.break_ctl =		pl2303_break_ctl,  	.set_termios =		pl2303_set_termios,  	.tiocmget =		pl2303_tiocmget,  	.tiocmset =		pl2303_tiocmset, -	.tiocmiwait =		pl2303_tiocmiwait, +	.tiocmiwait =		usb_serial_generic_tiocmiwait,  	.process_read_urb =	pl2303_process_read_urb,  	.read_int_callback =	pl2303_read_int_callback, +	.probe =		pl2303_probe,  	.attach =		pl2303_startup,  	.release =		pl2303_release,  	.port_probe =		pl2303_port_probe, @@ -986,5 +935,5 @@ static struct usb_serial_driver * const serial_drivers[] = {  module_usb_serial_driver(serial_drivers, id_table); -MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_DESCRIPTION("Prolific PL2303 USB to serial adaptor driver");  MODULE_LICENSE("GPL");  | 
