mct_u232.c 21.6 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0+
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver
 *
 *   Copyright (C) 2000 Wolfgang Grandegger (wolfgang@ces.ch)
 *
 * This program is largely derived from the Belkin USB Serial Adapter Driver
 * (see belkin_sa.[ch]). All of the information about the device was acquired
 * by using SniffUSB on Windows98. For technical details see mct_u232.h.
 *
 * William G. Greathouse and Greg Kroah-Hartman provided great help on how to
 * do the reverse engineering and how to write a USB serial device driver.
 *
 * TO BE DONE, TO BE CHECKED:
 *   DTR/RTS signal handling may be incomplete or incorrect. I have mainly
 *   implemented what I have seen with SniffUSB or found in belkin_sa.c.
 *   For further TODOs check also belkin_sa.c.
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
Alan Cox's avatar
Alan Cox committed
28
#include <linux/uaccess.h>
29
#include <asm/unaligned.h>
Linus Torvalds's avatar
Linus Torvalds committed
30
#include <linux/usb.h>
31
#include <linux/usb/serial.h>
32
#include <linux/serial.h>
Linus Torvalds's avatar
Linus Torvalds committed
33 34 35 36 37 38 39 40
#include "mct_u232.h"

#define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
#define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"

/*
 * Function prototypes
 */
41
static int  mct_u232_port_probe(struct usb_serial_port *port);
42
static void mct_u232_port_remove(struct usb_serial_port *remove);
43
static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port);
44 45
static void mct_u232_close(struct usb_serial_port *port);
static void mct_u232_dtr_rts(struct usb_serial_port *port, int on);
Alan Cox's avatar
Alan Cox committed
46 47
static void mct_u232_read_int_callback(struct urb *urb);
static void mct_u232_set_termios(struct tty_struct *tty,
48 49
				 struct usb_serial_port *port,
				 const struct ktermios *old_termios);
50
static int  mct_u232_break_ctl(struct tty_struct *tty, int break_state);
51
static int  mct_u232_tiocmget(struct tty_struct *tty);
52
static int  mct_u232_tiocmset(struct tty_struct *tty,
Alan Cox's avatar
Alan Cox committed
53 54 55
			unsigned int set, unsigned int clear);
static void mct_u232_throttle(struct tty_struct *tty);
static void mct_u232_unthrottle(struct tty_struct *tty);
56 57


Linus Torvalds's avatar
Linus Torvalds committed
58 59 60
/*
 * All of the device info needed for the MCT USB-RS232 converter.
 */
61
static const struct usb_device_id id_table[] = {
Linus Torvalds's avatar
Linus Torvalds committed
62 63 64 65 66 67
	{ USB_DEVICE(MCT_U232_VID, MCT_U232_PID) },
	{ USB_DEVICE(MCT_U232_VID, MCT_U232_SITECOM_PID) },
	{ USB_DEVICE(MCT_U232_VID, MCT_U232_DU_H3SP_PID) },
	{ USB_DEVICE(MCT_U232_BELKIN_F5U109_VID, MCT_U232_BELKIN_F5U109_PID) },
	{ }		/* Terminating entry */
};
68
MODULE_DEVICE_TABLE(usb, id_table);
Linus Torvalds's avatar
Linus Torvalds committed
69

70
static struct usb_serial_driver mct_u232_device = {
71 72
	.driver = {
		.owner =	THIS_MODULE,
73
		.name =		"mct_u232",
74
	},
75
	.description =	     "MCT U232",
76
	.id_table =	     id_table,
Linus Torvalds's avatar
Linus Torvalds committed
77 78 79
	.num_ports =	     1,
	.open =		     mct_u232_open,
	.close =	     mct_u232_close,
80
	.dtr_rts =	     mct_u232_dtr_rts,
81 82
	.throttle =	     mct_u232_throttle,
	.unthrottle =	     mct_u232_unthrottle,
Linus Torvalds's avatar
Linus Torvalds committed
83 84 85 86 87
	.read_int_callback = mct_u232_read_int_callback,
	.set_termios =	     mct_u232_set_termios,
	.break_ctl =	     mct_u232_break_ctl,
	.tiocmget =	     mct_u232_tiocmget,
	.tiocmset =	     mct_u232_tiocmset,
88
	.tiocmiwait =        usb_serial_generic_tiocmiwait,
89 90
	.port_probe =        mct_u232_port_probe,
	.port_remove =       mct_u232_port_remove,
91
	.get_icount =        usb_serial_generic_get_icount,
Linus Torvalds's avatar
Linus Torvalds committed
92 93
};

