diff -ur linux/Documentation/Configure.help linux.ext/Documentation/Configure.help
--- linux/Documentation/Configure.help	Fri Aug 31 23:37:25 2001
+++ linux.ext/Documentation/Configure.help	Tue Sep  4 22:52:40 2001
@@ -11311,7 +11311,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/Documentation/usb/usb-serial.txt linux.ext/Documentation/usb/usb-serial.txt
--- linux/Documentation/usb/usb-serial.txt	Tue Aug  7 17:11:53 2001
+++ linux.ext/Documentation/usb/usb-serial.txt	Tue Sep  4 23:32:38 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 revisons do not support settinng all
+    parameters listed below. The driver does not check whether
+    the paramaters 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/drivers/usb/serial/pl2303.c linux.ext/drivers/usb/serial/pl2303.c
--- linux/drivers/usb/serial/pl2303.c	Tue Aug  7 17:12:33 2001
+++ linux.ext/drivers/usb/serial/pl2303.c	Tue Sep  4 23:06:36 2001
@@ -1,3 +1,6 @@
+#define GENERIC_WRITE 1
+#define GENERIC_READ 1
+#define NEW_SOFT_IRQ 1
 /*
  * Prolific PL2303 USB to serial adaptor driver
  *
@@ -15,6 +18,47 @@
  * 2001_Jun_06 gkh
  *	finished porting to 2.4 format.
  * 
+ * 2001-08-16 Henner Eisen
+ * 
+ * With my ATEN 232a, the former (initial) versions of this driver crashed
+ * while the generic driver worked sort of (just the baudrate seemed to be
+ * in an undefined state. Thus, I started to use the generic usb_serial
+ * methods and only applied the termios setting and initialization from the
+ * former driver, which basically worked. Then I startet to look at the the
+ * NetBSD driver and at ../acm.c and added additional features. Main changes:
+ *
+ * - Fixed index paramter of certain control messages to conatin 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 broke those 
+ *   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 initialisation in startup and open.
+ *
+ * - 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.
+ *
+ * - more port->sem locking.
+ *
+ * - handle setting of B0 speed
+ *
+ *
+ * In contrast to the former version, the current version uses the generic
+ * output routines and subsequently does not maintain an own output buffer.
+ *
+ * TODO:
+ * - Review locking and impact of non-atomic memmory acces in interrupts.
+ * - Add output buffering again.
+ *
  */
 
 #include <linux/config.h>
@@ -67,16 +111,62 @@
 
 
 
+
 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.
+	   * 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);
 
+#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
+
+/*
+ * IRQs.
+ */
+
+#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 wIndex field 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;
+	char is_initialized;
 	spinlock_t	lock;
+	/* old stuff not (yet) used in new implementation */
 	unsigned char	*xmit_buf;
 	int		xmit_head;
 	int		xmit_tail;
@@ -85,6 +175,7 @@
 
 /* function prototypes for a PL2303 serial converter */
 static int pl2303_startup (struct usb_serial *serial);
