diff -ur linux-isdn/Documentation/Configure.help linux-pl2303/Documentation/Configure.help
--- linux-isdn/Documentation/Configure.help	Wed Oct  3 09:56:46 2001
+++ linux-pl2303/Documentation/Configure.help	Wed Oct 10 23:56:06 2001
@@ -11352,7 +11352,9 @@
 USB Prolific 2303 Single Port Serial Driver
 CONFIG_USB_SERIAL_PL2303
   Say Y here if you want to use the PL2303 USB Serial single port
-  adapter from Prolific.
+  adapter from Prolific. The driver should also work for certain other
+  adapters that use the same chip-set (ATEN UC-232A, IOGEAR, PLANEX,
+  IO/Data, ...)
 
   This code is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
diff -ur linux-isdn/Documentation/usb/usb-serial.txt linux-pl2303/Documentation/usb/usb-serial.txt
--- linux-isdn/Documentation/usb/usb-serial.txt	Tue Aug  7 17:11:53 2001
+++ linux-pl2303/Documentation/usb/usb-serial.txt	Wed Oct 10 23:56:06 2001
@@ -256,6 +256,72 @@
   maintainer (linux-usb@sii.li).
 
 
+Prolific 2303 driver
+
+  The Prolific 2303 (http://www.prolific.com.tw) is a single chip
+  USB-RS232 converter which is used inside certain USB-serial-
+  adapters. The current driver recognises the following products:
+
+  - ATEN UC-232A (USB to RS232/DB9 adapter)
+  - All products which use Prolific's generic USB vendor and product id
+  - IOGEAR, PLANEX, IO/Data, ... (untested)
+
+  The chip-set honors certain CDC ACM control messages where these
+  can be reasonably applied in a USB-serial-adapter context. Thus,
+  some code is adapted from the ACM driver.
+
+Current status:
+
+    Most features which are
+    - supported by the chip-set according to data sheet Rev. 0.3
+      (see Prolific's web site above)           AND
+    - covered by USB CDC ACM primitives         AND
+    - controllable by means of Linux termios and ioctl interface
+    are implemented.
+    However, not all are tested and might be buggy. And CDC ACM
+    alone is not sufficient for implementing all features availabele 
+    at the Linux tty driver interface.
+
+    Maybe certain chip revisions do not support setting all
+    parameters listed below. The driver does not check whether
+    the parameters have really been accepted by the chip. Thus,
+    if certain parameters were unsupported by certain chip revisions,
+    after setting such unsupported parameters the chipset might be in an
+    undefined (maybe non-functional) state without notice.
+
+    Baud rates   75, 150, 300, 600, 1200, 2400, 4800, 9600, 19200,
+		 38400, 57600, 115200, 230400, 460800, 921600
+		 (tested: 300 - 57600, the largest rates are probably
+		 useless with RS232 electrical interface)
+    Data bits    5-8 
+    Stop bits    1,2
+    Parity       N,E,O,M,S
+    Handshake    None, Software (XON/XOFF, untested, maybe not
+		 complete), Hardware (RTSCTS for output, tested; not
+		 yet implemented and tested for input);
+    Break        Set and clear, detection (untested)
+    Line contrl  Input/Output query and control
+    Error events Parity, Framing, Overrun and Break events are
+		 detected and reported to upper tty layers (only
+		 partially tested). However, reported errors might not
+		 necessarily be synchronized with the read characters
+		 the events belong to.
+
+TO DO List:
+  -- Testing
+  -- TESTING
+  -- even more testing
+  -- check (and implement if missing) input hardware flow control, and
+     xon/off
+  -- output buffering for write, maybe with configurable buffer size.
+  -- clean up code
+  -- Ask manufacturer for documentation, figure out what (and how)
+     other fuctions (e.g. flush) can be implemented
+  -- The chip-set supports certain features (e.g. other baud rates,
+     16 data bits, half-duplex mode) which are not supported by
+     standard Linux API. Maybe some device specific ioctl make sense.
+
+
 Generic Serial driver
 
   If your device is not one of the above listed devices, compatible with
diff -ur linux-isdn/drivers/usb/serial/pl2303.c linux-pl2303/drivers/usb/serial/pl2303.c
--- linux-isdn/drivers/usb/serial/pl2303.c	Wed Oct  3 09:57:59 2001
+++ linux-pl2303/drivers/usb/serial/pl2303.c	Thu Oct 11 00:21:39 2001
@@ -12,6 +12,43 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
+ * 2001-10-11 Henner Eisen
+ * 
+ * Several additional features, partially inspired from the 
+ * NetBSD driver and ../acm.c. Main changes:
+ *
+ * - Fixed index parameter of certain control messages to contain the
+ *   appropriate interface index (inspired from NetBSD driver):
+ *
+ *   Not all chipsets suffer from the `looney way its endpoints are set up'
+ *   mentioned in usbserial.c. But the original driver could fail with
+ *   chipsets which behave politely because the interface index was hardcoded.
+ *
+ * - Added code to set, query (ioctl) and detect changes (interrupt_in) of
+ *   RS-232 line states (inspired from NetBSD and former Linux driver,
+ *   several pieces of code adopted from ../acm.c).
+ *
+ * - dto. for implementing break control.
+ *
+ * - Reorganised (de)initialisation in startup, open, close, shutdown.
+ *
+ * - Changed timeout value of misc. usb_control_msg() statements from 100 to HZ
+ *   
+ * - Break events, parity, framing and overrun errors are now passed to upper
+ *   layers.
+ *
+ * - implemented throttle(), unthrottle() and x_char processing.
+ *
+ * - resurrected PL303_LOCK()/UNLOCK(). Might be necessary for the 
+ *   throttle code. Maybe locking is too strict right now. But better
+ *   locking too much than too less.
+ *
+ * - handle setting of B0 speed
+ *
+ * TODO:
+ * - Review locking and impact of non-atomic memory acces in interrupts.
+ * - Review existing and implement missing flow control features.
+ *
  * 2001_Aug_30 gkh
  *	fixed oops in write_bulk_callback.
  *
@@ -56,16 +93,73 @@
 #define DRIVER_VERSION "v0.7"
 #define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
 
+#define	PL2303_LOCK(port,flags)					\
+		do {						\
+		spin_lock_irqsave(&((struct pl2303_private *)(port->private))->lock, flags);	\
+		} while (0)
+
+#define	PL2303_UNLOCK(port,flags)				\
+		do {						\
+		spin_unlock_irqrestore(&((struct pl2303_private *)(port->private))->lock, flags);	\
+		} while (0)
 
 
 static __devinitdata struct usb_device_id id_table [] = {
 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
 	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
+	  /* The NetBSD driver also supports the following vendor/product ids.
+	   * Will probably work, but is totally untested, with Linux */
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
+	{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
 	{ }					/* Terminating entry */
 };
 
 MODULE_DEVICE_TABLE (usb, id_table);
 
+/*
+ * ACM defines from acm.c.
+ * Maybe these definitions should be moved to a shared public header file
+ */
+#define ACM_REQ_COMMAND		0x00
+#define ACM_REQ_RESPONSE	0x01
+#define ACM_REQ_SET_FEATURE	0x02
+#define ACM_REQ_GET_FEATURE	0x03
+#define ACM_REQ_CLEAR_FEATURE	0x04
+
+#define ACM_REQ_SET_LINE	0x20
+#define ACM_REQ_GET_LINE	0x21
+#define ACM_REQ_SET_CONTROL	0x22
+#define ACM_REQ_SEND_BREAK	0x23
+
+#define ACM_IRQ_NETWORK		0x00
+#define ACM_IRQ_LINE_STATE	0x20
+#define ACM_IRQ_RESPONSE	0x01
+
+struct pl2303_private {
+	int ctl_ifc; /* interface referenced in index parameter of certain
+			ctrl messages, same as bulk transfer interface.
+			For `nice' ships, this is also the same as the
+			interrupt interface, but certain chips aren't nice. */
+	int	dte; /* dte states, controlled by us (V.24 output pins) */
+#define ACM_CTRL_DTR		0x01
+#define ACM_CTRL_RTS		0x02
+	volatile int dce; /* dce states, controlled by modem (V.24 input pins)
+			   * and receive error conditions */
+	volatile int rxe; /* pending receive error conditions, to be
+			   * reported during next tty read */
+#define ACM_CTRL_DCD		0x01
+#define ACM_CTRL_DSR		0x02
+#define ACM_CTRL_BRK		0x04
+#define ACM_CTRL_RI		0x08
+			/* error flags, for data received from modem */
+#define ACM_CTRL_FRAMING	0x10
+#define ACM_CTRL_PARITY		0x20
+#define ACM_CTRL_OVERRUN	0x40
+	volatile char reading, throttled, writing, x_char;
+	spinlock_t	lock;
+	char flags;
+#define PL2303_CLOSING 0
+};
 
 /* function prototypes for a PL2303 serial converter */
 static int pl2303_open (struct usb_serial_port *port, struct file *filp);
@@ -80,6 +174,11 @@
 static int pl2303_write (struct usb_serial_port *port, int from_user,
 			 const unsigned char *buf, int count);
 static void pl2303_break_ctl(struct usb_serial_port *port,int break_state);
+static int pl2303_startup (struct usb_serial *serial);
+static void pl2303_shutdown(struct usb_serial *serial);
+static void pl2303_throttle (struct usb_serial_port *port);
+static void pl2303_unthrottle (struct usb_serial_port *port);
+static void pl2303_submit_xchar(struct usb_serial_port *port);
 
 
 /* All of the device info needed for the PL2303 SIO serial converter */
@@ -102,13 +201,19 @@
 	read_bulk_callback:	pl2303_read_bulk_callback,
 	read_int_callback:	pl2303_read_int_callback,
 	write_bulk_callback:	pl2303_write_bulk_callback,
+	startup:		pl2303_startup,
+	shutdown:		pl2303_shutdown,
+	throttle:		pl2303_throttle,
+	unthrottle:		pl2303_unthrottle,
 };
 
 
 
 static int pl2303_write (struct usb_serial_port *port, int from_user,  const unsigned char *buf, int count)
 {
-	int result;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+	unsigned long flags;
+	int result=0;
 
 	dbg (__FUNCTION__ " - port %d, %d bytes", port->number, count);
 
@@ -117,29 +222,41 @@
 		return 0;
 	}
 
