Hi,
Here's a patch against 2.4.10-ac7 that adds proper RTS/DTR control for
the pl2303 driver. Parts of this patch was done by Johannes
Deisenhofer.
thanks,
greg k-h
diff --minimal -Nru a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
--- a/drivers/usb/serial/pl2303.c Sat Oct 6 22:20:45 2001
+++ b/drivers/usb/serial/pl2303.c Sat Oct 6 22:20:45 2001
@@ -12,6 +12,9 @@
*
* 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 [EMAIL PROTECTED] for parts of it.
+ *
* 2001_Sep_19 gkh
* Added break support.
*
@@ -56,13 +59,15 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.8"
+#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 */
};
@@ -75,6 +80,8 @@
#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
@@ -103,6 +110,7 @@
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);
@@ -126,10 +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)
{
@@ -173,6 +211,7 @@
static void pl2303_set_termios (struct usb_serial_port *port, struct termios
*old_termios)
{
struct usb_serial *serial = port->serial;
+ struct pl2303_private *priv;
unsigned int cflag;
unsigned char *buf;
int baud;
@@ -228,6 +267,7 @@
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;
@@ -289,21 +329,15 @@
0, 0, buf, 7, 100);
dbg ("0x21:0x20:0:0 %d", i);
- i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
- SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
- 1, 0, NULL, 0, 100);
- dbg ("0x21:0x22:1:0 %d", i);
-#if 0
- i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
- SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
- 1, 0, NULL, 0, 100);
- dbg ("0x21:0x22:1:0 %d", i);
-
- i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
- SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
- 3, 0, NULL, 0, 100);
- dbg ("0x21:0x22:3:0 %d", i);
-#endif
+ 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),
@@ -400,6 +434,7 @@
static void pl2303_close (struct usb_serial_port *port, struct file *filp)
{
+ struct pl2303_private *priv;
unsigned int c_cflag;
int result;
@@ -414,8 +449,10 @@
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 */
@@ -440,35 +477,79 @@
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 get_modem_info (struct usb_serial_port *port, unsigned int *value)
+{
+ 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 (__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 ("pl2303_sio ioctl 0x%04x", cmd);
+ 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;
}
- return 0;
+ return -ENOIOCTLCMD;
}
@@ -502,8 +583,10 @@
/* stop everything on all ports */
for (i = 0; i < serial->num_ports; ++i)
- while (serial->port[i].open_count > 0)
+ while (serial->port[i].open_count > 0) {
pl2303_close (&serial->port[i], NULL);
+ kfree (serial->port[i].private);
+ }
}
diff --minimal -Nru a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
--- a/drivers/usb/serial/pl2303.h Sat Oct 6 22:20:45 2001
+++ b/drivers/usb/serial/pl2303.h Sat Oct 6 22:20:45 2001
@@ -9,7 +9,10 @@
*/
#define PL2303_VENDOR_ID 0x067b
#define PL2303_PRODUCT_ID 0x2303
+#define PL2303_PRODUCT_ID_RSAQ2 0x04bb
#define ATEN_VENDOR_ID 0x0557
#define ATEN_PRODUCT_ID 0x2008
+#define IODATA_VENDOR_ID 0x04bb
+#define IODATA_PRODUCT_ID 0x0a03