pl2303.c 23.7 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3
/*
 * Prolific PL2303 USB to serial adaptor driver
 *
4 5
 * Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2003 IBM Corp.
Linus Torvalds's avatar
Linus Torvalds committed
6 7 8 9 10 11 12 13 14 15
 *
 * Original driver for 2.2.x by anonymous
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 * See Documentation/usb/usb-serial.txt for more information on using this driver
 *
16 17 18 19
 * 2002_Mar_26 gkh
 *	allowed driver to work properly if there is no tty assigned to a port
 *	(this happens for serial console devices.)
 *
Linus Torvalds's avatar
Linus Torvalds committed
20 21 22
 * 2001_Oct_06 gkh
 *	Added RTS and DTR line control.  Thanks to joe@bndlg.de for parts of it.
 *
Linus Torvalds's avatar
Linus Torvalds committed
23 24 25
 * 2001_Sep_19 gkh
 *	Added break support.
 *
Linus Torvalds's avatar
Linus Torvalds committed
26 27 28 29 30 31 32
 * 2001_Aug_30 gkh
 *	fixed oops in write_bulk_callback.
 *
 * 2001_Aug_28 gkh
 *	reworked buffer logic to be like other usb-serial drivers.  Hopefully
 *	removing some reported problems.
 *
Linus Torvalds's avatar
Linus Torvalds committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
 * 2001_Jun_06 gkh
 *	finished porting to 2.4 format.
 * 
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <linux/spinlock.h>
49
#include <asm/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63
#include <linux/usb.h>

#ifdef CONFIG_USB_SERIAL_DEBUG
	static int debug = 1;
#else
	static int debug;
#endif

#include "usb-serial.h"
#include "pl2303.h"

/*
 * Version Information
 */
64
#define DRIVER_VERSION "v0.10"
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"



69
static struct usb_device_id id_table [] = {
Linus Torvalds's avatar
Linus Torvalds committed
70
	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
Linus Torvalds's avatar
Linus Torvalds committed
71 72
	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
	{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
Linus Torvalds's avatar
Linus Torvalds committed
73
	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
74
	{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
75
	{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
76
	{ USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) },
77
	{ USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) },
Dana Lacoste's avatar
Dana Lacoste committed
78
	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) },
