belkin_sa.c 17.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9
/*
 * Belkin USB Serial Adapter Driver
 *
 *  Copyright (C) 2000		William Greathouse (wgreathouse@smva.com)
 *  Copyright (C) 2000-2001 	Greg Kroah-Hartman (greg@kroah.com)
 *
 *  This program is largely derived from work by the linux-usb group
 *  and associated source files.  Please see the usb/serial files for
 *  individual credits and copyrights.
Alan Cox's avatar
Alan Cox committed
10
 *
Linus Torvalds's avatar
Linus Torvalds committed
11 12 13 14 15
 * 	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.
 *
Alan Cox's avatar
Alan Cox committed
16 17
 * See Documentation/usb/usb-serial.txt for more information on using this
 * driver
Linus Torvalds's avatar
Linus Torvalds committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 * TODO:
 * -- Add true modem contol line query capability.  Currently we track the
 *    states reported by the interrupt and the states we request.
 * -- Add error reporting back to application for UART error conditions.
 *    Just point me at how to implement this and I'll do it. I've put the
 *    framework in, but haven't analyzed the "tty_flip" interface yet.
 * -- Add support for flush commands
 * -- Add everything that is missing :)
 *
 * 27-Nov-2001 gkh
 * 	compressed all the differnent device entries into 1.
 *
 * 30-May-2001 gkh
Alan Cox's avatar
Alan Cox committed
32 33
 *	switched from using spinlock to a semaphore, which fixes lots of
 *	problems.
Linus Torvalds's avatar
Linus Torvalds committed
34 35 36 37 38 39 40 41 42 43 44 45
 *
 * 08-Apr-2001 gb
 *	- Identify version on module load.
 *
 * 12-Mar-2001 gkh
 *	- Added support for the GoHubs GO-COM232 device which is the same as the
 *	  Peracom device.
 *
 * 06-Nov-2000 gkh
 *	- Added support for the old Belkin and Peracom devices.
 *	- Made the port able to be opened multiple times.
 *	- Added some defaults incase the line settings are things these devices
Alan Cox's avatar
Alan Cox committed
46
 *	  can't support.
Linus Torvalds's avatar
Linus Torvalds committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
 *
 * 18-Oct-2000 William Greathouse
 *    Released into the wild (linux-usb-devel)
 *
 * 17-Oct-2000 William Greathouse
 *    Add code to recognize firmware version and set hardware flow control
 *    appropriately.  Belkin states that firmware prior to 3.05 does not
 *    operate correctly in hardware handshake mode.  I have verified this
 *    on firmware 2.05 -- for both RTS and DTR input flow control, the control
 *    line is not reset.  The test performed by the Belkin Win* driver is
 *    to enable hardware flow control for firmware 2.06 or greater and
 *    for 1.00 or prior.  I am only enabling for 2.06 or greater.
 *
 * 12-Oct-2000 William Greathouse
 *    First cut at supporting Belkin USB Serial Adapter F5U103
 *    I did not have a copy of the original work to support this
 *    adapter, so pardon any stupid mistakes.  All of the information
 *    I am using to write this driver was acquired by using a modified
 *    UsbSnoop on Windows2000 and from examining the other USB drivers.
 */

#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/module.h>
#include <linux/spinlock.h>
Alan Cox's avatar
Alan Cox committed
77
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
78
#include <linux/usb.h>
79
#include <linux/usb/serial.h>
Linus Torvalds's avatar
Linus Torvalds committed
80 81 82 83 84 85 86 87 88 89 90 91
#include "belkin_sa.h"

static int debug;

/*
 * Version Information
 */
#define DRIVER_VERSION "v1.2"
#define DRIVER_AUTHOR "William Greathouse <wgreathouse@smva.com>"
#define DRIVER_DESC "USB Belkin Serial converter driver"

