Newer
Older
if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
FTDI_SIO_SET_DATA_REQUEST_TYPE,
urb_value , priv->interface,
buf, 0, WDR_TIMEOUT) < 0) {
err("%s FAILED to enable/disable break state (state was %d)", __func__,break_state);
dbg("%s break state is %d - urb is %d", __func__,break_state, urb_value);
}
/* 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
*/
static void ftdi_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{ /* ftdi_termios */
struct usb_device *dev = port->serial->dev;
struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned int cflag = termios->c_cflag;
__u16 urb_value; /* will hold the new flags */
char buf[1]; /* Perhaps I should dynamically alloc this? */
unsigned int iflag = termios->c_iflag;
/* Force baud rate if this device requires it, unless it is set to B0. */
if (priv->force_baud && ((termios->c_cflag & CBAUD) != B0)) {
dbg("%s: forcing baud rate for this device", __func__);
priv->force_baud);
}
/* Force RTS-CTS if this device requires it. */
if (priv->force_rtscts) {
dbg("%s: forcing rtscts for this device", __func__);
termios->c_cflag |= CRTSCTS;
cflag = termios->c_cflag;
/* FIXME -For this cut I don't care if the line is really changing or
not - so just do the change regardless - should be able to
/* NOTE These routines can get interrupted by
ftdi_sio_read_bulk_callback - need to examine what this
termios->c_cflag &= ~CMSPAR;
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:
err("CSIZE was set but not CS5-CS8");
}
}
/* This is needed by the break command since it uses the same command - but is
* or'ed with this value */
priv->last_set_data_urb_value = urb_value;
FTDI_SIO_SET_DATA_REQUEST_TYPE,
urb_value , priv->interface,
buf, 0, WDR_SHORT_TIMEOUT) < 0) {
err("%s FAILED to set databits/stopbits/parity", __func__);
/* Now do the baudrate */
if ((cflag & CBAUD) == B0 ) {
/* Disable flow control */
if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
err("%s error from disable flowcontrol urb", __func__);
clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
} else {
/* set the baudrate determined before */
err("%s urb failed to set baudrate", __func__);
Peter Favrholdt
committed
}
/* Ensure RTS and DTR are raised when baudrate changed from 0 */
if (!old_termios || (old_termios->c_cflag & CBAUD) == B0) {
Peter Favrholdt
committed
set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
}
}
/* 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", __func__);
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
buf, 0, WDR_TIMEOUT) < 0) {
err("urb failed to set to rts/cts flow control");
/*
* Xon/Xoff code
*
* Check the IXOFF status in the iflag component of the termios structure
* if IXOFF is not set, the pre-xon/xoff code is executed.
*/
if (iflag & IXOFF) {
dbg("%s request to enable xonxoff iflag=%04x",__func__,iflag);
// Try to enable the XON/XOFF on the ftdi_sio
// Set the vstart and vstop -- could have been done up above where
// a lot of other dereferencing is done but that would be very
// inefficient as vstart and vstop are not always needed
vstart = termios->c_cc[VSTART];
vstop = termios->c_cc[VSTOP];
urb_value=(vstop << 8) | (vstart);
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
| priv->interface),
buf, 0, WDR_TIMEOUT) < 0) {
err("urb failed to set to xon/xoff flow control");
}
} else {
/* 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", __func__);
buf, 0, WDR_TIMEOUT) < 0) {
err("urb failed to clear flow control");
static int ftdi_tiocmget(struct tty_struct *tty, struct file *file)
struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned char buf[2];
int ret;
dbg("%s TIOCMGET", __func__);
switch (priv->chip_type) {
case SIO:
/* Request the status from the device */
if ((ret = usb_control_msg(port->serial->dev,
err("%s Could not get modem status of device - err: %d", __func__,
ret);
return(ret);
}
break;
case FT8U232AM:
case FT232BM:
case FT2232C:
case FT232RL:
/* 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(port->serial->dev,
err("%s Could not get modem status of device - err: %d", __func__,
ret);
return(ret);
}
break;
default:
return -EFAULT;
break;
}
return (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) |
static int ftdi_tiocmset(struct tty_struct *tty, struct file * file,
unsigned int set, unsigned int clear)
dbg("%s TIOCMSET", __func__);
return update_mctrl(port, set, clear);
static int ftdi_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
struct ftdi_private *priv = usb_get_serial_port_data(port);
dbg("%s cmd 0x%04x", __func__, cmd);
/* Based on code from acm.c and others */
switch (cmd) {
case TIOCGSERIAL: /* gets serial port data */
return get_serial_info(port, (struct serial_struct __user *) arg);
case TIOCSSERIAL: /* sets serial port data */
return set_serial_info(tty, port, (struct serial_struct __user *) arg);
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was.
*
* This code is borrowed from linux/drivers/char/serial.c
*/
case TIOCMIWAIT:
while (priv != NULL) {
interruptible_sleep_on(&priv->delta_msr_wait);
/* see if a signal did it */
if (signal_pending(current))
return -ERESTARTSYS;
else {
char diff = priv->diff_status;
if (diff == 0) {
return -EIO; /* no change => error */
}
/* Consume all events */
priv->diff_status = 0;
/* Return 0 if caller wanted to know about these bits */
if ( ((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) ||
((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) ||
((arg & TIOCM_CD) && (diff & FTDI_RS0_RLSD)) ||
((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS)) ) {
return 0;
}
/*
* Otherwise caller can't care less about what happened,
* and so we continue to wait for more events.
*/
}
}
/* This is not necessarily an error - turns out the higher layers will do
dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __func__, cmd);
static void ftdi_throttle(struct tty_struct *tty)
struct ftdi_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
dbg("%s - port %d", __func__, port->number);
spin_lock_irqsave(&priv->rx_lock, flags);
priv->rx_flags |= THROTTLED;
spin_unlock_irqrestore(&priv->rx_lock, flags);
}
static void ftdi_unthrottle(struct tty_struct *tty)
struct ftdi_private *priv = usb_get_serial_port_data(port);
int actually_throttled;
unsigned long flags;
dbg("%s - port %d", __func__, port->number);
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);
if (actually_throttled)
schedule_delayed_work(&priv->rx_work, 0);
}
static int __init ftdi_init (void)
{
int retval;
if (vendor > 0 && product > 0) {
/* Add user specified VID/PID to reserved element of table. */
int i;
for (i = 0; id_table_combined[i].idVendor; i++)
;
id_table_combined[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
id_table_combined[i].idVendor = vendor;
id_table_combined[i].idProduct = product;
}
retval = usb_serial_register(&ftdi_sio_device);
goto failed_sio_register;
goto failed_usb_register;
info(DRIVER_VERSION ":" DRIVER_DESC);
return 0;
failed_usb_register:
usb_serial_deregister(&ftdi_sio_device);
failed_sio_register:
return retval;
}
static void __exit ftdi_exit (void)
{
usb_serial_deregister (&ftdi_sio_device);
}
module_init(ftdi_init);
module_exit(ftdi_exit);
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
module_param(vendor, ushort, 0);
MODULE_PARM_DESC(vendor, "User specified vendor ID (default="
__MODULE_STRING(FTDI_VID)")");
module_param(product, ushort, 0);
MODULE_PARM_DESC(vendor, "User specified product ID");