94 95 96 97
static struct usb_serial_driver * const serial_drivers[] = {
	&mct_u232_device, NULL
};

Linus Torvalds's avatar
Linus Torvalds committed
98
struct mct_u232_private {
99
	struct urb *read_urb;
Linus Torvalds's avatar
Linus Torvalds committed
100 101 102 103 104
	spinlock_t lock;
	unsigned int	     control_state; /* Modem Line Setting (TIOCM) */
	unsigned char        last_lcr;      /* Line Control Register */
	unsigned char	     last_lsr;      /* Line Status Register */
	unsigned char	     last_msr;      /* Modem Status Register */
105
	unsigned int	     rx_flags;      /* Throttling flags */
Linus Torvalds's avatar
Linus Torvalds committed
106 107
};

108 109
#define THROTTLED		0x01

Linus Torvalds's avatar
Linus Torvalds committed
110 111 112 113 114 115 116 117 118 119
/*
 * Handle vendor specific USB requests
 */

#define WDR_TIMEOUT 5000 /* default urb timeout */

/*
 * Later day 2.6.0-test kernels have new baud rates like B230400 which
 * we do not know how to support. We ignore them for the moment.
 */
Alan Cox's avatar
Alan Cox committed
120 121
static int mct_u232_calculate_baud_rate(struct usb_serial *serial,
					speed_t value, speed_t *result)
Linus Torvalds's avatar
Linus Torvalds committed
122
{
123 124
	*result = value;

Linus Torvalds's avatar
Linus Torvalds committed
125
	if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID
Alan Cox's avatar
Alan Cox committed
126
		|| le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) {
Linus Torvalds's avatar
Linus Torvalds committed
127
		switch (value) {
Alan Cox's avatar
Alan Cox committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
		case 300:
			return 0x01;
		case 600:
			return 0x02; /* this one not tested */
		case 1200:
			return 0x03;
		case 2400:
			return 0x04;
		case 4800:
			return 0x06;
		case 9600:
			return 0x08;
		case 19200:
			return 0x09;
		case 38400:
			return 0x0a;
		case 57600:
			return 0x0b;
		case 115200:
			return 0x0c;
Linus Torvalds's avatar
Linus Torvalds committed
148
		default:
149
			*result = 9600;
Linus Torvalds's avatar
Linus Torvalds committed
150 151 152
			return 0x08;
		}
	} else {
153 154 155
		/* FIXME: Can we use any divider - should we do
		   divider = 115200/value;
		   real baud = 115200/divider */
Linus Torvalds's avatar
Linus Torvalds committed
156
		switch (value) {
157 158 159 160 161 162 163 164 165 166 167 168
		case 300: break;
		case 600: break;
		case 1200: break;
		case 2400: break;
		case 4800: break;
		case 9600: break;
		case 19200: break;
		case 38400: break;
		case 57600: break;
		case 115200: break;
		default:
			value = 9600;
169
			*result = 9600;
Linus Torvalds's avatar
Linus Torvalds committed
170 171 172 173 174
		}
		return 115200/value;
	}
}

Alan Cox's avatar
Alan Cox committed
175 176
static int mct_u232_set_baud_rate(struct tty_struct *tty,
	struct usb_serial *serial, struct usb_serial_port *port, speed_t value)
Linus Torvalds's avatar
Linus Torvalds committed
177
{
178
	unsigned int divisor;
Alan Cox's avatar
Alan Cox committed
179
	int rc;
180
	unsigned char *buf;
Alan Cox's avatar
Alan Cox committed
181 182 183
	unsigned char cts_enable_byte = 0;
	speed_t speed;

184 185 186
	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
	if (buf == NULL)
		return -ENOMEM;
Alan Cox's avatar
Alan Cox committed
187

188
	divisor = mct_u232_calculate_baud_rate(serial, value, &speed);
189
	put_unaligned_le32(divisor, buf);
Alan Cox's avatar
Alan Cox committed
190 191 192
	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
				MCT_U232_SET_BAUD_RATE_REQUEST,
				MCT_U232_SET_REQUEST_TYPE,
193
				0, 0, buf, MCT_U232_SET_BAUD_RATE_SIZE,
Alan Cox's avatar
Alan Cox committed
194
				WDR_TIMEOUT);
195
	if (rc < 0)	/*FIXME: What value speed results */
196 197
		dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n",
			value, rc);
198
	else
Alan Cox's avatar
Alan Cox committed
199
		tty_encode_baud_rate(tty, speed, speed);