-	if (port->write_urb->status == -EINPROGRESS) {
+	if (info->writing) {
 		dbg (__FUNCTION__ " - already writing");
 		return 0;
 	}
 
 	count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
 	if (from_user) {
-		if (copy_from_user (port->write_urb->transfer_buffer, buf, count))
-			return -EFAULT;
+		down(&port->sem);
+		result = copy_from_user(port->write_urb->transfer_buffer, buf, count);
+		up(&port->sem);
+		if(result) return -EFAULT;
+		if (info->writing){
+			dbg (__FUNCTION__ " - closing or already writing");
+			return (0);
+		}
 	} else {
 		memcpy (port->write_urb->transfer_buffer, buf, count);
 	}
 	
 	usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
 
+	PL2303_LOCK(port,flags);
 	port->write_urb->transfer_buffer_length = count;
 	port->write_urb->dev = port->serial->dev;
+	if(!test_bit(PL2303_CLOSING,&info->flags)){
 	result = usb_submit_urb (port->write_urb);
-	if (result)
+	if (result){
 		err(__FUNCTION__ " - failed submitting write urb, error %d", result);
-	else
+	} else {
 		result = count;
 
+		info->writing = 1;
+	}
+	}
+	PL2303_UNLOCK(port,flags);
 	return result;
 }
 