/* function prototypes for a Belkin USB Serial Adapter F5U103 */
Alan Cox's avatar
Alan Cox committed
92
static int  belkin_sa_startup(struct usb_serial *serial);
93
static void belkin_sa_release(struct usb_serial *serial);
Alan Cox's avatar
Alan Cox committed
94
static int  belkin_sa_open(struct tty_struct *tty,
95
			struct usb_serial_port *port);
96
static void belkin_sa_close(struct usb_serial_port *port);
Alan Cox's avatar
Alan Cox committed
97 98 99 100 101 102 103
static void belkin_sa_read_int_callback(struct urb *urb);
static void belkin_sa_set_termios(struct tty_struct *tty,
			struct usb_serial_port *port, struct ktermios * old);
static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state);
static int  belkin_sa_tiocmget(struct tty_struct *tty, struct file *file);
static int  belkin_sa_tiocmset(struct tty_struct *tty, struct file *file,
					unsigned int set, unsigned int clear);
Linus Torvalds's avatar
Linus Torvalds committed
104 105 106 107 108 109 110 111 112


static struct usb_device_id id_table_combined [] = {
	{ USB_DEVICE(BELKIN_SA_VID, BELKIN_SA_PID) },
	{ USB_DEVICE(BELKIN_OLD_VID, BELKIN_OLD_PID) },
	{ USB_DEVICE(PERACOM_VID, PERACOM_PID) },
	{ USB_DEVICE(GOHUBS_VID, GOHUBS_PID) },
	{ USB_DEVICE(GOHUBS_VID, HANDYLINK_PID) },
	{ USB_DEVICE(BELKIN_DOCKSTATION_VID, BELKIN_DOCKSTATION_PID) },
Alan Cox's avatar
Alan Cox committed
113
	{ }	/* Terminating entry */
Linus Torvalds's avatar
Linus Torvalds committed
114 115
};

Alan Cox's avatar
Alan Cox committed
116
MODULE_DEVICE_TABLE(usb, id_table_combined);
Linus Torvalds's avatar
Linus Torvalds committed
117 118 119 120 121 122

static struct usb_driver belkin_driver = {
	.name =		"belkin",
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	id_table_combined,
123
	.no_dynamic_id = 	1,
Linus Torvalds's avatar
Linus Torvalds committed
124 125 126
};

/* All of the device info needed for the serial converters */
127
static struct usb_serial_driver belkin_device = {
128 129
	.driver = {
		.owner =	THIS_MODULE,
130
		.name =		"belkin",
131
	},
132
	.description =		"Belkin / Peracom / GoHubs USB Serial Adapter",
133
	.usb_driver =		&belkin_driver,
Linus Torvalds's avatar
Linus Torvalds committed
134 135 136 137
	.id_table =		id_table_combined,
	.num_ports =		1,
	.open =			belkin_sa_open,
	.close =		belkin_sa_close,
Alan Cox's avatar
Alan Cox committed
138 139
	.read_int_callback =	belkin_sa_read_int_callback,
					/* How we get the status info */
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142 143 144
	.set_termios =		belkin_sa_set_termios,
	.break_ctl =		belkin_sa_break_ctl,
	.tiocmget =		belkin_sa_tiocmget,
	.tiocmset =		belkin_sa_tiocmset,
	.attach =		belkin_sa_startup,
145
	.release =		belkin_sa_release,
Linus Torvalds's avatar
Linus Torvalds committed
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
};


struct belkin_sa_private {
	spinlock_t		lock;
	unsigned long		control_state;
	unsigned char		last_lsr;
	unsigned char		last_msr;
	int			bad_flow_control;
};


/*
 * ***************************************************************************
 * Belkin USB Serial Adapter F5U103 specific driver functions
 * ***************************************************************************
 */

#define WDR_TIMEOUT 5000 /* default urb timeout */

