pl2303.c 18.9 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Prolific PL2303 USB to serial adaptor driver
 *
 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
 *
 * 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
 *
Linus Torvalds's avatar
Linus Torvalds committed
15 16 17
 * 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
18 19 20
 * 2001_Sep_19 gkh
 *	Added break support.
 *
Linus Torvalds's avatar
Linus Torvalds committed
21 22 23 24 25 26 27
 * 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
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
 * 2001_Jun_06 gkh
 *	finished porting to 2.4 format.
 * 
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fcntl.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>
#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
 */
Linus Torvalds's avatar
Linus Torvalds committed
62
#define DRIVER_VERSION "v0.9"
Linus Torvalds's avatar
Linus Torvalds committed
63 64 65 66 67 68
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"



static __devinitdata struct usb_device_id id_table [] = {
	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
Linus Torvalds's avatar
Linus Torvalds committed
69 70
	{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
	{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
Linus Torvalds's avatar
Linus Torvalds committed
71 72 73 74 75 76 77
	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
	{ }					/* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, id_table);


Linus Torvalds's avatar
Linus Torvalds committed
78 79 80 81 82
#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
83 84
#define CONTROL_DTR			0x01
#define CONTROL_RTS			0x02
Linus Torvalds's avatar
Linus Torvalds committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

#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

Linus Torvalds's avatar
Linus Torvalds committed
100 101 102 103 104 105 106 107 108 109 110 111 112
/* 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);
static void pl2303_read_int_callback (struct urb *urb);
static void pl2303_read_bulk_callback (struct urb *urb);
static void pl2303_write_bulk_callback (struct urb *urb);
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);
Linus Torvalds's avatar
Linus Torvalds committed
113
static int pl2303_startup (struct usb_serial *serial);
Linus Torvalds's avatar
Linus Torvalds committed
114
static void pl2303_shutdown (struct usb_serial *serial);
Linus Torvalds's avatar
Linus Torvalds committed
115 116 117 118


/* All of the device info needed for the PL2303 SIO serial converter */
static struct usb_serial_device_type pl2303_device = {
Linus Torvalds's avatar
Linus Torvalds committed
119
	owner:			THIS_MODULE,
Linus Torvalds's avatar
Linus Torvalds committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
	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,
	read_bulk_callback:	pl2303_read_bulk_callback,
	read_int_callback:	pl2303_read_int_callback,
	write_bulk_callback:	pl2303_write_bulk_callback,
Linus Torvalds's avatar
Linus Torvalds committed
135
	startup:		pl2303_startup,
Linus Torvalds's avatar
Linus Torvalds committed
136
	shutdown:		pl2303_shutdown,
Linus Torvalds's avatar
Linus Torvalds committed
137 138
};

Linus Torvalds's avatar
Linus Torvalds committed
139 140 141 142 143 144 145 146 147
struct pl2303_private { 
	u8 line_control;
};


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

Linus Torvalds's avatar
Linus Torvalds committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
	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));
		serial->port[i].private = priv;
	}
	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);
	dbg (__FUNCTION__" - value = %d, retval = %d", value, retval);
	return retval;
}
Linus Torvalds's avatar
Linus Torvalds committed
169

Linus Torvalds's avatar
Linus Torvalds committed
170 171 172
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
173

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

Linus Torvalds's avatar
Linus Torvalds committed
176 177 178
	if (!port->tty) {
		err (__FUNCTION__ " - no tty???");
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
179 180
	}

Linus Torvalds's avatar
Linus Torvalds committed
181 182
	if (port->write_urb->status == -EINPROGRESS) {
		dbg (__FUNCTION__ " - already writing");
Linus Torvalds's avatar
Linus Torvalds committed
183 184 185
		return 0;
	}

Linus Torvalds's avatar
Linus Torvalds committed
186
	count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
Linus Torvalds's avatar
Linus Torvalds committed
187
	if (from_user) {
Linus Torvalds's avatar
Linus Torvalds committed
188 189
		if (copy_from_user (port->write_urb->transfer_buffer, buf, count))
			return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
190
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
191
		memcpy (port->write_urb->transfer_buffer, buf, count);
Linus Torvalds's avatar
Linus Torvalds committed
192
	}