200
	dev_dbg(&port->dev, "set_baud_rate: value: 0x%x, divisor: 0x%x\n", value, divisor);
Linus Torvalds's avatar
Linus Torvalds committed
201 202 203 204 205 206

	/* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which
	   always sends two extra USB 'device request' messages after the
	   'baud rate change' message.  The actual functionality of the
	   request codes in these messages is not fully understood but these
	   particular codes are never seen in any operation besides a baud
207 208 209 210 211 212 213
	   rate change.  Both of these messages send a single byte of data.
	   In the first message, the value of this byte is always zero.

	   The second message has been determined experimentally to control
	   whether data will be transmitted to a device which is not asserting
	   the 'CTS' signal.  If the second message's data byte is zero, data
	   will be transmitted even if 'CTS' is not asserted (i.e. no hardware
Alan Cox's avatar
Alan Cox committed
214 215 216
	   flow control).  if the second message's data byte is nonzero (a
	   value of 1 is used by this driver), data will not be transmitted to
	   a device which is not asserting 'CTS'.
217
	*/
Linus Torvalds's avatar
Linus Torvalds committed
218

219
	buf[0] = 0;
Linus Torvalds's avatar
Linus Torvalds committed
220
	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
Alan Cox's avatar
Alan Cox committed
221 222
				MCT_U232_SET_UNKNOWN1_REQUEST,
				MCT_U232_SET_REQUEST_TYPE,
223
				0, 0, buf, MCT_U232_SET_UNKNOWN1_SIZE,
Alan Cox's avatar
Alan Cox committed
224
				WDR_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
225
	if (rc < 0)
226 227 228
		dev_err(&port->dev, "Sending USB device request code %d "
			"failed (error = %d)\n", MCT_U232_SET_UNKNOWN1_REQUEST,
			rc);
Linus Torvalds's avatar
Linus Torvalds committed
229

Alan Cox's avatar
Alan Cox committed
230
	if (port && C_CRTSCTS(tty))
231 232
	   cts_enable_byte = 1;

233 234
	dev_dbg(&port->dev, "set_baud_rate: send second control message, data = %02X\n",
		cts_enable_byte);
235
	buf[0] = cts_enable_byte;
Linus Torvalds's avatar
Linus Torvalds committed
236
	rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
Alan Cox's avatar
Alan Cox committed
237 238
			MCT_U232_SET_CTS_REQUEST,
			MCT_U232_SET_REQUEST_TYPE,
239
			0, 0, buf, MCT_U232_SET_CTS_SIZE,
Alan Cox's avatar
Alan Cox committed
240
			WDR_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
241
	if (rc < 0)
242 243
		dev_err(&port->dev, "Sending USB device request code %d "
			"failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc);
Linus Torvalds's avatar
Linus Torvalds committed
244

245
	kfree(buf);
Alan Cox's avatar
Alan Cox committed
246
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
247 248
} /* mct_u232_set_baud_rate */

249 250
static int mct_u232_set_line_ctrl(struct usb_serial_port *port,
				  unsigned char lcr)
