patch-2.4.11-dontuse linux/drivers/usb/serial/pl2303.c

Next file: linux/drivers/usb/serial/pl2303.h
Previous file: linux/drivers/usb/serial/keyspan_usa49w_fw.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.10/linux/drivers/usb/serial/pl2303.c linux/drivers/usb/serial/pl2303.c
@@ -12,6 +12,12 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
+ * 2001_Oct_06 gkh
+ *	Added RTS and DTR line control.  Thanks to joe@bndlg.de for parts of it.
+ *
+ * 2001_Sep_19 gkh
+ *	Added break support.
+ *
  * 2001_Aug_30 gkh
  *	fixed oops in write_bulk_callback.
  *
@@ -53,13 +59,15 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.7"
+#define DRIVER_VERSION "v0.9"
 #define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
 
 
 
 static __devinitdata struct usb_device_id id_table [] = {
 	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
+	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
+	{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
 	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
 	{ }					/* Terminating entry */
 };
@@ -67,6 +75,28 @@
 MODULE_DEVICE_TABLE (usb, id_table);
 
 
+#define SET_LINE_REQUEST_TYPE		0x21
+#define SET_LINE_REQUEST		0x20
+
+#define SET_CONTROL_REQUEST_TYPE	0x21
+#define SET_CONTROL_REQUEST		0x22
+#define CONTROL_DTR			0x01
+#define CONTROL_RTS			0x02
+
+#define BREAK_REQUEST_TYPE		0x21
+#define BREAK_REQUEST			0x23	
+#define BREAK_ON			0xffff
+#define BREAK_OFF			0x0000
+
+#define GET_LINE_REQUEST_TYPE		0xa1
+#define GET_LINE_REQUEST		0x21
+
+#define VENDOR_WRITE_REQUEST_TYPE	0x40
+#define VENDOR_WRITE_REQUEST		0x01
+
+#define VENDOR_READ_REQUEST_TYPE	0xc0
+#define VENDOR_READ_REQUEST		0x01
+
 /* function prototypes for a PL2303 serial converter */
 static int pl2303_open (struct usb_serial_port *port, struct file *filp);
 static void pl2303_close (struct usb_serial_port *port, struct file *filp);
@@ -80,6 +110,8 @@
 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);
 
 
 /* All of the device info needed for the PL2303 SIO serial converter */
@@ -102,9 +134,40 @@
 	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,
 };
 
+struct pl2303_private { 
+	u8 line_control;
+};
+
+
+static int pl2303_startup (struct usb_serial *serial)
+{
+	struct pl2303_private *priv;
+	int i;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL);
+		if (!priv)
+			return -ENOMEM;
+		memset (priv, 0x00, sizeof (struct pl2303_private));
+		serial->port[i].private = priv;
+	}
+	return 0;
+}
 
+static int set_control_lines (struct usb_device *dev, u8 value)
+{
+	int retval;
+	
+	retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0),
+				  SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
+				  value, 0, NULL, 0, 100);
+	dbg (__FUNCTION__" - value = %d, retval = %d", value, retval);
+	return retval;
+}
 
 static int pl2303_write (struct usb_serial_port *port, int from_user,  const unsigned char *buf, int count)
 {
@@ -145,58 +208,66 @@
 
 
 
-static void
-pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
-{				/* pl2303_set_termios */
+static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_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};
+	struct pl2303_private *priv;
+	unsigned int cflag;
+	unsigned char *buf;
 	int baud;
 	int i;
 
+	dbg (__FUNCTION__ " -  port %d", port->number);
 
-	dbg ("pl2303_set_termios port %d", port->number);
+	if ((!port->tty) || (!port->tty->termios)) {
+		dbg(__FUNCTION__" - no tty structures");
+		return;
+	}
 
+	cflag = port->tty->termios->c_cflag;
+	/* check that they really want us to change something */
+	if (old_termios) {
+		if ((cflag == old_termios->c_cflag) &&
+		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
+		    dbg(__FUNCTION__ " - nothing to change...");
+		    return;
+		}
+	}
 
+	buf = kmalloc (7, GFP_KERNEL);
+	if (!buf) {
+		err(__FUNCTION__ " - out of memory.");
+		return;
+	}
+	memset (buf, 0x00, 0x07);
+	
 	i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
-			     0x21, 0xa1, 0, 0, buf, 7, 100);
-
+			     GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+			     0, 0, buf, 7, 100);
 	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);