@@ -152,23 +269,23 @@
 	unsigned int cflag = port->tty->termios->c_cflag;
 	unsigned char buf[7] = { 0, 0, 0, 0, 0, 0, 0};
 	int baud;
-	int i;
+	int i, ix=( (struct pl2303_private*) port->private ) ->	ctl_ifc;
 
 
 	dbg ("pl2303_set_termios port %d", port->number);
 
 
 	i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
-			     0x21, 0xa1, 0, 0, buf, 7, 100);
+			     0x21, 0xa1, 0, 0, buf, 7, HZ);
 
 	dbg ("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
 	     buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
 
 
 	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-			     1, 0x40, 0, 1, NULL, 0, 100);
+			     1, 0x40, 0, ix, NULL, 0, HZ);
 
-	dbg ("0x40:1:0:1  %d", i);
+	dbg ("0x40:1:0:%d  %d", ix, i);
 
 
 
@@ -192,11 +309,15 @@
 				break;
 			default:
 				err ("CSIZE was set but not CS5-CS8");
+				/* Remark: Chip also supports 16 */
 		}
 	}
 
 	baud = 0;
 	switch (cflag & CBAUD) {
+		case B0:
+			/* special handling later */
+			break;
 		case B75:	baud = 75;	break;
 		case B150:	baud = 150;	break;
 		case B300:	baud = 300;	break;
@@ -212,6 +333,7 @@
 		case B115200:	baud = 115200;	break;
 		case B230400:	baud = 230400;	break;
 		case B460800:	baud = 460800;	break;
+		case B921600:	baud = 921600;  break;
 		default:
 			err ("pl2303 driver does not support the baudrate requested (fix it)");
 			break;
@@ -245,27 +367,30 @@
 		} else {
 			buf[5] = 2;
 		}