+static void pl2303_shutdown(struct usb_serial *serial);
 static int pl2303_open (struct usb_serial_port *port, struct file *filp);
 static void pl2303_close (struct usb_serial_port *port, struct file *filp);
 static void pl2303_set_termios (struct usb_serial_port *port,
@@ -95,14 +186,18 @@
 static void pl2303_unthrottle (struct usb_serial_port *port);
 static void pl2303_read_int_callback (struct urb *urb);
 static void pl2303_read_bulk_callback (struct urb *urb);
+static void new_generic_read_bulk_callback (struct urb *urb);
 static void pl2303_write_bulk_callback (struct urb *urb);
+static void new_generic_write_bulk_callback (struct urb *urb);
 static int pl2303_write (struct usb_serial_port *port, int from_user,
 			 const unsigned char *buf, int count);
+static int new_generic_write (struct usb_serial_port *port, int from_user,
+			 const unsigned char *buf, int count);
 static int pl2303_write_room(struct usb_serial_port *port);
 static int pl2303_chars_in_buffer(struct usb_serial_port *port);
 static void pl2303_break_ctl(struct usb_serial_port *port,int break_state);
 static void start_xmit (struct usb_serial_port *port);
-
+static void new_port_softint(void *private);
 
 /* All of the device info needed for the PL2303 SIO serial converter */
 static struct usb_serial_device_type pl2303_device = {
@@ -116,19 +211,32 @@
 	num_bulk_out:		1,
 	num_ports:		1,
 	open:			pl2303_open,
-	close:			pl2303_close,
+	close:			pl2303_close, 
 	throttle:		pl2303_throttle,
 	unthrottle:		pl2303_unthrottle,
+#ifdef GENERIC_WRITE
+	write:			new_generic_write,
+#else
 	write:			pl2303_write,
-	ioctl:			pl2303_ioctl,
 	write_room:		pl2303_write_room,
 	chars_in_buffer:	pl2303_chars_in_buffer,
+#endif
+	ioctl:			pl2303_ioctl,
 	break_ctl:		pl2303_break_ctl,
 	set_termios:		pl2303_set_termios,
-	read_bulk_callback:	pl2303_read_bulk_callback,
+#ifdef GENERIC_READ
+	read_bulk_callback:	new_generic_read_bulk_callback,
+#else
+	read_bulk_callback:	pl2303_read_bulk_callback, */
+#endif
 	read_int_callback:	pl2303_read_int_callback,
+#ifdef GENERIC_WRITE
+	write_bulk_callback:	new_generic_write_bulk_callback,
+#else
 	write_bulk_callback:	pl2303_write_bulk_callback,
+#endif
 	startup:		pl2303_startup,
+	shutdown:		pl2303_shutdown,
 };
 
 
@@ -160,7 +268,7 @@
 
 
 	PL2303_LOCK(port,flags);
-
+// FIXME: calling blocking code (down(), copy_from_user()) with lock_irq is not allowed
 
 	if (from_user) {
 		down(&tmp_buf_sem);
@@ -234,86 +342,138 @@
 	return info->xmit_cnt;
 }
 
+/*
+ * schedule writing of the 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(port->active && !info->writing){
+		*(char*) port->write_urb->transfer_buffer = info->x_char;
+		FILL_BULK_URB(port->write_urb, serial->dev, usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+			      port->write_urb->transfer_buffer, 1,
+			      new_generic_write_bulk_callback, port);
+		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)
 {
-#if 0
-	//struct usb_serial *serial = port->serial;
 	struct tty_struct *tty=port->tty;
 	unsigned long flags;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
 
 
 	char    buf[64];
 
-	dbg("throttle %s: %d....", tty_name(tty, buf),
-	    tty->ldisc.chars_in_buffer(tty));
-
-//FIXME FIXME FIXME	
-	if (I_IXOFF(tty))
-		rs_send_xchar(tty, STOP_CHAR(tty));
+	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);
+	}
+
 	//Should remove read request if one is present
+		/* probably not necessary to do this in a hurry.
+		 * Read chunks are small enough, at least for n_tty
+		 * line discipline, such that it is 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.
+	 */
 	PL2303_UNLOCK(port,flags);
-#endif
 }
 
 static void pl2303_unthrottle(struct usb_serial_port *port)
 {
-#if 0
-	//struct usb_serial *serial = port->serial;
+	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;
+	char buf[64];
 
+	dbg(__FUNCTION__ ": %d....", tty->ldisc.chars_in_buffer(tty));
 
-	char    buf[64];
-
-	dbg("unthrottle %s: %d....", tty_name(tty, buf),
-	    tty->ldisc.chars_in_buffer(tty));
-
-	//FIXME FIXME FIXME FIXME FIXME
+	PL2303_LOCK(port,flags);
 	if (I_IXOFF(tty)) {
-		if (info->x_char)
+		if (info->x_char){
 			info->x_char = 0;
-		else
-			rs_send_xchar(tty, START_CHAR(tty));
+		} else {
+			info->x_char = START_CHAR(tty);
+			pl2303_submit_xchar(port);
+		}
+	}
+	if( port->active && !info->reading ){
+		port->read_urb->dev = serial->dev;
+		result = usb_submit_urb(port->read_urb);
+		if (result){
+			err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+		} else {
+			info->reading = 1;
+		}
 	}
-
-	PL2303_LOCK(port,flags);
-	//Should add read request if one is not present
 	PL2303_UNLOCK(fport,flags);
-#endif
 }
 
 
 static void
-pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+pl2303_set_termios_unlocked (struct usb_serial_port *port, struct termios *old_termios)
 {				/* pl2303_set_termios */
 	struct usb_serial *serial = port->serial;
 	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);
+	dbg ("pl2303_set_termios_unlocked 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, /*1*/ix, NULL, 0, HZ);
 
-	dbg ("0x40:1:0:1  %d", i);
+	dbg ("0x40:1:0:/*1*/%d  %d", ix, i);
 
 
 
 	if (cflag & CSIZE) {
 		switch (cflag & CSIZE) {
+			case CS5:
+				buf[6] = 5;
+				dbg ("Setting CS5");
+				break;
 			case CS6:
 				buf[6] = 6;
 				dbg ("Setting CS6");
@@ -328,13 +488,20 @@
 				break;
 			default:
 				err ("CSIZE was set but not CS6-CS8");
+				/* Remark: Chip also supports 16 */
 		}
 	}
 
 	baud = 0;
 	switch (cflag & CBAUD) {
 		case B0:
-			err ("Can't do B0 yet");  //FIXME
+			/* use default rate, special handling later */
+			break;
+		case B75:
+			baud = 75;
+			break;
+		case B150:
+			baud = 150;
 			break;
 		case B300:
 			baud = 300;
@@ -366,8 +533,17 @@
 		case B115200:
 			baud = 115200;
 			break;
+		case B230400:
+			baud = 230400;
+			break;
+		case B460800:
+			baud = 460800;
+			break;
+		case B921600:
+			baud = 921600;
+			break;
 		default:
-			dbg ("pl2303 driver does not support the baudrate requested (fix it)");
+		dbg ("pl2303 driver does not support the baudrate requested (fix it)");
 			break;
 	}
 
@@ -394,27 +570,28 @@
 		} else {
 			buf[5] = 2;
 		}
+		if (cflag & CMSPAR) buf[5] += 2;
 	}
 
 	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]);
@@ -422,17 +599,77 @@
 	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, info->ctl_ifc, NULL, 0, HZ*5);
+	}
 	return;
 }       
 
 
+static void
+pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+{
+	dbg ("pl2303_set_termios port %d", port->number);
+	down(&port->sem);
+	pl2303_set_termios_unlocked (port, old_termios);
+	up(&port->sem);
+}
+
+
+static int
+pl2303_reset_port (struct usb_serial_port *port)
+{
+	int i, ix = ((struct pl2303_private*) port->private) -> ctl_ifc;
+	struct usb_serial *serial = port->serial;
+	char buf[10];
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+/*extern int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);*/
+
+	/* reset the port */
+
+	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
+			     0x01, 0x40, 0x0, /*?0x41*/ ix, NULL, 0, HZ);
+
+	dbg ("0x40:0x1:0x0:%d  %d", ix, i);
+
+#if 0
+#define FISH(a,b,c,d) \
+	i=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0), \
+		    b, a,c  , d, buf, 1, HZ); \
+	dbg("0x%x:0x%x:0x%x:0x%x  %d - %x",a,b,c,d,i,buf[0]);
+
+#define SOUP(a,b,c,d) \
+	i=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), \
+		    b, a,c  , d, /*NULL*/buf, 0, HZ); \
+	dbg("0x%x:0x%x:0x%x:0x%x  %d",a,b,c,d,i);
+	FISH (0xc0, 1, 0x8484, 0);
+	SOUP (0x40, 1, 0x0404, 0);
+	FISH (0xc0, 1, 0x8484, 0);
+	FISH (0xc0, 1, 0x8383, 0);
+	FISH (0xc0, 1, 0x8484, 0);
+	SOUP (0x40, 1, 0x0404, 1);
+	FISH (0xc0, 1, 0x8484, 0);
+	FISH (0xc0, 1, 0x8383, 0);
+	SOUP (0x40, 1, 0, 1);
+	SOUP (0x40, 1, 1, 0xc0);
+	SOUP (0x40, 1, 2, 4);
+#endif
+	return 0;
+}
+
+#if 0
 static int
 pl2303_open (struct usb_serial_port *port, struct file *filp)
 {				/* pl2303_open */
@@ -447,12 +684,12 @@
 
 #define FISH(a,b,c,d) \
 	i=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,i,buf[0]);
 
 #define SOUP(a,b,c,d) \
 	i=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), \