+			     VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
+			     0, 1, NULL, 0, 100);
 
 	dbg ("0x40:1:0:1  %d", i);
 
-
-
 	if (cflag & CSIZE) {
 		switch (cflag & CSIZE) {
-			case CS5:
-				buf[6] = 5;
-				dbg ("Setting CS5");
-				break;
-			case CS6:
-				buf[6] = 6;
-				dbg ("Setting CS6");
-				break;
-			case CS7:
-				buf[6] = 7;
-				dbg ("Setting CS7");
-				break;
-			case CS8:
-				buf[6] = 8;
-				dbg ("Setting CS8");
-				break;
+			case CS5:	buf[6] = 5;	break;
+			case CS6:	buf[6] = 6;	break;
+			case CS7:	buf[6] = 7;	break;
 			default:
-				err ("CSIZE was set but not CS5-CS8");
+			case CS8:	buf[6] = 8;	break;
 		}
+		dbg (__FUNCTION__ " - data bits = %d", buf[6]);
 	}
 
 	baud = 0;
 	switch (cflag & CBAUD) {
+		case B0:	baud = 0;	break;
 		case B75:	baud = 75;	break;
 		case B150:	baud = 150;	break;
 		case B300:	baud = 300;	break;
@@ -216,7 +287,7 @@
 			err ("pl2303 driver does not support the baudrate requested (fix it)");
 			break;
 	}
-
+	dbg (__FUNCTION__ " - baud = %d", baud);
 	if (baud) {
 		buf[0] = baud & 0xff;
 		buf[1] = (baud >> 8) & 0xff;
@@ -224,16 +295,17 @@
 		buf[3] = (baud >> 24) & 0xff;
 	}
 
-
 	/* For reference buf[4]=0 is 1 stop bits */
 	/* For reference buf[4]=1 is 1.5 stop bits */
 	/* For reference buf[4]=2 is 2 stop bits */
-
 	if (cflag & CSTOPB) {
 		buf[4] = 2;
+		dbg(__FUNCTION__ " - stop bits = 2");
+	} else {
+		buf[4] = 0;
+		dbg(__FUNCTION__ " - stop bits = 1");
 	}
 
-
 	if (cflag & PARENB) {
 		/* For reference buf[5]=0 is none parity */
 		/* For reference buf[5]=1 is odd parity */
@@ -242,45 +314,46 @@
 		/* For reference buf[5]=4 is space parity */
 		if (cflag & PARODD) {
 			buf[5] = 1;
+			dbg(__FUNCTION__ " - parity = odd");
 		} else {
 			buf[5] = 2;
+			dbg(__FUNCTION__ " - parity = even");
 		}
+	} else {
+		buf[5] = 0;
+		dbg(__FUNCTION__ " - parity = none");
 	}
 
 	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-			     0x20, 0x21, 0, 0, buf, 7, 100);
-
+			     SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, 
+			     0, 0, buf, 7, 100);
 	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);
-
-	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);
-
-	dbg ("0x21:0x22:3:0  %d", i);
-
+	if (cflag && CBAUD) {
+		priv = port->private;
+		if ((cflag && CBAUD) == B0)
+			priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+		else
+			priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
+		set_control_lines (serial->dev, priv->line_control);
+	}
+	
 	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);
-
+			     GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+			     0, 0, buf, 7, 100);
 	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]);
 
 	if (cflag & CRTSCTS) {
-
 		i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
-				     0x01, 0x40, 0x0, 0x41, NULL, 0, 100);
-
+				     VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST_TYPE,
+				     0x0, 0x41, NULL, 0, 100);
 		dbg ("0x40:0x1:0x0:0x41  %d", i);
-
 	}
 
-
-	return;
+	kfree (buf);
 }       
 
 
@@ -294,7 +367,7 @@
 	if (port_paranoia_check (port, __FUNCTION__))
 		return -ENODEV;
 		
-	dbg (__FUNCTION__ "-  port %d", port->number);
+	dbg (__FUNCTION__ " -  port %d", port->number);
 
 	down (&port->sem);
 
@@ -311,20 +384,20 @@
 
 #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, 100);			\
 		dbg("0x%x:0x%x:0x%x:0x%x  %d",a,b,c,d,result);
 