/* assumes that struct usb_serial *serial is available */
Alan Cox's avatar
Alan Cox committed
167
#define BSA_USB_CMD(c, v) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \
Linus Torvalds's avatar
Linus Torvalds committed
168 169 170 171
					    (c), BELKIN_SA_SET_REQUEST_TYPE, \
					    (v), 0, NULL, 0, WDR_TIMEOUT)

/* do some startup allocations not currently performed by usb_serial_probe() */
Alan Cox's avatar
Alan Cox committed
172
static int belkin_sa_startup(struct usb_serial *serial)
Linus Torvalds's avatar
Linus Torvalds committed
173 174 175 176 177 178 179
{
	struct usb_device *dev = serial->dev;
	struct belkin_sa_private *priv;

	/* allocate the private data structure */
	priv = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL);
	if (!priv)
Alan Cox's avatar
Alan Cox committed
180
		return -1; /* error */
Linus Torvalds's avatar
Linus Torvalds committed
181 182 183 184 185 186
	/* set initial values for control structures */
	spin_lock_init(&priv->lock);
	priv->control_state = 0;
	priv->last_lsr = 0;
	priv->last_msr = 0;
	/* see comments at top of file */
Alan Cox's avatar
Alan Cox committed
187 188
	priv->bad_flow_control =
		(le16_to_cpu(dev->descriptor.bcdDevice) <= 0x0206) ? 1 : 0;
189
	dev_info(&dev->dev, "bcdDevice: %04x, bfc: %d\n",
Alan Cox's avatar
Alan Cox committed
190 191
					le16_to_cpu(dev->descriptor.bcdDevice),
					priv->bad_flow_control);
Linus Torvalds's avatar
Linus Torvalds committed
192 193 194

	init_waitqueue_head(&serial->port[0]->write_wait);
	usb_set_serial_port_data(serial->port[0], priv);
Alan Cox's avatar
Alan Cox committed
195 196

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
197 198 199
}