+#ifdef CMSPAR 	/* some arch's header files don't define that */
+		if (cflag & CMSPAR) buf[5] += 2;
+#endif
 	}
 
 	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-			     0x20, 0x21, 0, 0, buf, 7, 100);
+			     0x20, 0x21, 0, 0, buf, 7, HZ);
 
 	dbg ("0x21:0x20:0:0  %d", i);
 
 	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-			     0x22, 0x21, 1, 0, NULL, 0, 100);
+			     0x22, 0x21, 1, 0, NULL, 0, HZ);
 
 	dbg ("0x21:0x22:1:0  %d", i);
 
 	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-			     0x22, 0x21, 3, 0, NULL, 0, 100);
+			     0x22, 0x21, 3, 0, NULL, 0, HZ);
 
 	dbg ("0x21:0x22:3:0  %d", i);
 
 	buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;
 
 	i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
-			     0x21, 0xa1, 0, 0, buf, 7, 100);
+			     0x21, 0xa1, 0, 0, buf, 7, HZ);
 
 	dbg ("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
 	     buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
@@ -273,12 +398,21 @@
 	if (cflag & CRTSCTS) {
 
 		i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-				     0x01, 0x40, 0x0, 0x41, NULL, 0, 100);
+				     0x01, 0x40, 0x0, 0x41, NULL, 0, HZ);
 
 		dbg ("0x40:0x1:0x0:0x41  %d", i);
 
 	}
 
+#define USB_RT_ACM	(USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+	if( (cflag & CBAUD) == B0 ){
+		struct pl2303_private *info 
+			= (struct pl2303_private *)port->private;
+		info->dte &= ~(ACM_CTRL_RTS|ACM_CTRL_DTR);
+		i = usb_control_msg(serial->dev,usb_sndctrlpipe(serial->dev,0),
+				    ACM_REQ_SET_CONTROL, USB_RT_ACM,
+				    info->dte, ix, NULL, 0, HZ*5);
+	}
 
 	return;
 }       
@@ -302,16 +436,19 @@
 	MOD_INC_USE_COUNT;
 
 	if (!port->active) {
+		struct pl2303_private *info = (struct pl2303_private *)port->private;
+		int ix=info -> ctl_ifc;
 		port->active = 1;
 
+#if 0
 #define FISH(a,b,c,d)									\
 		result=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0),	\
-				       b, a, c, d, buf, 1, 100);			\
+				       b, a, c, d, buf, 1, HZ);			\
 		dbg("0x%x:0x%x:0x%x:0x%x  %d - %x",a,b,c,d,result,buf[0]);
 
 #define SOUP(a,b,c,d)									\
 		result=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0),	\
-				       b, a, c , d, NULL, 0, 100);			\
+				       b, a, c , d, NULL, 0, HZ);			\
 		dbg("0x%x:0x%x:0x%x:0x%x  %d",a,b,c,d,result);
 
 		FISH (0xc0, 1, 0x8484, 0);
@@ -325,7 +462,11 @@
 		SOUP (0x40, 1, 0, 1);
 		SOUP (0x40, 1, 1, 0xc0);
 		SOUP (0x40, 1, 2, 4);
-
+#else
+		result = usb_control_msg (serial->dev,
+					  usb_sndctrlpipe (serial->dev, 0),
+					  0x01, 0x40, 0x0, ix, NULL, 0, HZ);
+#endif
 		/* Setup termios */
 		*(port->tty->termios) = tty_std_termios;
 		port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
@@ -333,6 +474,14 @@
 		pl2303_set_termios (port, &tmp_termios);
 
 		//FIXME: need to assert RTS and DTR if CRTSCTS off
+		usb_control_msg(serial->dev,usb_sndctrlpipe(serial->dev,0),
+				    ACM_REQ_SET_CONTROL, USB_RT_ACM,
+				    info->dte = ACM_CTRL_DTR|ACM_CTRL_RTS,
+				    ix, NULL, 0, HZ*5);
+		info->writing = 0;
+		info->x_char  = 0;
+		clear_bit(PL2303_CLOSING,&info->flags);
+		smp_mb__after_clear_bit();
 
 		port->read_urb->dev = serial->dev;
 		result = usb_submit_urb (port->read_urb);