Linus Torvalds's avatar
Linus Torvalds committed
251
{
Alan Cox's avatar
Alan Cox committed
252
	int rc;
253 254 255 256 257 258 259
	unsigned char *buf;

	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
	if (buf == NULL)
		return -ENOMEM;

	buf[0] = lcr;
260
	rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
Alan Cox's avatar
Alan Cox committed
261 262
			MCT_U232_SET_LINE_CTRL_REQUEST,
			MCT_U232_SET_REQUEST_TYPE,
263
			0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE,
Alan Cox's avatar
Alan Cox committed
264
			WDR_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
265
	if (rc < 0)
266 267
		dev_err(&port->dev, "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
	dev_dbg(&port->dev, "set_line_ctrl: 0x%x\n", lcr);
268
	kfree(buf);
Alan Cox's avatar
Alan Cox committed
269
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
270 271
} /* mct_u232_set_line_ctrl */

272
static int mct_u232_set_modem_ctrl(struct usb_serial_port *port,
Linus Torvalds's avatar
Linus Torvalds committed
273 274
				   unsigned int control_state)
{
Alan Cox's avatar
Alan Cox committed
275
	int rc;
276 277 278 279 280 281
	unsigned char mcr;
	unsigned char *buf;

	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
	if (buf == NULL)
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
282

283
	mcr = MCT_U232_MCR_NONE;
Linus Torvalds's avatar
Linus Torvalds committed
284 285 286 287 288
	if (control_state & TIOCM_DTR)
		mcr |= MCT_U232_MCR_DTR;
	if (control_state & TIOCM_RTS)
		mcr |= MCT_U232_MCR_RTS;

289
	buf[0] = mcr;
290
	rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
Alan Cox's avatar
Alan Cox committed
291 292
			MCT_U232_SET_MODEM_CTRL_REQUEST,
			MCT_U232_SET_REQUEST_TYPE,
293
			0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE,
Alan Cox's avatar
Alan Cox committed
294
			WDR_TIMEOUT);
295 296
	kfree(buf);

297
	dev_dbg(&port->dev, "set_modem_ctrl: state=0x%x ==> mcr=0x%x\n", control_state, mcr);
Linus Torvalds's avatar
Linus Torvalds committed
298

299
	if (rc < 0) {
300
		dev_err(&port->dev, "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
301 302 303
		return rc;
	}
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
304 305
} /* mct_u232_set_modem_ctrl */

306 307
static int mct_u232_get_modem_stat(struct usb_serial_port *port,
				   unsigned char *msr)
Linus Torvalds's avatar
Linus Torvalds committed
308
{
Alan Cox's avatar
Alan Cox committed
309
	int rc;
310 311 312 313 314 315 316
	unsigned char *buf;

	buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
	if (buf == NULL) {
		*msr = 0;
		return -ENOMEM;
	}
317
	rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),
Alan Cox's avatar
Alan Cox committed
318 319
			MCT_U232_GET_MODEM_STAT_REQUEST,
			MCT_U232_GET_REQUEST_TYPE,
320
			0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
Alan Cox's avatar
Alan Cox committed
321
			WDR_TIMEOUT);
322
	if (rc < MCT_U232_GET_MODEM_STAT_SIZE) {
323
		dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc);
324 325 326 327

		if (rc >= 0)
			rc = -EIO;

Linus Torvalds's avatar
Linus Torvalds committed
328
		*msr = 0;
329 330
	} else {
		*msr = buf[0];
Linus Torvalds's avatar
Linus Torvalds committed
331
	}
332
	dev_dbg(&port->dev, "get_modem_stat: 0x%x\n", *msr);
333
	kfree(buf);
Alan Cox's avatar
Alan Cox committed
334
	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
335 336
} /* mct_u232_get_modem_stat */

337 338 339 340 341 342 343 344 345 346 347 348 349 350
static void mct_u232_msr_to_icount(struct async_icount *icount,
						unsigned char msr)
{
	/* Translate Control Line states */
	if (msr & MCT_U232_MSR_DDSR)
		icount->dsr++;
	if (msr & MCT_U232_MSR_DCTS)
		icount->cts++;
	if (msr & MCT_U232_MSR_DRI)
		icount->rng++;
	if (msr & MCT_U232_MSR_DCD)
		icount->dcd++;
} /* mct_u232_msr_to_icount */

351 352
static void mct_u232_msr_to_state(struct usb_serial_port *port,
				  unsigned int *control_state, unsigned char msr)
Linus Torvalds's avatar
Linus Torvalds committed
353
{
Alan Cox's avatar
Alan Cox committed
354
	/* Translate Control Line states */
Linus Torvalds's avatar
Linus Torvalds committed
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
	if (msr & MCT_U232_MSR_DSR)
		*control_state |=  TIOCM_DSR;
	else
		*control_state &= ~TIOCM_DSR;
	if (msr & MCT_U232_MSR_CTS)
		*control_state |=  TIOCM_CTS;
	else
		*control_state &= ~TIOCM_CTS;
	if (msr & MCT_U232_MSR_RI)
		*control_state |=  TIOCM_RI;
	else
		*control_state &= ~TIOCM_RI;
	if (msr & MCT_U232_MSR_CD)
		*control_state |=  TIOCM_CD;
	else
		*control_state &= ~TIOCM_CD;
371
	dev_dbg(&port->dev, "msr_to_state: msr=0x%x ==> state=0x%x\n", msr, *control_state);
Linus Torvalds's avatar
Linus Torvalds committed
372 373 374 375 376 377
} /* mct_u232_msr_to_state */

/*
 * Driver's tty interface functions
 */

378 379
static int mct_u232_port_probe(struct usb_serial_port *port)
{
380
	struct usb_serial *serial = port->serial;
381 382
	struct mct_u232_private *priv;

383 384 385 386 387 388
	/* check first to simplify error handling */
	if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) {
		dev_err(&port->dev, "expected endpoint missing\n");
		return -ENODEV;
	}

389 390 391
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
392

393
	/* Use second interrupt-in endpoint for reading. */
