162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This program is largely derived from the Belkin USB Serial Adapter Driver 862306a36Sopenharmony_ci * (see belkin_sa.[ch]). All of the information about the device was acquired 962306a36Sopenharmony_ci * by using SniffUSB on Windows98. For technical details see mct_u232.h. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * William G. Greathouse and Greg Kroah-Hartman provided great help on how to 1262306a36Sopenharmony_ci * do the reverse engineering and how to write a USB serial device driver. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * TO BE DONE, TO BE CHECKED: 1562306a36Sopenharmony_ci * DTR/RTS signal handling may be incomplete or incorrect. I have mainly 1662306a36Sopenharmony_ci * implemented what I have seen with SniffUSB or found in belkin_sa.c. 1762306a36Sopenharmony_ci * For further TODOs check also belkin_sa.c. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/tty.h> 2462306a36Sopenharmony_ci#include <linux/tty_driver.h> 2562306a36Sopenharmony_ci#include <linux/tty_flip.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/spinlock.h> 2862306a36Sopenharmony_ci#include <linux/uaccess.h> 2962306a36Sopenharmony_ci#include <asm/unaligned.h> 3062306a36Sopenharmony_ci#include <linux/usb.h> 3162306a36Sopenharmony_ci#include <linux/usb/serial.h> 3262306a36Sopenharmony_ci#include <linux/serial.h> 3362306a36Sopenharmony_ci#include "mct_u232.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>" 3662306a36Sopenharmony_ci#define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * Function prototypes 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic int mct_u232_port_probe(struct usb_serial_port *port); 4262306a36Sopenharmony_cistatic void mct_u232_port_remove(struct usb_serial_port *remove); 4362306a36Sopenharmony_cistatic int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port); 4462306a36Sopenharmony_cistatic void mct_u232_close(struct usb_serial_port *port); 4562306a36Sopenharmony_cistatic void mct_u232_dtr_rts(struct usb_serial_port *port, int on); 4662306a36Sopenharmony_cistatic void mct_u232_read_int_callback(struct urb *urb); 4762306a36Sopenharmony_cistatic void mct_u232_set_termios(struct tty_struct *tty, 4862306a36Sopenharmony_ci struct usb_serial_port *port, 4962306a36Sopenharmony_ci const struct ktermios *old_termios); 5062306a36Sopenharmony_cistatic int mct_u232_break_ctl(struct tty_struct *tty, int break_state); 5162306a36Sopenharmony_cistatic int mct_u232_tiocmget(struct tty_struct *tty); 5262306a36Sopenharmony_cistatic int mct_u232_tiocmset(struct tty_struct *tty, 5362306a36Sopenharmony_ci unsigned int set, unsigned int clear); 5462306a36Sopenharmony_cistatic void mct_u232_throttle(struct tty_struct *tty); 5562306a36Sopenharmony_cistatic void mct_u232_unthrottle(struct tty_struct *tty); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * All of the device info needed for the MCT USB-RS232 converter. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = { 6262306a36Sopenharmony_ci { USB_DEVICE(MCT_U232_VID, MCT_U232_PID) }, 6362306a36Sopenharmony_ci { USB_DEVICE(MCT_U232_VID, MCT_U232_SITECOM_PID) }, 6462306a36Sopenharmony_ci { USB_DEVICE(MCT_U232_VID, MCT_U232_DU_H3SP_PID) }, 6562306a36Sopenharmony_ci { USB_DEVICE(MCT_U232_BELKIN_F5U109_VID, MCT_U232_BELKIN_F5U109_PID) }, 6662306a36Sopenharmony_ci { } /* Terminating entry */ 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct usb_serial_driver mct_u232_device = { 7162306a36Sopenharmony_ci .driver = { 7262306a36Sopenharmony_ci .owner = THIS_MODULE, 7362306a36Sopenharmony_ci .name = "mct_u232", 7462306a36Sopenharmony_ci }, 7562306a36Sopenharmony_ci .description = "MCT U232", 7662306a36Sopenharmony_ci .id_table = id_table, 7762306a36Sopenharmony_ci .num_ports = 1, 7862306a36Sopenharmony_ci .open = mct_u232_open, 7962306a36Sopenharmony_ci .close = mct_u232_close, 8062306a36Sopenharmony_ci .dtr_rts = mct_u232_dtr_rts, 8162306a36Sopenharmony_ci .throttle = mct_u232_throttle, 8262306a36Sopenharmony_ci .unthrottle = mct_u232_unthrottle, 8362306a36Sopenharmony_ci .read_int_callback = mct_u232_read_int_callback, 8462306a36Sopenharmony_ci .set_termios = mct_u232_set_termios, 8562306a36Sopenharmony_ci .break_ctl = mct_u232_break_ctl, 8662306a36Sopenharmony_ci .tiocmget = mct_u232_tiocmget, 8762306a36Sopenharmony_ci .tiocmset = mct_u232_tiocmset, 8862306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 8962306a36Sopenharmony_ci .port_probe = mct_u232_port_probe, 9062306a36Sopenharmony_ci .port_remove = mct_u232_port_remove, 9162306a36Sopenharmony_ci .get_icount = usb_serial_generic_get_icount, 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 9562306a36Sopenharmony_ci &mct_u232_device, NULL 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct mct_u232_private { 9962306a36Sopenharmony_ci struct urb *read_urb; 10062306a36Sopenharmony_ci spinlock_t lock; 10162306a36Sopenharmony_ci unsigned int control_state; /* Modem Line Setting (TIOCM) */ 10262306a36Sopenharmony_ci unsigned char last_lcr; /* Line Control Register */ 10362306a36Sopenharmony_ci unsigned char last_lsr; /* Line Status Register */ 10462306a36Sopenharmony_ci unsigned char last_msr; /* Modem Status Register */ 10562306a36Sopenharmony_ci unsigned int rx_flags; /* Throttling flags */ 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define THROTTLED 0x01 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * Handle vendor specific USB requests 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define WDR_TIMEOUT 5000 /* default urb timeout */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * Later day 2.6.0-test kernels have new baud rates like B230400 which 11862306a36Sopenharmony_ci * we do not know how to support. We ignore them for the moment. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_cistatic int mct_u232_calculate_baud_rate(struct usb_serial *serial, 12162306a36Sopenharmony_ci speed_t value, speed_t *result) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci *result = value; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID 12662306a36Sopenharmony_ci || le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) { 12762306a36Sopenharmony_ci switch (value) { 12862306a36Sopenharmony_ci case 300: 12962306a36Sopenharmony_ci return 0x01; 13062306a36Sopenharmony_ci case 600: 13162306a36Sopenharmony_ci return 0x02; /* this one not tested */ 13262306a36Sopenharmony_ci case 1200: 13362306a36Sopenharmony_ci return 0x03; 13462306a36Sopenharmony_ci case 2400: 13562306a36Sopenharmony_ci return 0x04; 13662306a36Sopenharmony_ci case 4800: 13762306a36Sopenharmony_ci return 0x06; 13862306a36Sopenharmony_ci case 9600: 13962306a36Sopenharmony_ci return 0x08; 14062306a36Sopenharmony_ci case 19200: 14162306a36Sopenharmony_ci return 0x09; 14262306a36Sopenharmony_ci case 38400: 14362306a36Sopenharmony_ci return 0x0a; 14462306a36Sopenharmony_ci case 57600: 14562306a36Sopenharmony_ci return 0x0b; 14662306a36Sopenharmony_ci case 115200: 14762306a36Sopenharmony_ci return 0x0c; 14862306a36Sopenharmony_ci default: 14962306a36Sopenharmony_ci *result = 9600; 15062306a36Sopenharmony_ci return 0x08; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } else { 15362306a36Sopenharmony_ci /* FIXME: Can we use any divider - should we do 15462306a36Sopenharmony_ci divider = 115200/value; 15562306a36Sopenharmony_ci real baud = 115200/divider */ 15662306a36Sopenharmony_ci switch (value) { 15762306a36Sopenharmony_ci case 300: break; 15862306a36Sopenharmony_ci case 600: break; 15962306a36Sopenharmony_ci case 1200: break; 16062306a36Sopenharmony_ci case 2400: break; 16162306a36Sopenharmony_ci case 4800: break; 16262306a36Sopenharmony_ci case 9600: break; 16362306a36Sopenharmony_ci case 19200: break; 16462306a36Sopenharmony_ci case 38400: break; 16562306a36Sopenharmony_ci case 57600: break; 16662306a36Sopenharmony_ci case 115200: break; 16762306a36Sopenharmony_ci default: 16862306a36Sopenharmony_ci value = 9600; 16962306a36Sopenharmony_ci *result = 9600; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci return 115200/value; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int mct_u232_set_baud_rate(struct tty_struct *tty, 17662306a36Sopenharmony_ci struct usb_serial *serial, struct usb_serial_port *port, speed_t value) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci unsigned int divisor; 17962306a36Sopenharmony_ci int rc; 18062306a36Sopenharmony_ci unsigned char *buf; 18162306a36Sopenharmony_ci unsigned char cts_enable_byte = 0; 18262306a36Sopenharmony_ci speed_t speed; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL); 18562306a36Sopenharmony_ci if (buf == NULL) 18662306a36Sopenharmony_ci return -ENOMEM; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci divisor = mct_u232_calculate_baud_rate(serial, value, &speed); 18962306a36Sopenharmony_ci put_unaligned_le32(divisor, buf); 19062306a36Sopenharmony_ci rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 19162306a36Sopenharmony_ci MCT_U232_SET_BAUD_RATE_REQUEST, 19262306a36Sopenharmony_ci MCT_U232_SET_REQUEST_TYPE, 19362306a36Sopenharmony_ci 0, 0, buf, MCT_U232_SET_BAUD_RATE_SIZE, 19462306a36Sopenharmony_ci WDR_TIMEOUT); 19562306a36Sopenharmony_ci if (rc < 0) /*FIXME: What value speed results */ 19662306a36Sopenharmony_ci dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n", 19762306a36Sopenharmony_ci value, rc); 19862306a36Sopenharmony_ci else 19962306a36Sopenharmony_ci tty_encode_baud_rate(tty, speed, speed); 20062306a36Sopenharmony_ci dev_dbg(&port->dev, "set_baud_rate: value: 0x%x, divisor: 0x%x\n", value, divisor); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which 20362306a36Sopenharmony_ci always sends two extra USB 'device request' messages after the 20462306a36Sopenharmony_ci 'baud rate change' message. The actual functionality of the 20562306a36Sopenharmony_ci request codes in these messages is not fully understood but these 20662306a36Sopenharmony_ci particular codes are never seen in any operation besides a baud 20762306a36Sopenharmony_ci rate change. Both of these messages send a single byte of data. 20862306a36Sopenharmony_ci In the first message, the value of this byte is always zero. 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci The second message has been determined experimentally to control 21162306a36Sopenharmony_ci whether data will be transmitted to a device which is not asserting 21262306a36Sopenharmony_ci the 'CTS' signal. If the second message's data byte is zero, data 21362306a36Sopenharmony_ci will be transmitted even if 'CTS' is not asserted (i.e. no hardware 21462306a36Sopenharmony_ci flow control). if the second message's data byte is nonzero (a 21562306a36Sopenharmony_ci value of 1 is used by this driver), data will not be transmitted to 21662306a36Sopenharmony_ci a device which is not asserting 'CTS'. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci buf[0] = 0; 22062306a36Sopenharmony_ci rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 22162306a36Sopenharmony_ci MCT_U232_SET_UNKNOWN1_REQUEST, 22262306a36Sopenharmony_ci MCT_U232_SET_REQUEST_TYPE, 22362306a36Sopenharmony_ci 0, 0, buf, MCT_U232_SET_UNKNOWN1_SIZE, 22462306a36Sopenharmony_ci WDR_TIMEOUT); 22562306a36Sopenharmony_ci if (rc < 0) 22662306a36Sopenharmony_ci dev_err(&port->dev, "Sending USB device request code %d " 22762306a36Sopenharmony_ci "failed (error = %d)\n", MCT_U232_SET_UNKNOWN1_REQUEST, 22862306a36Sopenharmony_ci rc); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (port && C_CRTSCTS(tty)) 23162306a36Sopenharmony_ci cts_enable_byte = 1; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci dev_dbg(&port->dev, "set_baud_rate: send second control message, data = %02X\n", 23462306a36Sopenharmony_ci cts_enable_byte); 23562306a36Sopenharmony_ci buf[0] = cts_enable_byte; 23662306a36Sopenharmony_ci rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 23762306a36Sopenharmony_ci MCT_U232_SET_CTS_REQUEST, 23862306a36Sopenharmony_ci MCT_U232_SET_REQUEST_TYPE, 23962306a36Sopenharmony_ci 0, 0, buf, MCT_U232_SET_CTS_SIZE, 24062306a36Sopenharmony_ci WDR_TIMEOUT); 24162306a36Sopenharmony_ci if (rc < 0) 24262306a36Sopenharmony_ci dev_err(&port->dev, "Sending USB device request code %d " 24362306a36Sopenharmony_ci "failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci kfree(buf); 24662306a36Sopenharmony_ci return rc; 24762306a36Sopenharmony_ci} /* mct_u232_set_baud_rate */ 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int mct_u232_set_line_ctrl(struct usb_serial_port *port, 25062306a36Sopenharmony_ci unsigned char lcr) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci int rc; 25362306a36Sopenharmony_ci unsigned char *buf; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL); 25662306a36Sopenharmony_ci if (buf == NULL) 25762306a36Sopenharmony_ci return -ENOMEM; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci buf[0] = lcr; 26062306a36Sopenharmony_ci rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), 26162306a36Sopenharmony_ci MCT_U232_SET_LINE_CTRL_REQUEST, 26262306a36Sopenharmony_ci MCT_U232_SET_REQUEST_TYPE, 26362306a36Sopenharmony_ci 0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE, 26462306a36Sopenharmony_ci WDR_TIMEOUT); 26562306a36Sopenharmony_ci if (rc < 0) 26662306a36Sopenharmony_ci dev_err(&port->dev, "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc); 26762306a36Sopenharmony_ci dev_dbg(&port->dev, "set_line_ctrl: 0x%x\n", lcr); 26862306a36Sopenharmony_ci kfree(buf); 26962306a36Sopenharmony_ci return rc; 27062306a36Sopenharmony_ci} /* mct_u232_set_line_ctrl */ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int mct_u232_set_modem_ctrl(struct usb_serial_port *port, 27362306a36Sopenharmony_ci unsigned int control_state) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci int rc; 27662306a36Sopenharmony_ci unsigned char mcr; 27762306a36Sopenharmony_ci unsigned char *buf; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL); 28062306a36Sopenharmony_ci if (buf == NULL) 28162306a36Sopenharmony_ci return -ENOMEM; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci mcr = MCT_U232_MCR_NONE; 28462306a36Sopenharmony_ci if (control_state & TIOCM_DTR) 28562306a36Sopenharmony_ci mcr |= MCT_U232_MCR_DTR; 28662306a36Sopenharmony_ci if (control_state & TIOCM_RTS) 28762306a36Sopenharmony_ci mcr |= MCT_U232_MCR_RTS; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci buf[0] = mcr; 29062306a36Sopenharmony_ci rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), 29162306a36Sopenharmony_ci MCT_U232_SET_MODEM_CTRL_REQUEST, 29262306a36Sopenharmony_ci MCT_U232_SET_REQUEST_TYPE, 29362306a36Sopenharmony_ci 0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE, 29462306a36Sopenharmony_ci WDR_TIMEOUT); 29562306a36Sopenharmony_ci kfree(buf); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci dev_dbg(&port->dev, "set_modem_ctrl: state=0x%x ==> mcr=0x%x\n", control_state, mcr); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (rc < 0) { 30062306a36Sopenharmony_ci dev_err(&port->dev, "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc); 30162306a36Sopenharmony_ci return rc; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} /* mct_u232_set_modem_ctrl */ 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int mct_u232_get_modem_stat(struct usb_serial_port *port, 30762306a36Sopenharmony_ci unsigned char *msr) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int rc; 31062306a36Sopenharmony_ci unsigned char *buf; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL); 31362306a36Sopenharmony_ci if (buf == NULL) { 31462306a36Sopenharmony_ci *msr = 0; 31562306a36Sopenharmony_ci return -ENOMEM; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), 31862306a36Sopenharmony_ci MCT_U232_GET_MODEM_STAT_REQUEST, 31962306a36Sopenharmony_ci MCT_U232_GET_REQUEST_TYPE, 32062306a36Sopenharmony_ci 0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE, 32162306a36Sopenharmony_ci WDR_TIMEOUT); 32262306a36Sopenharmony_ci if (rc < MCT_U232_GET_MODEM_STAT_SIZE) { 32362306a36Sopenharmony_ci dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (rc >= 0) 32662306a36Sopenharmony_ci rc = -EIO; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci *msr = 0; 32962306a36Sopenharmony_ci } else { 33062306a36Sopenharmony_ci *msr = buf[0]; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci dev_dbg(&port->dev, "get_modem_stat: 0x%x\n", *msr); 33362306a36Sopenharmony_ci kfree(buf); 33462306a36Sopenharmony_ci return rc; 33562306a36Sopenharmony_ci} /* mct_u232_get_modem_stat */ 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void mct_u232_msr_to_icount(struct async_icount *icount, 33862306a36Sopenharmony_ci unsigned char msr) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci /* Translate Control Line states */ 34162306a36Sopenharmony_ci if (msr & MCT_U232_MSR_DDSR) 34262306a36Sopenharmony_ci icount->dsr++; 34362306a36Sopenharmony_ci if (msr & MCT_U232_MSR_DCTS) 34462306a36Sopenharmony_ci icount->cts++; 34562306a36Sopenharmony_ci if (msr & MCT_U232_MSR_DRI) 34662306a36Sopenharmony_ci icount->rng++; 34762306a36Sopenharmony_ci if (msr & MCT_U232_MSR_DCD) 34862306a36Sopenharmony_ci icount->dcd++; 34962306a36Sopenharmony_ci} /* mct_u232_msr_to_icount */ 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void mct_u232_msr_to_state(struct usb_serial_port *port, 35262306a36Sopenharmony_ci unsigned int *control_state, unsigned char msr) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci /* Translate Control Line states */ 35562306a36Sopenharmony_ci if (msr & MCT_U232_MSR_DSR) 35662306a36Sopenharmony_ci *control_state |= TIOCM_DSR; 35762306a36Sopenharmony_ci else 35862306a36Sopenharmony_ci *control_state &= ~TIOCM_DSR; 35962306a36Sopenharmony_ci if (msr & MCT_U232_MSR_CTS) 36062306a36Sopenharmony_ci *control_state |= TIOCM_CTS; 36162306a36Sopenharmony_ci else 36262306a36Sopenharmony_ci *control_state &= ~TIOCM_CTS; 36362306a36Sopenharmony_ci if (msr & MCT_U232_MSR_RI) 36462306a36Sopenharmony_ci *control_state |= TIOCM_RI; 36562306a36Sopenharmony_ci else 36662306a36Sopenharmony_ci *control_state &= ~TIOCM_RI; 36762306a36Sopenharmony_ci if (msr & MCT_U232_MSR_CD) 36862306a36Sopenharmony_ci *control_state |= TIOCM_CD; 36962306a36Sopenharmony_ci else 37062306a36Sopenharmony_ci *control_state &= ~TIOCM_CD; 37162306a36Sopenharmony_ci dev_dbg(&port->dev, "msr_to_state: msr=0x%x ==> state=0x%x\n", msr, *control_state); 37262306a36Sopenharmony_ci} /* mct_u232_msr_to_state */ 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/* 37562306a36Sopenharmony_ci * Driver's tty interface functions 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic int mct_u232_port_probe(struct usb_serial_port *port) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 38162306a36Sopenharmony_ci struct mct_u232_private *priv; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* check first to simplify error handling */ 38462306a36Sopenharmony_ci if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) { 38562306a36Sopenharmony_ci dev_err(&port->dev, "expected endpoint missing\n"); 38662306a36Sopenharmony_ci return -ENODEV; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 39062306a36Sopenharmony_ci if (!priv) 39162306a36Sopenharmony_ci return -ENOMEM; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* Use second interrupt-in endpoint for reading. */ 39462306a36Sopenharmony_ci priv->read_urb = serial->port[1]->interrupt_in_urb; 39562306a36Sopenharmony_ci priv->read_urb->context = port; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci spin_lock_init(&priv->lock); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci usb_set_serial_port_data(port, priv); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic void mct_u232_port_remove(struct usb_serial_port *port) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct mct_u232_private *priv; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci priv = usb_get_serial_port_data(port); 40962306a36Sopenharmony_ci kfree(priv); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 41562306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 41662306a36Sopenharmony_ci int retval = 0; 41762306a36Sopenharmony_ci unsigned int control_state; 41862306a36Sopenharmony_ci unsigned long flags; 41962306a36Sopenharmony_ci unsigned char last_lcr; 42062306a36Sopenharmony_ci unsigned char last_msr; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* Compensate for a hardware bug: although the Sitecom U232-P25 42362306a36Sopenharmony_ci * device reports a maximum output packet size of 32 bytes, 42462306a36Sopenharmony_ci * it seems to be able to accept only 16 bytes (and that's what 42562306a36Sopenharmony_ci * SniffUSB says too...) 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci if (le16_to_cpu(serial->dev->descriptor.idProduct) 42862306a36Sopenharmony_ci == MCT_U232_SITECOM_PID) 42962306a36Sopenharmony_ci port->bulk_out_size = 16; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Do a defined restart: the normal serial device seems to 43262306a36Sopenharmony_ci * always turn on DTR and RTS here, so do the same. I'm not 43362306a36Sopenharmony_ci * sure if this is really necessary. But it should not harm 43462306a36Sopenharmony_ci * either. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 43762306a36Sopenharmony_ci if (tty && C_BAUD(tty)) 43862306a36Sopenharmony_ci priv->control_state = TIOCM_DTR | TIOCM_RTS; 43962306a36Sopenharmony_ci else 44062306a36Sopenharmony_ci priv->control_state = 0; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci priv->last_lcr = (MCT_U232_DATA_BITS_8 | 44362306a36Sopenharmony_ci MCT_U232_PARITY_NONE | 44462306a36Sopenharmony_ci MCT_U232_STOP_BITS_1); 44562306a36Sopenharmony_ci control_state = priv->control_state; 44662306a36Sopenharmony_ci last_lcr = priv->last_lcr; 44762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 44862306a36Sopenharmony_ci mct_u232_set_modem_ctrl(port, control_state); 44962306a36Sopenharmony_ci mct_u232_set_line_ctrl(port, last_lcr); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* Read modem status and update control state */ 45262306a36Sopenharmony_ci mct_u232_get_modem_stat(port, &last_msr); 45362306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 45462306a36Sopenharmony_ci priv->last_msr = last_msr; 45562306a36Sopenharmony_ci mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr); 45662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci retval = usb_submit_urb(priv->read_urb, GFP_KERNEL); 45962306a36Sopenharmony_ci if (retval) { 46062306a36Sopenharmony_ci dev_err(&port->dev, 46162306a36Sopenharmony_ci "usb_submit_urb(read) failed pipe 0x%x err %d\n", 46262306a36Sopenharmony_ci port->read_urb->pipe, retval); 46362306a36Sopenharmony_ci goto error; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 46762306a36Sopenharmony_ci if (retval) { 46862306a36Sopenharmony_ci usb_kill_urb(priv->read_urb); 46962306a36Sopenharmony_ci dev_err(&port->dev, 47062306a36Sopenharmony_ci "usb_submit_urb(read int) failed pipe 0x%x err %d", 47162306a36Sopenharmony_ci port->interrupt_in_urb->pipe, retval); 47262306a36Sopenharmony_ci goto error; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci return 0; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cierror: 47762306a36Sopenharmony_ci return retval; 47862306a36Sopenharmony_ci} /* mct_u232_open */ 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void mct_u232_dtr_rts(struct usb_serial_port *port, int on) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci unsigned int control_state; 48362306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci spin_lock_irq(&priv->lock); 48662306a36Sopenharmony_ci if (on) 48762306a36Sopenharmony_ci priv->control_state |= TIOCM_DTR | TIOCM_RTS; 48862306a36Sopenharmony_ci else 48962306a36Sopenharmony_ci priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); 49062306a36Sopenharmony_ci control_state = priv->control_state; 49162306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci mct_u232_set_modem_ctrl(port, control_state); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void mct_u232_close(struct usb_serial_port *port) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci usb_kill_urb(priv->read_urb); 50162306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci usb_serial_generic_close(port); 50462306a36Sopenharmony_ci} /* mct_u232_close */ 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic void mct_u232_read_int_callback(struct urb *urb) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 51062306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 51162306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 51262306a36Sopenharmony_ci int retval; 51362306a36Sopenharmony_ci int status = urb->status; 51462306a36Sopenharmony_ci unsigned long flags; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci switch (status) { 51762306a36Sopenharmony_ci case 0: 51862306a36Sopenharmony_ci /* success */ 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci case -ECONNRESET: 52162306a36Sopenharmony_ci case -ENOENT: 52262306a36Sopenharmony_ci case -ESHUTDOWN: 52362306a36Sopenharmony_ci /* this urb is terminated, clean up */ 52462306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n", 52562306a36Sopenharmony_ci __func__, status); 52662306a36Sopenharmony_ci return; 52762306a36Sopenharmony_ci default: 52862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n", 52962306a36Sopenharmony_ci __func__, status); 53062306a36Sopenharmony_ci goto exit; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * Work-a-round: handle the 'usual' bulk-in pipe here 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci if (urb->transfer_buffer_length > 2) { 53962306a36Sopenharmony_ci if (urb->actual_length) { 54062306a36Sopenharmony_ci tty_insert_flip_string(&port->port, data, 54162306a36Sopenharmony_ci urb->actual_length); 54262306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci goto exit; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* 54862306a36Sopenharmony_ci * The interrupt-in pipe signals exceptional conditions (modem line 54962306a36Sopenharmony_ci * signal changes and errors). data[0] holds MSR, data[1] holds LSR. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 55262306a36Sopenharmony_ci priv->last_msr = data[MCT_U232_MSR_INDEX]; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Record Control Line states */ 55562306a36Sopenharmony_ci mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci mct_u232_msr_to_icount(&port->icount, priv->last_msr); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci#if 0 56062306a36Sopenharmony_ci /* Not yet handled. See belkin_sa.c for further information */ 56162306a36Sopenharmony_ci /* Now to report any errors */ 56262306a36Sopenharmony_ci priv->last_lsr = data[MCT_U232_LSR_INDEX]; 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * fill in the flip buffer here, but I do not know the relation 56562306a36Sopenharmony_ci * to the current/next receive buffer or characters. I need 56662306a36Sopenharmony_ci * to look in to this before committing any code. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci if (priv->last_lsr & MCT_U232_LSR_ERR) { 56962306a36Sopenharmony_ci tty = tty_port_tty_get(&port->port); 57062306a36Sopenharmony_ci /* Overrun Error */ 57162306a36Sopenharmony_ci if (priv->last_lsr & MCT_U232_LSR_OE) { 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci /* Parity Error */ 57462306a36Sopenharmony_ci if (priv->last_lsr & MCT_U232_LSR_PE) { 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci /* Framing Error */ 57762306a36Sopenharmony_ci if (priv->last_lsr & MCT_U232_LSR_FE) { 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci /* Break Indicator */ 58062306a36Sopenharmony_ci if (priv->last_lsr & MCT_U232_LSR_BI) { 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci tty_kref_put(tty); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci#endif 58562306a36Sopenharmony_ci wake_up_interruptible(&port->port.delta_msr_wait); 58662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 58762306a36Sopenharmony_ciexit: 58862306a36Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 58962306a36Sopenharmony_ci if (retval) 59062306a36Sopenharmony_ci dev_err(&port->dev, 59162306a36Sopenharmony_ci "%s - usb_submit_urb failed with result %d\n", 59262306a36Sopenharmony_ci __func__, retval); 59362306a36Sopenharmony_ci} /* mct_u232_read_int_callback */ 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic void mct_u232_set_termios(struct tty_struct *tty, 59662306a36Sopenharmony_ci struct usb_serial_port *port, 59762306a36Sopenharmony_ci const struct ktermios *old_termios) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 60062306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 60162306a36Sopenharmony_ci struct ktermios *termios = &tty->termios; 60262306a36Sopenharmony_ci unsigned int cflag = termios->c_cflag; 60362306a36Sopenharmony_ci unsigned int old_cflag = old_termios->c_cflag; 60462306a36Sopenharmony_ci unsigned long flags; 60562306a36Sopenharmony_ci unsigned int control_state; 60662306a36Sopenharmony_ci unsigned char last_lcr; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* get a local copy of the current port settings */ 60962306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 61062306a36Sopenharmony_ci control_state = priv->control_state; 61162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 61262306a36Sopenharmony_ci last_lcr = 0; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* 61562306a36Sopenharmony_ci * Update baud rate. 61662306a36Sopenharmony_ci * Do not attempt to cache old rates and skip settings, 61762306a36Sopenharmony_ci * disconnects screw such tricks up completely. 61862306a36Sopenharmony_ci * Premature optimization is the root of all evil. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* reassert DTR and RTS on transition from B0 */ 62262306a36Sopenharmony_ci if ((old_cflag & CBAUD) == B0) { 62362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s: baud was B0\n", __func__); 62462306a36Sopenharmony_ci control_state |= TIOCM_DTR | TIOCM_RTS; 62562306a36Sopenharmony_ci mct_u232_set_modem_ctrl(port, control_state); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty)); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if ((cflag & CBAUD) == B0) { 63162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s: baud is B0\n", __func__); 63262306a36Sopenharmony_ci /* Drop RTS and DTR */ 63362306a36Sopenharmony_ci control_state &= ~(TIOCM_DTR | TIOCM_RTS); 63462306a36Sopenharmony_ci mct_u232_set_modem_ctrl(port, control_state); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* 63862306a36Sopenharmony_ci * Update line control register (LCR) 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* set the parity */ 64262306a36Sopenharmony_ci if (cflag & PARENB) 64362306a36Sopenharmony_ci last_lcr |= (cflag & PARODD) ? 64462306a36Sopenharmony_ci MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN; 64562306a36Sopenharmony_ci else 64662306a36Sopenharmony_ci last_lcr |= MCT_U232_PARITY_NONE; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* set the number of data bits */ 64962306a36Sopenharmony_ci switch (cflag & CSIZE) { 65062306a36Sopenharmony_ci case CS5: 65162306a36Sopenharmony_ci last_lcr |= MCT_U232_DATA_BITS_5; break; 65262306a36Sopenharmony_ci case CS6: 65362306a36Sopenharmony_ci last_lcr |= MCT_U232_DATA_BITS_6; break; 65462306a36Sopenharmony_ci case CS7: 65562306a36Sopenharmony_ci last_lcr |= MCT_U232_DATA_BITS_7; break; 65662306a36Sopenharmony_ci case CS8: 65762306a36Sopenharmony_ci last_lcr |= MCT_U232_DATA_BITS_8; break; 65862306a36Sopenharmony_ci default: 65962306a36Sopenharmony_ci dev_err(&port->dev, 66062306a36Sopenharmony_ci "CSIZE was not CS5-CS8, using default of 8\n"); 66162306a36Sopenharmony_ci last_lcr |= MCT_U232_DATA_BITS_8; 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci termios->c_cflag &= ~CMSPAR; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* set the number of stop bits */ 66862306a36Sopenharmony_ci last_lcr |= (cflag & CSTOPB) ? 66962306a36Sopenharmony_ci MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci mct_u232_set_line_ctrl(port, last_lcr); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* save off the modified port settings */ 67462306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 67562306a36Sopenharmony_ci priv->control_state = control_state; 67662306a36Sopenharmony_ci priv->last_lcr = last_lcr; 67762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 67862306a36Sopenharmony_ci} /* mct_u232_set_termios */ 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int mct_u232_break_ctl(struct tty_struct *tty, int break_state) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 68362306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 68462306a36Sopenharmony_ci unsigned char lcr; 68562306a36Sopenharmony_ci unsigned long flags; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 68862306a36Sopenharmony_ci lcr = priv->last_lcr; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (break_state) 69162306a36Sopenharmony_ci lcr |= MCT_U232_SET_BREAK; 69262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return mct_u232_set_line_ctrl(port, lcr); 69562306a36Sopenharmony_ci} /* mct_u232_break_ctl */ 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int mct_u232_tiocmget(struct tty_struct *tty) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 70162306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 70262306a36Sopenharmony_ci unsigned int control_state; 70362306a36Sopenharmony_ci unsigned long flags; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 70662306a36Sopenharmony_ci control_state = priv->control_state; 70762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return control_state; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int mct_u232_tiocmset(struct tty_struct *tty, 71362306a36Sopenharmony_ci unsigned int set, unsigned int clear) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 71662306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 71762306a36Sopenharmony_ci unsigned int control_state; 71862306a36Sopenharmony_ci unsigned long flags; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 72162306a36Sopenharmony_ci control_state = priv->control_state; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (set & TIOCM_RTS) 72462306a36Sopenharmony_ci control_state |= TIOCM_RTS; 72562306a36Sopenharmony_ci if (set & TIOCM_DTR) 72662306a36Sopenharmony_ci control_state |= TIOCM_DTR; 72762306a36Sopenharmony_ci if (clear & TIOCM_RTS) 72862306a36Sopenharmony_ci control_state &= ~TIOCM_RTS; 72962306a36Sopenharmony_ci if (clear & TIOCM_DTR) 73062306a36Sopenharmony_ci control_state &= ~TIOCM_DTR; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci priv->control_state = control_state; 73362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 73462306a36Sopenharmony_ci return mct_u232_set_modem_ctrl(port, control_state); 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic void mct_u232_throttle(struct tty_struct *tty) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 74062306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 74162306a36Sopenharmony_ci unsigned int control_state; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci spin_lock_irq(&priv->lock); 74462306a36Sopenharmony_ci priv->rx_flags |= THROTTLED; 74562306a36Sopenharmony_ci if (C_CRTSCTS(tty)) { 74662306a36Sopenharmony_ci priv->control_state &= ~TIOCM_RTS; 74762306a36Sopenharmony_ci control_state = priv->control_state; 74862306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 74962306a36Sopenharmony_ci mct_u232_set_modem_ctrl(port, control_state); 75062306a36Sopenharmony_ci } else { 75162306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic void mct_u232_unthrottle(struct tty_struct *tty) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 75862306a36Sopenharmony_ci struct mct_u232_private *priv = usb_get_serial_port_data(port); 75962306a36Sopenharmony_ci unsigned int control_state; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci spin_lock_irq(&priv->lock); 76262306a36Sopenharmony_ci if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { 76362306a36Sopenharmony_ci priv->rx_flags &= ~THROTTLED; 76462306a36Sopenharmony_ci priv->control_state |= TIOCM_RTS; 76562306a36Sopenharmony_ci control_state = priv->control_state; 76662306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 76762306a36Sopenharmony_ci mct_u232_set_modem_ctrl(port, control_state); 76862306a36Sopenharmony_ci } else { 76962306a36Sopenharmony_ci spin_unlock_irq(&priv->lock); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 77662306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 77762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 778