200
static void belkin_sa_release(struct usb_serial *serial)
Linus Torvalds's avatar
Linus Torvalds committed
201 202 203
{
	struct belkin_sa_private *priv;
	int i;
Alan Cox's avatar
Alan Cox committed
204 205

	dbg("%s", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
206

Alan Cox's avatar
Alan Cox committed
207
	for (i = 0; i < serial->num_ports; ++i) {
Linus Torvalds's avatar
Linus Torvalds committed
208 209
		/* My special items, the standard routines free my urbs */
		priv = usb_get_serial_port_data(serial->port[i]);
210
		kfree(priv);
Linus Torvalds's avatar
Linus Torvalds committed
211 212 213 214
	}
}


Alan Cox's avatar
Alan Cox committed
215
static int  belkin_sa_open(struct tty_struct *tty,
216
					struct usb_serial_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
217 218 219
{
	int retval = 0;

220
	dbg("%s port %d", __func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
221 222 223 224 225 226 227 228

	/*Start reading from the device*/
	/* TODO: Look at possibility of submitting multiple URBs to device to
	 *       enhance buffering.  Win trace shows 16 initial read URBs.
	 */
	port->read_urb->dev = port->serial->dev;
	retval = usb_submit_urb(port->read_urb, GFP_KERNEL);
	if (retval) {
229
		dev_err(&port->dev, "usb_submit_urb(read bulk) failed\n");
Linus Torvalds's avatar
Linus Torvalds committed
230 231 232 233 234 235 236
		goto exit;
	}

	port->interrupt_in_urb->dev = port->serial->dev;
	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
	if (retval) {
		usb_kill_urb(port->read_urb);
237
		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
Linus Torvalds's avatar
Linus Torvalds committed
238 239 240 241 242 243 244
	}

exit:
	return retval;
} /* belkin_sa_open */


245
static void belkin_sa_close(struct usb_serial_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
246
{
247
	dbg("%s port %d", __func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
248 249 250 251 252 253 254 255

	/* shutdown our bulk reads and writes */
	usb_kill_urb(port->write_urb);
	usb_kill_urb(port->read_urb);
	usb_kill_urb(port->interrupt_in_urb);
} /* belkin_sa_close */


Alan Cox's avatar
Alan Cox committed
256
static void belkin_sa_read_int_callback(struct urb *urb)
Linus Torvalds's avatar
Linus Torvalds committed
257
{
258
	struct usb_serial_port *port = urb->context;
Linus Torvalds's avatar
Linus Torvalds committed
259 260 261
	struct belkin_sa_private *priv;
	unsigned char *data = urb->transfer_buffer;
	int retval;
262
	int status = urb->status;
Linus Torvalds's avatar
Linus Torvalds committed
263 264
	unsigned long flags;

265
	switch (status) {
Linus Torvalds's avatar
Linus Torvalds committed
266 267 268 269 270 271 272
	case 0:
		/* success */
		break;
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* this urb is terminated, clean up */
273
		dbg("%s - urb shutting down with status: %d",
274
		    __func__, status);
Linus Torvalds's avatar
Linus Torvalds committed
275 276
		return;
	default:
277
		dbg("%s - nonzero urb status received: %d",
278
		    __func__, status);
Linus Torvalds's avatar
Linus Torvalds committed
279 280 281
		goto exit;
	}

Alan Cox's avatar
Alan Cox committed
282 283
	usb_serial_debug_data(debug, &port->dev, __func__,
					urb->actual_length, data);
Linus Torvalds's avatar
Linus Torvalds committed
284 285 286 287 288 289 290

	/* Handle known interrupt data */
	/* ignore data[0] and data[1] */

	priv = usb_get_serial_port_data(port);
	spin_lock_irqsave(&priv->lock, flags);
	priv->last_msr = data[BELKIN_SA_MSR_INDEX];
Alan Cox's avatar
Alan Cox committed
291

Linus Torvalds's avatar
Linus Torvalds committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
	/* Record Control Line states */
	if (priv->last_msr & BELKIN_SA_MSR_DSR)
		priv->control_state |= TIOCM_DSR;
	else
		priv->control_state &= ~TIOCM_DSR;

	if (priv->last_msr & BELKIN_SA_MSR_CTS)
		priv->control_state |= TIOCM_CTS;
	else
		priv->control_state &= ~TIOCM_CTS;

	if (priv->last_msr & BELKIN_SA_MSR_RI)
		priv->control_state |= TIOCM_RI;
	else
		priv->control_state &= ~TIOCM_RI;

	if (priv->last_msr & BELKIN_SA_MSR_CD)
		priv->control_state |= TIOCM_CD;
	else
		priv->control_state &= ~TIOCM_CD;

	/* Now to report any errors */
	priv->last_lsr = data[BELKIN_SA_LSR_INDEX];
#if 0
	/*
	 * 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 & BELKIN_SA_LSR_ERR) {
Alan Cox's avatar
Alan Cox committed
322
		tty = tty_port_tty_get(&port->port);
Linus Torvalds's avatar
Linus Torvalds committed
323 324 325 326 327 328 329 330 331 332 333 334
		/* Overrun Error */
		if (priv->last_lsr & BELKIN_SA_LSR_OE) {
		}
		/* Parity Error */
		if (priv->last_lsr & BELKIN_SA_LSR_PE) {
		}
		/* Framing Error */
		if (priv->last_lsr & BELKIN_SA_LSR_FE) {
		}
		/* Break Indicator */
		if (priv->last_lsr & BELKIN_SA_LSR_BI) {
		}
Alan Cox's avatar
Alan Cox committed
335
		tty_kref_put(tty);
Linus Torvalds's avatar
Linus Torvalds committed
336 337 338 339
	}
#endif
	spin_unlock_irqrestore(&priv->lock, flags);
exit:
Alan Cox's avatar
Alan Cox committed
340
	retval = usb_submit_urb(urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
341
	if (retval)
342 343
		dev_err(&port->dev, "%s - usb_submit_urb failed with "
			"result %d\n", __func__, retval);
Linus Torvalds's avatar
Linus Torvalds committed
344 345
}

Alan Cox's avatar
Alan Cox committed
346 347
static void belkin_sa_set_termios(struct tty_struct *tty,
		struct usb_serial_port *port, struct ktermios *old_termios)
Linus Torvalds's avatar
Linus Torvalds committed
348 349 350 351 352 353 354 355 356 357 358
{
	struct usb_serial *serial = port->serial;
	struct belkin_sa_private *priv = usb_get_serial_port_data(port);
	unsigned int iflag;
	unsigned int cflag;
	unsigned int old_iflag = 0;
	unsigned int old_cflag = 0;
	__u16 urb_value = 0; /* Will hold the new flags */
	unsigned long flags;
	unsigned long control_state;
	int bad_flow_control;
359
	speed_t baud;
Alan Cox's avatar
Alan Cox committed
360
	struct ktermios *termios = tty->termios;
Alan Cox's avatar
Alan Cox committed
361

362 363
	iflag = termios->c_iflag;
	cflag = termios->c_cflag;
Linus Torvalds's avatar
Linus Torvalds committed
364

365
	termios->c_cflag &= ~CMSPAR;
Linus Torvalds's avatar
Linus Torvalds committed
366 367 368 369 370 371

	/* get a local copy of the current port settings */
	spin_lock_irqsave(&priv->lock, flags);
	control_state = priv->control_state;
	bad_flow_control = priv->bad_flow_control;
	spin_unlock_irqrestore(&priv->lock, flags);
Alan Cox's avatar
Alan Cox committed
372

373 374
	old_iflag = old_termios->c_iflag;
	old_cflag = old_termios->c_cflag;
Linus Torvalds's avatar
Linus Torvalds committed
375 376

	/* Set the baud rate */
377
	if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
Linus Torvalds's avatar
Linus Torvalds committed
378
		/* reassert DTR and (maybe) RTS on transition from B0 */
Alan Cox's avatar
Alan Cox committed
379
		if ((old_cflag & CBAUD) == B0) {
Linus Torvalds's avatar
Linus Torvalds committed
380 381
			control_state |= (TIOCM_DTR|TIOCM_RTS);
			if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 1) < 0)
382
				dev_err(&port->dev, "Set DTR error\n");
Linus Torvalds's avatar
Linus Torvalds committed
383
			/* don't set RTS if using hardware flow control */
384
			if (!(old_cflag & CRTSCTS))
Alan Cox's avatar
Alan Cox committed
385 386
				if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST
								, 1) < 0)
387
					dev_err(&port->dev, "Set RTS error\n");
Linus Torvalds's avatar
Linus Torvalds committed
388
		}
389
	}
Linus Torvalds's avatar
Linus Torvalds committed
390

Alan Cox's avatar
Alan Cox committed
391
	baud = tty_get_baud_rate(tty);
392 393 394 395 396 397 398 399 400
	if (baud) {
		urb_value = BELKIN_SA_BAUD(baud);
		/* Clip to maximum speed */
		if (urb_value == 0)
			urb_value = 1;
		/* Turn it back into a resulting real baud rate */
		baud = BELKIN_SA_BAUD(urb_value);

		/* Report the actual baud rate back to the caller */
Alan Cox's avatar
Alan Cox committed
401
		tty_encode_baud_rate(tty, baud, baud);
402
		if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0)
403
			dev_err(&port->dev, "Set baudrate error\n");
404 405
	} else {
		/* Disable flow control */
Alan Cox's avatar
Alan Cox committed
406 407
		if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST,
						BELKIN_SA_FLOW_NONE) < 0)