394
	priv->read_urb = serial->port[1]->interrupt_in_urb;
395 396
	priv->read_urb->context = port;

397 398 399 400 401 402 403
	spin_lock_init(&priv->lock);

	usb_set_serial_port_data(port, priv);

	return 0;
}

404
static void mct_u232_port_remove(struct usb_serial_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
405 406
{
	struct mct_u232_private *priv;
Alan Cox's avatar
Alan Cox committed
407

408 409 410
	priv = usb_get_serial_port_data(port);
	kfree(priv);
}
Linus Torvalds's avatar
Linus Torvalds committed
411

412
static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
413 414 415 416 417 418 419 420 421 422 423 424 425 426
{
	struct usb_serial *serial = port->serial;
	struct mct_u232_private *priv = usb_get_serial_port_data(port);
	int retval = 0;
	unsigned int control_state;
	unsigned long flags;
	unsigned char last_lcr;
	unsigned char last_msr;

	/* Compensate for a hardware bug: although the Sitecom U232-P25
	 * device reports a maximum output packet size of 32 bytes,
	 * it seems to be able to accept only 16 bytes (and that's what
	 * SniffUSB says too...)
	 */
Alan Cox's avatar
Alan Cox committed
427 428
	if (le16_to_cpu(serial->dev->descriptor.idProduct)
						== MCT_U232_SITECOM_PID)
Linus Torvalds's avatar
Linus Torvalds committed
429 430
		port->bulk_out_size = 16;

Alan Cox's avatar
Alan Cox committed
431
	/* Do a defined restart: the normal serial device seems to
Linus Torvalds's avatar
Linus Torvalds committed
432 433 434 435 436
	 * always turn on DTR and RTS here, so do the same. I'm not
	 * sure if this is really necessary. But it should not harm
	 * either.
	 */
	spin_lock_irqsave(&priv->lock, flags);
437
	if (tty && C_BAUD(tty))
Linus Torvalds's avatar
Linus Torvalds committed
438 439 440
		priv->control_state = TIOCM_DTR | TIOCM_RTS;
	else
		priv->control_state = 0;
Alan Cox's avatar
Alan Cox committed
441 442

	priv->last_lcr = (MCT_U232_DATA_BITS_8 |
Linus Torvalds's avatar
Linus Torvalds committed
443 444 445 446 447
			  MCT_U232_PARITY_NONE |
			  MCT_U232_STOP_BITS_1);
	control_state = priv->control_state;
	last_lcr = priv->last_lcr;
	spin_unlock_irqrestore(&priv->lock, flags);
448 449
	mct_u232_set_modem_ctrl(port, control_state);
	mct_u232_set_line_ctrl(port, last_lcr);
Linus Torvalds's avatar
Linus Torvalds committed
450 451

	/* Read modem status and update control state */
452
	mct_u232_get_modem_stat(port, &last_msr);
Linus Torvalds's avatar
Linus Torvalds committed
453 454
	spin_lock_irqsave(&priv->lock, flags);
	priv->last_msr = last_msr;
455
	mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
Linus Torvalds's avatar
Linus Torvalds committed
456 457
	spin_unlock_irqrestore(&priv->lock, flags);

458
	retval = usb_submit_urb(priv->read_urb, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
459
	if (retval) {
460
		dev_err(&port->dev,
461
			"usb_submit_urb(read) failed pipe 0x%x err %d\n",
462
			port->read_urb->pipe, retval);
463
		goto error;
Linus Torvalds's avatar
Linus Torvalds committed
464 465 466
	}

	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
467
	if (retval) {
468
		usb_kill_urb(priv->read_urb);
469 470 471
		dev_err(&port->dev,
			"usb_submit_urb(read int) failed pipe 0x%x err %d",
			port->interrupt_in_urb->pipe, retval);
472 473
		goto error;
	}
Linus Torvalds's avatar
Linus Torvalds committed
474
	return 0;
475 476 477

error:
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
478 479
} /* mct_u232_open */

480
static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
Linus Torvalds's avatar
Linus Torvalds committed
481
{
482 483
	unsigned int control_state;
	struct mct_u232_private *priv = usb_get_serial_port_data(port);
Linus Torvalds's avatar
Linus Torvalds committed
484

485 486 487 488 489 490 491 492 493
	spin_lock_irq(&priv->lock);
	if (on)
		priv->control_state |= TIOCM_DTR | TIOCM_RTS;
	else
		priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
	control_state = priv->control_state;
	spin_unlock_irq(&priv->lock);

	mct_u232_set_modem_ctrl(port, control_state);
494
}
495

496 497
static void mct_u232_close(struct usb_serial_port *port)
{
498 499 500
	struct mct_u232_private *priv = usb_get_serial_port_data(port);

	usb_kill_urb(priv->read_urb);
501 502 503
	usb_kill_urb(port->interrupt_in_urb);

	usb_serial_generic_close(port);
Linus Torvalds's avatar
Linus Torvalds committed
504 505 506
} /* mct_u232_close */


Alan Cox's avatar
Alan Cox committed
507
static void mct_u232_read_int_callback(struct urb *urb)
Linus Torvalds's avatar
Linus Torvalds committed
508
{
509
	struct usb_serial_port *port = urb->context;
Linus Torvalds's avatar
Linus Torvalds committed
510 511
	struct mct_u232_private *priv = usb_get_serial_port_data(port);
	unsigned char *data = urb->transfer_buffer;
512 513
	int retval;
	int status = urb->status;
Linus Torvalds's avatar
Linus Torvalds committed
514 515
	unsigned long flags;

516
	switch (status) {
Linus Torvalds's avatar
Linus Torvalds committed
517 518 519 520 521 522 523
	case 0:
		/* success */
		break;
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* this urb is terminated, clean up */
524 525
		dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
			__func__, status);
Linus Torvalds's avatar
Linus Torvalds committed
526 527
		return;
	default:
528 529
		dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
			__func__, status);
Linus Torvalds's avatar
Linus Torvalds committed
530 531 532
		goto exit;
	}

533
	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
Linus Torvalds's avatar
Linus Torvalds committed
534 535 536 537 538 539

	/*
	 * Work-a-round: handle the 'usual' bulk-in pipe here
	 */
	if (urb->transfer_buffer_length > 2) {
		if (urb->actual_length) {
540 541 542
			tty_insert_flip_string(&port->port, data,
					urb->actual_length);
			tty_flip_buffer_push(&port->port);
Linus Torvalds's avatar
Linus Torvalds committed
543 544 545
		}
		goto exit;
	}
Alan Cox's avatar
Alan Cox committed
546

Linus Torvalds's avatar
Linus Torvalds committed
547 548 549 550 551 552
	/*
	 * The interrupt-in pipe signals exceptional conditions (modem line
	 * signal changes and errors). data[0] holds MSR, data[1] holds LSR.
	 */
	spin_lock_irqsave(&priv->lock, flags);
	priv->last_msr = data[MCT_U232_MSR_INDEX];
Alan Cox's avatar
Alan Cox committed
553

Linus Torvalds's avatar
Linus Torvalds committed
554
	/* Record Control Line states */
555
	mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
Linus Torvalds's avatar
Linus Torvalds committed
556

557
	mct_u232_msr_to_icount(&port->icount, priv->last_msr);
558

Linus Torvalds's avatar
Linus Torvalds committed
559
#if 0
Alan Cox's avatar
Alan Cox committed
560
	/* Not yet handled. See belkin_sa.c for further information */
Linus Torvalds's avatar
Linus Torvalds committed
561 562 563 564 565 566 567 568
	/* Now to report any errors */
	priv->last_lsr = data[MCT_U232_LSR_INDEX];
	/*
	 * fill in the flip buffer here, but I do not know the relation
	 * to the current/next receive buffer or characters.  I need
	 * to look in to this before committing any code.
	 */
	if (priv->last_lsr & MCT_U232_LSR_ERR) {
Alan Cox's avatar
Alan Cox committed
569
		tty = tty_port_tty_get(&port->port);
Linus Torvalds's avatar
Linus Torvalds committed
570 571 572 573 574 575 576 577 578 579 580 581
		/* Overrun Error */
		if (priv->last_lsr & MCT_U232_LSR_OE) {
		}
		/* Parity Error */
		if (priv->last_lsr & MCT_U232_LSR_PE) {
		}
		/* Framing Error */
		if (priv->last_lsr & MCT_U232_LSR_FE) {
		}
		/* Break Indicator */
		if (priv->last_lsr & MCT_U232_LSR_BI) {
		}
Alan Cox's avatar
Alan Cox committed
582
		tty_kref_put(tty);
Linus Torvalds's avatar
Linus Torvalds committed
583 584
	}
#endif
585
	wake_up_interruptible(&port->port.delta_msr_wait);
Linus Torvalds's avatar
Linus Torvalds committed
586 587
	spin_unlock_irqrestore(&priv->lock, flags);
exit:
Alan Cox's avatar
Alan Cox committed
588
	retval = usb_submit_urb(urb, GFP_ATOMIC);
589
	if (retval)
590 591 592
		dev_err(&port->dev,
			"%s - usb_submit_urb failed with result %d\n",
			__func__, retval);
Linus Torvalds's avatar
Linus Torvalds committed
593 594
} /* mct_u232_read_int_callback */

