patch-2.3.48 linux/drivers/usb/usb-serial.c

Next file: linux/drivers/usb/usb-serial.h
Previous file: linux/drivers/usb/usb-ohci.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.47/linux/drivers/usb/usb-serial.c linux/drivers/usb/usb-serial.c
@@ -14,6 +14,17 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  * 
+ * (02/21/2000) gkh
+ *	Made it so that any serial devices only have to specify which functions
+ *	they want to overload from the generic function calls (great, 
+ *	inheritance in C, in a driver, just what I wanted...)
+ *	Added support for set_termios and ioctl function calls. No drivers take
+ *	advantage of this yet.
+ *	Removed the #ifdef MODULE, now there is no module specific code.
+ *	Cleaned up a few comments in usb-serial.h that were wrong (thanks again
+ *	to Miles Lott).
+ *	Small fix to get_free_serial.
+ *
  * (02/14/2000) gkh
  *	Removed the Belkin and Peracom functionality from the driver due to
  *	the lack of support from the vendor, and me not wanting people to 
@@ -169,6 +180,19 @@
 
 #include "usb-serial.h"
 
+/* parity check flag */
+#define RELEVANT_IFLAG(iflag)	(iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+/* local function prototypes */
+static int  serial_open (struct tty_struct *tty, struct file * filp);
+static void serial_close (struct tty_struct *tty, struct file * filp);
+static int  serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);
+static int  serial_write_room (struct tty_struct *tty);
+static int  serial_chars_in_buffer (struct tty_struct *tty);
+static void serial_throttle (struct tty_struct * tty);
+static void serial_unthrottle (struct tty_struct * tty);
+static int  serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
+static void serial_set_termios (struct tty_struct *tty, struct termios * old);
 
 static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum);
 static void usb_serial_disconnect(struct usb_device *dev, void *ptr);
@@ -224,7 +248,7 @@
 			continue;
 
 		good_spot = 1;
-		for (j = 0; j < num_ports-1; ++j)
+		for (j = 1; j <= num_ports-1; ++j)
 			if (serial_table[i+j])
 				good_spot = 0;
 		if (good_spot == 0)
@@ -328,12 +352,12 @@
 	tty->driver_data = serial;
 	serial->tty = tty;
 	 
-	/* pass on to the driver specific version of this function */
+	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->open) {
 		return (serial->type->open(tty, filp));
+	} else {
+		return (generic_serial_open(tty, filp));
 	}
-		
-	return (0);
 }
 
 
@@ -363,9 +387,11 @@
 		return;
 	}
 
-	/* pass on to the driver specific version of this function */
+	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->close) {
 		serial->type->close(tty, filp);
+	} else {
+		generic_serial_close(tty, filp);
 	}
 }	
 
@@ -391,13 +417,12 @@
 		return (-EINVAL);
 	}
 	
-	/* pass on to the driver specific version of this function */
+	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->write) {
 		return (serial->type->write(tty, from_user, buf, count));
+	} else {
+		return (generic_serial_write(tty, from_user, buf, count));
 	}
-
-	/* no specific driver, so return that we didn't write anything */
-	return (0);
 }
 
 
@@ -422,12 +447,12 @@
 		return (-EINVAL);
 	}
 	
-	/* pass on to the driver specific version of this function */
+	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->write_room) {
 		return (serial->type->write_room(tty));
+	} else {
+		return (generic_write_room(tty));
 	}
-
-	return (0);
 }
 
 
@@ -452,12 +477,12 @@
 		return (-EINVAL);
 	}
 	
-	/* pass on to the driver specific version of this function */
+	/* pass on to the driver specific version of this function if it is available */
 	if (serial->type->chars_in_buffer) {
 		return (serial->type->chars_in_buffer(tty));
+	} else {
+		return (generic_chars_in_buffer(tty));
 	}
-
-	return (0);
 }
 
 