@@ -341,6 +490,8 @@
 			up (&port->sem);
 			pl2303_close (port, NULL);
 			return -EPROTO;
+		} else {
+			info->reading = 1;
 		}
 
 		port->interrupt_in_urb->dev = serial->dev;
@@ -370,15 +521,28 @@
 
 	--port->open_count;
 	if (port->open_count <= 0) {
+		struct pl2303_private *info = (struct pl2303_private *)port->private;
+		/* inhibit urb auto-re-submit */
+		set_bit(PL2303_CLOSING,&info->flags);
+
 		c_cflag = port->tty->termios->c_cflag;
 		if (c_cflag & HUPCL) {
-			//FIXME: Do drop DTR
-			//FIXME: Do drop RTS
+			int i;
+			struct usb_serial *serial=get_usb_serial (port, __FUNCTION__);
+
+			i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0),
+					    ACM_REQ_SET_CONTROL, USB_RT_ACM,
+					    info->dte = 0,
+					    info->ctl_ifc, NULL, 0, HZ*5);
+			dbg("close line %d, ret=%d",port->number,i);
 		}
 
-		/* shutdown our urbs */
+		/* shutdown any bulk reads that might be going on */
 		usb_unlink_urb (port->write_urb);
+		info->x_char = 0;
+		info->writing=0;
 		usb_unlink_urb (port->read_urb);
+		info->reading=0;
 		usb_unlink_urb (port->interrupt_in_urb);
 
 		port->active = 0;
@@ -386,6 +550,7 @@
 	}
 
 	up (&port->sem);
+	MOD_DEC_USE_COUNT;
 }
 
 
@@ -393,45 +558,83 @@
 pl2303_ioctl (struct usb_serial_port *port, struct file *file,
 	      unsigned int cmd, unsigned long arg)
 {
-// struct usb_serial *serial = port->serial;
+	struct usb_serial *serial = port->serial;
 // __u16 urb_value=0; /* Will hold the new flags */
 // char buf[1];
 // int  ret, mask;
+	unsigned int value, mask, newctrl;
+	int i, ret=0;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
 
-
+	down (&port->sem);
 	dbg ("pl2303_sio ioctl 0x%04x", cmd);
 
 	/* Based on code from acm.c and others */
 	switch (cmd) {
-		
-		case TIOCMGET:
-			dbg ("TIOCMGET");
-
-
-			return put_user (0, (unsigned long *) arg);
+	case TIOCMGET:
+		dbg ("TIOCMGET");
+		value = ((info->dte & ACM_CTRL_RTS) ? TIOCM_RTS : 0) |
+			((info->dte & ACM_CTRL_DTR) ? TIOCM_DTR : 0) |
+			/* Bit 0x80 is not used (but reserved) by ACM, but it
+			   seems that the adapter uses it for CTS indication.
+			   (querying for CTS state is not covered by ACM) */
+			((info->dce & 0x80)         ? TIOCM_CTS : 0) |
+			((info->dce & ACM_CTRL_DSR) ? TIOCM_DSR : 0) |
+			((info->dce & ACM_CTRL_DCD) ? TIOCM_CAR : 0) |
+			((info->dce & ACM_CTRL_RI)  ? TIOCM_RNG : 0); 
+
+		if (put_user(value, (unsigned int *) arg))
+			ret= -EFAULT;
+		break;
+	case TIOCMSET:
+	case TIOCMBIS:
+	case TIOCMBIC:
+		dbg ("TIOCMSET/BIS/BIC");
+		if (get_user(value, (unsigned int *) arg)){
+			ret = -EFAULT;
 			break;
-		case TIOCMBIS:
-		case TIOCMBIC:
-		case TIOCMSET:
-			return 0;
+		}
+		mask  = ((value & TIOCM_RTS) ? ACM_CTRL_RTS : 0);
+		mask |= ((value & TIOCM_DTR) ? ACM_CTRL_DTR : 0);
 
-		default:
-			/* This is not an error - turns out the higher layers will do 
-			 *  some ioctls itself (see comment above)
-			 */
-			dbg ("pl2303_sio ioctl arg not supported - it was 0x%04x", cmd);
-			return(-ENOIOCTLCMD);
-			break;
-	}
-	dbg ("pl2303_ioctl returning 0");
+		newctrl=info->dte;
+		switch (cmd) {
+		case TIOCMSET: newctrl  =  mask; break;
+		case TIOCMBIS: newctrl |=  mask; break;
+		case TIOCMBIC: newctrl &= ~mask; break;
+		}
 
-	return 0;
+		if (info->dte == newctrl) break;
+		info->dte = newctrl;
+		i = usb_control_msg(serial->dev,usb_sndctrlpipe(serial->dev,0),
+				    ACM_REQ_SET_CONTROL, USB_RT_ACM, newctrl,
+				    info->ctl_ifc, NULL, 0, HZ*5);
+		break;
+	default:
+		/* This is not an error - turns out the higher layers will do 
+		 *  some ioctls itself (see comment above)
+		 */
+		dbg ("pl2303_sio ioctl arg not supported - it was 0x%04x", cmd);
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+	dbg ("pl2303_ioctl returning %d",ret);
+	up(&port->sem);
+	return ret;
 }				/* pl2303_ioctl */
 
 
 static void pl2303_break_ctl(struct usb_serial_port *port,int break_state)
 {
-//FIXME
+	struct usb_serial *serial = port->serial;
+	int i, ix=( (struct pl2303_private*) port->private ) -> ctl_ifc;
+
+	dbg(__FUNCTION__ " %x",break_state);
+
+	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
+			     ACM_REQ_SEND_BREAK, USB_RT_ACM,
+			     break_state ? 0xffff : 0, ix,
+			     NULL, 0, HZ);
 }
 
 