Alan Cox's avatar
Alan Cox committed
595 596
static void mct_u232_set_termios(struct tty_struct *tty,
				 struct usb_serial_port *port,
597
				 const struct ktermios *old_termios)
Linus Torvalds's avatar
Linus Torvalds committed
598 599 600
{
	struct usb_serial *serial = port->serial;
	struct mct_u232_private *priv = usb_get_serial_port_data(port);
601
	struct ktermios *termios = &tty->termios;
602
	unsigned int cflag = termios->c_cflag;
Linus Torvalds's avatar
Linus Torvalds committed
603 604
	unsigned int old_cflag = old_termios->c_cflag;
	unsigned long flags;
605
	unsigned int control_state;
Linus Torvalds's avatar
Linus Torvalds committed
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
	unsigned char last_lcr;

	/* get a local copy of the current port settings */
	spin_lock_irqsave(&priv->lock, flags);
	control_state = priv->control_state;
	spin_unlock_irqrestore(&priv->lock, flags);
	last_lcr = 0;

	/*
	 * Update baud rate.
	 * Do not attempt to cache old rates and skip settings,
	 * disconnects screw such tricks up completely.
	 * Premature optimization is the root of all evil.
	 */

Alan Cox's avatar
Alan Cox committed
621
	/* reassert DTR and RTS on transition from B0 */
Linus Torvalds's avatar
Linus Torvalds committed
622
	if ((old_cflag & CBAUD) == B0) {
623
		dev_dbg(&port->dev, "%s: baud was B0\n", __func__);
624
		control_state |= TIOCM_DTR | TIOCM_RTS;
625
		mct_u232_set_modem_ctrl(port, control_state);
Linus Torvalds's avatar
Linus Torvalds committed
626 627
	}

Alan Cox's avatar
Alan Cox committed
628
	mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty));