79
	{ USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
80
	{ USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
81
	{ USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
82
	{ USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
Linus Torvalds's avatar
Linus Torvalds committed
83 84 85 86 87
	{ }					/* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, id_table);

88
static struct usb_driver pl2303_driver = {
89
	.owner =	THIS_MODULE,
90 91 92 93 94
	.name =		"pl2303",
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	id_table,
};
Linus Torvalds's avatar
Linus Torvalds committed
95

Linus Torvalds's avatar
Linus Torvalds committed
96 97 98 99 100
#define SET_LINE_REQUEST_TYPE		0x21
#define SET_LINE_REQUEST		0x20

#define SET_CONTROL_REQUEST_TYPE	0x21
#define SET_CONTROL_REQUEST		0x22
Linus Torvalds's avatar
Linus Torvalds committed
101 102
#define CONTROL_DTR			0x01
#define CONTROL_RTS			0x02
Linus Torvalds's avatar
Linus Torvalds committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

#define BREAK_REQUEST_TYPE		0x21
#define BREAK_REQUEST			0x23	
#define BREAK_ON			0xffff
#define BREAK_OFF			0x0000

#define GET_LINE_REQUEST_TYPE		0xa1
#define GET_LINE_REQUEST		0x21

#define VENDOR_WRITE_REQUEST_TYPE	0x40
#define VENDOR_WRITE_REQUEST		0x01

#define VENDOR_READ_REQUEST_TYPE	0xc0
#define VENDOR_READ_REQUEST		0x01

118 119 120 121 122 123 124 125 126 127
#define UART_STATE			0x08
#define UART_DCD			0x01
#define UART_DSR			0x02
#define UART_BREAK_ERROR		0x04
#define UART_RING			0x08
#define UART_FRAME_ERROR		0x10
#define UART_PARITY_ERROR		0x20
#define UART_OVERRUN_ERROR		0x40
#define UART_CTS			0x80

Linus Torvalds's avatar
Linus Torvalds committed
128 129 130 131 132 133 134
/* function prototypes for a PL2303 serial converter */
static int pl2303_open (struct usb_serial_port *port, struct file *filp);
static void pl2303_close (struct usb_serial_port *port, struct file *filp);
static void pl2303_set_termios (struct usb_serial_port *port,
				struct termios *old);
static int pl2303_ioctl (struct usb_serial_port *port, struct file *file,
			 unsigned int cmd, unsigned long arg);
David S. Miller's avatar
David S. Miller committed
135 136 137
static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs);
static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
Linus Torvalds's avatar
Linus Torvalds committed
138 139 140
static int pl2303_write (struct usb_serial_port *port, int from_user,
			 const unsigned char *buf, int count);
static void pl2303_break_ctl(struct usb_serial_port *port,int break_state);
141 142 143
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file);
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
			    unsigned int set, unsigned int clear);
Linus Torvalds's avatar
Linus Torvalds committed
144
static int pl2303_startup (struct usb_serial *serial);
Linus Torvalds's avatar
Linus Torvalds committed
145
static void pl2303_shutdown (struct usb_serial *serial);
Linus Torvalds's avatar
Linus Torvalds committed
146 147 148 149


/* All of the device info needed for the PL2303 SIO serial converter */
static struct usb_serial_device_type pl2303_device = {
150 151 152 153 154 155 156 157 158 159 160 161 162
	.owner =		THIS_MODULE,
	.name =			"PL-2303",
	.id_table =		id_table,
	.num_interrupt_in =	NUM_DONT_CARE,
	.num_bulk_in =		1,
	.num_bulk_out =		1,
	.num_ports =		1,
	.open =			pl2303_open,
	.close =		pl2303_close,
	.write =		pl2303_write,
	.ioctl =		pl2303_ioctl,
	.break_ctl =		pl2303_break_ctl,
	.set_termios =		pl2303_set_termios,
163 164
	.tiocmget =		pl2303_tiocmget,
	.tiocmset =		pl2303_tiocmset,
165 166 167 168 169
	.read_bulk_callback =	pl2303_read_bulk_callback,
	.read_int_callback =	pl2303_read_int_callback,
	.write_bulk_callback =	pl2303_write_bulk_callback,
	.attach =		pl2303_startup,
	.shutdown =		pl2303_shutdown,
Linus Torvalds's avatar
Linus Torvalds committed
170 171
};

172 173
struct pl2303_private {
	spinlock_t lock;
174
	wait_queue_head_t delta_msr_wait;
Linus Torvalds's avatar
Linus Torvalds committed
175
	u8 line_control;
176
	u8 line_status;
177
	u8 termios_initialized;
Linus Torvalds's avatar
Linus Torvalds committed
178 179 180 181 182 183 184
};


static int pl2303_startup (struct usb_serial *serial)
{
	struct pl2303_private *priv;
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
185

Linus Torvalds's avatar
Linus Torvalds committed
186 187 188 189 190
	for (i = 0; i < serial->num_ports; ++i) {
		priv = kmalloc (sizeof (struct pl2303_private), GFP_KERNEL);
		if (!priv)
			return -ENOMEM;
		memset (priv, 0x00, sizeof (struct pl2303_private));
191
		spin_lock_init(&priv->lock);
192
		init_waitqueue_head(&priv->delta_msr_wait);
193
		usb_set_serial_port_data(serial->port[i], priv);
Linus Torvalds's avatar
Linus Torvalds committed
194 195 196 197 198 199 200 201 202 203 204
	}
	return 0;
}

static int set_control_lines (struct usb_device *dev, u8 value)
{
	int retval;
	
	retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0),
				  SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
				  value, 0, NULL, 0, 100);
205
	dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval);
Linus Torvalds's avatar
Linus Torvalds committed
206 207
	return retval;
}
Linus Torvalds's avatar
Linus Torvalds committed
208

Linus Torvalds's avatar
Linus Torvalds committed
209 210 211
static int pl2303_write (struct usb_serial_port *port, int from_user,  const unsigned char *buf, int count)
{
	int result;
Linus Torvalds's avatar
Linus Torvalds committed
212

213
	dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
Linus Torvalds's avatar
Linus Torvalds committed
214

Linus Torvalds's avatar
Linus Torvalds committed
215
	if (port->write_urb->status == -EINPROGRESS) {
216
		dbg("%s - already writing", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
217 218 219
		return 0;
	}

Linus Torvalds's avatar
Linus Torvalds committed
220
	count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
Linus Torvalds's avatar
Linus Torvalds committed
221
	if (from_user) {
Linus Torvalds's avatar
Linus Torvalds committed
222 223
		if (copy_from_user (port->write_urb->transfer_buffer, buf, count))
			return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
224
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
225
		memcpy (port->write_urb->transfer_buffer, buf, count);
Linus Torvalds's avatar
Linus Torvalds committed
226
	}
Linus Torvalds's avatar
Linus Torvalds committed
227 228 229 230 231
	
	usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);

	port->write_urb->transfer_buffer_length = count;
	port->write_urb->dev = port->serial->dev;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
232
	result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
233
	if (result)
234
		dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
Linus Torvalds's avatar
Linus Torvalds committed
235 236 237 238
	else
		result = count;

	return result;
Linus Torvalds's avatar
Linus Torvalds committed
239 240 241 242
}