Linus Torvalds's avatar
Linus Torvalds committed
193 194 195 196 197
	
	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
198
	result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
199 200 201 202 203 204
	if (result)
		err(__FUNCTION__ " - failed submitting write urb, error %d", result);
	else
		result = count;

	return result;
Linus Torvalds's avatar
Linus Torvalds committed
205 206 207 208
}



Linus Torvalds's avatar
Linus Torvalds committed
209 210
static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{
Linus Torvalds's avatar
Linus Torvalds committed
211
	struct usb_serial *serial = port->serial;
Linus Torvalds's avatar
Linus Torvalds committed
212
	struct pl2303_private *priv;
Linus Torvalds's avatar
Linus Torvalds committed
213 214
	unsigned int cflag;
	unsigned char *buf;
Linus Torvalds's avatar
Linus Torvalds committed
215 216 217
	int baud;
	int i;

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

Linus Torvalds's avatar
Linus Torvalds committed
220 221 222 223
	if ((!port->tty) || (!port->tty->termios)) {
		dbg(__FUNCTION__" - no tty structures");
		return;
	}
Linus Torvalds's avatar
Linus Torvalds committed
224

Linus Torvalds's avatar
Linus Torvalds committed
225 226 227 228 229 230 231 232 233
	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))) {
		    dbg(__FUNCTION__ " - nothing to change...");
		    return;
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
234

Linus Torvalds's avatar
Linus Torvalds committed
235 236 237 238 239 240 241
	buf = kmalloc (7, GFP_KERNEL);
	if (!buf) {
		err(__FUNCTION__ " - out of memory.");
		return;
	}
	memset (buf, 0x00, 0x07);
	
Linus Torvalds's avatar
Linus Torvalds committed
242
	i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
243 244
			     GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
			     0, 0, buf, 7, 100);
Linus Torvalds's avatar
Linus Torvalds committed
245 246 247 248 249
	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]);


	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
250 251
			     VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
			     0, 1, NULL, 0, 100);
Linus Torvalds's avatar
Linus Torvalds committed
252 253 254 255 256

	dbg ("0x40:1:0:1  %d", i);

	if (cflag & CSIZE) {
		switch (cflag & CSIZE) {
Linus Torvalds's avatar
Linus Torvalds committed
257 258 259
			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
260
			default:
Linus Torvalds's avatar
Linus Torvalds committed
261
			case CS8:	buf[6] = 8;	break;
Linus Torvalds's avatar
Linus Torvalds committed
262
		}
Linus Torvalds's avatar
Linus Torvalds committed
263
		dbg (__FUNCTION__ " - data bits = %d", buf[6]);
Linus Torvalds's avatar
Linus Torvalds committed
264 265 266 267
	}

	baud = 0;
	switch (cflag & CBAUD) {
Linus Torvalds's avatar
Linus Torvalds committed
268
		case B0:	baud = 0;	break;
Linus Torvalds's avatar
Linus Torvalds committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
		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
284
		default:
Linus Torvalds's avatar
Linus Torvalds committed
285
			err ("pl2303 driver does not support the baudrate requested (fix it)");
Linus Torvalds's avatar
Linus Torvalds committed
286 287
			break;
	}
Linus Torvalds's avatar
Linus Torvalds committed
288
	dbg (__FUNCTION__ " - baud = %d", baud);
Linus Torvalds's avatar
Linus Torvalds committed
289 290 291 292 293 294 295
	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
296
	/* For reference buf[4]=0 is 1 stop bits */
Linus Torvalds's avatar
Linus Torvalds committed
297
	/* For reference buf[4]=1 is 1.5 stop bits */
Linus Torvalds's avatar
Linus Torvalds committed
298
	/* For reference buf[4]=2 is 2 stop bits */