@@ -440,8 +643,11 @@
 {
 	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
 	struct usb_serial *serial = get_usb_serial (port, "pl2303_read_int_callback");
-	//unsigned char *data = urb->transfer_buffer;
-	//int i;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+	devrequest *dr = urb->transfer_buffer;
+	unsigned char *data = (unsigned char *)(dr + 1);
+	int newctrl;
+	unsigned long flags;
 
 //ints auto restart...
 
@@ -450,16 +656,55 @@
 	}
 
 	if (urb->status) {
+		dbg("nonzero ctrl irq status received: %d", urb->status);
 		urb->status = 0;
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
+	PL2303_LOCK(port,flags);
+
+	switch (dr->request) {
+
+		case ACM_IRQ_NETWORK:
+
+			dbg(__FUNCTION__ " %s network", data[0] ? "connected to" : "disconnected from");
+			break;
+
+		case ACM_IRQ_RESPONSE:
+
+			dbg(__FUNCTION__ " response available");
+			break;
+
+		case ACM_IRQ_LINE_STATE:
+
+			dbg(__FUNCTION__ " line state 0x%x 0x%x", data[0], data[1]);
+
+			newctrl = le16_to_cpup((__u16 *) data);
+
 #if 0
-//FIXME need to update state of terminal lines variable
+			if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
+				dbg("calling hangup");
+				tty_hangup(acm->tty);
+			}
 #endif
+			info->dce  =  newctrl;
+			info->rxe |= (newctrl
+				      & (ACM_CTRL_BRK|ACM_CTRL_FRAMING|
+					 ACM_CTRL_PARITY|ACM_CTRL_OVERRUN)  );
+			dbg(__FUNCTION__ "input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
+				info->dce & ACM_CTRL_DCD ? '+' : '-',	info->dce & ACM_CTRL_DSR ? '+' : '-',
+				info->dce & ACM_CTRL_BRK ? '+' : '-',	info->dce & ACM_CTRL_RI  ? '+' : '-',
+				info->dce & ACM_CTRL_FRAMING ? '+' : '-',	info->dce & ACM_CTRL_PARITY ? '+' : '-',
+				info->dce & ACM_CTRL_OVERRUN ? '+' : '-');
 
-	return;
+			break;
+
+		default:
+			dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d",
+				dr->request, dr->index, dr->length, data[0], data[1]);
+			break;
+	}
+	PL2303_UNLOCK(port,flags);
 }
 
 
@@ -470,7 +715,9 @@
 	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
 	int i;
-	int result;
+	int result, flag=0, len;
+	unsigned long flags;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
 
 	if (port_paranoia_check (port, __FUNCTION__))
 		return;
@@ -482,36 +729,63 @@
 		return;
 	}
 
+	PL2303_LOCK(port,flags);
+	info->reading = 0;
+
 	/* PL2303 mysteriously fails with -EPROTO reschedule the read */
 	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
 		urb->status = 0;
