summaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial/ftdi_sio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/serial/ftdi_sio.c')
-rw-r--r--drivers/usb/serial/ftdi_sio.c127
1 files changed, 91 insertions, 36 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index be84587..edd162d 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -73,7 +73,6 @@ struct ftdi_private {
char prev_status; /* Used for TIOCMIWAIT */
bool dev_gone; /* Used to abort TIOCMIWAIT */
char transmit_empty; /* If transmitter is empty or not */
- struct usb_serial_port *port;
__u16 interface; /* FT2232C, FT2232H or FT4232H port interface
(0 for FT232/245) */
@@ -192,6 +191,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_THROTTLE_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GATEWAY_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GBM_PID) },
+ { USB_DEVICE(NEWPORT_VID, NEWPORT_AGILIS_PID) },
{ USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) },
{ USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SPROG_II) },
@@ -584,6 +584,7 @@ static struct usb_device_id id_table_combined [] = {
/*
* ELV devices:
*/
+ { USB_DEVICE(FTDI_ELV_VID, FTDI_ELV_WS300_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) },
@@ -670,6 +671,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) },
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) },
{ USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_OMNI1509) },
{ USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_MHAM_KW_PID) },
@@ -875,6 +877,8 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) },
+ /* Crucible Devices */
+ { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
@@ -923,6 +927,9 @@ static int ftdi_get_icount(struct tty_struct *tty,
static int ftdi_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
static void ftdi_break_ctl(struct tty_struct *tty, int break_state);
+static int ftdi_chars_in_buffer(struct tty_struct *tty);
+static int ftdi_get_modem_status(struct tty_struct *tty,
+ unsigned char status[2]);
static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base);
static unsigned short int ftdi_232am_baud_to_divisor(int baud);
@@ -957,6 +964,7 @@ static struct usb_serial_driver ftdi_sio_device = {
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
+ .chars_in_buffer = ftdi_chars_in_buffer,
};
static struct usb_serial_driver * const serial_drivers[] = {
@@ -1090,6 +1098,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
__func__,
(set & TIOCM_DTR) ? "HIGH" : (clear & TIOCM_DTR) ? "LOW" : "unchanged",
(set & TIOCM_RTS) ? "HIGH" : (clear & TIOCM_RTS) ? "LOW" : "unchanged");
+ rv = usb_translate_errors(rv);
} else {
dev_dbg(dev, "%s - DTR %s, RTS %s\n", __func__,
(set & TIOCM_DTR) ? "HIGH" : (clear & TIOCM_DTR) ? "LOW" : "unchanged",
@@ -1682,7 +1691,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
kref_init(&priv->kref);
mutex_init(&priv->cfg_lock);
- memset(&priv->icount, 0x00, sizeof(priv->icount));
init_waitqueue_head(&priv->delta_msr_wait);
priv->flags = ASYNC_LOW_LATENCY;
@@ -1691,7 +1699,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
if (quirk && quirk->port_probe)
quirk->port_probe(priv);
- priv->port = port;
usb_set_serial_port_data(port, priv);
ftdi_determine_type(port);
@@ -1781,7 +1788,7 @@ static int ftdi_8u2232c_probe(struct usb_serial *serial)
struct usb_device *udev = serial->dev;
if ((udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) ||
- (udev->product && !strcmp(udev->product, "BeagleBone/XDS100")))
+ (udev->product && !strcmp(udev->product, "BeagleBone/XDS100V2")))
return ftdi_jtag_probe(serial);
return 0;
@@ -1879,24 +1886,22 @@ static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
- mutex_lock(&port->serial->disc_mutex);
- if (!port->serial->disconnected) {
- /* Disable flow control */
- if (!on && usb_control_msg(port->serial->dev,
+ /* Disable flow control */
+ if (!on) {
+ 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, NULL, 0,
WDR_TIMEOUT) < 0) {
- dev_err(&port->dev, "error from flowcontrol urb\n");
+ dev_err(&port->dev, "error from flowcontrol urb\n");
}
- /* drop RTS and DTR */
- if (on)
- set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
- else
- clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
}
- mutex_unlock(&port->serial->disc_mutex);
+ /* drop RTS and DTR */
+ if (on)
+ set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ else
+ clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
}
/*
@@ -1955,9 +1960,8 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
#define FTDI_RS_ERR_MASK (FTDI_RS_BI | FTDI_RS_PE | FTDI_RS_FE | FTDI_RS_OE)
-static int ftdi_process_packet(struct tty_struct *tty,
- struct usb_serial_port *port, struct ftdi_private *priv,
- char *packet, int len)
+static int ftdi_process_packet(struct usb_serial_port *port,
+ struct ftdi_private *priv, char *packet, int len)
{
int i;
char status;
@@ -2007,7 +2011,7 @@ static int ftdi_process_packet(struct tty_struct *tty,
/* Overrun is special, not associated with a char */
if (packet[1] & FTDI_RS_OE) {
priv->icount.overrun++;
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
}
}
@@ -2026,10 +2030,10 @@ static int ftdi_process_packet(struct tty_struct *tty,
if (port->port.console && port->sysrq) {
for (i = 0; i < len; i++, ch++) {
if (!usb_serial_handle_sysrq_char(port, *ch))
- tty_insert_flip_char(tty, *ch, flag);
+ tty_insert_flip_char(&port->port, *ch, flag);
}
} else {
- tty_insert_flip_string_fixed_flag(tty, ch, flag, len);
+ tty_insert_flip_string_fixed_flag(&port->port, ch, flag, len);
}
return len;
@@ -2038,25 +2042,19 @@ static int ftdi_process_packet(struct tty_struct *tty,
static void ftdi_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- struct tty_struct *tty;
struct ftdi_private *priv = usb_get_serial_port_data(port);
char *data = (char *)urb->transfer_buffer;
int i;
int len;
int count = 0;
- tty = tty_port_tty_get(&port->port);
- if (!tty)
- return;
-
for (i = 0; i < urb->actual_length; i += priv->max_packet_size) {
len = min_t(int, urb->actual_length - i, priv->max_packet_size);
- count += ftdi_process_packet(tty, port, priv, &data[i], len);
+ count += ftdi_process_packet(port, priv, &data[i], len);
}
if (count)
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+ tty_flip_buffer_push(&port->port);
}
static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
@@ -2089,6 +2087,29 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
}
+static int ftdi_chars_in_buffer(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ int chars;
+ unsigned char buf[2];
+ int ret;
+
+ chars = usb_serial_generic_chars_in_buffer(tty);
+ if (chars)
+ goto out;
+
+ /* Check if hardware buffer is empty. */
+ ret = ftdi_get_modem_status(tty, buf);
+ if (ret == 2) {
+ if (!(buf[1] & FTDI_RS_TEMT))
+ chars = 1;
+ }
+out:
+ dev_dbg(&port->dev, "%s - %d\n", __func__, chars);
+
+ return chars;
+}
+
/* old_termios contains the original termios settings and tty->termios contains
* the new setting to be used
* WARNING: set_termios calls this with old_termios in kernel space
@@ -2272,7 +2293,14 @@ no_c_cflag_changes:
}
}
-static int ftdi_tiocmget(struct tty_struct *tty)
+/*
+ * Get modem-control status.
+ *
+ * Returns the number of status bytes retrieved (device dependant), or
+ * negative error code.
+ */
+static int ftdi_get_modem_status(struct tty_struct *tty,
+ unsigned char status[2])
{
struct usb_serial_port *port = tty->driver_data;
struct ftdi_private *priv = usb_get_serial_port_data(port);
@@ -2312,16 +2340,43 @@ static int ftdi_tiocmget(struct tty_struct *tty)
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, priv->interface,
buf, len, WDR_TIMEOUT);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(&port->dev, "failed to get modem status: %d\n", ret);
+ ret = usb_translate_errors(ret);
goto out;
+ }
- ret = (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) |
- priv->last_dtr_rts;
+ status[0] = buf[0];
+ if (ret > 1)
+ status[1] = buf[1];
+ else
+ status[1] = 0;
+
+ dev_dbg(&port->dev, "%s - 0x%02x%02x\n", __func__, status[0],
+ status[1]);
out:
kfree(buf);
+
+ return ret;
+}
+
+static int ftdi_tiocmget(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct ftdi_private *priv = usb_get_serial_port_data(port);
+ unsigned char buf[2];
+ int ret;
+
+ ret = ftdi_get_modem_status(tty, buf);
+ if (ret < 0)
+ return ret;
+
+ ret = (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) |
+ priv->last_dtr_rts;
+
return ret;
}
OpenPOWER on IntegriCloud