-		    b, a,c  , d, NULL, 0, 100); \
+		    b, a,c  , d, /*NULL*/buf, 0, HZ); \
 	dbg("0x%x:0x%x:0x%x:0x%x  %d",a,b,c,d,i);
 
 
@@ -534,7 +771,6 @@
 			dbg ("i=%d", i);
 			pl2303_close (port, NULL);
 			return -EPROTO;
-
 		}
 
 		if ((i = usb_submit_urb (port->interrupt_in_urb))) {
@@ -548,8 +784,238 @@
 
 	return(0);
 }				/* pl2303_open */
+#endif
+
+static int pl2303_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial = port->serial;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+	int result = 0;
+	unsigned long flags;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return -ENODEV;
+
+	MOD_INC_USE_COUNT;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	down (&port->sem);
+	
+	++port->open_count;
+	
+	if (!port->active) {
+		struct termios tmp_termios;
+
+		port->active = 1;
+
+		/* force low_latency on so that our tty_push actually forces the data through, 
+		   otherwise it is scheduled, and with high data rates (like with OHCI) data
+		   can get lost. */
+		port->tty->low_latency = 1;
+
+#define ALWAYS_RESET 1
+		if(!info->is_initialized){
+			/* initialize termios defaults settimgs. */
+			*(port->tty->termios) = tty_std_termios;
+			port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+#ifdef NEW_SOFT_IRQ
+			port->tqueue.routine = new_port_softint;
+#endif
 
+#if ! ALWAYS_RESET			
+			pl2303_reset_port(port);
+#endif
+			spin_lock_init(&info->lock);
+			info->is_initialized = 1;
+		}
+#if ALWAYS_RESET			
+		pl2303_reset_port(port);
+#endif
+		/* 
+		 * some termios settings should be maintained even while
+		 * tty was closed.
+		 */
+		pl2303_set_termios_unlocked (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,
+				    info->ctl_ifc, NULL, 0, HZ*5);
+		
+		info->writing = 0;
+		info->x_char  = 0;
 
+		/* Start reading from the device */
+		FILL_BULK_URB(port->read_urb, serial->dev, 
+			      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+			      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+			      new_generic_read_bulk_callback, 
+			      port);
+		/* after submitting ths first urb, we might be hit by
+		 * callbacks and need to protect ourselves */
+		PL2303_LOCK(port,flags);
+		result = usb_submit_urb(port->read_urb);
+		if (result){
+			err(__FUNCTION__ " - failed initially submitting read urb, error %d", result);
+			goto cleanup;
+		} else {
+			info->reading = 1;
+		}
+		
+		FILL_INT_URB(port->interrupt_in_urb, serial->dev, 
+			     usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
+			     port->interrupt_in_buffer,
+			     port->interrupt_in_urb->transfer_buffer_length, 
+			     pl2303_read_int_callback, port, 
+			     port->interrupt_in_urb->interval);
+		dbg(__FUNCTION__ ": submitting int urb, interval=%d",port->interrupt_in_urb->interval);
+		if ((result = usb_submit_urb (port->interrupt_in_urb))){
+			err (__FUNCTION__" usb_submit_urb(interrupt ink) failed, error %d",result);
+			goto cleanup;
+		}
+	cleanup:
+		if(result){
+			usb_unlink_urb (port->read_urb);
+			info->reading = 0;
+			usb_unlink_urb (port->interrupt_in_urb);
+			--port->open_count;
+			port->active = 1;
+			MOD_DEC_USE_COUNT;
+		}
+		PL2303_UNLOCK(port,flags);
+	}
+	up (&port->sem);
+	return result;
+}
+
+
+/*****************************************************************************
+ * derived from generic devices specific driver functions
+ *****************************************************************************/
+/* just a copy because it is referenced and not exportet by usbserial.c */
+static void new_generic_read_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int i, len;
+	int result;
+	unsigned long flags;
+	char flag = TTY_NORMAL;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	PL2303_LOCK(port,flags);
+
+	info->reading = 0;
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		goto error;
+	}
+	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 */
+		len=1;
+		data[0]=0;
+	}
+
+	tty = port->tty;
+	if (len) {
+		for (i = 0; i < len; ++i) {
+			/* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+				tty_flip_buffer_push(tty);
+			}
+			/* this doesn't actually push the data through unless tty->low_latency is set */
+			tty_insert_flip_char(tty, data[i], flag);
+		}
+	  	tty_flip_buffer_push(tty);
+	}
+
+	/* Continue trying to always read  */
+	
+	if( port->active && !info->throttled ){
+		port->read_urb->dev = serial->dev;
+		result = usb_submit_urb(port->read_urb);
+
+		if (result){
+			err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+		} else {
+			info->reading = 1;
+		}
+	}
+ error:
+	PL2303_UNLOCK(port,flags);
+}
+
+
+/* mainly a copy of generic_close, it needs to decrement our own mod ref count,
+   and an additional unlink_urb for the interrupt-in. Also resets control
+   line stat e */
+static void pl2303_close (struct usb_serial_port *port, struct file * filp)
+{
+	struct usb_serial *serial = port->serial;
+	unsigned long flags;
+
+	dbg(__FUNCTION__ " - port %d, cnt=%d", port->number,port->open_count);
+
+	down (&port->sem);
+
+	--port->open_count;
+
+	if (port->open_count <= 0) {
+		struct pl2303_private *info = (struct pl2303_private *)port->private;
+		int i;
+		if(port->tty->termios->c_cflag & HUPCL){
+			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 any bulk reads that might be going on */
+		PL2303_LOCK(port,flags);
+		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;
+		PL2303_UNLOCK(info,flags);
+		port->open_count = 0;
+	}
+	up (&port->sem);
+	MOD_DEC_USE_COUNT;
+}
+
+
+#if 0
 static void
 pl2303_close (struct usb_serial_port *port, struct file *filp)
 {				/* pl2303_close */
@@ -562,11 +1028,12 @@
 	/* shutdown our bulk reads and writes */
 	if (port->active == 1) {
 
+#if 0
 		if (c_cflag & HUPCL) {
 			//FIXME: Do drop DTR
 			//FIXME: Do drop RTS
 		}
-
+#endif
 		usb_unlink_urb (port->write_urb);
 		usb_unlink_urb (port->read_urb);
 		usb_unlink_urb (port->interrupt_in_urb);
@@ -589,59 +1056,130 @@
 	}
 	port->active--;
 }				/* pl2303_close */
-
+#endif
 
 /* do some startup allocations not currently performed by usb_serial_probe() */
 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));