Linus Torvalds's avatar
Linus Torvalds committed
629

Alan Cox's avatar
Alan Cox committed
630
	if ((cflag & CBAUD) == B0) {
631
		dev_dbg(&port->dev, "%s: baud is B0\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
632 633
		/* Drop RTS and DTR */
		control_state &= ~(TIOCM_DTR | TIOCM_RTS);
634
		mct_u232_set_modem_ctrl(port, control_state);
Linus Torvalds's avatar
Linus Torvalds committed
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
	}

	/*
	 * Update line control register (LCR)
	 */

	/* set the parity */
	if (cflag & PARENB)
		last_lcr |= (cflag & PARODD) ?
			MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
	else
		last_lcr |= MCT_U232_PARITY_NONE;

	/* set the number of data bits */
	switch (cflag & CSIZE) {
	case CS5:
		last_lcr |= MCT_U232_DATA_BITS_5; break;
	case CS6:
		last_lcr |= MCT_U232_DATA_BITS_6; break;
	case CS7:
		last_lcr |= MCT_U232_DATA_BITS_7; break;
	case CS8:
		last_lcr |= MCT_U232_DATA_BITS_8; break;
	default:
659 660
		dev_err(&port->dev,
			"CSIZE was not CS5-CS8, using default of 8\n");
Linus Torvalds's avatar
Linus Torvalds committed
661 662 663 664
		last_lcr |= MCT_U232_DATA_BITS_8;
		break;
	}

665 666
	termios->c_cflag &= ~CMSPAR;

Linus Torvalds's avatar
Linus Torvalds committed
667 668 669 670
	/* set the number of stop bits */
	last_lcr |= (cflag & CSTOPB) ?
		MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;

671
	mct_u232_set_line_ctrl(port, last_lcr);
Linus Torvalds's avatar
Linus Torvalds committed
672 673 674 675 676 677 678 679

	/* save off the modified port settings */
	spin_lock_irqsave(&priv->lock, flags);
	priv->control_state = control_state;
	priv->last_lcr = last_lcr;
	spin_unlock_irqrestore(&priv->lock, flags);
} /* mct_u232_set_termios */