408
			dev_err(&port->dev, "Disable flowcontrol error\n");
409 410 411
		/* Drop RTS and DTR */
		control_state &= ~(TIOCM_DTR | TIOCM_RTS);
		if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0)
412
			dev_err(&port->dev, "DTR LOW error\n");
413
		if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0)
414
			dev_err(&port->dev, "RTS LOW error\n");
Linus Torvalds's avatar
Linus Torvalds committed
415 416 417
	}

	/* set the parity */
Alan Cox's avatar
Alan Cox committed
418
	if ((cflag ^ old_cflag) & (PARENB | PARODD)) {
Linus Torvalds's avatar
Linus Torvalds committed
419
		if (cflag & PARENB)
Alan Cox's avatar
Alan Cox committed
420 421
			urb_value = (cflag & PARODD) ?  BELKIN_SA_PARITY_ODD
						: BELKIN_SA_PARITY_EVEN;
Linus Torvalds's avatar
Linus Torvalds committed
422 423 424
		else
			urb_value = BELKIN_SA_PARITY_NONE;
		if (BSA_USB_CMD(BELKIN_SA_SET_PARITY_REQUEST, urb_value) < 0)
425
			dev_err(&port->dev, "Set parity error\n");
Linus Torvalds's avatar
Linus Torvalds committed
426 427 428
	}

	/* set the number of data bits */