+
+	/*
+	 * Certain control messages need to refer the interface index of
+	 * the bulk transfer interface. As some older chip revisions use 
+	 * two interfaces for a single 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 */;
+}
+
 
 static int
 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 /*?0x21*/,
+			     break_state ? 0xffff : 0, ix,
+			     NULL, 0, HZ);
 }
 
 
@@ -650,8 +1188,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...
 
@@ -659,25 +1200,57 @@
 		return;
 	}
 
+	PL2303_LOCK(port,flags);
+	
 	if (urb->status) {
+		dbg("nonzero ctrl irq status received: %d", urb->status);
 		urb->status = 0;
-		return;
+		goto error;
 	}
 
+	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 (urb->actual_length) {
-		printk (KERN_DEBUG __FILE__ ": INT data read - length = %d, data = ",
-			urb->actual_length);
-		for (i = 0; i < urb->actual_length; ++i) {
-			printk ("%.2x ", data[i]);
-		}
-		printk ("\n");
-	}
+			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;
+	}
+ error:
+	PL2303_UNLOCK(port,flags);
 }
 
 static void
@@ -695,6 +1268,7 @@
 
 // PL2303 mysteriously fails with -EPROTO reschedule the read
 	if (urb->status) {
+		dbg (__FUNCTION__ "failed, status=%d", urb->status);
 		urb->status = 0;
 		if (usb_submit_urb (urb))
 			dbg ("failed resubmitting read bulk urb");
@@ -820,6 +1394,132 @@
 }
 
 