680
static int mct_u232_break_ctl(struct tty_struct *tty, int break_state)
Linus Torvalds's avatar
Linus Torvalds committed
681
{
Alan Cox's avatar
Alan Cox committed
682
	struct usb_serial_port *port = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
683 684 685 686 687 688 689 690 691
	struct mct_u232_private *priv = usb_get_serial_port_data(port);
	unsigned char lcr;
	unsigned long flags;

	spin_lock_irqsave(&priv->lock, flags);
	lcr = priv->last_lcr;

	if (break_state)
		lcr |= MCT_U232_SET_BREAK;
692
	spin_unlock_irqrestore(&priv->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
693

694
	return mct_u232_set_line_ctrl(port, lcr);
Linus Torvalds's avatar
Linus Torvalds committed
695 696 697
} /* mct_u232_break_ctl */


698
static int mct_u232_tiocmget(struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
699
{
Alan Cox's avatar
Alan Cox committed
700
	struct usb_serial_port *port = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
701 702 703
	struct mct_u232_private *priv = usb_get_serial_port_data(port);
	unsigned int control_state;
	unsigned long flags;
Alan Cox's avatar
Alan Cox committed
704

Linus Torvalds's avatar
Linus Torvalds committed
705 706 707 708 709 710 711
	spin_lock_irqsave(&priv->lock, flags);
	control_state = priv->control_state;
	spin_unlock_irqrestore(&priv->lock, flags);

	return control_state;
}

712
static int mct_u232_tiocmset(struct tty_struct *tty,
Linus Torvalds's avatar
Linus Torvalds committed
713 714
			      unsigned int set, unsigned int clear)
{
Alan Cox's avatar
Alan Cox committed
715
	struct usb_serial_port *port = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
716 717 718
	struct mct_u232_private *priv = usb_get_serial_port_data(port);
	unsigned int control_state;
	unsigned long flags;
Alan Cox's avatar
Alan Cox committed
719

Linus Torvalds's avatar
Linus Torvalds committed
720 721 722 723 724 725 726 727 728 729 730 731 732 733
	spin_lock_irqsave(&priv->lock, flags);
	control_state = priv->control_state;

	if (set & TIOCM_RTS)
		control_state |= TIOCM_RTS;
	if (set & TIOCM_DTR)
		control_state |= TIOCM_DTR;
	if (clear & TIOCM_RTS)
		control_state &= ~TIOCM_RTS;
	if (clear & TIOCM_DTR)
		control_state &= ~TIOCM_DTR;

	priv->control_state = control_state;
	spin_unlock_irqrestore(&priv->lock, flags);
734
	return mct_u232_set_modem_ctrl(port, control_state);
Linus Torvalds's avatar
Linus Torvalds committed
735 736
}

Alan Cox's avatar
Alan Cox committed
737
static void mct_u232_throttle(struct tty_struct *tty)
738
{
Alan Cox's avatar
Alan Cox committed
739
	struct usb_serial_port *port = tty->driver_data;
740 741 742
	struct mct_u232_private *priv = usb_get_serial_port_data(port);
	unsigned int control_state;

743
	spin_lock_irq(&priv->lock);
744 745
	priv->rx_flags |= THROTTLED;
	if (C_CRTSCTS(tty)) {
Alan Cox's avatar
Alan Cox committed
746 747
		priv->control_state &= ~TIOCM_RTS;
		control_state = priv->control_state;
748
		spin_unlock_irq(&priv->lock);
749
		mct_u232_set_modem_ctrl(port, control_state);
750
	} else {
751
		spin_unlock_irq(&priv->lock);
752 753 754
	}
}

Alan Cox's avatar
Alan Cox committed
755
static void mct_u232_unthrottle(struct tty_struct *tty)
756
{
Alan Cox's avatar
Alan Cox committed
757
	struct usb_serial_port *port = tty->driver_data;
758 759 760
	struct mct_u232_private *priv = usb_get_serial_port_data(port);
	unsigned int control_state;

761
	spin_lock_irq(&priv->lock);
762
	if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) {
Alan Cox's avatar
Alan Cox committed
763 764 765
		priv->rx_flags &= ~THROTTLED;
		priv->control_state |= TIOCM_RTS;
		control_state = priv->control_state;
766
		spin_unlock_irq(&priv->lock);
767
		mct_u232_set_modem_ctrl(port, control_state);
768
	} else {
769
		spin_unlock_irq(&priv->lock);
770 771
	}
}
Linus Torvalds's avatar
Linus Torvalds committed
772

773
module_usb_serial_driver(serial_drivers, id_table);
Linus Torvalds's avatar
Linus Torvalds committed
774

Alan Cox's avatar
Alan Cox committed
775 776
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds's avatar
Linus Torvalds committed
777
MODULE_LICENSE("GPL");