Linus Torvalds's avatar
Linus Torvalds committed
243 244
static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{
Linus Torvalds's avatar
Linus Torvalds committed
245
	struct usb_serial *serial = port->serial;
246
	struct pl2303_private *priv = usb_get_serial_port_data(port);
247
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
248 249
	unsigned int cflag;
	unsigned char *buf;
Linus Torvalds's avatar
Linus Torvalds committed
250 251
	int baud;
	int i;
252
	u8 control;
Linus Torvalds's avatar
Linus Torvalds committed
253

254
	dbg("%s -  port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
255

Linus Torvalds's avatar
Linus Torvalds committed
256
	if ((!port->tty) || (!port->tty->termios)) {
257
		dbg("%s - no tty structures", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
258 259
		return;
	}
Linus Torvalds's avatar
Linus Torvalds committed
260

261
	spin_lock_irqsave(&priv->lock, flags);
262
	if (!priv->termios_initialized) {
263 264
		*(port->tty->termios) = tty_std_termios;
		port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
265
		priv->termios_initialized = 1;
266
	}
267
	spin_unlock_irqrestore(&priv->lock, flags);
268

Linus Torvalds's avatar
Linus Torvalds committed
269 270 271 272 273
	cflag = port->tty->termios->c_cflag;
	/* check that they really want us to change something */
	if (old_termios) {
		if ((cflag == old_termios->c_cflag) &&
		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
274
		    dbg("%s - nothing to change...", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
275 276 277
		    return;
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
278

Linus Torvalds's avatar
Linus Torvalds committed
279 280
	buf = kmalloc (7, GFP_KERNEL);
	if (!buf) {
281
		dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
282 283 284 285
		return;
	}
	memset (buf, 0x00, 0x07);
	
Linus Torvalds's avatar
Linus Torvalds committed
286
	i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
287 288
			     GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
			     0, 0, buf, 7, 100);
Linus Torvalds's avatar
Linus Torvalds committed
289 290 291 292 293 294
	dbg ("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
	     buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);


	if (cflag & CSIZE) {
		switch (cflag & CSIZE) {
Linus Torvalds's avatar
Linus Torvalds committed
295 296 297
			case CS5:	buf[6] = 5;	break;
			case CS6:	buf[6] = 6;	break;
			case CS7:	buf[6] = 7;	break;
Linus Torvalds's avatar
Linus Torvalds committed
298
			default:
Linus Torvalds's avatar
Linus Torvalds committed
299
			case CS8:	buf[6] = 8;	break;
Linus Torvalds's avatar
Linus Torvalds committed
300
		}
301
		dbg("%s - data bits = %d", __FUNCTION__, buf[6]);
Linus Torvalds's avatar
Linus Torvalds committed
302 303 304 305
	}

	baud = 0;
	switch (cflag & CBAUD) {
Linus Torvalds's avatar
Linus Torvalds committed
306
		case B0:	baud = 0;	break;
Linus Torvalds's avatar
Linus Torvalds committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
		case B75:	baud = 75;	break;
		case B150:	baud = 150;	break;
		case B300:	baud = 300;	break;
		case B600:	baud = 600;	break;
		case B1200:	baud = 1200;	break;
		case B1800:	baud = 1800;	break;
		case B2400:	baud = 2400;	break;
		case B4800:	baud = 4800;	break;
		case B9600:	baud = 9600;	break;
		case B19200:	baud = 19200;	break;
		case B38400:	baud = 38400;	break;
		case B57600:	baud = 57600;	break;
		case B115200:	baud = 115200;	break;
		case B230400:	baud = 230400;	break;
		case B460800:	baud = 460800;	break;
Linus Torvalds's avatar
Linus Torvalds committed
322
		default:
323
			dev_err(&port->dev, "pl2303 driver does not support the baudrate requested (fix it)\n");
Linus Torvalds's avatar
Linus Torvalds committed
324 325
			break;
	}
326
	dbg("%s - baud = %d", __FUNCTION__, baud);
Linus Torvalds's avatar
Linus Torvalds committed
327 328 329 330 331 332 333
	if (baud) {
		buf[0] = baud & 0xff;
		buf[1] = (baud >> 8) & 0xff;
		buf[2] = (baud >> 16) & 0xff;
		buf[3] = (baud >> 24) & 0xff;
	}

Linus Torvalds's avatar
Linus Torvalds committed
334
	/* For reference buf[4]=0 is 1 stop bits */
Linus Torvalds's avatar
Linus Torvalds committed
335
	/* For reference buf[4]=1 is 1.5 stop bits */
Linus Torvalds's avatar
Linus Torvalds committed
336
	/* For reference buf[4]=2 is 2 stop bits */
Linus Torvalds's avatar
Linus Torvalds committed
337 338
	if (cflag & CSTOPB) {
		buf[4] = 2;
339
		dbg("%s - stop bits = 2", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
340 341
	} else {
		buf[4] = 0;
342
		dbg("%s - stop bits = 1", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
343 344 345
	}

	if (cflag & PARENB) {
Linus Torvalds's avatar
Linus Torvalds committed
346 347 348
		/* For reference buf[5]=0 is none parity */
		/* For reference buf[5]=1 is odd parity */
		/* For reference buf[5]=2 is even parity */
Linus Torvalds's avatar
Linus Torvalds committed
349 350 351 352
		/* For reference buf[5]=3 is mark parity */
		/* For reference buf[5]=4 is space parity */
		if (cflag & PARODD) {
			buf[5] = 1;
353
			dbg("%s - parity = odd", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
354 355
		} else {
			buf[5] = 2;
356
			dbg("%s - parity = even", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
357
		}
Linus Torvalds's avatar
Linus Torvalds committed
358 359
	} else {
		buf[5] = 0;
360
		dbg("%s - parity = none", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
361 362 363
	}

	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
364 365
			     SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, 
			     0, 0, buf, 7, 100);
Linus Torvalds's avatar
Linus Torvalds committed
366 367
	dbg ("0x21:0x20:0:0  %d", i);

368 369 370 371 372 373 374 375
	/* change control lines if we are switching to or from B0 */
	spin_lock_irqsave(&priv->lock, flags);
	control = priv->line_control;
	if ((cflag & CBAUD) == B0)
		priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
	else
		priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
	if (control != priv->line_control) {
376
		control = priv->line_control;
377
		spin_unlock_irqrestore(&priv->lock, flags);
378 379 380
		set_control_lines(serial->dev, control);
	} else {
		spin_unlock_irqrestore(&priv->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
381 382
	}
	
Linus Torvalds's avatar
Linus Torvalds committed
383 384 385
	buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;

	i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
386 387
			     GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
			     0, 0, buf, 7, 100);
Linus Torvalds's avatar
Linus Torvalds committed
388 389 390 391 392
	dbg ("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
	     buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);

	if (cflag & CRTSCTS) {
		i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
393
				     VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
Linus Torvalds's avatar
Linus Torvalds committed
394
				     0x0, 0x41, NULL, 0, 100);
Linus Torvalds's avatar
Linus Torvalds committed
395 396 397
		dbg ("0x40:0x1:0x0:0x41  %d", i);
	}

Linus Torvalds's avatar
Linus Torvalds committed
398
	kfree (buf);
Linus Torvalds's avatar
Linus Torvalds committed
399 400 401
}       


Linus Torvalds's avatar
Linus Torvalds committed
402 403
static int pl2303_open (struct usb_serial_port *port, struct file *filp)
{
Linus Torvalds's avatar
Linus Torvalds committed
404 405
	struct termios tmp_termios;
	struct usb_serial *serial = port->serial;
406
	unsigned char *buf;
Linus Torvalds's avatar
Linus Torvalds committed
407
	int result;
Linus Torvalds's avatar
Linus Torvalds committed
408

Linus Torvalds's avatar
Linus Torvalds committed
409 410 411
	if (port_paranoia_check (port, __FUNCTION__))
		return -ENODEV;
		
412
	dbg("%s -  port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
413

414 415 416
	usb_clear_halt(serial->dev, port->write_urb->pipe);
	usb_clear_halt(serial->dev, port->read_urb->pipe);

417 418 419 420
	buf = kmalloc(10, GFP_KERNEL);
	if (buf==NULL)
		return -ENOMEM;

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
#define FISH(a,b,c,d)								\
	result=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0),	\
			       b, a, c, d, buf, 1, 100);			\
	dbg("0x%x:0x%x:0x%x:0x%x  %d - %x",a,b,c,d,result,buf[0]);

#define SOUP(a,b,c,d)								\
	result=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0),	\
			       b, a, c, d, NULL, 0, 100);			\
	dbg("0x%x:0x%x:0x%x:0x%x  %d",a,b,c,d,result);

	FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 0);
	FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
	FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);
	FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1);
	FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0);
	FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0);