Linus Torvalds's avatar
Linus Torvalds committed
299 300
	if (cflag & CSTOPB) {
		buf[4] = 2;
Linus Torvalds's avatar
Linus Torvalds committed
301 302 303 304
		dbg(__FUNCTION__ " - stop bits = 2");
	} else {
		buf[4] = 0;
		dbg(__FUNCTION__ " - stop bits = 1");
Linus Torvalds's avatar
Linus Torvalds committed
305 306 307
	}

	if (cflag & PARENB) {
Linus Torvalds's avatar
Linus Torvalds committed
308 309 310
		/* 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
311 312 313 314
		/* For reference buf[5]=3 is mark parity */
		/* For reference buf[5]=4 is space parity */
		if (cflag & PARODD) {
			buf[5] = 1;
Linus Torvalds's avatar
Linus Torvalds committed
315
			dbg(__FUNCTION__ " - parity = odd");
Linus Torvalds's avatar
Linus Torvalds committed
316 317
		} else {
			buf[5] = 2;
Linus Torvalds's avatar
Linus Torvalds committed
318
			dbg(__FUNCTION__ " - parity = even");
Linus Torvalds's avatar
Linus Torvalds committed
319
		}
Linus Torvalds's avatar
Linus Torvalds committed
320 321 322
	} else {
		buf[5] = 0;
		dbg(__FUNCTION__ " - parity = none");
Linus Torvalds's avatar
Linus Torvalds committed
323 324 325
	}

	i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
Linus Torvalds's avatar
Linus Torvalds committed
326 327
			     SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, 
			     0, 0, buf, 7, 100);
Linus Torvalds's avatar
Linus Torvalds committed
328 329
	dbg ("0x21:0x20:0:0  %d", i);

Linus Torvalds's avatar
Linus Torvalds committed
330 331 332 333 334 335 336 337 338
	if (cflag && CBAUD) {
		priv = port->private;
		if ((cflag && CBAUD) == B0)
			priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
		else
			priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
		set_control_lines (serial->dev, priv->line_control);
	}
	
Linus Torvalds's avatar
Linus Torvalds committed
339 340 341
	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
342 343
			     GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
			     0, 0, buf, 7, 100);
Linus Torvalds's avatar
Linus Torvalds committed
344 345 346 347 348
	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),
Linus Torvalds's avatar
Linus Torvalds committed
349 350
				     VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST_TYPE,
				     0x0, 0x41, NULL, 0, 100);
Linus Torvalds's avatar
Linus Torvalds committed
351 352 353
		dbg ("0x40:0x1:0x0:0x41  %d", i);
	}

Linus Torvalds's avatar
Linus Torvalds committed
354
	kfree (buf);
Linus Torvalds's avatar
Linus Torvalds committed
355 356 357
}       


Linus Torvalds's avatar
Linus Torvalds committed
358 359
static int pl2303_open (struct usb_serial_port *port, struct file *filp)
{
Linus Torvalds's avatar
Linus Torvalds committed
360 361 362
	struct termios tmp_termios;
	struct usb_serial *serial = port->serial;
	unsigned char buf[10];
Linus Torvalds's avatar
Linus Torvalds committed
363
	int result;
Linus Torvalds's avatar
Linus Torvalds committed
364

Linus Torvalds's avatar
Linus Torvalds committed
365 366 367
	if (port_paranoia_check (port, __FUNCTION__))
		return -ENODEV;
		
Linus Torvalds's avatar
Linus Torvalds committed
368
	dbg (__FUNCTION__ " -  port %d", port->number);
Linus Torvalds's avatar
Linus Torvalds committed
369

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
#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);
	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1);
	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0xc0);
	SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 4);

	/* Setup termios */
	*(port->tty->termios) = tty_std_termios;
	port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;

	pl2303_set_termios (port, &tmp_termios);

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

	dbg (__FUNCTION__ " - submitting read urb");
	port->read_urb->dev = serial->dev;
	result = usb_submit_urb (port->read_urb, GFP_KERNEL);
	if (result) {
		err(__FUNCTION__ " - failed submitting read urb, error %d", result);
		pl2303_close (port, NULL);
		return -EPROTO;
	}
Linus Torvalds's avatar
Linus Torvalds committed
408

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
409 410 411 412 413 414 415
	dbg (__FUNCTION__ " - submitting interrupt urb");
	port->interrupt_in_urb->dev = serial->dev;
	result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL);
	if (result) {
		err(__FUNCTION__ " - failed submitting interrupt urb, error %d", result);
		pl2303_close (port, NULL);
		return -EPROTO;
Linus Torvalds's avatar
Linus Torvalds committed
416
	}
Linus Torvalds's avatar
Linus Torvalds committed
417 418
	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
419 420