-		urb->dev = serial->dev;
-		result = usb_submit_urb(urb);
-		if (result)
-			err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
-		return;
-	}
-
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	} else {
 
+		usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+		/*
+		 * As errors are reported via a different (read_int_callback)
+		 * channel, we don't know to which particular received
+		 * character a particular error flag applies. Therefore, we
+		 * mark every received character with the error flag.
+		 */
+		if     ( info->rxe & ACM_CTRL_BRK     ) { flag=TTY_BREAK; }
+		else if( info->rxe & ACM_CTRL_FRAMING ) { flag=TTY_FRAME; }
+		else if( info->rxe & ACM_CTRL_PARITY  ) { flag=TTY_PARITY; }
+		else if( info->rxe & ACM_CTRL_OVERRUN ) { flag=TTY_OVERRUN; }
+		info -> rxe = 0;
+
+		len = urb->actual_length;
+		if( flag && (len==0) ){
+			/* in case we received no data but rx errors are
+			 * pending, deliver error flags for a 0 char */
+			len=1;
+			data[0]=0;
+		}
+	
+		PL2303_UNLOCK(port,flags);
 	tty = port->tty;
-	if (urb->actual_length) {
-		for (i = 0; i < urb->actual_length; ++i) {
+	if (len) {
+		for (i = 0; i < len; ++i) {
 			if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
 				tty_flip_buffer_push(tty);
 			}
-			tty_insert_flip_char (tty, data[i], 0);
+			tty_insert_flip_char (tty, data[i], flag);
 		}
 		tty_flip_buffer_push (tty);
 	}
 
+		PL2303_LOCK(port,flags);
+	}
 	/* Schedule the next read*/
-	urb->dev = serial->dev;
-	result = usb_submit_urb(urb);
-	if (result)
-		err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
-
-	return;
+	if( !info->throttled ){
+		urb->dev = serial->dev;
+		if(!test_bit(PL2303_CLOSING,&info->flags)){
+		result = usb_submit_urb(urb);
+		if (result){
+			err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+		} else {
+			info->reading = 1;
+		}
+		}
+	}
+	PL2303_UNLOCK(port,flags);
 }
 
 