-		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);
+		FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
+		SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 0);
+		FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
+		FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);
+		FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
+		SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1);
+		FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
+		FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);
+		SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1);
+		SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0xc0);
+		SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 4);
 
 		/* Setup termios */
 		*(port->tty->termios) = tty_std_termios;
@@ -334,6 +407,7 @@
 
 		//FIXME: need to assert RTS and DTR if CRTSCTS off
 
+		dbg (__FUNCTION__ " - submitting read urb");
 		port->read_urb->dev = serial->dev;
 		result = usb_submit_urb (port->read_urb);
 		if (result) {
@@ -343,6 +417,7 @@
 			return -EPROTO;
 		}
 
+		dbg (__FUNCTION__ " - submitting interrupt urb");
 		port->interrupt_in_urb->dev = serial->dev;
 		result = usb_submit_urb (port->interrupt_in_urb);
 		if (result) {
@@ -359,7 +434,9 @@
 
 static void pl2303_close (struct usb_serial_port *port, struct file *filp)
 {
+	struct pl2303_private *priv;
 	unsigned int c_cflag;
+	int result;
 
 	if (port_paranoia_check (port, __FUNCTION__))
 		return;
@@ -372,74 +449,151 @@
 	if (port->open_count <= 0) {
 		c_cflag = port->tty->termios->c_cflag;
 		if (c_cflag & HUPCL) {
-			//FIXME: Do drop DTR
-			//FIXME: Do drop RTS
+			/* drop DTR and RTS */
+			priv = port->private;
+			priv->line_control = 0;
+			set_control_lines (port->serial->dev, priv->line_control);
 		}
 
 		/* shutdown our urbs */
-		usb_unlink_urb (port->write_urb);
-		usb_unlink_urb (port->read_urb);
-		usb_unlink_urb (port->interrupt_in_urb);
+		dbg (__FUNCTION__ " - shutting down urbs");
+		result = usb_unlink_urb (port->write_urb);
+		if (result)
+			dbg (__FUNCTION__ " - usb_unlink_urb (write_urb) failed with reason: %d", result);
+
+		result = usb_unlink_urb (port->read_urb);
+		if (result)
+			dbg (__FUNCTION__ " - usb_unlink_urb (read_urb) failed with reason: %d", result);
+
+		result = usb_unlink_urb (port->interrupt_in_urb);
+		if (result)
+			dbg (__FUNCTION__ " - usb_unlink_urb (interrupt_in_urb) failed with reason: %d", result);
 
 		port->active = 0;
 		port->open_count = 0;
 	}
 
 	up (&port->sem);
+	MOD_DEC_USE_COUNT;
 }
 
+static int set_modem_info (struct usb_serial_port *port, unsigned int cmd, unsigned int *value)
+{
+	struct pl2303_private *priv = port->private;
+	unsigned int arg;
+
+	if (copy_from_user(&arg, value, sizeof(int)))
+		return -EFAULT;
+
+	switch (cmd) {
+		case TIOCMBIS:
+			if (arg & TIOCM_RTS)
+				priv->line_control |= CONTROL_RTS;
+			if (arg & TIOCM_DTR)
+				priv->line_control |= CONTROL_DTR;
+			break;
+
+		case TIOCMBIC:
+			if (arg & TIOCM_RTS)
+				priv->line_control &= ~CONTROL_RTS;
+			if (arg & TIOCM_DTR)
+				priv->line_control &= ~CONTROL_DTR;
+			break;
+
+		case TIOCMSET:
+			/* turn off RTS and DTR and then only turn
+			   on what was asked to */
+			priv->line_control &= ~(CONTROL_RTS | CONTROL_DTR);
+			priv->line_control |= ((arg & TIOCM_RTS) ? CONTROL_RTS : 0);
+			priv->line_control |= ((arg & TIOCM_DTR) ? CONTROL_DTR : 0);
+			break;
+	}
+
+	return set_control_lines (port->serial->dev, priv->line_control);
+}
 
-static int
-pl2303_ioctl (struct usb_serial_port *port, struct file *file,
-	      unsigned int cmd, unsigned long arg)
+static int get_modem_info (struct usb_serial_port *port, unsigned int *value)
 {
-// struct usb_serial *serial = port->serial;
-// __u16 urb_value=0; /* Will hold the new flags */
-// char buf[1];
-// int  ret, mask;
+	struct pl2303_private *priv = port->private;
+	unsigned int mcr = priv->line_control;
+	unsigned int result;
 
+	result = ((mcr & CONTROL_DTR)		? TIOCM_DTR : 0)
+		  | ((mcr & CONTROL_RTS)	? TIOCM_RTS : 0);
 
-	dbg ("pl2303_sio ioctl 0x%04x", cmd);
+	dbg (__FUNCTION__ " - result = %x", result);
+
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	dbg (__FUNCTION__" (%d) cmd = 0x%04x", port->number, cmd);
 
-	/* Based on code from acm.c and others */
 	switch (cmd) {
 		
 		case TIOCMGET:
-			dbg ("TIOCMGET");
+			dbg (__FUNCTION__" (%d) TIOCMGET", port->number);
+			return get_modem_info (port, (unsigned int *)arg);
 
-
-			return put_user (0, (unsigned long *) arg);
-			break;
 		case TIOCMBIS:
 		case TIOCMBIC:
 		case TIOCMSET:
-			return 0;
+			dbg(__FUNCTION__" (%d) TIOCMSET/TIOCMBIC/TIOCMSET",  port->number);
+			return set_modem_info(port, cmd, (unsigned int *) arg);
 
 		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);
+			dbg (__FUNCTION__" not supported = 0x%04x", cmd);
 			break;
 	}
-	dbg ("pl2303_ioctl returning 0");
 
-	return 0;
-}				/* pl2303_ioctl */
+	return -ENOIOCTLCMD;
+}
 
 
-static void pl2303_break_ctl(struct usb_serial_port *port,int break_state)
+static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
 {
-//FIXME
+	struct usb_serial *serial = port->serial;
+	u16 state;
+	int result;
+
+	dbg (__FUNCTION__ " - port %d", port->number);
+
+	if (break_state == 0)
+		state = BREAK_OFF;
+	else
+		state = BREAK_ON;
+	dbg (__FUNCTION__" - turning break %s", state==BREAK_OFF ? "off" : "on");
+
+	result = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
+				  BREAK_REQUEST, BREAK_REQUEST_TYPE, state, 
+				  0, NULL, 0, 100);
+	if (result)
+		dbg (__FUNCTION__" - error sending break = %d", result);
+}
+
+
+static void pl2303_shutdown (struct usb_serial *serial)
+{
+	int i;
+
+	dbg (__FUNCTION__);
+
+	/* stop everything on all ports */
+	for (i = 0; i < serial->num_ports; ++i)
+		while (serial->port[i].open_count > 0) {
+			pl2303_close (&serial->port[i], NULL);
+			kfree (serial->port[i].private);
+		}
 }
 
 