+static int new_generic_write (struct usb_serial_port *port, int from_user,
+			      const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = port->serial;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+	int result;
+	unsigned int flags;
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (count == 0) {
+		dbg(__FUNCTION__ " - write request of 0 bytes");
+		return (0);
+	}
+	
+	/* only do something if we have a bulk out endpoint */
+	PL2303_LOCK(port,flags);
+
+	if (info->writing  || !port->active) {
+		PL2303_UNLOCK(port,flags);
+		dbg (__FUNCTION__ " - already writing");
+		return (0);
+	}
+	count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+	
+	if (from_user) {
+		PL2303_UNLOCK(port,flags);
+		down(&port->sem);
+		result = copy_from_user(port->write_urb->transfer_buffer, buf, count);
+		up(&port->sem);
+		if(result) return -EFAULT;
+		PL2303_LOCK(port,flags);
+		if (info->writing || !port->active){
+			dbg (__FUNCTION__ " - closing or already writing");
+			PL2303_UNLOCK(port,flags);
+			return (0);
+		}
+	} else {
+		memcpy (port->write_urb->transfer_buffer, buf, count);
+	}  
+	
+	usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
+	
+	/* set up our urb */
+	FILL_BULK_URB(port->write_urb, serial->dev, 
+		      usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+		      port->write_urb->transfer_buffer, count,
+		      new_generic_write_bulk_callback,
+		      port);
+	
+	/* send the data out the bulk port */
+	result = usb_submit_urb(port->write_urb);
+	if (result){
+		err(__FUNCTION__ " - failed submitting write urb, error %d", result);
+	} else {
+		result = count;
+		info->writing = 1;
+	}
+
+	PL2303_UNLOCK(port,flags);
+	return result;
+}
+	
+	
+static void new_generic_write_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+	unsigned long flags;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	info->writing = 0;
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+		return;
+	}
+	PL2303_LOCK(port,flags);
+	if(info->x_char){
+		pl2303_submit_xchar(port);
+		if(info->writing){
+			/* submitting the x_char in progress, no wake up yet */
+			PL2303_UNLOCK(port,flags);
+			return;
+		}
+	}
+	PL2303_UNLOCK(port,flags);
+
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	
+	return;
+}
+
+
+static void new_port_softint(void *private)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)private;
+	struct pl2303_private *info = (struct pl2303_private *)port->private;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	struct tty_struct *tty;
+	unsigned long flags;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (!serial) {
+		return;
+	}
+
+	tty = port->tty;
+	
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+		dbg(__FUNCTION__ " - write wakeup call.");
+		(tty->ldisc.write_wakeup)(tty);
+	}
+
+	wake_up_interruptible(&tty->write_wait);
+}
+
+
 static int __init pl2303_init (void)
 {
 	usb_serial_register (&pl2303_device);
@@ -827,7 +1527,6 @@
 	return 0;
 }
 
-
 static void __exit pl2303_exit (void)
 {
 	usb_serial_deregister (&pl2303_device);
@@ -841,4 +1540,3 @@
 
 MODULE_PARM(debug, "i");
 MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff -ur linux/drivers/usb/serial/pl2303.h linux.ext/drivers/usb/serial/pl2303.h
--- linux/drivers/usb/serial/pl2303.h	Tue Aug  7 17:12:33 2001
+++ linux.ext/drivers/usb/serial/pl2303.h	Tue Sep  4 18:59:38 2001
@@ -13,3 +13,11 @@
 #define ATEN_VENDOR_ID		0x0557
 #define ATEN_PRODUCT_ID		0x2008
 
+/* NetBSD driver also knows about this: */
+
+#define PL2303_PRODUCT_ID_RSAQ2	0x04bb
+
+#define IODATA_VENDOR_ID        0x04bb
+#define IODATA_PRODUCT_ID	0x0a03
+
+