Alan Cox's avatar
Alan Cox committed
429
	if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
Linus Torvalds's avatar
Linus Torvalds committed
430
		switch (cflag & CSIZE) {
Alan Cox's avatar
Alan Cox committed
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
		case CS5:
			urb_value = BELKIN_SA_DATA_BITS(5);
			break;
		case CS6:
			urb_value = BELKIN_SA_DATA_BITS(6);
			break;
		case CS7:
			urb_value = BELKIN_SA_DATA_BITS(7);
			break;
		case CS8:
			urb_value = BELKIN_SA_DATA_BITS(8);
			break;
		default: dbg("CSIZE was not CS5-CS8, using default of 8");
			urb_value = BELKIN_SA_DATA_BITS(8);
			break;
Linus Torvalds's avatar
Linus Torvalds committed
446 447
		}
		if (BSA_USB_CMD(BELKIN_SA_SET_DATA_BITS_REQUEST, urb_value) < 0)
448
			dev_err(&port->dev, "Set data bits error\n");
Linus Torvalds's avatar
Linus Torvalds committed
449 450 451
	}

	/* set the number of stop bits */
Alan Cox's avatar
Alan Cox committed
452 453 454 455 456
	if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
		urb_value = (cflag & CSTOPB) ? BELKIN_SA_STOP_BITS(2)
						: BELKIN_SA_STOP_BITS(1);
		if (BSA_USB_CMD(BELKIN_SA_SET_STOP_BITS_REQUEST,
							urb_value) < 0)
457
			dev_err(&port->dev, "Set stop bits error\n");
Linus Torvalds's avatar
Linus Torvalds committed
458 459 460
	}

	/* Set flow control */
Alan Cox's avatar
Alan Cox committed
461 462
	if (((iflag ^ old_iflag) & (IXOFF | IXON)) ||
		((cflag ^ old_cflag) & CRTSCTS)) {
Linus Torvalds's avatar
Linus Torvalds committed
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
		urb_value = 0;
		if ((iflag & IXOFF) || (iflag & IXON))
			urb_value |= (BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON);
		else
			urb_value &= ~(BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON);

		if (cflag & CRTSCTS)
			urb_value |=  (BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS);
		else
			urb_value &= ~(BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS);

		if (bad_flow_control)
			urb_value &= ~(BELKIN_SA_FLOW_IRTS);

		if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, urb_value) < 0)
478
			dev_err(&port->dev, "Set flow control error\n");
Linus Torvalds's avatar
Linus Torvalds committed
479 480 481 482 483 484 485 486 487
	}

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


Alan Cox's avatar
Alan Cox committed
488
static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state)
Linus Torvalds's avatar
Linus Torvalds committed
489
{
Alan Cox's avatar
Alan Cox committed
490
	struct usb_serial_port *port = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
491 492 493
	struct usb_serial *serial = port->serial;

	if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0)