-static void
-pl2303_read_int_callback (struct urb *urb)
+static void pl2303_read_int_callback (struct urb *urb)
 {
 	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
-	struct usb_serial *serial = get_usb_serial (port, "pl2303_read_int_callback");
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 	//unsigned char *data = urb->transfer_buffer;
 	//int i;
 
@@ -482,13 +636,23 @@
 		return;
 	}
 
-	/* PL2303 mysteriously fails with -EPROTO reschedule the read */
 	if (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);
+		dbg (__FUNCTION__ " - urb->status = %d", urb->status);
+		if (!port->active) {
+			dbg (__FUNCTION__ " - port is closed, exiting.");
+			return;
+		}
+		if (urb->status == -EPROTO) {
+			/* PL2303 mysteriously fails with -EPROTO reschedule the read */
+			dbg (__FUNCTION__ " - caught -EPROTO, resubmitting the urb");
+			urb->status = 0;
+			urb->dev = serial->dev;
+			result = usb_submit_urb(urb);
+			if (result)
+				err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+			return;
+		}
+		dbg (__FUNCTION__ " - unable to handle the error, exiting.");
 		return;
 	}
 
@@ -505,11 +669,13 @@
 		tty_flip_buffer_push (tty);
 	}
 
-	/* Schedule the next read*/
-	urb->dev = serial->dev;
-	result = usb_submit_urb(urb);
-	if (result)
-		err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+	/* Schedule the next read _if_ we are still open */
+	if (port->active) {
+		urb->dev = serial->dev;
+		result = usb_submit_urb(urb);
+		if (result)
+			err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+	}
 
 	return;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)