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