@@ -485,6 +510,8 @@
 	/* pass on to the driver specific version of this function */
 	if (serial->type->throttle) {
 		serial->type->throttle(tty);
+	} else {
+		generic_throttle(tty);
 	}
 
 	return;
@@ -515,12 +542,86 @@
 	/* pass on to the driver specific version of this function */
 	if (serial->type->unthrottle) {
 		serial->type->unthrottle(tty);
+	} else {
+		generic_unthrottle(tty);
 	}
 
 	return;
 }
 
 
+static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
+	int port;	
+
+	dbg("serial_ioctl");
+
+	if (!serial) {
+		dbg("serial == NULL!");
+		return -ENODEV;
+	}
+
+	port = MINOR(tty->device) - serial->minor;
+
+	dbg("serial_ioctl port %d", port);
+	
+	/* do some sanity checking that we really have a device present */
+	if (!serial->type) {
+		dbg("serial->type == NULL!");
+		return -ENODEV;
+	}
+	if (!serial->active[port]) {
+		dbg ("device not open");
+		return -ENODEV;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->ioctl) {
+		return (serial->type->ioctl(tty, file, cmd, arg));
+	} else {
+		return (generic_ioctl (tty, file, cmd, arg));
+	}
+}
+
+
+static void serial_set_termios (struct tty_struct *tty, struct termios * old)
+{
+	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
+	int port;	
+
+	dbg("serial_set_termios");
+
+	if (!serial) {
+		dbg("serial == NULL!");
+		return;
+	}
+
+	port = MINOR(tty->device) - serial->minor;
+
+	dbg("serial_set_termios port %d", port);
+	
+	/* do some sanity checking that we really have a device present */
+	if (!serial->type) {
+		dbg("serial->type == NULL!");
+		return;
+	}
+	if (!serial->active[port]) {
+		dbg ("device not open");
+		return;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->set_termios) {
+		serial->type->set_termios(tty, old);
+	} else {
+		generic_set_termios (tty, old);
+	}
+	
+	return;
+}
+
+
 #ifdef CONFIG_USB_SERIAL_WHITEHEAT
 /*****************************************************************************
  * Connect Tech's White Heat specific driver functions
@@ -566,6 +667,29 @@
 }
 
 
+static void whiteheat_set_termios (struct tty_struct *tty, struct termios *old_termios)
+{
+	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
+	int port = MINOR(tty->device) - serial->minor;
+	unsigned int cflag = tty->termios->c_cflag;
+
+	dbg("whiteheat_set_termios port %d", port);
+
+	/* check that they really want us to change something */
+	if (old_termios) {
+		if ((cflag == old_termios->c_cflag) &&
+		    (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
+			dbg("nothing to change...");
+			return;
+		}
+	}
+
+	/* do the parsing of the cflag to see what to set the line to */
+	/* FIXME!! */
+
+	return;
+}
+
 static void whiteheat_throttle (struct tty_struct * tty)
 {
 	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
@@ -813,25 +937,96 @@
 /******************************************************************************
  * FTDI SIO Serial Converter specific driver functions
  ******************************************************************************/
+
+/* Bill Ryder - bryder@sgi.com - wrote the FTDI_SIO implementation */
+/* Thanx to FTDI for so kindly providing details of the protocol required */
+/*   to talk to the device */
+
+#include "ftdi_sio.h"
+
 static int  ftdi_sio_serial_open (struct tty_struct *tty, struct file *filp)
 {
 	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
+	char buf[1]; /* Needed for the usb_control_msg I think */
 	int port = MINOR(tty->device) - serial->minor;
 
-	dbg("ftdi_serial_open port %d", port);
+	dbg("ftdi_sio_serial_open port %d", port);
 
 	if (serial->active[port]) {
 		dbg ("device already open");
 		return -EINVAL;
 	}
 	serial->active[port] = 1;
- 
+
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, 
+			FTDI_SIO_RESET_SIO, 
+			0, buf, 0, HZ * 5);
+
+	/* FIXME - Should I really purge the buffers? */
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, 
+			FTDI_SIO_RESET_PURGE_RX, 
+			0, buf, 0, HZ * 5);
+
+	usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, 
+			FTDI_SIO_RESET_PURGE_TX, 
+			0, buf, 0, HZ * 5);	
+
+
+	/* As per usb_serial_init s/be CS8, B9600, 1 STOP BIT */
+	if ( usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			     FTDI_SIO_SET_BAUDRATE_REQUEST,
+			     FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
+			     ftdi_sio_b9600, 0, 
+			     buf, 0, HZ * 5) < 0){
+		dbg("Error from baudrate urb");
+		return(-EINVAL);
+	}
+
+	if ( usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			     FTDI_SIO_SET_DATA_REQUEST, 
+			     FTDI_SIO_SET_DATA_REQUEST_TYPE,
+			     8 | FTDI_SIO_SET_DATA_PARITY_NONE | 
+			     FTDI_SIO_SET_DATA_STOP_BITS_1, 0,
+			     buf, 0, HZ * 5) < 0){
+		dbg("Error from cs8/noparity/1 stopbit urb");
+		return(-EINVAL);
+	}
+
+	/* Disable flow control */
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
+			    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+			    0, 0, 
+			    buf, 0, HZ * 5) < 0) {
+		dbg("error from flowcontrol urb");
+		return(-EINVAL);
+	}	    
+
+	/* Turn on RTS and DTR since we are not flow controlling*/
+	/* FIXME - check for correct behaviour clocal vs non clocal */
+	/* FIXME - might be able to do both simultaneously */
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+			    (unsigned)FTDI_SIO_SET_DTR_HIGH, 0, 
+			    buf, 0, HZ * 5) < 0) {
+		dbg("Error from DTR HIGH urb");
+	}
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+			    (unsigned)FTDI_SIO_SET_RTS_HIGH, 0, 
+			    buf, 0, HZ * 5) < 0) {
+		dbg("Error from RTS HIGH urb");
+	}
+	
 	/*Start reading from the device*/
 	if (usb_submit_urb(&serial->read_urb[port]))
 		dbg("usb_submit_urb(read bulk) failed");
 