494
		dev_err(&port->dev, "Set break_ctl %d\n", break_state);
Linus Torvalds's avatar
Linus Torvalds committed
495 496 497
}


Alan Cox's avatar
Alan Cox committed
498
static int belkin_sa_tiocmget(struct tty_struct *tty, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
499
{
Alan Cox's avatar
Alan Cox committed
500
	struct usb_serial_port *port = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
501 502 503
	struct belkin_sa_private *priv = usb_get_serial_port_data(port);
	unsigned long control_state;
	unsigned long flags;
Alan Cox's avatar
Alan Cox committed
504

505
	dbg("%s", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
506 507 508 509 510 511 512 513 514

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

	return control_state;
}


Alan Cox's avatar
Alan Cox committed
515
static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file,
Linus Torvalds's avatar
Linus Torvalds committed
516 517
			       unsigned int set, unsigned int clear)
{
Alan Cox's avatar
Alan Cox committed
518
	struct usb_serial_port *port = tty->driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
519 520 521 522 523 524 525
	struct usb_serial *serial = port->serial;
	struct belkin_sa_private *priv = usb_get_serial_port_data(port);
	unsigned long control_state;
	unsigned long flags;
	int retval;
	int rts = 0;
	int dtr = 0;
Alan Cox's avatar
Alan Cox committed
526

527
	dbg("%s", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553

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

	if (set & TIOCM_RTS) {
		control_state |= TIOCM_RTS;
		rts = 1;
	}
	if (set & TIOCM_DTR) {
		control_state |= TIOCM_DTR;
		dtr = 1;
	}
	if (clear & TIOCM_RTS) {
		control_state &= ~TIOCM_RTS;
		rts = 0;
	}
	if (clear & TIOCM_DTR) {
		control_state &= ~TIOCM_DTR;
		dtr = 0;
	}

	priv->control_state = control_state;
	spin_unlock_irqrestore(&priv->lock, flags);

	retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, rts);
	if (retval < 0) {
554
		dev_err(&port->dev, "Set RTS error %d\n", retval);
Linus Torvalds's avatar
Linus Torvalds committed
555 556 557 558 559
		goto exit;
	}

	retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, dtr);
	if (retval < 0) {
560
		dev_err(&port->dev, "Set DTR error %d\n", retval);
Linus Torvalds's avatar
Linus Torvalds committed
561 562 563 564 565 566 567
		goto exit;
	}
exit:
	return retval;
}


Alan Cox's avatar
Alan Cox committed
568
static int __init belkin_sa_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
569 570 571 572 573 574 575 576
{
	int retval;
	retval = usb_serial_register(&belkin_device);
	if (retval)
		goto failed_usb_serial_register;
	retval = usb_register(&belkin_driver);
	if (retval)
		goto failed_usb_register;
577 578
	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
	       DRIVER_DESC "\n");
Linus Torvalds's avatar
Linus Torvalds committed
579 580 581 582 583 584 585 586 587 588
	return 0;
failed_usb_register:
	usb_serial_deregister(&belkin_device);
failed_usb_serial_register:
	return retval;
}


static void __exit belkin_sa_exit (void)
{
Alan Cox's avatar
Alan Cox committed
589
	usb_deregister(&belkin_driver);
Alan Cox's avatar
Alan Cox committed
590
	usb_serial_deregister(&belkin_device);
Linus Torvalds's avatar
Linus Torvalds committed
591 592 593
}


Alan Cox's avatar
Alan Cox committed
594 595
module_init(belkin_sa_init);
module_exit(belkin_sa_exit);
Linus Torvalds's avatar
Linus Torvalds committed
596

Alan Cox's avatar
Alan Cox committed
597 598 599
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
Linus Torvalds's avatar
Linus Torvalds committed
600 601 602 603
MODULE_LICENSE("GPL");

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");