Linus Torvalds's avatar
Linus Torvalds committed
421 422
static void pl2303_close (struct usb_serial_port *port, struct file *filp)
{
Linus Torvalds's avatar
Linus Torvalds committed
423
	struct usb_serial *serial;
Linus Torvalds's avatar
Linus Torvalds committed
424
	struct pl2303_private *priv;
Linus Torvalds's avatar
Linus Torvalds committed
425
	unsigned int c_cflag;
Linus Torvalds's avatar
Linus Torvalds committed
426
	int result;
Linus Torvalds's avatar
Linus Torvalds committed
427

Linus Torvalds's avatar
Linus Torvalds committed
428 429
	if (port_paranoia_check (port, __FUNCTION__))
		return;
Linus Torvalds's avatar
Linus Torvalds committed
430 431 432 433
	serial = get_usb_serial (port, __FUNCTION__);
	if (!serial)
		return;
	
Linus Torvalds's avatar
Linus Torvalds committed
434
	dbg (__FUNCTION__ " - port %d", port->number);
Linus Torvalds's avatar
Linus Torvalds committed
435

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
436 437 438 439 440 441 442 443
	if (serial->dev) {
		c_cflag = port->tty->termios->c_cflag;
		if (c_cflag & HUPCL) {
			/* drop DTR and RTS */
			priv = port->private;
			priv->line_control = 0;
			set_control_lines (port->serial->dev,
					   priv->line_control);
Linus Torvalds's avatar
Linus Torvalds committed
444
		}
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

		/* shutdown our urbs */
		dbg (__FUNCTION__ " - shutting down urbs");
		result = usb_unlink_urb (port->write_urb);
		if (result)
			dbg (__FUNCTION__ " - usb_unlink_urb "
			     "(write_urb) failed with reason: %d",
			     result);

		result = usb_unlink_urb (port->read_urb);
		if (result)
			dbg (__FUNCTION__ " - usb_unlink_urb "
			     "(read_urb) failed with reason: %d",
			     result);

		result = usb_unlink_urb (port->interrupt_in_urb);
		if (result)
			dbg (__FUNCTION__ " - usb_unlink_urb "
			     "(interrupt_in_urb) failed with reason: %d",
			     result);
Linus Torvalds's avatar
Linus Torvalds committed
465 466 467
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
static int set_modem_info (struct usb_serial_port *port, unsigned int cmd, unsigned int *value)
{
	struct pl2303_private *priv = port->private;
	unsigned int arg;

	if (copy_from_user(&arg, value, sizeof(int)))
		return -EFAULT;

	switch (cmd) {
		case TIOCMBIS:
			if (arg & TIOCM_RTS)
				priv->line_control |= CONTROL_RTS;
			if (arg & TIOCM_DTR)
				priv->line_control |= CONTROL_DTR;
			break;

		case TIOCMBIC:
			if (arg & TIOCM_RTS)
				priv->line_control &= ~CONTROL_RTS;
			if (arg & TIOCM_DTR)
				priv->line_control &= ~CONTROL_DTR;
			break;

		case TIOCMSET:
			/* turn off RTS and DTR and then only turn
			   on what was asked to */
			priv->line_control &= ~(CONTROL_RTS | CONTROL_DTR);
			priv->line_control |= ((arg & TIOCM_RTS) ? CONTROL_RTS : 0);
			priv->line_control |= ((arg & TIOCM_DTR) ? CONTROL_DTR : 0);
			break;
	}

	return set_control_lines (port->serial->dev, priv->line_control);
}

static int get_modem_info (struct usb_serial_port *port, unsigned int *value)
{
	struct pl2303_private *priv = port->private;
	unsigned int mcr = priv->line_control;
	unsigned int result;

	result = ((mcr & CONTROL_DTR)		? TIOCM_DTR : 0)
		  | ((mcr & CONTROL_RTS)	? TIOCM_RTS : 0);

	dbg (__FUNCTION__ " - result = %x", result);

	if (copy_to_user(value, &result, sizeof(int)))
		return -EFAULT;
	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
518

Linus Torvalds's avatar
Linus Torvalds committed
519
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
520
{
Linus Torvalds's avatar
Linus Torvalds committed
521
	dbg (__FUNCTION__" (%d) cmd = 0x%04x", port->number, cmd);
Linus Torvalds's avatar
Linus Torvalds committed
522 523 524 525

	switch (cmd) {
		
		case TIOCMGET:
Linus Torvalds's avatar
Linus Torvalds committed
526 527
			dbg (__FUNCTION__" (%d) TIOCMGET", port->number);
			return get_modem_info (port, (unsigned int *)arg);
Linus Torvalds's avatar
Linus Torvalds committed
528 529 530 531

		case TIOCMBIS:
		case TIOCMBIC:
		case TIOCMSET:
Linus Torvalds's avatar
Linus Torvalds committed
532 533
			dbg(__FUNCTION__" (%d) TIOCMSET/TIOCMBIC/TIOCMSET",  port->number);
			return set_modem_info(port, cmd, (unsigned int *) arg);
Linus Torvalds's avatar
Linus Torvalds committed
534 535

		default:
Linus Torvalds's avatar
Linus Torvalds committed
536
			dbg (__FUNCTION__" not supported = 0x%04x", cmd);
Linus Torvalds's avatar
Linus Torvalds committed
537 538 539
			break;
	}

Linus Torvalds's avatar
Linus Torvalds committed
540
	return -ENOIOCTLCMD;
Linus Torvalds's avatar
Linus Torvalds committed
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
}


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

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

	if (break_state == 0)
		state = BREAK_OFF;
	else
		state = BREAK_ON;
	dbg (__FUNCTION__" - turning break %s", state==BREAK_OFF ? "off" : "on");

	result = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
				  BREAK_REQUEST, BREAK_REQUEST_TYPE, state, 
				  0, NULL, 0, 100);
	if (result)
		dbg (__FUNCTION__" - error sending break = %d", result);
}
Linus Torvalds's avatar
Linus Torvalds committed
564 565


Linus Torvalds's avatar
Linus Torvalds committed
566
static void pl2303_shutdown (struct usb_serial *serial)
Linus Torvalds's avatar
Linus Torvalds committed
567
{
Linus Torvalds's avatar
Linus Torvalds committed
568 569 570 571 572
	int i;

	dbg (__FUNCTION__);

	for (i = 0; i < serial->num_ports; ++i)
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
573
		kfree (serial->port[i].private);
Linus Torvalds's avatar
Linus Torvalds committed
574 575 576
}


Linus Torvalds's avatar
Linus Torvalds committed
577
static void pl2303_read_int_callback (struct urb *urb)
Linus Torvalds's avatar
Linus Torvalds committed
578 579
{
	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
Linus Torvalds's avatar
Linus Torvalds committed
580
	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
581 582 583 584 585 586 587 588 589 590 591 592 593 594
	//unsigned char *data = urb->transfer_buffer;
	//int i;

//ints auto restart...

	if (!serial) {
		return;
	}

	if (urb->status) {
		urb->status = 0;
		return;
	}

Linus Torvalds's avatar
Linus Torvalds committed
595
	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
Linus Torvalds's avatar
Linus Torvalds committed
596 597 598 599 600 601 602
#if 0
//FIXME need to update state of terminal lines variable
#endif

	return;
}

Linus Torvalds's avatar
Linus Torvalds committed
603 604

static void pl2303_read_bulk_callback (struct urb *urb)
Linus Torvalds's avatar
Linus Torvalds committed
605 606
{
	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
Linus Torvalds's avatar
Linus Torvalds committed
607
	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
608 609 610
	struct tty_struct *tty;
	unsigned char *data = urb->transfer_buffer;
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
611 612 613 614 615 616
	int result;

	if (port_paranoia_check (port, __FUNCTION__))
		return;

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

	if (!serial) {
Linus Torvalds's avatar
Linus Torvalds committed
619
		dbg(__FUNCTION__ " - bad serial pointer, exiting");
Linus Torvalds's avatar
Linus Torvalds committed
620 621 622 623
		return;
	}

	if (urb->status) {
Linus Torvalds's avatar
Linus Torvalds committed
624
		dbg (__FUNCTION__ " - urb->status = %d", urb->status);
Linus Torvalds's avatar
Linus Torvalds committed
625
		if (!port->open_count) {
Linus Torvalds's avatar
Linus Torvalds committed
626 627 628 629 630 631 632 633
			dbg (__FUNCTION__ " - port is closed, exiting.");
			return;
		}
		if (urb->status == -EPROTO) {
			/* PL2303 mysteriously fails with -EPROTO reschedule the read */
			dbg (__FUNCTION__ " - caught -EPROTO, resubmitting the urb");
			urb->status = 0;
			urb->dev = serial->dev;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
634
			result = usb_submit_urb(urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
635 636 637 638 639
			if (result)
				err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
			return;
		}
		dbg (__FUNCTION__ " - unable to handle the error, exiting.");
Linus Torvalds's avatar
Linus Torvalds committed
640 641 642
		return;
	}

Linus Torvalds's avatar
Linus Torvalds committed
643
	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
Linus Torvalds's avatar
Linus Torvalds committed
644 645 646 647 648

	tty = port->tty;
	if (urb->actual_length) {
		for (i = 0; i < urb->actual_length; ++i) {
			if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
Linus Torvalds's avatar
Linus Torvalds committed
649
				tty_flip_buffer_push(tty);
Linus Torvalds's avatar
Linus Torvalds committed
650 651 652 653 654 655
			}
			tty_insert_flip_char (tty, data[i], 0);
		}
		tty_flip_buffer_push (tty);
	}

Linus Torvalds's avatar
Linus Torvalds committed
656
	/* Schedule the next read _if_ we are still open */
Linus Torvalds's avatar
Linus Torvalds committed
657
	if (port->open_count) {
Linus Torvalds's avatar
Linus Torvalds committed
658
		urb->dev = serial->dev;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
659
		result = usb_submit_urb(urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
660 661 662
		if (result)
			err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
	}
Linus Torvalds's avatar
Linus Torvalds committed
663 664 665 666 667 668

	return;
}



Linus Torvalds's avatar
Linus Torvalds committed
669
static void pl2303_write_bulk_callback (struct urb *urb)
Linus Torvalds's avatar
Linus Torvalds committed
670 671
{
	struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
Linus Torvalds's avatar
Linus Torvalds committed
672
	int result;
Linus Torvalds's avatar
Linus Torvalds committed
673

Linus Torvalds's avatar
Linus Torvalds committed
674
	if (port_paranoia_check (port, __FUNCTION__))
Linus Torvalds's avatar
Linus Torvalds committed
675
		return;
Linus Torvalds's avatar
Linus Torvalds committed
676 677 678
	
	dbg(__FUNCTION__ " - port %d", port->number);
	
Linus Torvalds's avatar
Linus Torvalds committed
679
	if (urb->status) {
Linus Torvalds's avatar
Linus Torvalds committed
680 681 682 683 684 685
		/* error in the urb, so we have to resubmit it */
		if (serial_paranoia_check (port->serial, __FUNCTION__)) {
			return;
		}
		dbg (__FUNCTION__ " - Overflow in write");
		dbg (__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
Linus Torvalds's avatar
Linus Torvalds committed
686
		port->write_urb->transfer_buffer_length = 1;
Linus Torvalds's avatar
Linus Torvalds committed
687
		port->write_urb->dev = port->serial->dev;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
688
		result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
689 690
		if (result)
			err(__FUNCTION__ " - failed resubmitting write urb, error %d", result);
Linus Torvalds's avatar
Linus Torvalds committed
691 692 693 694

		return;
	}

Linus Torvalds's avatar
Linus Torvalds committed
695 696
	queue_task(&port->tqueue, &tq_immediate);
	mark_bh(IMMEDIATE_BH);
Linus Torvalds's avatar
Linus Torvalds committed
697 698 699 700 701 702 703 704

	return;
}


static int __init pl2303_init (void)
{
	usb_serial_register (&pl2303_device);
Linus Torvalds's avatar
Linus Torvalds committed
705
	info(DRIVER_DESC " " DRIVER_VERSION);
Linus Torvalds's avatar
Linus Torvalds committed
706 707 708 709 710 711 712 713 714 715 716 717 718 719
	return 0;
}


static void __exit pl2303_exit (void)
{
	usb_serial_deregister (&pl2303_device);
}


module_init(pl2303_init);
module_exit(pl2303_exit);

MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds's avatar
Linus Torvalds committed
720
MODULE_LICENSE("GPL");
Linus Torvalds's avatar
Linus Torvalds committed
721 722 723 724

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