440 441
	kfree(buf);

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
442
	/* Setup termios */
443 444 445
	if (port->tty) {
		pl2303_set_termios (port, &tmp_termios);
	}
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
446 447 448

	//FIXME: need to assert RTS and DTR if CRTSCTS off

449
	dbg("%s - submitting read urb", __FUNCTION__);
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
450 451 452
	port->read_urb->dev = serial->dev;
	result = usb_submit_urb (port->read_urb, GFP_KERNEL);
	if (result) {
453
		dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
454 455 456
		pl2303_close (port, NULL);
		return -EPROTO;
	}
Linus Torvalds's avatar
Linus Torvalds committed
457

458
	dbg("%s - submitting interrupt urb", __FUNCTION__);
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
459 460 461
	port->interrupt_in_urb->dev = serial->dev;
	result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL);
	if (result) {
462
		dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, result);
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
463 464
		pl2303_close (port, NULL);
		return -EPROTO;
Linus Torvalds's avatar
Linus Torvalds committed
465
	}
Linus Torvalds's avatar
Linus Torvalds committed
466 467
	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
468 469


Linus Torvalds's avatar
Linus Torvalds committed
470 471
static void pl2303_close (struct usb_serial_port *port, struct file *filp)
{
Linus Torvalds's avatar
Linus Torvalds committed
472
	struct usb_serial *serial;
Linus Torvalds's avatar
Linus Torvalds committed
473
	struct pl2303_private *priv;
474
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
475
	unsigned int c_cflag;
Linus Torvalds's avatar
Linus Torvalds committed
476
	int result;
Linus Torvalds's avatar
Linus Torvalds committed
477

Linus Torvalds's avatar
Linus Torvalds committed
478 479
	if (port_paranoia_check (port, __FUNCTION__))
		return;
Linus Torvalds's avatar
Linus Torvalds committed
480 481 482 483
	serial = get_usb_serial (port, __FUNCTION__);
	if (!serial)
		return;
	
484
	dbg("%s - port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
485

486 487 488 489 490 491 492 493 494 495 496 497 498
	/* shutdown our urbs */
	dbg("%s - shutting down urbs", __FUNCTION__);
	result = usb_unlink_urb (port->write_urb);
	if (result)
		dbg("%s - usb_unlink_urb (write_urb)"
		    " failed with reason: %d", __FUNCTION__,
		     result);

	result = usb_unlink_urb (port->read_urb);
	if (result)
		dbg("%s - usb_unlink_urb (read_urb) "
		    "failed with reason: %d", __FUNCTION__,
		     result);
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
499

500 501 502 503 504 505 506 507 508 509 510
	result = usb_unlink_urb (port->interrupt_in_urb);
	if (result)
		dbg("%s - usb_unlink_urb (interrupt_in_urb)"
		    " failed with reason: %d", __FUNCTION__,
		     result);

	if (port->tty) {
		c_cflag = port->tty->termios->c_cflag;
		if (c_cflag & HUPCL) {
			/* drop DTR and RTS */
			priv = usb_get_serial_port_data(port);
511
			spin_lock_irqsave(&priv->lock, flags);
512
			priv->line_control = 0;
513
			spin_unlock_irqrestore (&priv->lock, flags);
514 515
			set_control_lines (port->serial->dev, 0);
		}
Linus Torvalds's avatar
Linus Torvalds committed
516
	}
517

Linus Torvalds's avatar
Linus Torvalds committed
518 519
}

520 521
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
			    unsigned int set, unsigned int clear)