-	/* Need to do device specific setup here (control lines, baud rate, etc.) */
-	/* FIXME!!! */
 
 	return (0);
 }
@@ -840,13 +1035,29 @@
 static void ftdi_sio_serial_close (struct tty_struct *tty, struct file *filp)
 {
 	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
+	char buf[1];
 	int port = MINOR(tty->device) - serial->minor;
 
-	dbg("ftdi_serial_close port %d", port);
-	
-	/* Need to change the control lines here */
-	/* FIXME */
+	dbg("ftdi_sio_serial_close port %d", port);
 	
+	/* FIXME - might be able to do both simultaneously */
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+			    (unsigned)FTDI_SIO_SET_DTR_LOW, 0, 
+			    buf, 0, HZ * 5) < 0) {
+		dbg("Error from DTR LOW urb");
+	}
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+			    FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+			    (unsigned)FTDI_SIO_SET_RTS_LOW, 0, 
+			    buf, 0, HZ * 5) < 0) {
+		dbg("Error from RTS LOW urb");
+	}	
+
+	/* FIXME Should I flush the device here? - not doing it for now */
+
 	/* shutdown our bulk reads and writes */
 	usb_unlink_urb (&serial->write_urb[port]);
 	usb_unlink_urb (&serial->read_urb[port]);
@@ -854,6 +1065,290 @@
 }
 
 
