18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *   Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This program is largely derived from the Belkin USB Serial Adapter Driver
88c2ecf20Sopenharmony_ci * (see belkin_sa.[ch]). All of the information about the device was acquired
98c2ecf20Sopenharmony_ci * by using SniffUSB on Windows98. For technical details see mct_u232.h.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * William G. Greathouse and Greg Kroah-Hartman provided great help on how to
128c2ecf20Sopenharmony_ci * do the reverse engineering and how to write a USB serial device driver.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * TO BE DONE, TO BE CHECKED:
158c2ecf20Sopenharmony_ci *   DTR/RTS signal handling may be incomplete or incorrect. I have mainly
168c2ecf20Sopenharmony_ci *   implemented what I have seen with SniffUSB or found in belkin_sa.c.
178c2ecf20Sopenharmony_ci *   For further TODOs check also belkin_sa.c.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/kernel.h>
218c2ecf20Sopenharmony_ci#include <linux/errno.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/tty.h>
248c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
258c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
288c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
298c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
308c2ecf20Sopenharmony_ci#include <linux/usb.h>
318c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
328c2ecf20Sopenharmony_ci#include <linux/serial.h>
338c2ecf20Sopenharmony_ci#include "mct_u232.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
368c2ecf20Sopenharmony_ci#define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * Function prototypes
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_cistatic int  mct_u232_port_probe(struct usb_serial_port *port);
428c2ecf20Sopenharmony_cistatic int  mct_u232_port_remove(struct usb_serial_port *remove);
438c2ecf20Sopenharmony_cistatic int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port);
448c2ecf20Sopenharmony_cistatic void mct_u232_close(struct usb_serial_port *port);
458c2ecf20Sopenharmony_cistatic void mct_u232_dtr_rts(struct usb_serial_port *port, int on);
468c2ecf20Sopenharmony_cistatic void mct_u232_read_int_callback(struct urb *urb);
478c2ecf20Sopenharmony_cistatic void mct_u232_set_termios(struct tty_struct *tty,
488c2ecf20Sopenharmony_ci			struct usb_serial_port *port, struct ktermios *old);
498c2ecf20Sopenharmony_cistatic void mct_u232_break_ctl(struct tty_struct *tty, int break_state);
508c2ecf20Sopenharmony_cistatic int  mct_u232_tiocmget(struct tty_struct *tty);
518c2ecf20Sopenharmony_cistatic int  mct_u232_tiocmset(struct tty_struct *tty,
528c2ecf20Sopenharmony_ci			unsigned int set, unsigned int clear);
538c2ecf20Sopenharmony_cistatic void mct_u232_throttle(struct tty_struct *tty);
548c2ecf20Sopenharmony_cistatic void mct_u232_unthrottle(struct tty_struct *tty);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/*
588c2ecf20Sopenharmony_ci * All of the device info needed for the MCT USB-RS232 converter.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = {
618c2ecf20Sopenharmony_ci	{ USB_DEVICE(MCT_U232_VID, MCT_U232_PID) },
628c2ecf20Sopenharmony_ci	{ USB_DEVICE(MCT_U232_VID, MCT_U232_SITECOM_PID) },
638c2ecf20Sopenharmony_ci	{ USB_DEVICE(MCT_U232_VID, MCT_U232_DU_H3SP_PID) },
648c2ecf20Sopenharmony_ci	{ USB_DEVICE(MCT_U232_BELKIN_F5U109_VID, MCT_U232_BELKIN_F5U109_PID) },
658c2ecf20Sopenharmony_ci	{ }		/* Terminating entry */
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic struct usb_serial_driver mct_u232_device = {
708c2ecf20Sopenharmony_ci	.driver = {
718c2ecf20Sopenharmony_ci		.owner =	THIS_MODULE,
728c2ecf20Sopenharmony_ci		.name =		"mct_u232",
738c2ecf20Sopenharmony_ci	},
748c2ecf20Sopenharmony_ci	.description =	     "MCT U232",
758c2ecf20Sopenharmony_ci	.id_table =	     id_table,
768c2ecf20Sopenharmony_ci	.num_ports =	     1,
778c2ecf20Sopenharmony_ci	.open =		     mct_u232_open,
788c2ecf20Sopenharmony_ci	.close =	     mct_u232_close,
798c2ecf20Sopenharmony_ci	.dtr_rts =	     mct_u232_dtr_rts,
808c2ecf20Sopenharmony_ci	.throttle =	     mct_u232_throttle,
818c2ecf20Sopenharmony_ci	.unthrottle =	     mct_u232_unthrottle,
828c2ecf20Sopenharmony_ci	.read_int_callback = mct_u232_read_int_callback,
838c2ecf20Sopenharmony_ci	.set_termios =	     mct_u232_set_termios,
848c2ecf20Sopenharmony_ci	.break_ctl =	     mct_u232_break_ctl,
858c2ecf20Sopenharmony_ci	.tiocmget =	     mct_u232_tiocmget,
868c2ecf20Sopenharmony_ci	.tiocmset =	     mct_u232_tiocmset,
878c2ecf20Sopenharmony_ci	.tiocmiwait =        usb_serial_generic_tiocmiwait,
888c2ecf20Sopenharmony_ci	.port_probe =        mct_u232_port_probe,
898c2ecf20Sopenharmony_ci	.port_remove =       mct_u232_port_remove,
908c2ecf20Sopenharmony_ci	.get_icount =        usb_serial_generic_get_icount,
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
948c2ecf20Sopenharmony_ci	&mct_u232_device, NULL
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistruct mct_u232_private {
988c2ecf20Sopenharmony_ci	struct urb *read_urb;
998c2ecf20Sopenharmony_ci	spinlock_t lock;
1008c2ecf20Sopenharmony_ci	unsigned int	     control_state; /* Modem Line Setting (TIOCM) */
1018c2ecf20Sopenharmony_ci	unsigned char        last_lcr;      /* Line Control Register */
1028c2ecf20Sopenharmony_ci	unsigned char	     last_lsr;      /* Line Status Register */
1038c2ecf20Sopenharmony_ci	unsigned char	     last_msr;      /* Modem Status Register */
1048c2ecf20Sopenharmony_ci	unsigned int	     rx_flags;      /* Throttling flags */
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define THROTTLED		0x01
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * Handle vendor specific USB requests
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define WDR_TIMEOUT 5000 /* default urb timeout */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * Later day 2.6.0-test kernels have new baud rates like B230400 which
1178c2ecf20Sopenharmony_ci * we do not know how to support. We ignore them for the moment.
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_cistatic int mct_u232_calculate_baud_rate(struct usb_serial *serial,
1208c2ecf20Sopenharmony_ci					speed_t value, speed_t *result)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	*result = value;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID
1258c2ecf20Sopenharmony_ci		|| le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) {
1268c2ecf20Sopenharmony_ci		switch (value) {
1278c2ecf20Sopenharmony_ci		case 300:
1288c2ecf20Sopenharmony_ci			return 0x01;
1298c2ecf20Sopenharmony_ci		case 600:
1308c2ecf20Sopenharmony_ci			return 0x02; /* this one not tested */
1318c2ecf20Sopenharmony_ci		case 1200:
1328c2ecf20Sopenharmony_ci			return 0x03;
1338c2ecf20Sopenharmony_ci		case 2400:
1348c2ecf20Sopenharmony_ci			return 0x04;
1358c2ecf20Sopenharmony_ci		case 4800:
1368c2ecf20Sopenharmony_ci			return 0x06;
1378c2ecf20Sopenharmony_ci		case 9600:
1388c2ecf20Sopenharmony_ci			return 0x08;
1398c2ecf20Sopenharmony_ci		case 19200:
1408c2ecf20Sopenharmony_ci			return 0x09;
1418c2ecf20Sopenharmony_ci		case 38400:
1428c2ecf20Sopenharmony_ci			return 0x0a;
1438c2ecf20Sopenharmony_ci		case 57600:
1448c2ecf20Sopenharmony_ci			return 0x0b;
1458c2ecf20Sopenharmony_ci		case 115200:
1468c2ecf20Sopenharmony_ci			return 0x0c;
1478c2ecf20Sopenharmony_ci		default:
1488c2ecf20Sopenharmony_ci			*result = 9600;
1498c2ecf20Sopenharmony_ci			return 0x08;
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci	} else {
1528c2ecf20Sopenharmony_ci		/* FIXME: Can we use any divider - should we do
1538c2ecf20Sopenharmony_ci		   divider = 115200/value;
1548c2ecf20Sopenharmony_ci		   real baud = 115200/divider */
1558c2ecf20Sopenharmony_ci		switch (value) {
1568c2ecf20Sopenharmony_ci		case 300: break;
1578c2ecf20Sopenharmony_ci		case 600: break;
1588c2ecf20Sopenharmony_ci		case 1200: break;
1598c2ecf20Sopenharmony_ci		case 2400: break;
1608c2ecf20Sopenharmony_ci		case 4800: break;
1618c2ecf20Sopenharmony_ci		case 9600: break;
1628c2ecf20Sopenharmony_ci		case 19200: break;
1638c2ecf20Sopenharmony_ci		case 38400: break;
1648c2ecf20Sopenharmony_ci		case 57600: break;
1658c2ecf20Sopenharmony_ci		case 115200: break;
1668c2ecf20Sopenharmony_ci		default:
1678c2ecf20Sopenharmony_ci			value = 9600;
1688c2ecf20Sopenharmony_ci			*result = 9600;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci		return 115200/value;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic int mct_u232_set_baud_rate(struct tty_struct *tty,
1758c2ecf20Sopenharmony_ci	struct usb_serial *serial, struct usb_serial_port *port, speed_t value)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	unsigned int divisor;
1788c2ecf20Sopenharmony_ci	int rc;
1798c2ecf20Sopenharmony_ci	unsigned char *buf;
1808c2ecf20Sopenharmony_ci	unsigned char cts_enable_byte = 0;
1818c2ecf20Sopenharmony_ci	speed_t speed;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
1848c2ecf20Sopenharmony_ci	if (buf == NULL)
1858c2ecf20Sopenharmony_ci		return -ENOMEM;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	divisor = mct_u232_calculate_baud_rate(serial, value, &speed);
1888c2ecf20Sopenharmony_ci	put_unaligned_le32(divisor, buf);
1898c2ecf20Sopenharmony_ci	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
1908c2ecf20Sopenharmony_ci				MCT_U232_SET_BAUD_RATE_REQUEST,
1918c2ecf20Sopenharmony_ci				MCT_U232_SET_REQUEST_TYPE,
1928c2ecf20Sopenharmony_ci				0, 0, buf, MCT_U232_SET_BAUD_RATE_SIZE,
1938c2ecf20Sopenharmony_ci				WDR_TIMEOUT);
1948c2ecf20Sopenharmony_ci	if (rc < 0)	/*FIXME: What value speed results */
1958c2ecf20Sopenharmony_ci		dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n",
1968c2ecf20Sopenharmony_ci			value, rc);
1978c2ecf20Sopenharmony_ci	else
1988c2ecf20Sopenharmony_ci		tty_encode_baud_rate(tty, speed, speed);
1998c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "set_baud_rate: value: 0x%x, divisor: 0x%x\n", value, divisor);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which
2028c2ecf20Sopenharmony_ci	   always sends two extra USB 'device request' messages after the
2038c2ecf20Sopenharmony_ci	   'baud rate change' message.  The actual functionality of the
2048c2ecf20Sopenharmony_ci	   request codes in these messages is not fully understood but these
2058c2ecf20Sopenharmony_ci	   particular codes are never seen in any operation besides a baud
2068c2ecf20Sopenharmony_ci	   rate change.  Both of these messages send a single byte of data.
2078c2ecf20Sopenharmony_ci	   In the first message, the value of this byte is always zero.
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	   The second message has been determined experimentally to control
2108c2ecf20Sopenharmony_ci	   whether data will be transmitted to a device which is not asserting
2118c2ecf20Sopenharmony_ci	   the 'CTS' signal.  If the second message's data byte is zero, data
2128c2ecf20Sopenharmony_ci	   will be transmitted even if 'CTS' is not asserted (i.e. no hardware
2138c2ecf20Sopenharmony_ci	   flow control).  if the second message's data byte is nonzero (a
2148c2ecf20Sopenharmony_ci	   value of 1 is used by this driver), data will not be transmitted to
2158c2ecf20Sopenharmony_ci	   a device which is not asserting 'CTS'.
2168c2ecf20Sopenharmony_ci	*/
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	buf[0] = 0;
2198c2ecf20Sopenharmony_ci	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
2208c2ecf20Sopenharmony_ci				MCT_U232_SET_UNKNOWN1_REQUEST,
2218c2ecf20Sopenharmony_ci				MCT_U232_SET_REQUEST_TYPE,
2228c2ecf20Sopenharmony_ci				0, 0, buf, MCT_U232_SET_UNKNOWN1_SIZE,
2238c2ecf20Sopenharmony_ci				WDR_TIMEOUT);
2248c2ecf20Sopenharmony_ci	if (rc < 0)
2258c2ecf20Sopenharmony_ci		dev_err(&port->dev, "Sending USB device request code %d "
2268c2ecf20Sopenharmony_ci			"failed (error = %d)\n", MCT_U232_SET_UNKNOWN1_REQUEST,
2278c2ecf20Sopenharmony_ci			rc);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (port && C_CRTSCTS(tty))
2308c2ecf20Sopenharmony_ci	   cts_enable_byte = 1;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "set_baud_rate: send second control message, data = %02X\n",
2338c2ecf20Sopenharmony_ci		cts_enable_byte);
2348c2ecf20Sopenharmony_ci	buf[0] = cts_enable_byte;
2358c2ecf20Sopenharmony_ci	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
2368c2ecf20Sopenharmony_ci			MCT_U232_SET_CTS_REQUEST,
2378c2ecf20Sopenharmony_ci			MCT_U232_SET_REQUEST_TYPE,
2388c2ecf20Sopenharmony_ci			0, 0, buf, MCT_U232_SET_CTS_SIZE,
2398c2ecf20Sopenharmony_ci			WDR_TIMEOUT);
2408c2ecf20Sopenharmony_ci	if (rc < 0)
2418c2ecf20Sopenharmony_ci		dev_err(&port->dev, "Sending USB device request code %d "
2428c2ecf20Sopenharmony_ci			"failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	kfree(buf);
2458c2ecf20Sopenharmony_ci	return rc;
2468c2ecf20Sopenharmony_ci} /* mct_u232_set_baud_rate */
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int mct_u232_set_line_ctrl(struct usb_serial_port *port,
2498c2ecf20Sopenharmony_ci				  unsigned char lcr)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	int rc;
2528c2ecf20Sopenharmony_ci	unsigned char *buf;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
2558c2ecf20Sopenharmony_ci	if (buf == NULL)
2568c2ecf20Sopenharmony_ci		return -ENOMEM;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	buf[0] = lcr;
2598c2ecf20Sopenharmony_ci	rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
2608c2ecf20Sopenharmony_ci			MCT_U232_SET_LINE_CTRL_REQUEST,
2618c2ecf20Sopenharmony_ci			MCT_U232_SET_REQUEST_TYPE,
2628c2ecf20Sopenharmony_ci			0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE,
2638c2ecf20Sopenharmony_ci			WDR_TIMEOUT);
2648c2ecf20Sopenharmony_ci	if (rc < 0)
2658c2ecf20Sopenharmony_ci		dev_err(&port->dev, "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
2668c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "set_line_ctrl: 0x%x\n", lcr);
2678c2ecf20Sopenharmony_ci	kfree(buf);
2688c2ecf20Sopenharmony_ci	return rc;
2698c2ecf20Sopenharmony_ci} /* mct_u232_set_line_ctrl */
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int mct_u232_set_modem_ctrl(struct usb_serial_port *port,
2728c2ecf20Sopenharmony_ci				   unsigned int control_state)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	int rc;
2758c2ecf20Sopenharmony_ci	unsigned char mcr;
2768c2ecf20Sopenharmony_ci	unsigned char *buf;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
2798c2ecf20Sopenharmony_ci	if (buf == NULL)
2808c2ecf20Sopenharmony_ci		return -ENOMEM;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	mcr = MCT_U232_MCR_NONE;
2838c2ecf20Sopenharmony_ci	if (control_state & TIOCM_DTR)
2848c2ecf20Sopenharmony_ci		mcr |= MCT_U232_MCR_DTR;
2858c2ecf20Sopenharmony_ci	if (control_state & TIOCM_RTS)
2868c2ecf20Sopenharmony_ci		mcr |= MCT_U232_MCR_RTS;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	buf[0] = mcr;
2898c2ecf20Sopenharmony_ci	rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
2908c2ecf20Sopenharmony_ci			MCT_U232_SET_MODEM_CTRL_REQUEST,
2918c2ecf20Sopenharmony_ci			MCT_U232_SET_REQUEST_TYPE,
2928c2ecf20Sopenharmony_ci			0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE,
2938c2ecf20Sopenharmony_ci			WDR_TIMEOUT);
2948c2ecf20Sopenharmony_ci	kfree(buf);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "set_modem_ctrl: state=0x%x ==> mcr=0x%x\n", control_state, mcr);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (rc < 0) {
2998c2ecf20Sopenharmony_ci		dev_err(&port->dev, "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
3008c2ecf20Sopenharmony_ci		return rc;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci	return 0;
3038c2ecf20Sopenharmony_ci} /* mct_u232_set_modem_ctrl */
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic int mct_u232_get_modem_stat(struct usb_serial_port *port,
3068c2ecf20Sopenharmony_ci				   unsigned char *msr)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	int rc;
3098c2ecf20Sopenharmony_ci	unsigned char *buf;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
3128c2ecf20Sopenharmony_ci	if (buf == NULL) {
3138c2ecf20Sopenharmony_ci		*msr = 0;
3148c2ecf20Sopenharmony_ci		return -ENOMEM;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci	rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),
3178c2ecf20Sopenharmony_ci			MCT_U232_GET_MODEM_STAT_REQUEST,
3188c2ecf20Sopenharmony_ci			MCT_U232_GET_REQUEST_TYPE,
3198c2ecf20Sopenharmony_ci			0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
3208c2ecf20Sopenharmony_ci			WDR_TIMEOUT);
3218c2ecf20Sopenharmony_ci	if (rc < MCT_U232_GET_MODEM_STAT_SIZE) {
3228c2ecf20Sopenharmony_ci		dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		if (rc >= 0)
3258c2ecf20Sopenharmony_ci			rc = -EIO;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		*msr = 0;
3288c2ecf20Sopenharmony_ci	} else {
3298c2ecf20Sopenharmony_ci		*msr = buf[0];
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "get_modem_stat: 0x%x\n", *msr);
3328c2ecf20Sopenharmony_ci	kfree(buf);
3338c2ecf20Sopenharmony_ci	return rc;
3348c2ecf20Sopenharmony_ci} /* mct_u232_get_modem_stat */
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic void mct_u232_msr_to_icount(struct async_icount *icount,
3378c2ecf20Sopenharmony_ci						unsigned char msr)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	/* Translate Control Line states */
3408c2ecf20Sopenharmony_ci	if (msr & MCT_U232_MSR_DDSR)
3418c2ecf20Sopenharmony_ci		icount->dsr++;
3428c2ecf20Sopenharmony_ci	if (msr & MCT_U232_MSR_DCTS)
3438c2ecf20Sopenharmony_ci		icount->cts++;
3448c2ecf20Sopenharmony_ci	if (msr & MCT_U232_MSR_DRI)
3458c2ecf20Sopenharmony_ci		icount->rng++;
3468c2ecf20Sopenharmony_ci	if (msr & MCT_U232_MSR_DCD)
3478c2ecf20Sopenharmony_ci		icount->dcd++;
3488c2ecf20Sopenharmony_ci} /* mct_u232_msr_to_icount */
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void mct_u232_msr_to_state(struct usb_serial_port *port,
3518c2ecf20Sopenharmony_ci				  unsigned int *control_state, unsigned char msr)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	/* Translate Control Line states */
3548c2ecf20Sopenharmony_ci	if (msr & MCT_U232_MSR_DSR)
3558c2ecf20Sopenharmony_ci		*control_state |=  TIOCM_DSR;
3568c2ecf20Sopenharmony_ci	else
3578c2ecf20Sopenharmony_ci		*control_state &= ~TIOCM_DSR;
3588c2ecf20Sopenharmony_ci	if (msr & MCT_U232_MSR_CTS)
3598c2ecf20Sopenharmony_ci		*control_state |=  TIOCM_CTS;
3608c2ecf20Sopenharmony_ci	else
3618c2ecf20Sopenharmony_ci		*control_state &= ~TIOCM_CTS;
3628c2ecf20Sopenharmony_ci	if (msr & MCT_U232_MSR_RI)
3638c2ecf20Sopenharmony_ci		*control_state |=  TIOCM_RI;
3648c2ecf20Sopenharmony_ci	else
3658c2ecf20Sopenharmony_ci		*control_state &= ~TIOCM_RI;
3668c2ecf20Sopenharmony_ci	if (msr & MCT_U232_MSR_CD)
3678c2ecf20Sopenharmony_ci		*control_state |=  TIOCM_CD;
3688c2ecf20Sopenharmony_ci	else
3698c2ecf20Sopenharmony_ci		*control_state &= ~TIOCM_CD;
3708c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "msr_to_state: msr=0x%x ==> state=0x%x\n", msr, *control_state);
3718c2ecf20Sopenharmony_ci} /* mct_u232_msr_to_state */
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/*
3748c2ecf20Sopenharmony_ci * Driver's tty interface functions
3758c2ecf20Sopenharmony_ci */
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int mct_u232_port_probe(struct usb_serial_port *port)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
3808c2ecf20Sopenharmony_ci	struct mct_u232_private *priv;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* check first to simplify error handling */
3838c2ecf20Sopenharmony_ci	if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) {
3848c2ecf20Sopenharmony_ci		dev_err(&port->dev, "expected endpoint missing\n");
3858c2ecf20Sopenharmony_ci		return -ENODEV;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
3898c2ecf20Sopenharmony_ci	if (!priv)
3908c2ecf20Sopenharmony_ci		return -ENOMEM;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* Use second interrupt-in endpoint for reading. */
3938c2ecf20Sopenharmony_ci	priv->read_urb = serial->port[1]->interrupt_in_urb;
3948c2ecf20Sopenharmony_ci	priv->read_urb->context = port;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	spin_lock_init(&priv->lock);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, priv);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return 0;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int mct_u232_port_remove(struct usb_serial_port *port)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct mct_u232_private *priv;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
4088c2ecf20Sopenharmony_ci	kfree(priv);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
4168c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
4178c2ecf20Sopenharmony_ci	int retval = 0;
4188c2ecf20Sopenharmony_ci	unsigned int control_state;
4198c2ecf20Sopenharmony_ci	unsigned long flags;
4208c2ecf20Sopenharmony_ci	unsigned char last_lcr;
4218c2ecf20Sopenharmony_ci	unsigned char last_msr;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* Compensate for a hardware bug: although the Sitecom U232-P25
4248c2ecf20Sopenharmony_ci	 * device reports a maximum output packet size of 32 bytes,
4258c2ecf20Sopenharmony_ci	 * it seems to be able to accept only 16 bytes (and that's what
4268c2ecf20Sopenharmony_ci	 * SniffUSB says too...)
4278c2ecf20Sopenharmony_ci	 */
4288c2ecf20Sopenharmony_ci	if (le16_to_cpu(serial->dev->descriptor.idProduct)
4298c2ecf20Sopenharmony_ci						== MCT_U232_SITECOM_PID)
4308c2ecf20Sopenharmony_ci		port->bulk_out_size = 16;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* Do a defined restart: the normal serial device seems to
4338c2ecf20Sopenharmony_ci	 * always turn on DTR and RTS here, so do the same. I'm not
4348c2ecf20Sopenharmony_ci	 * sure if this is really necessary. But it should not harm
4358c2ecf20Sopenharmony_ci	 * either.
4368c2ecf20Sopenharmony_ci	 */
4378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
4388c2ecf20Sopenharmony_ci	if (tty && C_BAUD(tty))
4398c2ecf20Sopenharmony_ci		priv->control_state = TIOCM_DTR | TIOCM_RTS;
4408c2ecf20Sopenharmony_ci	else
4418c2ecf20Sopenharmony_ci		priv->control_state = 0;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	priv->last_lcr = (MCT_U232_DATA_BITS_8 |
4448c2ecf20Sopenharmony_ci			  MCT_U232_PARITY_NONE |
4458c2ecf20Sopenharmony_ci			  MCT_U232_STOP_BITS_1);
4468c2ecf20Sopenharmony_ci	control_state = priv->control_state;
4478c2ecf20Sopenharmony_ci	last_lcr = priv->last_lcr;
4488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
4498c2ecf20Sopenharmony_ci	mct_u232_set_modem_ctrl(port, control_state);
4508c2ecf20Sopenharmony_ci	mct_u232_set_line_ctrl(port, last_lcr);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	/* Read modem status and update control state */
4538c2ecf20Sopenharmony_ci	mct_u232_get_modem_stat(port, &last_msr);
4548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
4558c2ecf20Sopenharmony_ci	priv->last_msr = last_msr;
4568c2ecf20Sopenharmony_ci	mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
4578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	retval = usb_submit_urb(priv->read_urb, GFP_KERNEL);
4608c2ecf20Sopenharmony_ci	if (retval) {
4618c2ecf20Sopenharmony_ci		dev_err(&port->dev,
4628c2ecf20Sopenharmony_ci			"usb_submit_urb(read) failed pipe 0x%x err %d\n",
4638c2ecf20Sopenharmony_ci			port->read_urb->pipe, retval);
4648c2ecf20Sopenharmony_ci		goto error;
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
4688c2ecf20Sopenharmony_ci	if (retval) {
4698c2ecf20Sopenharmony_ci		usb_kill_urb(priv->read_urb);
4708c2ecf20Sopenharmony_ci		dev_err(&port->dev,
4718c2ecf20Sopenharmony_ci			"usb_submit_urb(read int) failed pipe 0x%x err %d",
4728c2ecf20Sopenharmony_ci			port->interrupt_in_urb->pipe, retval);
4738c2ecf20Sopenharmony_ci		goto error;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci	return 0;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cierror:
4788c2ecf20Sopenharmony_ci	return retval;
4798c2ecf20Sopenharmony_ci} /* mct_u232_open */
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	unsigned int control_state;
4848c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
4878c2ecf20Sopenharmony_ci	if (on)
4888c2ecf20Sopenharmony_ci		priv->control_state |= TIOCM_DTR | TIOCM_RTS;
4898c2ecf20Sopenharmony_ci	else
4908c2ecf20Sopenharmony_ci		priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
4918c2ecf20Sopenharmony_ci	control_state = priv->control_state;
4928c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->lock);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	mct_u232_set_modem_ctrl(port, control_state);
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic void mct_u232_close(struct usb_serial_port *port)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	usb_kill_urb(priv->read_urb);
5028c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	usb_serial_generic_close(port);
5058c2ecf20Sopenharmony_ci} /* mct_u232_close */
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void mct_u232_read_int_callback(struct urb *urb)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
5118c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
5128c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
5138c2ecf20Sopenharmony_ci	int retval;
5148c2ecf20Sopenharmony_ci	int status = urb->status;
5158c2ecf20Sopenharmony_ci	unsigned long flags;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	switch (status) {
5188c2ecf20Sopenharmony_ci	case 0:
5198c2ecf20Sopenharmony_ci		/* success */
5208c2ecf20Sopenharmony_ci		break;
5218c2ecf20Sopenharmony_ci	case -ECONNRESET:
5228c2ecf20Sopenharmony_ci	case -ENOENT:
5238c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
5248c2ecf20Sopenharmony_ci		/* this urb is terminated, clean up */
5258c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
5268c2ecf20Sopenharmony_ci			__func__, status);
5278c2ecf20Sopenharmony_ci		return;
5288c2ecf20Sopenharmony_ci	default:
5298c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
5308c2ecf20Sopenharmony_ci			__func__, status);
5318c2ecf20Sopenharmony_ci		goto exit;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	/*
5378c2ecf20Sopenharmony_ci	 * Work-a-round: handle the 'usual' bulk-in pipe here
5388c2ecf20Sopenharmony_ci	 */
5398c2ecf20Sopenharmony_ci	if (urb->transfer_buffer_length > 2) {
5408c2ecf20Sopenharmony_ci		if (urb->actual_length) {
5418c2ecf20Sopenharmony_ci			tty_insert_flip_string(&port->port, data,
5428c2ecf20Sopenharmony_ci					urb->actual_length);
5438c2ecf20Sopenharmony_ci			tty_flip_buffer_push(&port->port);
5448c2ecf20Sopenharmony_ci		}
5458c2ecf20Sopenharmony_ci		goto exit;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/*
5498c2ecf20Sopenharmony_ci	 * The interrupt-in pipe signals exceptional conditions (modem line
5508c2ecf20Sopenharmony_ci	 * signal changes and errors). data[0] holds MSR, data[1] holds LSR.
5518c2ecf20Sopenharmony_ci	 */
5528c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
5538c2ecf20Sopenharmony_ci	priv->last_msr = data[MCT_U232_MSR_INDEX];
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* Record Control Line states */
5568c2ecf20Sopenharmony_ci	mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	mct_u232_msr_to_icount(&port->icount, priv->last_msr);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci#if 0
5618c2ecf20Sopenharmony_ci	/* Not yet handled. See belkin_sa.c for further information */
5628c2ecf20Sopenharmony_ci	/* Now to report any errors */
5638c2ecf20Sopenharmony_ci	priv->last_lsr = data[MCT_U232_LSR_INDEX];
5648c2ecf20Sopenharmony_ci	/*
5658c2ecf20Sopenharmony_ci	 * fill in the flip buffer here, but I do not know the relation
5668c2ecf20Sopenharmony_ci	 * to the current/next receive buffer or characters.  I need
5678c2ecf20Sopenharmony_ci	 * to look in to this before committing any code.
5688c2ecf20Sopenharmony_ci	 */
5698c2ecf20Sopenharmony_ci	if (priv->last_lsr & MCT_U232_LSR_ERR) {
5708c2ecf20Sopenharmony_ci		tty = tty_port_tty_get(&port->port);
5718c2ecf20Sopenharmony_ci		/* Overrun Error */
5728c2ecf20Sopenharmony_ci		if (priv->last_lsr & MCT_U232_LSR_OE) {
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci		/* Parity Error */
5758c2ecf20Sopenharmony_ci		if (priv->last_lsr & MCT_U232_LSR_PE) {
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci		/* Framing Error */
5788c2ecf20Sopenharmony_ci		if (priv->last_lsr & MCT_U232_LSR_FE) {
5798c2ecf20Sopenharmony_ci		}
5808c2ecf20Sopenharmony_ci		/* Break Indicator */
5818c2ecf20Sopenharmony_ci		if (priv->last_lsr & MCT_U232_LSR_BI) {
5828c2ecf20Sopenharmony_ci		}
5838c2ecf20Sopenharmony_ci		tty_kref_put(tty);
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci#endif
5868c2ecf20Sopenharmony_ci	wake_up_interruptible(&port->port.delta_msr_wait);
5878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
5888c2ecf20Sopenharmony_ciexit:
5898c2ecf20Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_ATOMIC);
5908c2ecf20Sopenharmony_ci	if (retval)
5918c2ecf20Sopenharmony_ci		dev_err(&port->dev,
5928c2ecf20Sopenharmony_ci			"%s - usb_submit_urb failed with result %d\n",
5938c2ecf20Sopenharmony_ci			__func__, retval);
5948c2ecf20Sopenharmony_ci} /* mct_u232_read_int_callback */
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic void mct_u232_set_termios(struct tty_struct *tty,
5978c2ecf20Sopenharmony_ci				 struct usb_serial_port *port,
5988c2ecf20Sopenharmony_ci				 struct ktermios *old_termios)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
6018c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
6028c2ecf20Sopenharmony_ci	struct ktermios *termios = &tty->termios;
6038c2ecf20Sopenharmony_ci	unsigned int cflag = termios->c_cflag;
6048c2ecf20Sopenharmony_ci	unsigned int old_cflag = old_termios->c_cflag;
6058c2ecf20Sopenharmony_ci	unsigned long flags;
6068c2ecf20Sopenharmony_ci	unsigned int control_state;
6078c2ecf20Sopenharmony_ci	unsigned char last_lcr;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	/* get a local copy of the current port settings */
6108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
6118c2ecf20Sopenharmony_ci	control_state = priv->control_state;
6128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
6138c2ecf20Sopenharmony_ci	last_lcr = 0;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	/*
6168c2ecf20Sopenharmony_ci	 * Update baud rate.
6178c2ecf20Sopenharmony_ci	 * Do not attempt to cache old rates and skip settings,
6188c2ecf20Sopenharmony_ci	 * disconnects screw such tricks up completely.
6198c2ecf20Sopenharmony_ci	 * Premature optimization is the root of all evil.
6208c2ecf20Sopenharmony_ci	 */
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	/* reassert DTR and RTS on transition from B0 */
6238c2ecf20Sopenharmony_ci	if ((old_cflag & CBAUD) == B0) {
6248c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s: baud was B0\n", __func__);
6258c2ecf20Sopenharmony_ci		control_state |= TIOCM_DTR | TIOCM_RTS;
6268c2ecf20Sopenharmony_ci		mct_u232_set_modem_ctrl(port, control_state);
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty));
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	if ((cflag & CBAUD) == B0) {
6328c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s: baud is B0\n", __func__);
6338c2ecf20Sopenharmony_ci		/* Drop RTS and DTR */
6348c2ecf20Sopenharmony_ci		control_state &= ~(TIOCM_DTR | TIOCM_RTS);
6358c2ecf20Sopenharmony_ci		mct_u232_set_modem_ctrl(port, control_state);
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	/*
6398c2ecf20Sopenharmony_ci	 * Update line control register (LCR)
6408c2ecf20Sopenharmony_ci	 */
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	/* set the parity */
6438c2ecf20Sopenharmony_ci	if (cflag & PARENB)
6448c2ecf20Sopenharmony_ci		last_lcr |= (cflag & PARODD) ?
6458c2ecf20Sopenharmony_ci			MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
6468c2ecf20Sopenharmony_ci	else
6478c2ecf20Sopenharmony_ci		last_lcr |= MCT_U232_PARITY_NONE;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	/* set the number of data bits */
6508c2ecf20Sopenharmony_ci	switch (cflag & CSIZE) {
6518c2ecf20Sopenharmony_ci	case CS5:
6528c2ecf20Sopenharmony_ci		last_lcr |= MCT_U232_DATA_BITS_5; break;
6538c2ecf20Sopenharmony_ci	case CS6:
6548c2ecf20Sopenharmony_ci		last_lcr |= MCT_U232_DATA_BITS_6; break;
6558c2ecf20Sopenharmony_ci	case CS7:
6568c2ecf20Sopenharmony_ci		last_lcr |= MCT_U232_DATA_BITS_7; break;
6578c2ecf20Sopenharmony_ci	case CS8:
6588c2ecf20Sopenharmony_ci		last_lcr |= MCT_U232_DATA_BITS_8; break;
6598c2ecf20Sopenharmony_ci	default:
6608c2ecf20Sopenharmony_ci		dev_err(&port->dev,
6618c2ecf20Sopenharmony_ci			"CSIZE was not CS5-CS8, using default of 8\n");
6628c2ecf20Sopenharmony_ci		last_lcr |= MCT_U232_DATA_BITS_8;
6638c2ecf20Sopenharmony_ci		break;
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	termios->c_cflag &= ~CMSPAR;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* set the number of stop bits */
6698c2ecf20Sopenharmony_ci	last_lcr |= (cflag & CSTOPB) ?
6708c2ecf20Sopenharmony_ci		MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	mct_u232_set_line_ctrl(port, last_lcr);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	/* save off the modified port settings */
6758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
6768c2ecf20Sopenharmony_ci	priv->control_state = control_state;
6778c2ecf20Sopenharmony_ci	priv->last_lcr = last_lcr;
6788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
6798c2ecf20Sopenharmony_ci} /* mct_u232_set_termios */
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_cistatic void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
6848c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
6858c2ecf20Sopenharmony_ci	unsigned char lcr;
6868c2ecf20Sopenharmony_ci	unsigned long flags;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
6898c2ecf20Sopenharmony_ci	lcr = priv->last_lcr;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (break_state)
6928c2ecf20Sopenharmony_ci		lcr |= MCT_U232_SET_BREAK;
6938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	mct_u232_set_line_ctrl(port, lcr);
6968c2ecf20Sopenharmony_ci} /* mct_u232_break_ctl */
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic int mct_u232_tiocmget(struct tty_struct *tty)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
7028c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
7038c2ecf20Sopenharmony_ci	unsigned int control_state;
7048c2ecf20Sopenharmony_ci	unsigned long flags;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
7078c2ecf20Sopenharmony_ci	control_state = priv->control_state;
7088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return control_state;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic int mct_u232_tiocmset(struct tty_struct *tty,
7148c2ecf20Sopenharmony_ci			      unsigned int set, unsigned int clear)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
7178c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
7188c2ecf20Sopenharmony_ci	unsigned int control_state;
7198c2ecf20Sopenharmony_ci	unsigned long flags;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
7228c2ecf20Sopenharmony_ci	control_state = priv->control_state;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
7258c2ecf20Sopenharmony_ci		control_state |= TIOCM_RTS;
7268c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
7278c2ecf20Sopenharmony_ci		control_state |= TIOCM_DTR;
7288c2ecf20Sopenharmony_ci	if (clear & TIOCM_RTS)
7298c2ecf20Sopenharmony_ci		control_state &= ~TIOCM_RTS;
7308c2ecf20Sopenharmony_ci	if (clear & TIOCM_DTR)
7318c2ecf20Sopenharmony_ci		control_state &= ~TIOCM_DTR;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	priv->control_state = control_state;
7348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
7358c2ecf20Sopenharmony_ci	return mct_u232_set_modem_ctrl(port, control_state);
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic void mct_u232_throttle(struct tty_struct *tty)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
7418c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
7428c2ecf20Sopenharmony_ci	unsigned int control_state;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
7458c2ecf20Sopenharmony_ci	priv->rx_flags |= THROTTLED;
7468c2ecf20Sopenharmony_ci	if (C_CRTSCTS(tty)) {
7478c2ecf20Sopenharmony_ci		priv->control_state &= ~TIOCM_RTS;
7488c2ecf20Sopenharmony_ci		control_state = priv->control_state;
7498c2ecf20Sopenharmony_ci		spin_unlock_irq(&priv->lock);
7508c2ecf20Sopenharmony_ci		mct_u232_set_modem_ctrl(port, control_state);
7518c2ecf20Sopenharmony_ci	} else {
7528c2ecf20Sopenharmony_ci		spin_unlock_irq(&priv->lock);
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_cistatic void mct_u232_unthrottle(struct tty_struct *tty)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
7598c2ecf20Sopenharmony_ci	struct mct_u232_private *priv = usb_get_serial_port_data(port);
7608c2ecf20Sopenharmony_ci	unsigned int control_state;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->lock);
7638c2ecf20Sopenharmony_ci	if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) {
7648c2ecf20Sopenharmony_ci		priv->rx_flags &= ~THROTTLED;
7658c2ecf20Sopenharmony_ci		priv->control_state |= TIOCM_RTS;
7668c2ecf20Sopenharmony_ci		control_state = priv->control_state;
7678c2ecf20Sopenharmony_ci		spin_unlock_irq(&priv->lock);
7688c2ecf20Sopenharmony_ci		mct_u232_set_modem_ctrl(port, control_state);
7698c2ecf20Sopenharmony_ci	} else {
7708c2ecf20Sopenharmony_ci		spin_unlock_irq(&priv->lock);
7718c2ecf20Sopenharmony_ci	}
7728c2ecf20Sopenharmony_ci}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
7778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
7788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
779