xref: /linux/drivers/usb/serial/mct_u232.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *   Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * This program is largely derived from the Belkin USB Serial Adapter Driver
81da177e4SLinus Torvalds  * (see belkin_sa.[ch]). All of the information about the device was acquired
91da177e4SLinus Torvalds  * by using SniffUSB on Windows98. For technical details see mct_u232.h.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * William G. Greathouse and Greg Kroah-Hartman provided great help on how to
121da177e4SLinus Torvalds  * do the reverse engineering and how to write a USB serial device driver.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  * TO BE DONE, TO BE CHECKED:
151da177e4SLinus Torvalds  *   DTR/RTS signal handling may be incomplete or incorrect. I have mainly
161da177e4SLinus Torvalds  *   implemented what I have seen with SniffUSB or found in belkin_sa.c.
171da177e4SLinus Torvalds  *   For further TODOs check also belkin_sa.c.
181da177e4SLinus Torvalds  */
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include <linux/kernel.h>
211da177e4SLinus Torvalds #include <linux/errno.h>
221da177e4SLinus Torvalds #include <linux/slab.h>
231da177e4SLinus Torvalds #include <linux/tty.h>
241da177e4SLinus Torvalds #include <linux/tty_driver.h>
251da177e4SLinus Torvalds #include <linux/tty_flip.h>
261da177e4SLinus Torvalds #include <linux/module.h>
271da177e4SLinus Torvalds #include <linux/spinlock.h>
28e19b2560SAlan Cox #include <linux/uaccess.h>
29af2ac1a0SPete Zaitcev #include <asm/unaligned.h>
301da177e4SLinus Torvalds #include <linux/usb.h>
31a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h>
327af75af2SVadim Tsozik #include <linux/serial.h>
331da177e4SLinus Torvalds #include "mct_u232.h"
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds #define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
361da177e4SLinus Torvalds #define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds /*
391da177e4SLinus Torvalds  * Function prototypes
401da177e4SLinus Torvalds  */
41a8f2ae7aSJohan Hovold static int  mct_u232_port_probe(struct usb_serial_port *port);
42c5d1448fSUwe Kleine-König static void mct_u232_port_remove(struct usb_serial_port *remove);
43a509a7e4SAlan Cox static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port);
44335f8514SAlan Cox static void mct_u232_close(struct usb_serial_port *port);
45335f8514SAlan Cox static void mct_u232_dtr_rts(struct usb_serial_port *port, int on);
467d12e780SDavid Howells static void mct_u232_read_int_callback(struct urb *urb);
4795da310eSAlan Cox static void mct_u232_set_termios(struct tty_struct *tty,
48f6d47fe5SIlpo Järvinen 				 struct usb_serial_port *port,
49f6d47fe5SIlpo Järvinen 				 const struct ktermios *old_termios);
50*6ff58ae1SJohan Hovold static int  mct_u232_break_ctl(struct tty_struct *tty, int break_state);
5160b33c13SAlan Cox static int  mct_u232_tiocmget(struct tty_struct *tty);
5220b9d177SAlan Cox static int  mct_u232_tiocmset(struct tty_struct *tty,
53e19b2560SAlan Cox 			unsigned int set, unsigned int clear);
5495da310eSAlan Cox static void mct_u232_throttle(struct tty_struct *tty);
5595da310eSAlan Cox static void mct_u232_unthrottle(struct tty_struct *tty);
5645b844dfSDave Platt 
5745b844dfSDave Platt 
581da177e4SLinus Torvalds /*
591da177e4SLinus Torvalds  * All of the device info needed for the MCT USB-RS232 converter.
601da177e4SLinus Torvalds  */
6168e24113SGreg Kroah-Hartman static const struct usb_device_id id_table[] = {
621da177e4SLinus Torvalds 	{ USB_DEVICE(MCT_U232_VID, MCT_U232_PID) },
631da177e4SLinus Torvalds 	{ USB_DEVICE(MCT_U232_VID, MCT_U232_SITECOM_PID) },
641da177e4SLinus Torvalds 	{ USB_DEVICE(MCT_U232_VID, MCT_U232_DU_H3SP_PID) },
651da177e4SLinus Torvalds 	{ USB_DEVICE(MCT_U232_BELKIN_F5U109_VID, MCT_U232_BELKIN_F5U109_PID) },
661da177e4SLinus Torvalds 	{ }		/* Terminating entry */
671da177e4SLinus Torvalds };
6868e24113SGreg Kroah-Hartman MODULE_DEVICE_TABLE(usb, id_table);
691da177e4SLinus Torvalds 
70ea65370dSGreg Kroah-Hartman static struct usb_serial_driver mct_u232_device = {
7118fcac35SGreg Kroah-Hartman 	.driver = {
721da177e4SLinus Torvalds 		.owner =	THIS_MODULE,
73269bda1cSGreg Kroah-Hartman 		.name =		"mct_u232",
7418fcac35SGreg Kroah-Hartman 	},
75269bda1cSGreg Kroah-Hartman 	.description =	     "MCT U232",
7668e24113SGreg Kroah-Hartman 	.id_table =	     id_table,
771da177e4SLinus Torvalds 	.num_ports =	     1,
781da177e4SLinus Torvalds 	.open =		     mct_u232_open,
791da177e4SLinus Torvalds 	.close =	     mct_u232_close,
80335f8514SAlan Cox 	.dtr_rts =	     mct_u232_dtr_rts,
8145b844dfSDave Platt 	.throttle =	     mct_u232_throttle,
8245b844dfSDave Platt 	.unthrottle =	     mct_u232_unthrottle,
831da177e4SLinus Torvalds 	.read_int_callback = mct_u232_read_int_callback,
841da177e4SLinus Torvalds 	.set_termios =	     mct_u232_set_termios,
851da177e4SLinus Torvalds 	.break_ctl =	     mct_u232_break_ctl,
861da177e4SLinus Torvalds 	.tiocmget =	     mct_u232_tiocmget,
871da177e4SLinus Torvalds 	.tiocmset =	     mct_u232_tiocmset,
88128434abSJohan Hovold 	.tiocmiwait =        usb_serial_generic_tiocmiwait,
89a8f2ae7aSJohan Hovold 	.port_probe =        mct_u232_port_probe,
90a8f2ae7aSJohan Hovold 	.port_remove =       mct_u232_port_remove,
91468c740eSJohan Hovold 	.get_icount =        usb_serial_generic_get_icount,
921da177e4SLinus Torvalds };
931da177e4SLinus Torvalds 
944d2a7affSAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
954d2a7affSAlan Stern 	&mct_u232_device, NULL
964d2a7affSAlan Stern };
974d2a7affSAlan Stern 
981da177e4SLinus Torvalds struct mct_u232_private {
9935807187SJohan Hovold 	struct urb *read_urb;
1001da177e4SLinus Torvalds 	spinlock_t lock;
1011da177e4SLinus Torvalds 	unsigned int	     control_state; /* Modem Line Setting (TIOCM) */
1021da177e4SLinus Torvalds 	unsigned char        last_lcr;      /* Line Control Register */
1031da177e4SLinus Torvalds 	unsigned char	     last_lsr;      /* Line Status Register */
1041da177e4SLinus Torvalds 	unsigned char	     last_msr;      /* Modem Status Register */
10545b844dfSDave Platt 	unsigned int	     rx_flags;      /* Throttling flags */
1061da177e4SLinus Torvalds };
1071da177e4SLinus Torvalds 
10845b844dfSDave Platt #define THROTTLED		0x01
10945b844dfSDave Platt 
1101da177e4SLinus Torvalds /*
1111da177e4SLinus Torvalds  * Handle vendor specific USB requests
1121da177e4SLinus Torvalds  */
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds #define WDR_TIMEOUT 5000 /* default urb timeout */
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds /*
1171da177e4SLinus Torvalds  * Later day 2.6.0-test kernels have new baud rates like B230400 which
1181da177e4SLinus Torvalds  * we do not know how to support. We ignore them for the moment.
1191da177e4SLinus Torvalds  */
mct_u232_calculate_baud_rate(struct usb_serial * serial,speed_t value,speed_t * result)120e19b2560SAlan Cox static int mct_u232_calculate_baud_rate(struct usb_serial *serial,
121e19b2560SAlan Cox 					speed_t value, speed_t *result)
1221da177e4SLinus Torvalds {
123d0fab0ddSAlan Cox 	*result = value;
124d0fab0ddSAlan Cox 
1251da177e4SLinus Torvalds 	if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID
1261da177e4SLinus Torvalds 		|| le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) {
1271da177e4SLinus Torvalds 		switch (value) {
128e19b2560SAlan Cox 		case 300:
129e19b2560SAlan Cox 			return 0x01;
130e19b2560SAlan Cox 		case 600:
131e19b2560SAlan Cox 			return 0x02; /* this one not tested */
132e19b2560SAlan Cox 		case 1200:
133e19b2560SAlan Cox 			return 0x03;
134e19b2560SAlan Cox 		case 2400:
135e19b2560SAlan Cox 			return 0x04;
136e19b2560SAlan Cox 		case 4800:
137e19b2560SAlan Cox 			return 0x06;
138e19b2560SAlan Cox 		case 9600:
139e19b2560SAlan Cox 			return 0x08;
140e19b2560SAlan Cox 		case 19200:
141e19b2560SAlan Cox 			return 0x09;
142e19b2560SAlan Cox 		case 38400:
143e19b2560SAlan Cox 			return 0x0a;
144e19b2560SAlan Cox 		case 57600:
145e19b2560SAlan Cox 			return 0x0b;
146e19b2560SAlan Cox 		case 115200:
147e19b2560SAlan Cox 			return 0x0c;
1481da177e4SLinus Torvalds 		default:
149d0fab0ddSAlan Cox 			*result = 9600;
1501da177e4SLinus Torvalds 			return 0x08;
1511da177e4SLinus Torvalds 		}
1521da177e4SLinus Torvalds 	} else {
153d0fab0ddSAlan Cox 		/* FIXME: Can we use any divider - should we do
154d0fab0ddSAlan Cox 		   divider = 115200/value;
155d0fab0ddSAlan Cox 		   real baud = 115200/divider */
1561da177e4SLinus Torvalds 		switch (value) {
157f4244900SAlan Cox 		case 300: break;
158f4244900SAlan Cox 		case 600: break;
159f4244900SAlan Cox 		case 1200: break;
160f4244900SAlan Cox 		case 2400: break;
161f4244900SAlan Cox 		case 4800: break;
162f4244900SAlan Cox 		case 9600: break;
163f4244900SAlan Cox 		case 19200: break;
164f4244900SAlan Cox 		case 38400: break;
165f4244900SAlan Cox 		case 57600: break;
166f4244900SAlan Cox 		case 115200: break;
1671da177e4SLinus Torvalds 		default:
1681da177e4SLinus Torvalds 			value = 9600;
169d0fab0ddSAlan Cox 			*result = 9600;
1701da177e4SLinus Torvalds 		}
1711da177e4SLinus Torvalds 		return 115200/value;
1721da177e4SLinus Torvalds 	}
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds 
mct_u232_set_baud_rate(struct tty_struct * tty,struct usb_serial * serial,struct usb_serial_port * port,speed_t value)17595da310eSAlan Cox static int mct_u232_set_baud_rate(struct tty_struct *tty,
17695da310eSAlan Cox 	struct usb_serial *serial, struct usb_serial_port *port, speed_t value)
1771da177e4SLinus Torvalds {
178af2ac1a0SPete Zaitcev 	unsigned int divisor;
1791da177e4SLinus Torvalds 	int rc;
180af2ac1a0SPete Zaitcev 	unsigned char *buf;
18145b844dfSDave Platt 	unsigned char cts_enable_byte = 0;
182d0fab0ddSAlan Cox 	speed_t speed;
1831da177e4SLinus Torvalds 
184af2ac1a0SPete Zaitcev 	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
185af2ac1a0SPete Zaitcev 	if (buf == NULL)
186af2ac1a0SPete Zaitcev 		return -ENOMEM;
1871da177e4SLinus Torvalds 
188af2ac1a0SPete Zaitcev 	divisor = mct_u232_calculate_baud_rate(serial, value, &speed);
18926cede34SJohan Hovold 	put_unaligned_le32(divisor, buf);
1901da177e4SLinus Torvalds 	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
1911da177e4SLinus Torvalds 				MCT_U232_SET_BAUD_RATE_REQUEST,
1921da177e4SLinus Torvalds 				MCT_U232_SET_REQUEST_TYPE,
193af2ac1a0SPete Zaitcev 				0, 0, buf, MCT_U232_SET_BAUD_RATE_SIZE,
1941da177e4SLinus Torvalds 				WDR_TIMEOUT);
195d0fab0ddSAlan Cox 	if (rc < 0)	/*FIXME: What value speed results */
196194343d9SGreg Kroah-Hartman 		dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n",
197194343d9SGreg Kroah-Hartman 			value, rc);
198d0fab0ddSAlan Cox 	else
19995da310eSAlan Cox 		tty_encode_baud_rate(tty, speed, speed);
2004a770ccaSGreg Kroah-Hartman 	dev_dbg(&port->dev, "set_baud_rate: value: 0x%x, divisor: 0x%x\n", value, divisor);
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 	/* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which
2031da177e4SLinus Torvalds 	   always sends two extra USB 'device request' messages after the
2041da177e4SLinus Torvalds 	   'baud rate change' message.  The actual functionality of the
2051da177e4SLinus Torvalds 	   request codes in these messages is not fully understood but these
2061da177e4SLinus Torvalds 	   particular codes are never seen in any operation besides a baud
20745b844dfSDave Platt 	   rate change.  Both of these messages send a single byte of data.
20845b844dfSDave Platt 	   In the first message, the value of this byte is always zero.
20945b844dfSDave Platt 
21045b844dfSDave Platt 	   The second message has been determined experimentally to control
21145b844dfSDave Platt 	   whether data will be transmitted to a device which is not asserting
21245b844dfSDave Platt 	   the 'CTS' signal.  If the second message's data byte is zero, data
21345b844dfSDave Platt 	   will be transmitted even if 'CTS' is not asserted (i.e. no hardware
214e19b2560SAlan Cox 	   flow control).  if the second message's data byte is nonzero (a
215e19b2560SAlan Cox 	   value of 1 is used by this driver), data will not be transmitted to
216e19b2560SAlan Cox 	   a device which is not asserting 'CTS'.
21745b844dfSDave Platt 	*/
2181da177e4SLinus Torvalds 
219af2ac1a0SPete Zaitcev 	buf[0] = 0;
2201da177e4SLinus Torvalds 	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
2211da177e4SLinus Torvalds 				MCT_U232_SET_UNKNOWN1_REQUEST,
2221da177e4SLinus Torvalds 				MCT_U232_SET_REQUEST_TYPE,
223af2ac1a0SPete Zaitcev 				0, 0, buf, MCT_U232_SET_UNKNOWN1_SIZE,
2241da177e4SLinus Torvalds 				WDR_TIMEOUT);
2251da177e4SLinus Torvalds 	if (rc < 0)
226194343d9SGreg Kroah-Hartman 		dev_err(&port->dev, "Sending USB device request code %d "
227194343d9SGreg Kroah-Hartman 			"failed (error = %d)\n", MCT_U232_SET_UNKNOWN1_REQUEST,
228194343d9SGreg Kroah-Hartman 			rc);
2291da177e4SLinus Torvalds 
230e19b2560SAlan Cox 	if (port && C_CRTSCTS(tty))
23145b844dfSDave Platt 	   cts_enable_byte = 1;
23245b844dfSDave Platt 
2334a770ccaSGreg Kroah-Hartman 	dev_dbg(&port->dev, "set_baud_rate: send second control message, data = %02X\n",
234e19b2560SAlan Cox 		cts_enable_byte);
235af2ac1a0SPete Zaitcev 	buf[0] = cts_enable_byte;
2361da177e4SLinus Torvalds 	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
23745b844dfSDave Platt 			MCT_U232_SET_CTS_REQUEST,
2381da177e4SLinus Torvalds 			MCT_U232_SET_REQUEST_TYPE,
239af2ac1a0SPete Zaitcev 			0, 0, buf, MCT_U232_SET_CTS_SIZE,
2401da177e4SLinus Torvalds 			WDR_TIMEOUT);
2411da177e4SLinus Torvalds 	if (rc < 0)
242194343d9SGreg Kroah-Hartman 		dev_err(&port->dev, "Sending USB device request code %d "
243194343d9SGreg Kroah-Hartman 			"failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc);
2441da177e4SLinus Torvalds 
245af2ac1a0SPete Zaitcev 	kfree(buf);
2461da177e4SLinus Torvalds 	return rc;
2471da177e4SLinus Torvalds } /* mct_u232_set_baud_rate */
2481da177e4SLinus Torvalds 
mct_u232_set_line_ctrl(struct usb_serial_port * port,unsigned char lcr)2494a770ccaSGreg Kroah-Hartman static int mct_u232_set_line_ctrl(struct usb_serial_port *port,
2504a770ccaSGreg Kroah-Hartman 				  unsigned char lcr)
2511da177e4SLinus Torvalds {
2521da177e4SLinus Torvalds 	int rc;
253af2ac1a0SPete Zaitcev 	unsigned char *buf;
254af2ac1a0SPete Zaitcev 
255af2ac1a0SPete Zaitcev 	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
256af2ac1a0SPete Zaitcev 	if (buf == NULL)
257af2ac1a0SPete Zaitcev 		return -ENOMEM;
258af2ac1a0SPete Zaitcev 
259af2ac1a0SPete Zaitcev 	buf[0] = lcr;
2604a770ccaSGreg Kroah-Hartman 	rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
2611da177e4SLinus Torvalds 			MCT_U232_SET_LINE_CTRL_REQUEST,
2621da177e4SLinus Torvalds 			MCT_U232_SET_REQUEST_TYPE,
263af2ac1a0SPete Zaitcev 			0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE,
2641da177e4SLinus Torvalds 			WDR_TIMEOUT);
2651da177e4SLinus Torvalds 	if (rc < 0)
2664a770ccaSGreg Kroah-Hartman 		dev_err(&port->dev, "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
2674a770ccaSGreg Kroah-Hartman 	dev_dbg(&port->dev, "set_line_ctrl: 0x%x\n", lcr);
268af2ac1a0SPete Zaitcev 	kfree(buf);
2691da177e4SLinus Torvalds 	return rc;
2701da177e4SLinus Torvalds } /* mct_u232_set_line_ctrl */
2711da177e4SLinus Torvalds 
mct_u232_set_modem_ctrl(struct usb_serial_port * port,unsigned int control_state)2724a770ccaSGreg Kroah-Hartman static int mct_u232_set_modem_ctrl(struct usb_serial_port *port,
2731da177e4SLinus Torvalds 				   unsigned int control_state)
2741da177e4SLinus Torvalds {
2751da177e4SLinus Torvalds 	int rc;
276af2ac1a0SPete Zaitcev 	unsigned char mcr;
277af2ac1a0SPete Zaitcev 	unsigned char *buf;
2781da177e4SLinus Torvalds 
279af2ac1a0SPete Zaitcev 	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
280af2ac1a0SPete Zaitcev 	if (buf == NULL)
281af2ac1a0SPete Zaitcev 		return -ENOMEM;
282af2ac1a0SPete Zaitcev 
283af2ac1a0SPete Zaitcev 	mcr = MCT_U232_MCR_NONE;
2841da177e4SLinus Torvalds 	if (control_state & TIOCM_DTR)
2851da177e4SLinus Torvalds 		mcr |= MCT_U232_MCR_DTR;
2861da177e4SLinus Torvalds 	if (control_state & TIOCM_RTS)
2871da177e4SLinus Torvalds 		mcr |= MCT_U232_MCR_RTS;
2881da177e4SLinus Torvalds 
289af2ac1a0SPete Zaitcev 	buf[0] = mcr;
2904a770ccaSGreg Kroah-Hartman 	rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
2911da177e4SLinus Torvalds 			MCT_U232_SET_MODEM_CTRL_REQUEST,
2921da177e4SLinus Torvalds 			MCT_U232_SET_REQUEST_TYPE,
293af2ac1a0SPete Zaitcev 			0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE,
2941da177e4SLinus Torvalds 			WDR_TIMEOUT);
2951aa3c63cSAlan Cox 	kfree(buf);
2961aa3c63cSAlan Cox 
2974a770ccaSGreg Kroah-Hartman 	dev_dbg(&port->dev, "set_modem_ctrl: state=0x%x ==> mcr=0x%x\n", control_state, mcr);
2981da177e4SLinus Torvalds 
2991aa3c63cSAlan Cox 	if (rc < 0) {
3004a770ccaSGreg Kroah-Hartman 		dev_err(&port->dev, "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
3011da177e4SLinus Torvalds 		return rc;
3021aa3c63cSAlan Cox 	}
3031aa3c63cSAlan Cox 	return 0;
3041da177e4SLinus Torvalds } /* mct_u232_set_modem_ctrl */
3051da177e4SLinus Torvalds 
mct_u232_get_modem_stat(struct usb_serial_port * port,unsigned char * msr)3064a770ccaSGreg Kroah-Hartman static int mct_u232_get_modem_stat(struct usb_serial_port *port,
307e19b2560SAlan Cox 				   unsigned char *msr)
3081da177e4SLinus Torvalds {
3091da177e4SLinus Torvalds 	int rc;
310af2ac1a0SPete Zaitcev 	unsigned char *buf;
311af2ac1a0SPete Zaitcev 
312af2ac1a0SPete Zaitcev 	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
313af2ac1a0SPete Zaitcev 	if (buf == NULL) {
314af2ac1a0SPete Zaitcev 		*msr = 0;
315af2ac1a0SPete Zaitcev 		return -ENOMEM;
316af2ac1a0SPete Zaitcev 	}
3174a770ccaSGreg Kroah-Hartman 	rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),
3181da177e4SLinus Torvalds 			MCT_U232_GET_MODEM_STAT_REQUEST,
3191da177e4SLinus Torvalds 			MCT_U232_GET_REQUEST_TYPE,
320af2ac1a0SPete Zaitcev 			0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
3211da177e4SLinus Torvalds 			WDR_TIMEOUT);
32236356a66SJohan Hovold 	if (rc < MCT_U232_GET_MODEM_STAT_SIZE) {
3234a770ccaSGreg Kroah-Hartman 		dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc);
32436356a66SJohan Hovold 
32536356a66SJohan Hovold 		if (rc >= 0)
32636356a66SJohan Hovold 			rc = -EIO;
32736356a66SJohan Hovold 
3281da177e4SLinus Torvalds 		*msr = 0;
329af2ac1a0SPete Zaitcev 	} else {
330af2ac1a0SPete Zaitcev 		*msr = buf[0];
3311da177e4SLinus Torvalds 	}
3324a770ccaSGreg Kroah-Hartman 	dev_dbg(&port->dev, "get_modem_stat: 0x%x\n", *msr);
333af2ac1a0SPete Zaitcev 	kfree(buf);
3341da177e4SLinus Torvalds 	return rc;
3351da177e4SLinus Torvalds } /* mct_u232_get_modem_stat */
3361da177e4SLinus Torvalds 
mct_u232_msr_to_icount(struct async_icount * icount,unsigned char msr)3377af75af2SVadim Tsozik static void mct_u232_msr_to_icount(struct async_icount *icount,
3387af75af2SVadim Tsozik 						unsigned char msr)
3397af75af2SVadim Tsozik {
3407af75af2SVadim Tsozik 	/* Translate Control Line states */
3417af75af2SVadim Tsozik 	if (msr & MCT_U232_MSR_DDSR)
3427af75af2SVadim Tsozik 		icount->dsr++;
3437af75af2SVadim Tsozik 	if (msr & MCT_U232_MSR_DCTS)
3447af75af2SVadim Tsozik 		icount->cts++;
3457af75af2SVadim Tsozik 	if (msr & MCT_U232_MSR_DRI)
3467af75af2SVadim Tsozik 		icount->rng++;
3477af75af2SVadim Tsozik 	if (msr & MCT_U232_MSR_DCD)
3487af75af2SVadim Tsozik 		icount->dcd++;
3497af75af2SVadim Tsozik } /* mct_u232_msr_to_icount */
3507af75af2SVadim Tsozik 
mct_u232_msr_to_state(struct usb_serial_port * port,unsigned int * control_state,unsigned char msr)3514a770ccaSGreg Kroah-Hartman static void mct_u232_msr_to_state(struct usb_serial_port *port,
3524a770ccaSGreg Kroah-Hartman 				  unsigned int *control_state, unsigned char msr)
3531da177e4SLinus Torvalds {
3541da177e4SLinus Torvalds 	/* Translate Control Line states */
3551da177e4SLinus Torvalds 	if (msr & MCT_U232_MSR_DSR)
3561da177e4SLinus Torvalds 		*control_state |=  TIOCM_DSR;
3571da177e4SLinus Torvalds 	else
3581da177e4SLinus Torvalds 		*control_state &= ~TIOCM_DSR;
3591da177e4SLinus Torvalds 	if (msr & MCT_U232_MSR_CTS)
3601da177e4SLinus Torvalds 		*control_state |=  TIOCM_CTS;
3611da177e4SLinus Torvalds 	else
3621da177e4SLinus Torvalds 		*control_state &= ~TIOCM_CTS;
3631da177e4SLinus Torvalds 	if (msr & MCT_U232_MSR_RI)
3641da177e4SLinus Torvalds 		*control_state |=  TIOCM_RI;
3651da177e4SLinus Torvalds 	else
3661da177e4SLinus Torvalds 		*control_state &= ~TIOCM_RI;
3671da177e4SLinus Torvalds 	if (msr & MCT_U232_MSR_CD)
3681da177e4SLinus Torvalds 		*control_state |=  TIOCM_CD;
3691da177e4SLinus Torvalds 	else
3701da177e4SLinus Torvalds 		*control_state &= ~TIOCM_CD;
3714a770ccaSGreg Kroah-Hartman 	dev_dbg(&port->dev, "msr_to_state: msr=0x%x ==> state=0x%x\n", msr, *control_state);
3721da177e4SLinus Torvalds } /* mct_u232_msr_to_state */
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds /*
3751da177e4SLinus Torvalds  * Driver's tty interface functions
3761da177e4SLinus Torvalds  */
3771da177e4SLinus Torvalds 
mct_u232_port_probe(struct usb_serial_port * port)378a8f2ae7aSJohan Hovold static int mct_u232_port_probe(struct usb_serial_port *port)
3791da177e4SLinus Torvalds {
3804e9a0b05SOliver Neukum 	struct usb_serial *serial = port->serial;
3811da177e4SLinus Torvalds 	struct mct_u232_private *priv;
3821da177e4SLinus Torvalds 
3834e9a0b05SOliver Neukum 	/* check first to simplify error handling */
3844e9a0b05SOliver Neukum 	if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) {
3854e9a0b05SOliver Neukum 		dev_err(&port->dev, "expected endpoint missing\n");
3864e9a0b05SOliver Neukum 		return -ENODEV;
3874e9a0b05SOliver Neukum 	}
3884e9a0b05SOliver Neukum 
389a8f2ae7aSJohan Hovold 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
390a8f2ae7aSJohan Hovold 	if (!priv)
391a8f2ae7aSJohan Hovold 		return -ENOMEM;
392a8f2ae7aSJohan Hovold 
39335807187SJohan Hovold 	/* Use second interrupt-in endpoint for reading. */
3944e9a0b05SOliver Neukum 	priv->read_urb = serial->port[1]->interrupt_in_urb;
39535807187SJohan Hovold 	priv->read_urb->context = port;
39635807187SJohan Hovold 
397a8f2ae7aSJohan Hovold 	spin_lock_init(&priv->lock);
398a8f2ae7aSJohan Hovold 
399a8f2ae7aSJohan Hovold 	usb_set_serial_port_data(port, priv);
400a8f2ae7aSJohan Hovold 
401a8f2ae7aSJohan Hovold 	return 0;
4021da177e4SLinus Torvalds }
403a8f2ae7aSJohan Hovold 
mct_u232_port_remove(struct usb_serial_port * port)404c5d1448fSUwe Kleine-König static void mct_u232_port_remove(struct usb_serial_port *port)
405a8f2ae7aSJohan Hovold {
406a8f2ae7aSJohan Hovold 	struct mct_u232_private *priv;
407a8f2ae7aSJohan Hovold 
408a8f2ae7aSJohan Hovold 	priv = usb_get_serial_port_data(port);
409a8f2ae7aSJohan Hovold 	kfree(priv);
410a8f2ae7aSJohan Hovold }
4111da177e4SLinus Torvalds 
mct_u232_open(struct tty_struct * tty,struct usb_serial_port * port)412a509a7e4SAlan Cox static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
4131da177e4SLinus Torvalds {
4141da177e4SLinus Torvalds 	struct usb_serial *serial = port->serial;
4151da177e4SLinus Torvalds 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
4161da177e4SLinus Torvalds 	int retval = 0;
4171da177e4SLinus Torvalds 	unsigned int control_state;
4181da177e4SLinus Torvalds 	unsigned long flags;
4191da177e4SLinus Torvalds 	unsigned char last_lcr;
4201da177e4SLinus Torvalds 	unsigned char last_msr;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	/* Compensate for a hardware bug: although the Sitecom U232-P25
4231da177e4SLinus Torvalds 	 * device reports a maximum output packet size of 32 bytes,
4241da177e4SLinus Torvalds 	 * it seems to be able to accept only 16 bytes (and that's what
4251da177e4SLinus Torvalds 	 * SniffUSB says too...)
4261da177e4SLinus Torvalds 	 */
427e19b2560SAlan Cox 	if (le16_to_cpu(serial->dev->descriptor.idProduct)
428e19b2560SAlan Cox 						== MCT_U232_SITECOM_PID)
4291da177e4SLinus Torvalds 		port->bulk_out_size = 16;
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	/* Do a defined restart: the normal serial device seems to
4321da177e4SLinus Torvalds 	 * always turn on DTR and RTS here, so do the same. I'm not
4331da177e4SLinus Torvalds 	 * sure if this is really necessary. But it should not harm
4341da177e4SLinus Torvalds 	 * either.
4351da177e4SLinus Torvalds 	 */
4361da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
4379db276f8SPeter Hurley 	if (tty && C_BAUD(tty))
4381da177e4SLinus Torvalds 		priv->control_state = TIOCM_DTR | TIOCM_RTS;
4391da177e4SLinus Torvalds 	else
4401da177e4SLinus Torvalds 		priv->control_state = 0;
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	priv->last_lcr = (MCT_U232_DATA_BITS_8 |
4431da177e4SLinus Torvalds 			  MCT_U232_PARITY_NONE |
4441da177e4SLinus Torvalds 			  MCT_U232_STOP_BITS_1);
4451da177e4SLinus Torvalds 	control_state = priv->control_state;
4461da177e4SLinus Torvalds 	last_lcr = priv->last_lcr;
4471da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
4484a770ccaSGreg Kroah-Hartman 	mct_u232_set_modem_ctrl(port, control_state);
4494a770ccaSGreg Kroah-Hartman 	mct_u232_set_line_ctrl(port, last_lcr);
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds 	/* Read modem status and update control state */
4524a770ccaSGreg Kroah-Hartman 	mct_u232_get_modem_stat(port, &last_msr);
4531da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
4541da177e4SLinus Torvalds 	priv->last_msr = last_msr;
4554a770ccaSGreg Kroah-Hartman 	mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
4561da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
4571da177e4SLinus Torvalds 
45835807187SJohan Hovold 	retval = usb_submit_urb(priv->read_urb, GFP_KERNEL);
4591da177e4SLinus Torvalds 	if (retval) {
460194343d9SGreg Kroah-Hartman 		dev_err(&port->dev,
46135807187SJohan Hovold 			"usb_submit_urb(read) failed pipe 0x%x err %d\n",
4621da177e4SLinus Torvalds 			port->read_urb->pipe, retval);
4632f007de2SOliver Neukum 		goto error;
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
4672f007de2SOliver Neukum 	if (retval) {
46835807187SJohan Hovold 		usb_kill_urb(priv->read_urb);
469194343d9SGreg Kroah-Hartman 		dev_err(&port->dev,
470194343d9SGreg Kroah-Hartman 			"usb_submit_urb(read int) failed pipe 0x%x err %d",
4711da177e4SLinus Torvalds 			port->interrupt_in_urb->pipe, retval);
4722f007de2SOliver Neukum 		goto error;
4732f007de2SOliver Neukum 	}
4741da177e4SLinus Torvalds 	return 0;
4752f007de2SOliver Neukum 
4762f007de2SOliver Neukum error:
4772f007de2SOliver Neukum 	return retval;
4781da177e4SLinus Torvalds } /* mct_u232_open */
4791da177e4SLinus Torvalds 
mct_u232_dtr_rts(struct usb_serial_port * port,int on)480335f8514SAlan Cox static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
4811da177e4SLinus Torvalds {
48245b844dfSDave Platt 	unsigned int control_state;
48345b844dfSDave Platt 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
4841da177e4SLinus Torvalds 
485e33fe4d8SOliver Neukum 	spin_lock_irq(&priv->lock);
486335f8514SAlan Cox 	if (on)
487335f8514SAlan Cox 		priv->control_state |= TIOCM_DTR | TIOCM_RTS;
488335f8514SAlan Cox 	else
48945b844dfSDave Platt 		priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
49045b844dfSDave Platt 	control_state = priv->control_state;
491e33fe4d8SOliver Neukum 	spin_unlock_irq(&priv->lock);
492b2ca6990SJohan Hovold 
4934a770ccaSGreg Kroah-Hartman 	mct_u232_set_modem_ctrl(port, control_state);
49445b844dfSDave Platt }
49545b844dfSDave Platt 
mct_u232_close(struct usb_serial_port * port)496335f8514SAlan Cox static void mct_u232_close(struct usb_serial_port *port)
497335f8514SAlan Cox {
49835807187SJohan Hovold 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
49935807187SJohan Hovold 
50035807187SJohan Hovold 	usb_kill_urb(priv->read_urb);
5011da177e4SLinus Torvalds 	usb_kill_urb(port->interrupt_in_urb);
5025260e458SJohan Hovold 
5035260e458SJohan Hovold 	usb_serial_generic_close(port);
5041da177e4SLinus Torvalds } /* mct_u232_close */
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 
mct_u232_read_int_callback(struct urb * urb)5077d12e780SDavid Howells static void mct_u232_read_int_callback(struct urb *urb)
5081da177e4SLinus Torvalds {
509cdc97792SMing Lei 	struct usb_serial_port *port = urb->context;
5101da177e4SLinus Torvalds 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
5111da177e4SLinus Torvalds 	unsigned char *data = urb->transfer_buffer;
512e96da398SGreg Kroah-Hartman 	int retval;
513e96da398SGreg Kroah-Hartman 	int status = urb->status;
5141da177e4SLinus Torvalds 	unsigned long flags;
5151da177e4SLinus Torvalds 
516e96da398SGreg Kroah-Hartman 	switch (status) {
5171da177e4SLinus Torvalds 	case 0:
5181da177e4SLinus Torvalds 		/* success */
5191da177e4SLinus Torvalds 		break;
5201da177e4SLinus Torvalds 	case -ECONNRESET:
5211da177e4SLinus Torvalds 	case -ENOENT:
5221da177e4SLinus Torvalds 	case -ESHUTDOWN:
5231da177e4SLinus Torvalds 		/* this urb is terminated, clean up */
5244a770ccaSGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
525441b62c1SHarvey Harrison 			__func__, status);
5261da177e4SLinus Torvalds 		return;
5271da177e4SLinus Torvalds 	default:
5284a770ccaSGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
529441b62c1SHarvey Harrison 			__func__, status);
5301da177e4SLinus Torvalds 		goto exit;
5311da177e4SLinus Torvalds 	}
5321da177e4SLinus Torvalds 
53359d33f2fSGreg Kroah-Hartman 	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds 	/*
5361da177e4SLinus Torvalds 	 * Work-a-round: handle the 'usual' bulk-in pipe here
5371da177e4SLinus Torvalds 	 */
5381da177e4SLinus Torvalds 	if (urb->transfer_buffer_length > 2) {
5391da177e4SLinus Torvalds 		if (urb->actual_length) {
54005c7cd39SJiri Slaby 			tty_insert_flip_string(&port->port, data,
541054f2346SJiri Slaby 					urb->actual_length);
5422e124b4aSJiri Slaby 			tty_flip_buffer_push(&port->port);
5431da177e4SLinus Torvalds 		}
5441da177e4SLinus Torvalds 		goto exit;
5451da177e4SLinus Torvalds 	}
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds 	/*
5481da177e4SLinus Torvalds 	 * The interrupt-in pipe signals exceptional conditions (modem line
5491da177e4SLinus Torvalds 	 * signal changes and errors). data[0] holds MSR, data[1] holds LSR.
5501da177e4SLinus Torvalds 	 */
5511da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
5521da177e4SLinus Torvalds 	priv->last_msr = data[MCT_U232_MSR_INDEX];
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	/* Record Control Line states */
5554a770ccaSGreg Kroah-Hartman 	mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
5561da177e4SLinus Torvalds 
557468c740eSJohan Hovold 	mct_u232_msr_to_icount(&port->icount, priv->last_msr);
5587af75af2SVadim Tsozik 
5591da177e4SLinus Torvalds #if 0
560e19b2560SAlan Cox 	/* Not yet handled. See belkin_sa.c for further information */
5611da177e4SLinus Torvalds 	/* Now to report any errors */
5621da177e4SLinus Torvalds 	priv->last_lsr = data[MCT_U232_LSR_INDEX];
5631da177e4SLinus Torvalds 	/*
5641da177e4SLinus Torvalds 	 * fill in the flip buffer here, but I do not know the relation
5651da177e4SLinus Torvalds 	 * to the current/next receive buffer or characters.  I need
5661da177e4SLinus Torvalds 	 * to look in to this before committing any code.
5671da177e4SLinus Torvalds 	 */
5681da177e4SLinus Torvalds 	if (priv->last_lsr & MCT_U232_LSR_ERR) {
5694a90f09bSAlan Cox 		tty = tty_port_tty_get(&port->port);
5701da177e4SLinus Torvalds 		/* Overrun Error */
5711da177e4SLinus Torvalds 		if (priv->last_lsr & MCT_U232_LSR_OE) {
5721da177e4SLinus Torvalds 		}
5731da177e4SLinus Torvalds 		/* Parity Error */
5741da177e4SLinus Torvalds 		if (priv->last_lsr & MCT_U232_LSR_PE) {
5751da177e4SLinus Torvalds 		}
5761da177e4SLinus Torvalds 		/* Framing Error */
5771da177e4SLinus Torvalds 		if (priv->last_lsr & MCT_U232_LSR_FE) {
5781da177e4SLinus Torvalds 		}
5791da177e4SLinus Torvalds 		/* Break Indicator */
5801da177e4SLinus Torvalds 		if (priv->last_lsr & MCT_U232_LSR_BI) {
5811da177e4SLinus Torvalds 		}
5824a90f09bSAlan Cox 		tty_kref_put(tty);
5831da177e4SLinus Torvalds 	}
5841da177e4SLinus Torvalds #endif
585128434abSJohan Hovold 	wake_up_interruptible(&port->port.delta_msr_wait);
5861da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
5871da177e4SLinus Torvalds exit:
588e96da398SGreg Kroah-Hartman 	retval = usb_submit_urb(urb, GFP_ATOMIC);
589e96da398SGreg Kroah-Hartman 	if (retval)
590194343d9SGreg Kroah-Hartman 		dev_err(&port->dev,
591194343d9SGreg Kroah-Hartman 			"%s - usb_submit_urb failed with result %d\n",
592441b62c1SHarvey Harrison 			__func__, retval);
5931da177e4SLinus Torvalds } /* mct_u232_read_int_callback */
5941da177e4SLinus Torvalds 
mct_u232_set_termios(struct tty_struct * tty,struct usb_serial_port * port,const struct ktermios * old_termios)59595da310eSAlan Cox static void mct_u232_set_termios(struct tty_struct *tty,
59695da310eSAlan Cox 				 struct usb_serial_port *port,
597f6d47fe5SIlpo Järvinen 				 const struct ktermios *old_termios)
5981da177e4SLinus Torvalds {
5991da177e4SLinus Torvalds 	struct usb_serial *serial = port->serial;
6001da177e4SLinus Torvalds 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
601adc8d746SAlan Cox 	struct ktermios *termios = &tty->termios;
602d0fab0ddSAlan Cox 	unsigned int cflag = termios->c_cflag;
6031da177e4SLinus Torvalds 	unsigned int old_cflag = old_termios->c_cflag;
6041da177e4SLinus Torvalds 	unsigned long flags;
60545b844dfSDave Platt 	unsigned int control_state;
6061da177e4SLinus Torvalds 	unsigned char last_lcr;
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds 	/* get a local copy of the current port settings */
6091da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
6101da177e4SLinus Torvalds 	control_state = priv->control_state;
6111da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
6121da177e4SLinus Torvalds 	last_lcr = 0;
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	/*
6151da177e4SLinus Torvalds 	 * Update baud rate.
6161da177e4SLinus Torvalds 	 * Do not attempt to cache old rates and skip settings,
6171da177e4SLinus Torvalds 	 * disconnects screw such tricks up completely.
6181da177e4SLinus Torvalds 	 * Premature optimization is the root of all evil.
6191da177e4SLinus Torvalds 	 */
6201da177e4SLinus Torvalds 
62145b844dfSDave Platt 	/* reassert DTR and RTS on transition from B0 */
6221da177e4SLinus Torvalds 	if ((old_cflag & CBAUD) == B0) {
6234a770ccaSGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s: baud was B0\n", __func__);
62445b844dfSDave Platt 		control_state |= TIOCM_DTR | TIOCM_RTS;
6254a770ccaSGreg Kroah-Hartman 		mct_u232_set_modem_ctrl(port, control_state);
6261da177e4SLinus Torvalds 	}
6271da177e4SLinus Torvalds 
62895da310eSAlan Cox 	mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty));
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds 	if ((cflag & CBAUD) == B0) {
6314a770ccaSGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s: baud is B0\n", __func__);
6321da177e4SLinus Torvalds 		/* Drop RTS and DTR */
6331da177e4SLinus Torvalds 		control_state &= ~(TIOCM_DTR | TIOCM_RTS);
6344a770ccaSGreg Kroah-Hartman 		mct_u232_set_modem_ctrl(port, control_state);
6351da177e4SLinus Torvalds 	}
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds 	/*
6381da177e4SLinus Torvalds 	 * Update line control register (LCR)
6391da177e4SLinus Torvalds 	 */
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	/* set the parity */
6421da177e4SLinus Torvalds 	if (cflag & PARENB)
6431da177e4SLinus Torvalds 		last_lcr |= (cflag & PARODD) ?
6441da177e4SLinus Torvalds 			MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
6451da177e4SLinus Torvalds 	else
6461da177e4SLinus Torvalds 		last_lcr |= MCT_U232_PARITY_NONE;
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 	/* set the number of data bits */
6491da177e4SLinus Torvalds 	switch (cflag & CSIZE) {
6501da177e4SLinus Torvalds 	case CS5:
6511da177e4SLinus Torvalds 		last_lcr |= MCT_U232_DATA_BITS_5; break;
6521da177e4SLinus Torvalds 	case CS6:
6531da177e4SLinus Torvalds 		last_lcr |= MCT_U232_DATA_BITS_6; break;
6541da177e4SLinus Torvalds 	case CS7:
6551da177e4SLinus Torvalds 		last_lcr |= MCT_U232_DATA_BITS_7; break;
6561da177e4SLinus Torvalds 	case CS8:
6571da177e4SLinus Torvalds 		last_lcr |= MCT_U232_DATA_BITS_8; break;
6581da177e4SLinus Torvalds 	default:
659194343d9SGreg Kroah-Hartman 		dev_err(&port->dev,
660194343d9SGreg Kroah-Hartman 			"CSIZE was not CS5-CS8, using default of 8\n");
6611da177e4SLinus Torvalds 		last_lcr |= MCT_U232_DATA_BITS_8;
6621da177e4SLinus Torvalds 		break;
6631da177e4SLinus Torvalds 	}
6641da177e4SLinus Torvalds 
665d0fab0ddSAlan Cox 	termios->c_cflag &= ~CMSPAR;
666d0fab0ddSAlan Cox 
6671da177e4SLinus Torvalds 	/* set the number of stop bits */
6681da177e4SLinus Torvalds 	last_lcr |= (cflag & CSTOPB) ?
6691da177e4SLinus Torvalds 		MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
6701da177e4SLinus Torvalds 
6714a770ccaSGreg Kroah-Hartman 	mct_u232_set_line_ctrl(port, last_lcr);
6721da177e4SLinus Torvalds 
6731da177e4SLinus Torvalds 	/* save off the modified port settings */
6741da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
6751da177e4SLinus Torvalds 	priv->control_state = control_state;
6761da177e4SLinus Torvalds 	priv->last_lcr = last_lcr;
6771da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
6781da177e4SLinus Torvalds } /* mct_u232_set_termios */
6791da177e4SLinus Torvalds 
mct_u232_break_ctl(struct tty_struct * tty,int break_state)680*6ff58ae1SJohan Hovold static int mct_u232_break_ctl(struct tty_struct *tty, int break_state)
6811da177e4SLinus Torvalds {
68295da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
6831da177e4SLinus Torvalds 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
6841da177e4SLinus Torvalds 	unsigned char lcr;
6851da177e4SLinus Torvalds 	unsigned long flags;
6861da177e4SLinus Torvalds 
6871da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
6881da177e4SLinus Torvalds 	lcr = priv->last_lcr;
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds 	if (break_state)
6911da177e4SLinus Torvalds 		lcr |= MCT_U232_SET_BREAK;
6926b447f04SAlan Cox 	spin_unlock_irqrestore(&priv->lock, flags);
6931da177e4SLinus Torvalds 
694*6ff58ae1SJohan Hovold 	return mct_u232_set_line_ctrl(port, lcr);
6951da177e4SLinus Torvalds } /* mct_u232_break_ctl */
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 
mct_u232_tiocmget(struct tty_struct * tty)69860b33c13SAlan Cox static int mct_u232_tiocmget(struct tty_struct *tty)
6991da177e4SLinus Torvalds {
70095da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
7011da177e4SLinus Torvalds 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
7021da177e4SLinus Torvalds 	unsigned int control_state;
7031da177e4SLinus Torvalds 	unsigned long flags;
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
7061da177e4SLinus Torvalds 	control_state = priv->control_state;
7071da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
7081da177e4SLinus Torvalds 
7091da177e4SLinus Torvalds 	return control_state;
7101da177e4SLinus Torvalds }
7111da177e4SLinus Torvalds 
mct_u232_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)71220b9d177SAlan Cox static int mct_u232_tiocmset(struct tty_struct *tty,
7131da177e4SLinus Torvalds 			      unsigned int set, unsigned int clear)
7141da177e4SLinus Torvalds {
71595da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
7161da177e4SLinus Torvalds 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
7171da177e4SLinus Torvalds 	unsigned int control_state;
7181da177e4SLinus Torvalds 	unsigned long flags;
7191da177e4SLinus Torvalds 
7201da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
7211da177e4SLinus Torvalds 	control_state = priv->control_state;
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds 	if (set & TIOCM_RTS)
7241da177e4SLinus Torvalds 		control_state |= TIOCM_RTS;
7251da177e4SLinus Torvalds 	if (set & TIOCM_DTR)
7261da177e4SLinus Torvalds 		control_state |= TIOCM_DTR;
7271da177e4SLinus Torvalds 	if (clear & TIOCM_RTS)
7281da177e4SLinus Torvalds 		control_state &= ~TIOCM_RTS;
7291da177e4SLinus Torvalds 	if (clear & TIOCM_DTR)
7301da177e4SLinus Torvalds 		control_state &= ~TIOCM_DTR;
7311da177e4SLinus Torvalds 
7321da177e4SLinus Torvalds 	priv->control_state = control_state;
7331da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
7344a770ccaSGreg Kroah-Hartman 	return mct_u232_set_modem_ctrl(port, control_state);
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds 
mct_u232_throttle(struct tty_struct * tty)73795da310eSAlan Cox static void mct_u232_throttle(struct tty_struct *tty)
7381da177e4SLinus Torvalds {
73995da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
74045b844dfSDave Platt 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
74145b844dfSDave Platt 	unsigned int control_state;
74245b844dfSDave Platt 
74363832515SOliver Neukum 	spin_lock_irq(&priv->lock);
74445b844dfSDave Platt 	priv->rx_flags |= THROTTLED;
74545b844dfSDave Platt 	if (C_CRTSCTS(tty)) {
74645b844dfSDave Platt 		priv->control_state &= ~TIOCM_RTS;
74745b844dfSDave Platt 		control_state = priv->control_state;
74863832515SOliver Neukum 		spin_unlock_irq(&priv->lock);
7494a770ccaSGreg Kroah-Hartman 		mct_u232_set_modem_ctrl(port, control_state);
75045b844dfSDave Platt 	} else {
75163832515SOliver Neukum 		spin_unlock_irq(&priv->lock);
75245b844dfSDave Platt 	}
75345b844dfSDave Platt }
75445b844dfSDave Platt 
mct_u232_unthrottle(struct tty_struct * tty)75595da310eSAlan Cox static void mct_u232_unthrottle(struct tty_struct *tty)
75645b844dfSDave Platt {
75795da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
75845b844dfSDave Platt 	struct mct_u232_private *priv = usb_get_serial_port_data(port);
75945b844dfSDave Platt 	unsigned int control_state;
76045b844dfSDave Platt 
76163832515SOliver Neukum 	spin_lock_irq(&priv->lock);
76245b844dfSDave Platt 	if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) {
76345b844dfSDave Platt 		priv->rx_flags &= ~THROTTLED;
76445b844dfSDave Platt 		priv->control_state |= TIOCM_RTS;
76545b844dfSDave Platt 		control_state = priv->control_state;
76663832515SOliver Neukum 		spin_unlock_irq(&priv->lock);
7674a770ccaSGreg Kroah-Hartman 		mct_u232_set_modem_ctrl(port, control_state);
76845b844dfSDave Platt 	} else {
76963832515SOliver Neukum 		spin_unlock_irq(&priv->lock);
77045b844dfSDave Platt 	}
77145b844dfSDave Platt }
7721da177e4SLinus Torvalds 
77368e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table);
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds MODULE_AUTHOR(DRIVER_AUTHOR);
7761da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
7771da177e4SLinus Torvalds MODULE_LICENSE("GPL");
778