+  
+/* The ftdi_sio requires the first byte to have:
+   B0 1
+   B1 0
+   B2..7 length of message excluding byte 0
+*/
+static int ftdi_sio_serial_write (struct tty_struct * tty, int from_user, 
+				  const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
+	int port = MINOR(tty->device) - serial->minor;
+	const int data_offset = 1;
+
+	dbg("ftdi_sio_serial_write port %d, %d bytes", port, count);
+
+	if (count == 0) {
+		dbg("write request of 0 bytes");
+		return (0);
+	}
+
+	/* only do something if we have a bulk out endpoint */
+	if (serial->num_bulk_out) {
+		unsigned char *first_byte = serial->write_urb[port].transfer_buffer;
+
+		if (serial->write_urb[port].status == -EINPROGRESS) {
+			dbg ("already writing");
+			return (0);
+		}
+
+		count += data_offset;
+		count = (count > serial->bulk_out_size[port]) ? 
+			serial->bulk_out_size[port] : count;
+
+
+		/* Copy in the data to send */
+		if (from_user) {
+			copy_from_user(serial->write_urb[port].transfer_buffer + data_offset , 
+				       buf, count - data_offset );
+		}
+		else {
+			memcpy(serial->write_urb[port].transfer_buffer + data_offset,
+			       buf, count - data_offset );
+		}  
+
+		/* Write the control byte at the front of the packet*/
+		first_byte = serial->write_urb[port].transfer_buffer;
+		*first_byte = 1 | ((count-data_offset) << 2) ; 
+
+#ifdef DEBUG
+		dbg("Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]);
+
+		if (count) {
+			int i;
+			printk (KERN_DEBUG __FILE__ ": data written - length = %d, data = ", count);
+			for (i = 0; i < count; ++i) {
+				printk ("0x%02x ", first_byte[i]);
+				if (first_byte[i] > ' ' && first_byte[i] < '~') {
+					printk("%c ", first_byte[i]);
+				} else {
+					printk("  ");
+				}
+			}
+
+		     
+			printk ("\n");
+		}
+
+#endif
+
+
+		/* send the data out the bulk port */
+		serial->write_urb[port].transfer_buffer_length = count;
+
+		if (usb_submit_urb(&serial->write_urb[port]))
+			dbg("usb_submit_urb(write bulk) failed");
+
+		dbg("write returning: %d",count - data_offset);
+		return (count - data_offset);
+	}
+	
+	/* no bulk out, so return 0 bytes written */
+	return (0);
+} 
+
+
+static void ftdi_sio_read_bulk_callback (struct urb *urb)
+{ /* ftdi_sio_serial_buld_callback */
+	struct usb_serial *serial = (struct usb_serial *)urb->context;
+       	struct tty_struct *tty = serial->tty; 
+       	unsigned char *data = urb->transfer_buffer;
+	const int data_offset = 2;
+	int i;
+
+	dbg("ftdi_sio_read_bulk_callback");
+
+	if (urb->status) {
+		dbg("nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+
+#ifdef DEBUG
+	if (urb->actual_length > 2) {
+		printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length);
+		for (i = 0; i < urb->actual_length; ++i) {
+			printk ("0x%.2x ", data[i]);
+			if (data[i] > ' ' && data[i] < '~') {
+				printk("%c ", data[i]);
+			} else {
+				printk("  ");
+			}
+		}
+		printk ("\n");
+	}
+#endif
+	
+
+	if (urb->actual_length > data_offset) {
+		for (i = data_offset ; i < urb->actual_length ; ++i) {
+			tty_insert_flip_char(tty, data[i], 0);
+	  	}
+	  	tty_flip_buffer_push(tty);
+	}
+
+	/* Continue trying to always read  */
+	if (usb_submit_urb(urb))
+		dbg("failed resubmitting read urb");
+
+	return;
+} /* ftdi_sio_serial_read_bulk_callback */
+
+static void ftdi_sio_set_termios (struct tty_struct *tty, struct termios *old_termios)
+{
+	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
+	int port = MINOR(tty->device) - serial->minor;
+	unsigned int cflag = tty->termios->c_cflag;
+	__u16 urb_value; /* Will hold the new flags */
+	char buf[1]; /* Perhaps I should dynamically alloc this? */
+	dbg("ftdi_sio_set_termios port %d", port);
+
+	/* FIXME - we should keep the old termios really */
+	/* FIXME -For this cut I don't care if the line is really changing or 
+	   not  - so just do the change regardless */
+	
+	/* Set number of data bits, parity, stop bits */
+	
+	urb_value = 0;
+	urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
+		      FTDI_SIO_SET_DATA_STOP_BITS_1);
+	urb_value |= (cflag & PARENB ? 
+		      (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD : 
+		       FTDI_SIO_SET_DATA_PARITY_EVEN) :
+		      FTDI_SIO_SET_DATA_PARITY_NONE);
+	if (cflag & CSIZE) {
+		switch (cflag & CSIZE) {
+		case CS5: urb_value |= 5; dbg("Setting CS5"); break;
+		case CS6: urb_value |= 6; dbg("Setting CS6"); break;
+		case CS7: urb_value |= 7; dbg("Setting CS7"); break;
+		case CS8: urb_value |= 8; dbg("Setting CS8"); break;
+		default:
+			dbg("CSIZE was set but not CS5-CS8");
+		}
+	}
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_DATA_REQUEST, 
+			    FTDI_SIO_SET_DATA_REQUEST_TYPE,
+			    urb_value , 0,
+			    buf, 0, 100) < 0) {
+		dbg("FAILED to set databits/stopbits/parity");
+	}	   
+
+	/* Now do the baudrate */
+	/* FIXME - should drop lines on B0 */
+	/* FIXME Should also handle CLOCAL here  */
+     
+	switch(cflag & CBAUD){
+	case B300: urb_value = ftdi_sio_b300; dbg("Set to 300"); break;
+	case B600: urb_value = ftdi_sio_b600; dbg("Set to 600") ; break;
+	case B1200: urb_value = ftdi_sio_b1200; dbg("Set to 1200") ; break;
+	case B2400: urb_value = ftdi_sio_b2400; dbg("Set to 2400") ; break;
+	case B4800: urb_value = ftdi_sio_b4800; dbg("Set to 4800") ; break;
+	case B9600: urb_value = ftdi_sio_b9600; dbg("Set to 9600") ; break;
+	case B19200: urb_value = ftdi_sio_b19200; dbg("Set to 19200") ; break;
+	case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break;
+	case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break;
+	case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break;
+	default: dbg("FTDI_SIO does not support the baudrate requested"); 
+		/* FIXME - how to return an error for this? */ break;
+	}
+	/* Send the URB */
+	if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			    FTDI_SIO_SET_BAUDRATE_REQUEST, 
+			    FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
+			    urb_value, 0, 
+			    buf, 0, 100) < 0) {
+		dbg("urb failed to set baurdrate");
+	}
+	return;
+}
+
+/*FIXME - the beginnings of this implementation - not even hooked into the driver yet */
+static int ftdi_sio_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
+	int port = MINOR(tty->device) - serial->minor;
+	__u16 urb_value=0; /* Will hold the new flags */
+	char buf[1];
+	int  ret, mask;
+	dbg("ftdi_sio_ioctl port %d", port);
+
+	/* Based on code from acm.c */
+	switch (cmd) {
+
+		case TIOCMGET:
+			/* Request the status from the device */
+			if ((ret = usb_control_msg(serial->dev, 
+					    usb_sndctrlpipe(serial->dev, 0),
+					    FTDI_SIO_GET_MODEM_STATUS_REQUEST, 
+					    FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
+					    0, 0, 
+					    buf, 1, HZ * 5)) < 0 ) {
+				dbg("Get not get modem status of device");
+				return(ret);
+			}
+
+			return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
+					(buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
+					(buf[0]  & FTDI_SIO_RI_MASK  ? TIOCM_RI  : 0) |
+					(buf[0]  & FTDI_SIO_RLSD_MASK ? TIOCM_CD  : 0),
+					(unsigned long *) arg);
+			break;
+
+		case TIOCMSET:
+		case TIOCMBIS:
+		case TIOCMBIC:
+			if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
+
+			/* FIXME Need to remember if we have set DTR or RTS since we
+			   can't ask the device  */
+			/* FIXME - also need to find the meaning of TIOCMBIS/BIC/SET */
+			if (mask & TIOCM_DTR) {
+				switch(cmd) {
+				case TIOCMSET:
+					urb_value = FTDI_SIO_SET_DTR_HIGH | FTDI_SIO_SET_RTS_LOW;
+					break;
+
+				case TIOCMBIS:
+					/* Will leave RTS alone and set DTR */
+					urb_value =  FTDI_SIO_SET_DTR_HIGH;
+					break;
+					
+				case TIOCMBIC:
+					urb_value = FTDI_SIO_SET_DTR_LOW;
+					break;
+				}
+			}
+
+			if (mask & TIOCM_RTS) {
+				switch(cmd) {
+				case TIOCMSET:
+					urb_value = FTDI_SIO_SET_DTR_LOW | FTDI_SIO_SET_RTS_HIGH;
+					break;
+
+				case TIOCMBIS:
+					/* Will leave DTR and set RTS */
+					urb_value = FTDI_SIO_SET_RTS_HIGH;
+					break;
+
+				case TIOCMBIC:
+					/* Will unset RTS */
+					urb_value = FTDI_SIO_SET_RTS_LOW;
+					break;
+				}
+			}	
+
+			
+			return(usb_control_msg(serial->dev, 
+					       usb_sndctrlpipe(serial->dev, 0),
+					       FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
+					       FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+					       urb_value , 0,
+					       buf, 0, HZ * 5));
+	}
+}
+
 #endif	/* CONFIG_USB_SERIAL_FTDI_SIO */
 
 