@@ -520,32 +794,192 @@
 {
 	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
 	int result;
+	unsigned long flags;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
 
 	if (port_paranoia_check (port, __FUNCTION__))
 		return;
 	
 	dbg(__FUNCTION__ " - port %d", port->number);
 	
+	PL2303_LOCK(port,flags);
+	info->writing = 0;
 	if (urb->status) {
 		/* error in the urb, so we have to resubmit it */
 		if (serial_paranoia_check (port->serial, __FUNCTION__)) {
+			PL2303_UNLOCK(port,flags);
 			return;
 		}
 		dbg (__FUNCTION__ " - Overflow in write");
 		dbg (__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+#if 0 /* not useful without port-private write buffer */
 		port->write_urb->transfer_buffer_length = 1;
 		port->write_urb->dev = port->serial->dev;
+		if(!test_bit(PL2303_CLOSING,&info->flags)){
 		result = usb_submit_urb (port->write_urb);
-		if (result)
+		if (result){
 			err(__FUNCTION__ " - failed resubmitting write urb, error %d", result);
 
+		} else {
+			info->writing = 1;
+		}
+		}
+#endif
+		PL2303_UNLOCK(port,flags);
 		return;
 	}
 
+	if(info->x_char){
+		pl2303_submit_xchar(port);
+		if(info->writing){
+			/* submitting the x_char in progress, no wake up yet */
+			return;
+		}
+	}
+
 	queue_task(&port->tqueue, &tq_immediate);
 	mark_bh(IMMEDIATE_BH);
 
 	return;
+}
+
+
+static int pl2303_startup (struct usb_serial *serial)
+{
+	struct pl2303_private * info;
+
+	info = (struct pl2303_private *) kmalloc (sizeof(*info), GFP_KERNEL);
+	if (info == NULL) {
+		err(__FUNCTION__ " - out of memory");
+		return -ENOMEM;
+	}
+	memset(info,0,sizeof(*info));
+	spin_lock_init(&info->lock);
+
+	/*
+	 * Certain control messages need to refer the interface index of
+	 * the bulk transfer interface. As some older chip revisions use 
+	 * two usb interfaces for a single serial port, but some products
+	 * (ATEN UC-232A at least) use identical vendor and product ids
+	 * with all chip revisions, we need to detect this by probing.
+	 */
+	serial->port->private = info;
+	if( (serial->num_bulk_in == 1) && (serial->num_bulk_out == 1) &&
+	    (serial->num_interrupt_in == 1) &&
+	    (serial->num_ports == 1) &&
+	    (serial->dev->actconfig->bNumInterfaces == 2) ){
+		info->ctl_ifc = 1;
+	} else {
+		info->ctl_ifc = 0;
+	}
+	dbg (__FUNCTION__ " tty ctrl msgs will refer interface %d",info->ctl_ifc);
+
+	return(0);
+}
+
+
+static void pl2303_shutdown (struct usb_serial *serial)
+{
+	kfree(serial->port->private) /* info */;
+}
+
+
+/*
+ * Schedule writing of a high priority x_char character.
+ * Must be called with the port lock hold;
+ */
+static void pl2303_submit_xchar(struct usb_serial_port *port)
+{
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+	struct usb_serial *serial = port->serial;
+	int result;
+
+	if(!info->writing){
+		*(char*) port->write_urb->transfer_buffer = info->x_char;
+		port->write_urb->dev = serial->dev;
+		if(!test_bit(PL2303_CLOSING,&info->flags)){
+		result = usb_submit_urb(port->write_urb);
+		if (result){
+			err(__FUNCTION__ " - failed submitting write urb with x_char, error %d", result);
+		} else {
+			info->writing = 1;
+			info->x_char = 0;
+		}
+		}
+	} else {
+		/* Another write is probably already active. Do nothing, x_char
+		   will be picked up later by write callback. */
+	}
+}
+
+
+static void pl2303_throttle(struct usb_serial_port *port)
+{
+	struct tty_struct *tty=port->tty;
+	unsigned long flags;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+
+	dbg(__FUNCTION__ ": %d....", tty->ldisc.chars_in_buffer(tty));
+
+	PL2303_LOCK(port,flags);
+	info->throttled = 1;
+	if (I_IXOFF(tty)){
+		info->x_char = STOP_CHAR(tty);
+		pl2303_submit_xchar(port);
+	}
+	PL2303_UNLOCK(port,flags);
+
+	/*
+	 * We don't remove a pending read request here in a hurry.
+	 * Read chunks are small enough, at least for n_tty
+	 * line discipline, such that it should be possible to
+	 * complete the current read.
+	 */
+
+	/*
+	 * If rts/cts flow control is in effect, we should also 
+	 * switch RTS off unless the receiver does it automatically
+	 * when it's input buffer gets full.  I don't know yet whether
+	 * the port does this automatically. If not, we need to do it here.
+	 *
+	 * But I guess this function can be called from interrupt context,
+	 * we may not use the blocking usb_control_message() for switching
+	 * off RTS then.
+	 */
+}
+
+
+static void pl2303_unthrottle(struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	struct tty_struct *tty=port->tty;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+	unsigned long flags;
+	int result;
+
+	dbg(__FUNCTION__ ": %d....", tty->ldisc.chars_in_buffer(tty));
+
+	PL2303_LOCK(port,flags);
+	if (I_IXOFF(tty)) {
+		if (info->x_char){
+			info->x_char = 0;
+		} else {
+			info->x_char = START_CHAR(tty);
+			pl2303_submit_xchar(port);
+		}
+	}
+	if( !info->reading ){
+		port->read_urb->dev = serial->dev;
+		if(!test_bit(PL2303_CLOSING,&info->flags)){
+		result = usb_submit_urb(port->read_urb);
+		if (result){
+			err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+		} else {
+			info->reading = 1;
+		}
+		}
+	}
+	PL2303_UNLOCK(port,flags);
 }
 
 
diff -ur linux-isdn/drivers/usb/serial/pl2303.h linux-pl2303/drivers/usb/serial/pl2303.h
--- linux-isdn/drivers/usb/serial/pl2303.h	Tue Aug  7 17:12:33 2001
+++ linux-pl2303/drivers/usb/serial/pl2303.h	Wed Oct  3 12:27:34 2001
@@ -13,3 +13,7 @@
 #define ATEN_VENDOR_ID		0x0557
 #define ATEN_PRODUCT_ID		0x2008
 
+#define PL2303_PRODUCT_ID_RSAQ2	0x04bb
+
+#define IODATA_VENDOR_ID        0x04bb
+#define IODATA_PRODUCT_ID	0x0a03