Linus Torvalds's avatar
Linus Torvalds committed
522
{
523
	struct pl2303_private *priv = usb_get_serial_port_data(port);
524
	unsigned long flags;
525
	u8 control;
Linus Torvalds's avatar
Linus Torvalds committed
526

527
	spin_lock_irqsave (&priv->lock, flags);
528 529 530 531 532 533 534 535
	if (set & TIOCM_RTS)
		priv->line_control |= CONTROL_RTS;
	if (set & TIOCM_DTR)
		priv->line_control |= CONTROL_DTR;
	if (clear & TIOCM_RTS)
		priv->line_control &= ~CONTROL_RTS;
	if (clear & TIOCM_DTR)
		priv->line_control &= ~CONTROL_DTR;
536
	control = priv->line_control;
537
	spin_unlock_irqrestore (&priv->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
538

539
	return set_control_lines (port->serial->dev, control);
Linus Torvalds's avatar
Linus Torvalds committed
540 541
}

542
static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
543
{
544
	struct pl2303_private *priv = usb_get_serial_port_data(port);
545
	unsigned long flags;
546
	unsigned int mcr;
547
	unsigned int status;
Linus Torvalds's avatar
Linus Torvalds committed
548 549
	unsigned int result;

550 551
	dbg("%s (%d)", __FUNCTION__, port->number);

552
	spin_lock_irqsave (&priv->lock, flags);
553
	mcr = priv->line_control;
554
	status = priv->line_status;
555
	spin_unlock_irqrestore (&priv->lock, flags);
556

Linus Torvalds's avatar
Linus Torvalds committed
557
	result = ((mcr & CONTROL_DTR)		? TIOCM_DTR : 0)
558 559
		  | ((mcr & CONTROL_RTS)	? TIOCM_RTS : 0)
		  | ((status & UART_CTS)	? TIOCM_CTS : 0)
560 561 562
		  | ((status & UART_DSR)	? TIOCM_DSR : 0)
		  | ((status & UART_RING)	? TIOCM_RI  : 0)
		  | ((status & UART_DCD)	? TIOCM_CD  : 0);
Linus Torvalds's avatar
Linus Torvalds committed
563

564
	dbg("%s - result = %x", __FUNCTION__, result);
Linus Torvalds's avatar
Linus Torvalds committed
565

566
	return result;
Linus Torvalds's avatar
Linus Torvalds committed
567
}
Linus Torvalds's avatar
Linus Torvalds committed
568

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
{
	struct pl2303_private *priv = usb_get_serial_port_data(port);
	unsigned long flags;
	unsigned int prevstatus;
	unsigned int status;
	unsigned int changed;

	spin_lock_irqsave (&priv->lock, flags);
	prevstatus = priv->line_status;
	spin_unlock_irqrestore (&priv->lock, flags);

	while (1) {
		interruptible_sleep_on(&priv->delta_msr_wait);
		/* see if a signal did it */
		if (signal_pending(current))
			return -ERESTARTSYS;
		
		spin_lock_irqsave (&priv->lock, flags);
		status = priv->line_status;
		spin_unlock_irqrestore (&priv->lock, flags);
		
		changed=prevstatus^status;
		
		if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
		    ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
		    ((arg & TIOCM_CD)  && (changed & UART_DCD)) ||
		    ((arg & TIOCM_CTS) && (changed & UART_CTS)) ) {
			return 0;
		}
		prevstatus = status;
	}
	/* NOTREACHED */
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
605
static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
Linus Torvalds's avatar
Linus Torvalds committed
606
{
607
	dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
Linus Torvalds's avatar
Linus Torvalds committed
608 609

	switch (cmd) {
610 611 612 613
		case TIOCMIWAIT:
			dbg("%s (%d) TIOCMIWAIT", __FUNCTION__,  port->number);
			return wait_modem_info(port, arg);

Linus Torvalds's avatar
Linus Torvalds committed
614
		default:
615
			dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
Linus Torvalds's avatar
Linus Torvalds committed
616 617 618
			break;
	}

Linus Torvalds's avatar
Linus Torvalds committed
619
	return -ENOIOCTLCMD;
Linus Torvalds's avatar
Linus Torvalds committed
620 621 622 623 624 625 626 627
}

static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
{
	struct usb_serial *serial = port->serial;
	u16 state;
	int result;

628
	dbg("%s - port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
629 630 631 632 633

	if (break_state == 0)
		state = BREAK_OFF;
	else
		state = BREAK_ON;
634
	dbg("%s - turning break %s", state==BREAK_OFF ? "off" : "on", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
635

636
	result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
637 638 639
				  BREAK_REQUEST, BREAK_REQUEST_TYPE, state, 
				  0, NULL, 0, 100);
	if (result)
640
		dbg("%s - error sending break = %d", __FUNCTION__, result);
Linus Torvalds's avatar
Linus Torvalds committed
641
}
Linus Torvalds's avatar
Linus Torvalds committed
642 643


Linus Torvalds's avatar
Linus Torvalds committed
644
static void pl2303_shutdown (struct usb_serial *serial)
Linus Torvalds's avatar
Linus Torvalds committed
645
{
Linus Torvalds's avatar
Linus Torvalds committed
646 647
	int i;

648
	dbg("%s", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
649

650
	for (i = 0; i < serial->num_ports; ++i) {
651 652
		kfree (usb_get_serial_port_data(serial->port[i]));
		usb_set_serial_port_data(serial->port[i], NULL);
653
	}		
Linus Torvalds's avatar
Linus Torvalds committed
654 655 656
}


David S. Miller's avatar
David S. Miller committed
657
static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
658 659
{
	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
Linus Torvalds's avatar
Linus Torvalds committed
660
	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
661 662 663
	struct pl2303_private *priv = usb_get_serial_port_data(port);
	unsigned char *data = urb->transfer_buffer;
	unsigned long flags;
664 665
	int status;

666 667
	dbg("%s (%d)", __FUNCTION__, port->number);

668 669 670 671 672 673 674 675 676
	switch (urb->status) {
	case 0:
		/* success */
		break;
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* this urb is terminated, clean up */
		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
Linus Torvalds's avatar
Linus Torvalds committed
677
		return;
678 679 680
	default:
		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
		goto exit;
Linus Torvalds's avatar
Linus Torvalds committed
681 682
	}

683
	if (!serial) {
Linus Torvalds's avatar
Linus Torvalds committed
684 685 686
		return;
	}

Linus Torvalds's avatar
Linus Torvalds committed
687
	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
Linus Torvalds's avatar
Linus Torvalds committed
688

689
	if (urb->actual_length < UART_STATE)
690
		goto exit;
691

692 693 694 695 696
	/* Save off the uart status for others to look at */
	spin_lock_irqsave(&priv->lock, flags);
	priv->line_status = data[UART_STATE];
	spin_unlock_irqrestore(&priv->lock, flags);
		
697 698 699
exit:
	status = usb_submit_urb (urb, GFP_ATOMIC);
	if (status)
700
		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
701
			__FUNCTION__, status);
Linus Torvalds's avatar
Linus Torvalds committed
702 703
}

Linus Torvalds's avatar
Linus Torvalds committed
704

David S. Miller's avatar
David S. Miller committed
705
static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
706 707
{
	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
Linus Torvalds's avatar
Linus Torvalds committed
708
	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
709
	struct pl2303_private *priv = usb_get_serial_port_data(port);
Linus Torvalds's avatar
Linus Torvalds committed
710 711
	struct tty_struct *tty;
	unsigned char *data = urb->transfer_buffer;
712
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
713
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
714
	int result;
715 716
	u8 status;
	char tty_flag;
Linus Torvalds's avatar
Linus Torvalds committed
717 718 719 720

	if (port_paranoia_check (port, __FUNCTION__))
		return;

721
	dbg("%s - port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
722 723

	if (!serial) {
724
		dbg("%s - bad serial pointer, exiting", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
725 726 727 728
		return;
	}

	if (urb->status) {
729
		dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
Linus Torvalds's avatar
Linus Torvalds committed
730
		if (!port->open_count) {
731
			dbg("%s - port is closed, exiting.", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
732 733 734 735
			return;
		}
		if (urb->status == -EPROTO) {
			/* PL2303 mysteriously fails with -EPROTO reschedule the read */
736
			dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
737 738
			urb->status = 0;
			urb->dev = serial->dev;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
739
			result = usb_submit_urb(urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
740
			if (result)
741
				dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
Linus Torvalds's avatar
Linus Torvalds committed
742 743
			return;
		}
744
		dbg("%s - unable to handle the error, exiting.", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
745 746 747
		return;
	}

Linus Torvalds's avatar
Linus Torvalds committed
748
	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
Linus Torvalds's avatar
Linus Torvalds committed
749

750 751 752 753 754 755
	/* get tty_flag from status */
	tty_flag = TTY_NORMAL;

	spin_lock_irqsave(&priv->lock, flags);
	status = priv->line_status;
	spin_unlock_irqrestore(&priv->lock, flags);
756
	wake_up_interruptible (&priv->delta_msr_wait);
757 758 759 760 761 762 763 764 765 766 767

	/* break takes precedence over parity, */
	/* which takes precedence over framing errors */
	if (status & UART_BREAK_ERROR )
		tty_flag = TTY_BREAK;
	else if (status & UART_PARITY_ERROR)
		tty_flag = TTY_PARITY;
	else if (status & UART_FRAME_ERROR)
		tty_flag = TTY_FRAME;
	dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag);

Linus Torvalds's avatar
Linus Torvalds committed
768
	tty = port->tty;
769
	if (tty && urb->actual_length) {
770 771 772 773
		/* overrun is special, not associated with a char */
		if (status & UART_OVERRUN_ERROR)
			tty_insert_flip_char(tty, 0, TTY_OVERRUN);

Linus Torvalds's avatar
Linus Torvalds committed
774 775
		for (i = 0; i < urb->actual_length; ++i) {
			if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
Linus Torvalds's avatar
Linus Torvalds committed
776
				tty_flip_buffer_push(tty);
Linus Torvalds's avatar
Linus Torvalds committed
777
			}
778
			tty_insert_flip_char (tty, data[i], tty_flag);
Linus Torvalds's avatar
Linus Torvalds committed
779 780 781 782
		}
		tty_flip_buffer_push (tty);
	}

Linus Torvalds's avatar
Linus Torvalds committed
783
	/* Schedule the next read _if_ we are still open */
Linus Torvalds's avatar
Linus Torvalds committed
784
	if (port->open_count) {
Linus Torvalds's avatar
Linus Torvalds committed
785
		urb->dev = serial->dev;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
786
		result = usb_submit_urb(urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
787
		if (result)
788
			dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
Linus Torvalds's avatar
Linus Torvalds committed
789
	}
Linus Torvalds's avatar
Linus Torvalds committed
790 791 792 793 794 795

	return;
}



David S. Miller's avatar
David S. Miller committed
796
static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
Linus Torvalds's avatar
Linus Torvalds committed
797 798
{
	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
Linus Torvalds's avatar
Linus Torvalds committed
799
	int result;
Linus Torvalds's avatar
Linus Torvalds committed
800

Linus Torvalds's avatar
Linus Torvalds committed
801
	if (port_paranoia_check (port, __FUNCTION__))
Linus Torvalds's avatar
Linus Torvalds committed
802
		return;
Linus Torvalds's avatar
Linus Torvalds committed
803
	
804
	dbg("%s - port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
805
	
Linus Torvalds's avatar
Linus Torvalds committed
806
	if (urb->status) {
Linus Torvalds's avatar
Linus Torvalds committed
807 808 809 810
		/* error in the urb, so we have to resubmit it */
		if (serial_paranoia_check (port->serial, __FUNCTION__)) {
			return;
		}
811 812
		dbg("%s - Overflow in write", __FUNCTION__);
		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
Linus Torvalds's avatar
Linus Torvalds committed
813
		port->write_urb->transfer_buffer_length = 1;
Linus Torvalds's avatar
Linus Torvalds committed
814
		port->write_urb->dev = port->serial->dev;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
815
		result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
816
		if (result)
817
			dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result);
Linus Torvalds's avatar
Linus Torvalds committed
818 819 820 821

		return;
	}

Ingo Molnar's avatar
Ingo Molnar committed
822
	schedule_work(&port->work);
Linus Torvalds's avatar
Linus Torvalds committed
823 824 825 826 827
}


static int __init pl2303_init (void)
{
828 829 830 831 832 833 834
	int retval;
	retval = usb_serial_register(&pl2303_device);
	if (retval)
		goto failed_usb_serial_register;
	retval = usb_register(&pl2303_driver);
	if (retval)
		goto failed_usb_register;
Linus Torvalds's avatar
Linus Torvalds committed
835
	info(DRIVER_DESC " " DRIVER_VERSION);
Linus Torvalds's avatar
Linus Torvalds committed
836
	return 0;
837 838 839 840
failed_usb_register:
	usb_serial_deregister(&pl2303_device);
failed_usb_serial_register:
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
841 842 843 844 845
}


static void __exit pl2303_exit (void)
{
846
	usb_deregister (&pl2303_driver);
Linus Torvalds's avatar
Linus Torvalds committed
847 848 849 850 851 852 853 854
	usb_serial_deregister (&pl2303_device);
}


module_init(pl2303_init);
module_exit(pl2303_exit);

MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds's avatar
Linus Torvalds committed
855
MODULE_LICENSE("GPL");
Linus Torvalds's avatar
Linus Torvalds committed
856 857 858 859

MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");