@@ -1041,6 +1536,34 @@
 }
 
 
+static void generic_throttle (struct tty_struct *tty) 
+{
+	/* do nothing for the generic device */
+	return;
+}
+
+
+static void generic_unthrottle (struct tty_struct *tty) 
+{
+	/* do nothing for the generic device */
+	return;
+}
+
+
+static int generic_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	/* generic driver doesn't support any ioctls yet */
+	return -ENOIOCTLCMD;
+}
+
+
+static void generic_set_termios (struct tty_struct *tty, struct termios * old)
+{
+	/* generic driver doesn't really care about setting any line settings */
+	return;
+}
+
+
 static void generic_read_bulk_callback (struct urb *urb)
 {
 	struct usb_serial *serial = (struct usb_serial *)urb->context;
@@ -1059,7 +1582,7 @@
 	if (urb->actual_length) {
 		printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length);
 		for (i = 0; i < urb->actual_length; ++i) {
-			printk ("0x%.2x ", data[i]);
+			printk ("%.2x ", data[i]);
 		}
 		printk ("\n");
 	}
@@ -1352,8 +1875,8 @@
 	put_char:		NULL,
 	flush_chars:		NULL,
 	write_room:		serial_write_room,
-	ioctl:			NULL,
-	set_termios:		NULL,
+	ioctl:			serial_ioctl,
+	set_termios:		serial_set_termios,
 	set_ldisc:		NULL, 
 	throttle:		serial_throttle,
 	unthrottle:		serial_unthrottle,
@@ -1397,19 +1920,14 @@
 }
 
 
-#ifdef MODULE
-int init_module(void)
-{
-	return usb_serial_init();
-}
-
-void cleanup_module(void)
+void usb_serial_exit(void)
 {
 	tty_unregister_driver(&serial_tty_driver);
 	usb_deregister(&usb_serial_driver);
 }
 
-#else
-__initcall(usb_serial_init);
-#endif
+
+module_init(usb_serial_init);
+module_exit(usb_serial_exit);
+
 

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