* See http://ftdi-usb-sio.sourceforge.net for upto date testing info
* and extra documentation
*
+ * (21/Jul/2004) Ian Abbott
+ * Incorporated Steven Turner's code to add support for the FT2232C chip.
+ * The prelimilary port to the 2.6 kernel was by Rus V. Brushkoff. I have
+ * fixed a couple of things.
+ *
+ * (27/May/2004) Ian Abbott
+ * Improved throttling code, mostly stolen from the WhiteHEAT driver.
+ *
* (26/Mar/2004) Jan Capek
* Added PID's for ICD-U20/ICD-U40 - incircuit PIC debuggers from CCS Inc.
*
#include <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/serial.h>
-#ifdef CONFIG_USB_SERIAL_DEBUG
- static int debug = 1;
-#else
- static int debug;
-#endif
-
#include "usb-serial.h"
#include "ftdi_sio.h"
/*
* Version Information
*/
-#define DRIVER_VERSION "v1.4.0"
+#define DRIVER_VERSION "v1.4.1"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>"
#define DRIVER_DESC "USB FTDI Serial Converters Driver"
+static int debug;
+
static struct usb_device_id id_table_sio [] = {
{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
{ } /* Terminating entry */
{ USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0, 0x3ff) },
+ { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) },
+ { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) },
{ USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_634_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_4, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_ELV_UO100_PID, 0, 0x3ff) },
{ USB_DEVICE_VER(FTDI_VID, INSIDE_ACCESSO, 0, 0x3ff) },
+ { USB_DEVICE_VER(INTREPID_VID, INTREPID_VALUECAN_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0, 0x3ff) },
+ { USB_DEVICE_VER(FTDI_VID, EVER_ECO_PRO_CDS, 0, 0x3ff) },
{ } /* Terminating entry */
};
{ USB_DEVICE_VER(FTDI_VID, LINX_FUTURE_2_PID, 0x400, 0xffff) },
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) },
+ { USB_DEVICE_VER(FTDI_VID, INSIDE_ACCESSO, 0x400, 0xffff) },
+ { USB_DEVICE_VER(INTREPID_VID, INTREPID_VALUECAN_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(INTREPID_VID, INTREPID_NEOVI_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FALCOM_VID, FALCOM_TWIST_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FTDI_VID, FTDI_SUUNTO_SPORTS_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FTDI_RM_VID, FTDI_RMCANVIEW_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USOTL4_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USTL4_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(BANDB_VID, BANDB_USO9ML2_PID, 0x400, 0xffff) },
+ { USB_DEVICE_VER(FTDI_VID, EVER_ECO_PRO_CDS, 0x400, 0xffff) },
{ } /* Terminating entry */
};
};
+static struct usb_device_id id_table_FT2232C[] = {
+ { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
+ { } /* Terminating entry */
+};
+
+
static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
+ { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) },
+ { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_XF_547_PID) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_5_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_MTXORB_6_PID, 0x400, 0xffff) },
{ USB_DEVICE_VER(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID, 0x400, 0xffff) },
+ { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) },
{ USB_DEVICE(FTDI_VID, INSIDE_ACCESSO) },
+ { USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) },
+ { USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) },
+ { USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) },
+ { USB_DEVICE(FTDI_RM_VID, FTDI_RMCANVIEW_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) },
+ { USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) },
{ } /* Terminating entry */
};
#define BUFSZ 512
#define PKTSZ 64
+/* rx_flags */
+#define THROTTLED 0x01
+#define ACTUALLY_THROTTLED 0x02
+
struct ftdi_private {
ftdi_chip_type_t chip_type;
/* type of the device, either SIO or FT8U232AM */
unsigned long last_dtr_rts; /* saved modem control outputs */
wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
char prev_status, diff_status; /* Used for TIOCMIWAIT */
+ __u8 rx_flags; /* receive state flags (throttling) */
+ spinlock_t rx_lock; /* spinlock for receive state */
+
+ __u16 interface; /* FT2232C port interface (0 for FT232/245) */
int force_baud; /* if non-zero, force the baud rate to this value */
int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */
static int ftdi_SIO_startup (struct usb_serial *serial);
static int ftdi_8U232AM_startup (struct usb_serial *serial);
static int ftdi_FT232BM_startup (struct usb_serial *serial);
+static int ftdi_FT2232C_startup (struct usb_serial *serial);
static int ftdi_USB_UIRT_startup (struct usb_serial *serial);
static int ftdi_HE_TIRA1_startup (struct usb_serial *serial);
static void ftdi_shutdown (struct usb_serial *serial);
static int ftdi_open (struct usb_serial_port *port, struct file *filp);
static void ftdi_close (struct usb_serial_port *port, struct file *filp);
-static int ftdi_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static int ftdi_write (struct usb_serial_port *port, const unsigned char *buf, int count);
static int ftdi_write_room (struct usb_serial_port *port);
static int ftdi_chars_in_buffer (struct usb_serial_port *port);
static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
+static void ftdi_process_read (struct usb_serial_port *port);
static void ftdi_set_termios (struct usb_serial_port *port, struct termios * old);
static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file);
static int ftdi_tiocmset (struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear);
.shutdown = ftdi_shutdown,
};
+static struct usb_serial_device_type ftdi_FT2232C_device = {
+ .owner = THIS_MODULE,
+ .name = "FTDI FT2232C Compatible",
+ .id_table = id_table_FT2232C,
+ .num_interrupt_in = 0,
+ .num_bulk_in = 1,
+ .num_bulk_out = 1,
+ .num_ports = 1,
+ .open = ftdi_open,
+ .close = ftdi_close,
+ .throttle = ftdi_throttle,
+ .unthrottle = ftdi_unthrottle,
+ .write = ftdi_write,
+ .write_room = ftdi_write_room,
+ .chars_in_buffer = ftdi_chars_in_buffer,
+ .read_bulk_callback = ftdi_read_bulk_callback,
+ .write_bulk_callback = ftdi_write_bulk_callback,
+ .tiocmget = ftdi_tiocmget,
+ .tiocmset = ftdi_tiocmset,
+ .ioctl = ftdi_ioctl,
+ .set_termios = ftdi_set_termios,
+ .break_ctl = ftdi_break_ctl,
+ .attach = ftdi_FT2232C_startup,
+ .shutdown = ftdi_shutdown,
+};
+
static struct usb_serial_device_type ftdi_USB_UIRT_device = {
.owner = THIS_MODULE,
.name = "USB-UIRT Infrared Tranceiver",
usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_MODEM_CTRL_REQUEST,
FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- ftdi_high_or_low, 0,
+ ftdi_high_or_low, priv->interface,
buf, 0, WDR_TIMEOUT);
kfree(buf);
usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_MODEM_CTRL_REQUEST,
FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- ftdi_high_or_low, 0,
+ ftdi_high_or_low, priv->interface,
buf, 0, WDR_TIMEOUT);
kfree(buf);
static int change_speed(struct usb_serial_port *port)
{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
char *buf;
__u16 urb_value;
__u16 urb_index;
urb_index_value = get_ftdi_divisor(port);
urb_value = (__u16)urb_index_value;
urb_index = (__u16)(urb_index_value >> 16);
+ if (priv->interface) { /* FT2232C */
+ urb_index = (__u16)((urb_index << 8) | priv->interface);
+ }
rv = usb_control_msg(port->serial->dev,
usb_sndctrlpipe(port->serial->dev, 0),
}
break;
case FT232BM: /* FT232BM chip */
- chip_name = "FT232BM";
+ case FT2232C: /* FT2232C chip */
+ if (priv->chip_type == FT2232C) {
+ chip_name = "FT2232C";
+ } else {
+ chip_name = "FT232BM";
+ }
if (baud <= 3000000) {
div_value = ftdi_232bm_baud_to_divisor(baud);
} else {
}
-static int get_serial_info(struct usb_serial_port * port, struct serial_struct * retinfo)
+static int get_serial_info(struct usb_serial_port * port, struct serial_struct __user * retinfo)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct serial_struct tmp;
} /* get_serial_info */
-static int set_serial_info(struct usb_serial_port * port, struct serial_struct * newinfo)
+static int set_serial_info(struct usb_serial_port * port, struct serial_struct __user * newinfo)
{ /* set_serial_info */
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct serial_struct new_serial;
goto check_and_exit;
}
- if ((new_serial.baud_base != priv->baud_base) ||
+ if ((new_serial.baud_base != priv->baud_base) &&
(new_serial.baud_base < 9600))
return -EINVAL;
} /* set_serial_info */
+
+/*
+ * ***************************************************************************
+ * Sysfs Attribute
+ * ***************************************************************************
+ */
+
+ssize_t show_latency_timer(struct device *dev, char *buf)
+{
+ struct usb_serial_port *port = to_usb_serial_port(dev);
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_device *udev;
+ unsigned short latency = 0;
+ int rv = 0;
+
+ udev = to_usb_device(dev);
+
+ dbg("%s",__FUNCTION__);
+
+ rv = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
+ FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE,
+ 0, priv->interface,
+ (char*) &latency, 1, WDR_TIMEOUT);
+
+ if (rv < 0) {
+ dev_err(dev, "Unable to read latency timer: %i", rv);
+ return -EIO;
+ }
+ return sprintf(buf, "%i\n", latency);
+}
+
+/* Write a new value of the latency timer, in units of milliseconds. */
+ssize_t store_latency_timer(struct device *dev, const char *valbuf, size_t count)
+{
+ struct usb_serial_port *port = to_usb_serial_port(dev);
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_device *udev;
+ char buf[1];
+ int v = simple_strtoul(valbuf, NULL, 10);
+ int rv = 0;
+
+ udev = to_usb_device(dev);
+
+ dbg("%s: setting latency timer = %i", __FUNCTION__, v);
+
+ rv = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
+ v, priv->interface,
+ buf, 0, WDR_TIMEOUT);
+
+ if (rv < 0) {
+ dev_err(dev, "Unable to write latency timer: %i", rv);
+ return -EIO;
+ }
+
+ return count;
+}
+
+/* Write an event character directly to the FTDI register. The ASCII
+ value is in the low 8 bits, with the enable bit in the 9th bit. */
+ssize_t store_event_char(struct device *dev, const char *valbuf, size_t count)
+{
+ struct usb_serial_port *port = to_usb_serial_port(dev);
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct usb_device *udev;
+ char buf[1];
+ int v = simple_strtoul(valbuf, NULL, 10);
+ int rv = 0;
+
+ udev = to_usb_device(dev);
+
+ dbg("%s: setting event char = %i", __FUNCTION__, v);
+
+ rv = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_SET_EVENT_CHAR_REQUEST,
+ FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE,
+ v, priv->interface,
+ buf, 0, WDR_TIMEOUT);
+
+ if (rv < 0) {
+ dbg("Unable to write event character: %i", rv);
+ return -EIO;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(latency_timer, S_IWUGO | S_IRUGO, show_latency_timer, store_latency_timer);
+static DEVICE_ATTR(event_char, S_IWUGO, NULL, store_event_char);
+
+void create_sysfs_attrs(struct usb_serial *serial)
+{
+ struct ftdi_private *priv;
+ struct usb_device *udev;
+
+ dbg("%s",__FUNCTION__);
+
+ priv = usb_get_serial_port_data(serial->port[0]);
+ udev = serial->dev;
+
+ if (priv->chip_type == FT232BM) {
+ dbg("sysfs attributes for FT232BM");
+ device_create_file(&udev->dev, &dev_attr_event_char);
+ device_create_file(&udev->dev, &dev_attr_latency_timer);
+ }
+}
+
+void remove_sysfs_attrs(struct usb_serial *serial)
+{
+ struct ftdi_private *priv;
+ struct usb_device *udev;
+
+ dbg("%s",__FUNCTION__);
+
+ priv = usb_get_serial_port_data(serial->port[0]);
+ udev = serial->dev;
+
+ if (priv->chip_type == FT232BM) {
+ device_remove_file(&udev->dev, &dev_attr_event_char);
+ device_remove_file(&udev->dev, &dev_attr_latency_timer);
+ }
+
+}
+
/*
* ***************************************************************************
* FTDI driver specific functions
}
memset(priv, 0, sizeof(*priv));
+ spin_lock_init(&priv->rx_lock);
init_waitqueue_head(&priv->delta_msr_wait);
/* This will push the characters through immediately rather
than queue a task to deliver them */
priv->chip_type = FT232BM;
priv->baud_base = 48000000 / 2; /* Would be / 16, but FT232BM supports multiple of 0.125 divisor fractions! */
+ create_sysfs_attrs(serial);
+
return (0);
} /* ftdi_FT232BM_startup */
+/* Startup for the FT2232C chip */
+/* Called from usbserial:serial_probe */
+static int ftdi_FT2232C_startup (struct usb_serial *serial)
+{ /* ftdi_FT2232C_startup */
+ struct ftdi_private *priv;
+ int err;
+ int inter;
+
+ dbg("%s",__FUNCTION__);
+ err = ftdi_common_startup(serial);
+ if (err){
+ return (err);
+ }
+
+ priv = usb_get_serial_port_data(serial->port[0]);
+ priv->chip_type = FT2232C;
+ inter = serial->interface->altsetting->desc.bInterfaceNumber;
+
+ if (inter) {
+ priv->interface = INTERFACE_B;
+ }
+ else {
+ priv->interface = INTERFACE_A;
+ }
+ priv->baud_base = 48000000 / 2; /* Would be / 16, but FT2232C supports multiple of 0.125 divisor fractions! */
+
+ return (0);
+} /* ftdi_FT2232C_startup */
+
/* Startup for the USB-UIRT device, which requires hardwired baudrate (38400 gets mapped to 312500) */
/* Called from usbserial:serial_probe */
static int ftdi_USB_UIRT_startup (struct usb_serial *serial)
dbg("%s", __FUNCTION__);
+ remove_sysfs_attrs(serial);
+
/* all open ports are closed at this point
* (by usbserial.c:__serial_close, which calls ftdi_close)
*/
if (priv) {
- kfree(priv);
usb_set_serial_port_data(port, NULL);
+ kfree(priv);
}
} /* ftdi_shutdown */
static int ftdi_open (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_open */
struct termios tmp_termios;
- struct usb_serial *serial = port->serial;
+ struct usb_device *dev = port->serial->dev;
struct ftdi_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
int result = 0;
char buf[1]; /* Needed for the usb_control_msg I think */
/* No error checking for this (will get errors later anyway) */
/* See ftdi_sio.h for description of what is reset */
- usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
FTDI_SIO_RESET_SIO,
- 0, buf, 0, WDR_TIMEOUT);
+ priv->interface, buf, 0, WDR_TIMEOUT);
/* Termios defaults are set by usb_serial_init. We don't change
port->tty->termios - this would loose speed settings, etc.
err("%s Error from RTS HIGH urb", __FUNCTION__);
}
+ /* Not throttled */
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+
/* Start reading from the device */
- usb_fill_bulk_urb(port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ usb_fill_bulk_urb(port->read_urb, dev,
+ usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
ftdi_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb, GFP_KERNEL);
static void ftdi_close (struct usb_serial_port *port, struct file *filp)
{ /* ftdi_close */
- struct usb_serial *serial;
unsigned int c_cflag = port->tty->termios->c_cflag;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
char buf[1];
dbg("%s", __FUNCTION__);
- serial = get_usb_serial ( port, __FUNCTION__);
- if (!serial)
- return;
-
- if (serial->dev) {
- if (c_cflag & HUPCL){
- /* 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, WDR_TIMEOUT) < 0) {
- err("error from flowcontrol urb");
- }
+ if (c_cflag & HUPCL){
+ /* Disable flow control */
+ if (usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+ 0, priv->interface, buf, 0,
+ WDR_TIMEOUT) < 0) {
+ err("error from flowcontrol urb");
+ }
- /* drop DTR */
- if (set_dtr(port, LOW) < 0){
- err("Error from DTR LOW urb");
- }
- /* drop RTS */
- if (set_rts(port, LOW) < 0) {
- err("Error from RTS LOW urb");
- }
- } /* Note change no line if hupcl is off */
-
- /* shutdown our bulk read */
- if (port->read_urb) {
- if (usb_unlink_urb (port->read_urb) < 0) {
- err("Error unlinking read urb");
- }
+ /* drop DTR */
+ if (set_dtr(port, LOW) < 0){
+ err("Error from DTR LOW urb");
}
-
- } /* if (serial->dev) */
-
-
+ /* drop RTS */
+ if (set_rts(port, LOW) < 0) {
+ err("Error from RTS LOW urb");
+ }
+ } /* Note change no line if hupcl is off */
+
+ /* shutdown our bulk read */
+ if (port->read_urb)
+ usb_kill_urb(port->read_urb);
} /* ftdi_close */
*
* The new devices do not require this byte
*/
-static int ftdi_write (struct usb_serial_port *port, int from_user,
+static int ftdi_write (struct usb_serial_port *port,
const unsigned char *buf, int count)
{ /* ftdi_write */
- struct usb_serial *serial = get_usb_serial ( port, __FUNCTION__);
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct urb *urb;
unsigned char *buffer;
dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count);
if (count == 0) {
- err("write request of 0 bytes");
+ dbg("write request of 0 bytes");
return 0;
}
/* Write the control byte at the front of the packet*/
*first_byte = 1 | ((user_pktsz) << 2);
/* Copy data for packet */
- if (from_user) {
- if (copy_from_user (first_byte + data_offset,
- current_position, user_pktsz)){
- kfree (buffer);
- usb_free_urb (urb);
- return -EFAULT;
- }
- } else {
- memcpy (first_byte + data_offset,
- current_position, user_pktsz);
- }
+ memcpy (first_byte + data_offset,
+ current_position, user_pktsz);
first_byte += user_pktsz + data_offset;
current_position += user_pktsz;
todo -= user_pktsz;
} else {
/* No control byte required. */
/* Copy in the data to send */
- if (from_user) {
- if (copy_from_user (buffer, buf, count)) {
- kfree (buffer);
- usb_free_urb (urb);
- return -EFAULT;
- }
- } else {
- memcpy (buffer, buf, count);
- }
+ memcpy (buffer, buf, count);
}
- usb_serial_debug_data (__FILE__, __FUNCTION__, transfer_size, buffer);
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, transfer_size, buffer);
/* fill the buffer and send it */
- usb_fill_bulk_urb(urb, serial->dev,
- usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+ usb_fill_bulk_urb(urb, port->serial->dev,
+ usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress),
buffer, transfer_size,
ftdi_write_bulk_callback, port);
if (status) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
count = status;
+ kfree (buffer);
}
/* we are done with this urb, so let the host driver
/* free up the transfer buffer, as usb_free_urb() does not do this */
kfree (urb->transfer_buffer);
- if (port_paranoia_check (port, __FUNCTION__))
- return;
-
dbg("%s - port %d", __FUNCTION__, port->number);
if (urb->status) {
static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{ /* ftdi_read_bulk_callback */
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial;
struct tty_struct *tty;
struct ftdi_private *priv;
- char error_flag;
- unsigned char *data = urb->transfer_buffer;
-
- int i;
- int result;
- int need_flip;
- int packet_offset;
if (urb->number_of_packets > 0) {
err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
err("%s transfer_flags %x ", __FUNCTION__,urb->transfer_flags );
}
- dbg("%s", __FUNCTION__);
+ dbg("%s - port %d", __FUNCTION__, port->number);
- if (port_paranoia_check (port, __FUNCTION__)) {
- return;
- }
if (port->open_count <= 0)
return;
- serial = get_usb_serial(port,__FUNCTION__);
- if (!serial){
- dbg("%s - bad serial pointer - exiting",__FUNCTION__);
- return;
- }
-
tty = port->tty;
if (!tty) {
dbg("%s - bad tty pointer - exiting",__FUNCTION__);
}
priv = usb_get_serial_port_data(port);
+ if (!priv) {
+ dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
+ return;
+ }
+
+ if (urb != port->read_urb) {
+ err("%s - Not my urb!", __FUNCTION__);
+ }
if (urb->status) {
/* This will happen at close every time so it is a dbg not an err */
return;
}
+ /* If throttled, delay receive processing until unthrottled. */
+ spin_lock(&priv->rx_lock);
+ if (priv->rx_flags & THROTTLED) {
+ dbg("Deferring read urb processing until unthrottled");
+ priv->rx_flags |= ACTUALLY_THROTTLED;
+ spin_unlock(&priv->rx_lock);
+ return;
+ }
+ spin_unlock(&priv->rx_lock);
+
+ ftdi_process_read(port);
+
+} /* ftdi_read_bulk_callback */
+
+
+static void ftdi_process_read (struct usb_serial_port *port)
+{ /* ftdi_process_read */
+ struct urb *urb;
+ struct tty_struct *tty;
+ struct ftdi_private *priv;
+ char error_flag;
+ unsigned char *data;
+
+ int i;
+ int result;
+ int need_flip;
+ int packet_offset;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (port->open_count <= 0)
+ return;
+
+ tty = port->tty;
+ if (!tty) {
+ dbg("%s - bad tty pointer - exiting",__FUNCTION__);
+ return;
+ }
+
+ priv = usb_get_serial_port_data(port);
+ if (!priv) {
+ dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
+ return;
+ }
+
+ urb = port->read_urb;
+ if (!urb) {
+ dbg("%s - bad read_urb pointer - exiting", __FUNCTION__);
+ return;
+ }
+
+ data = urb->transfer_buffer;
+
/* The first two bytes of every read packet are status */
if (urb->actual_length > 2) {
- usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
} else {
dbg("Status only: %03oo %03oo",data[0],data[1]);
}
/* if the port is closed stop trying to read */
if (port->open_count > 0){
/* Continue trying to always read */
- usb_fill_bulk_urb(port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+ usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
ftdi_read_bulk_callback, port);
}
return;
-} /* ftdi_read_bulk_callback */
+} /* ftdi_process_read */
static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
{
- struct usb_serial *serial = port->serial;
struct ftdi_private *priv = usb_get_serial_port_data(port);
__u16 urb_value = 0;
char buf[1];
}
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_DATA_REQUEST,
FTDI_SIO_SET_DATA_REQUEST_TYPE,
- urb_value , 0,
+ urb_value , priv->interface,
buf, 0, WDR_TIMEOUT) < 0) {
err("%s FAILED to enable/disable break state (state was %d)", __FUNCTION__,break_state);
}
static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{ /* ftdi_termios */
- struct usb_serial *serial = port->serial;
+ struct usb_device *dev = port->serial->dev;
unsigned int cflag = port->tty->termios->c_cflag;
struct ftdi_private *priv = usb_get_serial_port_data(port);
__u16 urb_value; /* will hold the new flags */
* or'ed with this value */
priv->last_set_data_urb_value = urb_value;
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_DATA_REQUEST,
FTDI_SIO_SET_DATA_REQUEST_TYPE,
- urb_value , 0,
+ urb_value , priv->interface,
buf, 0, 100) < 0) {
err("%s FAILED to set databits/stopbits/parity", __FUNCTION__);
}
/* Now do the baudrate */
if ((cflag & CBAUD) == B0 ) {
/* Disable flow control */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0, 0,
+ 0, priv->interface,
buf, 0, WDR_TIMEOUT) < 0) {
err("%s error from disable flowcontrol urb", __FUNCTION__);
}
if (change_speed(port)) {
err("%s urb failed to set baurdrate", __FUNCTION__);
}
+ /* Ensure RTS and DTR are raised */
+ else if (set_dtr(port, HIGH) < 0){
+ err("%s Error from DTR HIGH urb", __FUNCTION__);
+ }
+ else if (set_rts(port, HIGH) < 0){
+ err("%s Error from RTS HIGH urb", __FUNCTION__);
+ }
}
/* Set flow control */
/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
if (cflag & CRTSCTS) {
dbg("%s Setting to CRTSCTS flow control", __FUNCTION__);
- if (usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0 , FTDI_SIO_RTS_CTS_HS,
+ 0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
buf, 0, WDR_TIMEOUT) < 0) {
err("urb failed to set to rts/cts flow control");
}
vstop=port->tty->termios->c_cc[VSTOP];
urb_value=(vstop << 8) | (vstart);
- if (usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- urb_value , FTDI_SIO_XON_XOFF_HS,
+ urb_value , (FTDI_SIO_XON_XOFF_HS
+ | priv->interface),
buf, 0, WDR_TIMEOUT) < 0) {
err("urb failed to set to xon/xoff flow control");
}
/* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */
/* CHECKME Assuming XON/XOFF handled by tty stack - not by device */
dbg("%s Turning off hardware flow control", __FUNCTION__);
- if (usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
+ if (usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0, 0,
+ 0, priv->interface,
buf, 0, WDR_TIMEOUT) < 0) {
err("urb failed to clear flow control");
}
static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
{
- struct usb_serial *serial = port->serial;
struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned char buf[2];
int ret;
switch (priv->chip_type) {
case SIO:
/* Request the status from the device */
- if ((ret = usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev, 0),
+ if ((ret = usb_control_msg(port->serial->dev,
+ usb_rcvctrlpipe(port->serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, 0,
break;
case FT8U232AM:
case FT232BM:
+ case FT2232C:
/* the 8U232AM returns a two byte value (the sio is a 1 byte value) - in the same
format as the data returned from the in point */
- if ((ret = usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev, 0),
+ if ((ret = usb_control_msg(port->serial->dev,
+ usb_rcvctrlpipe(port->serial->dev, 0),
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
- 0, 0,
+ 0, priv->interface,
buf, 2, WDR_TIMEOUT)) < 0 ) {
err("%s Could not get modem status of device - err: %d", __FUNCTION__,
ret);
{
int ret;
+ dbg("%s TIOCMSET", __FUNCTION__);
if (set & TIOCM_DTR){
if ((ret = set_dtr(port, HIGH)) < 0) {
err("Urb to set DTR failed");
case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
dbg("%s TIOCMBIS", __FUNCTION__);
- if (get_user(mask, (unsigned long *) arg))
+ if (get_user(mask, (unsigned long __user *) arg))
return -EFAULT;
if (mask & TIOCM_DTR){
if ((ret = set_dtr(port, HIGH)) < 0) {
case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */
dbg("%s TIOCMBIC", __FUNCTION__);
- if (get_user(mask, (unsigned long *) arg))
+ if (get_user(mask, (unsigned long __user *) arg))
return -EFAULT;
if (mask & TIOCM_DTR){
if ((ret = set_dtr(port, LOW)) < 0){
*/
case TIOCGSERIAL: /* gets serial port data */
- return get_serial_info(port, (struct serial_struct *) arg);
+ return get_serial_info(port, (struct serial_struct __user *) arg);
case TIOCSSERIAL: /* sets serial port data */
- return set_serial_info(port, (struct serial_struct *) arg);
+ return set_serial_info(port, (struct serial_struct __user *) arg);
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
static void ftdi_throttle (struct usb_serial_port *port)
{
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
dbg("%s - port %d", __FUNCTION__, port->number);
- usb_unlink_urb (port->read_urb);
+
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ priv->rx_flags |= THROTTLED;
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
}
static void ftdi_unthrottle (struct usb_serial_port *port)
{
- int result;
- struct usb_serial *serial = port->serial;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ int actually_throttled;
+ unsigned long flags;
dbg("%s - port %d", __FUNCTION__, port->number);
- port->read_urb->dev = serial->dev;
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
+ priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
- usb_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,
- ftdi_read_bulk_callback, port);
-
- result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
- if (result)
- err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+ if (actually_throttled)
+ ftdi_process_read(port);
}
static int __init ftdi_init (void)
retval = usb_serial_register(&ftdi_FT232BM_device);
if (retval)
goto failed_FT232BM_register;
+ retval = usb_serial_register(&ftdi_FT2232C_device);
+ if (retval)
+ goto failed_FT2232C_register;
retval = usb_serial_register(&ftdi_USB_UIRT_device);
if (retval)
goto failed_USB_UIRT_register;
failed_HE_TIRA1_register:
usb_serial_deregister(&ftdi_USB_UIRT_device);
failed_USB_UIRT_register:
+ usb_serial_deregister(&ftdi_FT2232C_device);
+failed_FT2232C_register:
usb_serial_deregister(&ftdi_FT232BM_device);
failed_FT232BM_register:
usb_serial_deregister(&ftdi_8U232AM_device);
usb_deregister (&ftdi_driver);
usb_serial_deregister (&ftdi_HE_TIRA1_device);
usb_serial_deregister (&ftdi_USB_UIRT_device);
+ usb_serial_deregister (&ftdi_FT2232C_device);
usb_serial_deregister (&ftdi_FT232BM_device);
usb_serial_deregister (&ftdi_8U232AM_device);
usb_serial_deregister (&ftdi_SIO_device);
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
-MODULE_PARM(debug, "i");
+module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");