isicom.c 41.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9
/*
 *	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.
 *
 *	Original driver code supplied by Multi-Tech
 *
 *	Changes
10 11
 *	1/9/98	alan@lxorguk.ukuu.org.uk
 *					Merge to 2.0.x kernel tree
L
Linus Torvalds 已提交
12 13 14 15
 *					Obtain and use official major/minors
 *					Loader switched to a misc device
 *					(fixed range check bug as a side effect)
 *					Printk clean up
16 17
 *	9/12/98	alan@lxorguk.ukuu.org.uk
 *					Rough port to 2.1.x
L
Linus Torvalds 已提交
18 19 20 21 22 23 24
 *
 *	10/6/99 sameer			Merged the ISA and PCI drivers to
 *					a new unified driver.
 *
 *	3/9/99	sameer			Added support for ISI4616 cards.
 *
 *	16/9/99	sameer			We do not force RTS low anymore.
25
 *					This is to prevent the firmware
L
Linus Torvalds 已提交
26 27 28 29 30 31 32 33 34 35
 *					from getting confused.
 *
 *	26/10/99 sameer			Cosmetic changes:The driver now
 *					dumps the Port Count information
 *					along with I/O address and IRQ.
 *
 *	13/12/99 sameer			Fixed the problem with IRQ sharing.
 *
 *	10/5/00  sameer			Fixed isicom_shutdown_board()
 *					to not lower DTR on all the ports
36
 *					when the last port on the card is
L
Linus Torvalds 已提交
37 38 39
 *					closed.
 *
 *	10/5/00  sameer			Signal mask setup command added
40
 *					to  isicom_setup_port and
L
Linus Torvalds 已提交
41 42 43
 *					isicom_shutdown_port.
 *
 *	24/5/00  sameer			The driver is now SMP aware.
44 45
 *
 *
L
Linus Torvalds 已提交
46
 *	27/11/00 Vinayak P Risbud	Fixed the Driver Crash Problem
47 48
 *
 *
L
Linus Torvalds 已提交
49 50 51 52 53 54
 *	03/01/01  anil .s		Added support for resetting the
 *					internal modems on ISI cards.
 *
 *	08/02/01  anil .s		Upgraded the driver for kernel
 *					2.4.x
 *
55
 *	11/04/01  Kevin			Fixed firmware load problem with
L
Linus Torvalds 已提交
56
 *					ISIHP-4X card
57
 *
L
Linus Torvalds 已提交
58 59 60 61 62 63 64 65 66
 *	30/04/01  anil .s		Fixed the remote login through
 *					ISI port problem. Now the link
 *					does not go down before password
 *					prompt.
 *
 *	03/05/01  anil .s		Fixed the problem with IRQ sharing
 *					among ISI-PCI cards.
 *
 *	03/05/01  anil .s		Added support to display the version
67
 *					info during insmod as well as module
L
Linus Torvalds 已提交
68
 *					listing by lsmod.
69
 *
L
Linus Torvalds 已提交
70 71 72 73 74 75 76 77
 *	10/05/01  anil .s		Done the modifications to the source
 *					file and Install script so that the
 *					same installation can be used for
 *					2.2.x and 2.4.x kernel.
 *
 *	06/06/01  anil .s		Now we drop both dtr and rts during
 *					shutdown_port as well as raise them
 *					during isicom_config_port.
78
 *
L
Linus Torvalds 已提交
79 80 81 82 83
 *	09/06/01 acme@conectiva.com.br	use capable, not suser, do
 *					restore_flags on failure in
 *					isicom_send_break, verify put_user
 *					result
 *
84 85 86 87 88 89 90
 *	11/02/03  ranjeeth		Added support for 230 Kbps and 460 Kbps
 *					Baud index extended to 21
 *
 *	20/03/03  ranjeeth		Made to work for Linux Advanced server.
 *					Taken care of license warning.
 *
 *	10/12/03  Ravindra		Made to work for Fedora Core 1 of
L
Linus Torvalds 已提交
91 92 93 94 95 96 97
 *					Red Hat Distribution
 *
 *	06/01/05  Alan Cox 		Merged the ISI and base kernel strands
 *					into a single 2.6 driver
 *
 *	***********************************************************
 *
98
 *	To use this driver you also need the support package. You
L
Linus Torvalds 已提交
99 100
 *	can find this in RPM format on
 *		ftp://ftp.linux.org.uk/pub/linux/alan
101
 *
L
Linus Torvalds 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115
 *	You can find the original tools for this direct from Multitech
 *		ftp://ftp.multitech.com/ISI-Cards/
 *
 *	Having installed the cards the module options (/etc/modprobe.conf)
 *
 *	options isicom   io=card1,card2,card3,card4 irq=card1,card2,card3,card4
 *
 *	Omit those entries for boards you don't have installed.
 *
 *	TODO
 *		Merge testing
 *		64-bit verification
 */

116 117
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

L
Linus Torvalds 已提交
118
#include <linux/module.h>
119
#include <linux/firmware.h>
L
Linus Torvalds 已提交
120 121
#include <linux/kernel.h>
#include <linux/tty.h>
A
Alan Cox 已提交
122
#include <linux/tty_flip.h>
L
Linus Torvalds 已提交
123 124 125 126 127 128 129 130 131
#include <linux/termios.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/serial.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/ioport.h>
132
#include <linux/slab.h>
L
Linus Torvalds 已提交
133

A
Alan Cox 已提交
134 135
#include <linux/uaccess.h>
#include <linux/io.h>
L
Linus Torvalds 已提交
136 137 138 139 140 141
#include <asm/system.h>

#include <linux/pci.h>

#include <linux/isicom.h>

142 143 144 145 146 147 148 149 150
#define InterruptTheCard(base) outw(0, (base) + 0xc)
#define ClearInterrupt(base) inw((base) + 0x0a)

#ifdef DEBUG
#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c))
#else
#define isicom_paranoia_check(a, b, c) 0
#endif

151 152 153
static int isicom_probe(struct pci_dev *, const struct pci_device_id *);
static void __devexit isicom_remove(struct pci_dev *);

L
Linus Torvalds 已提交
154
static struct pci_device_id isicom_pci_tbl[] = {
155 156 157 158 159 160 161 162 163
	{ PCI_DEVICE(VENDOR_ID, 0x2028) },
	{ PCI_DEVICE(VENDOR_ID, 0x2051) },
	{ PCI_DEVICE(VENDOR_ID, 0x2052) },
	{ PCI_DEVICE(VENDOR_ID, 0x2053) },
	{ PCI_DEVICE(VENDOR_ID, 0x2054) },
	{ PCI_DEVICE(VENDOR_ID, 0x2055) },
	{ PCI_DEVICE(VENDOR_ID, 0x2056) },
	{ PCI_DEVICE(VENDOR_ID, 0x2057) },
	{ PCI_DEVICE(VENDOR_ID, 0x2058) },
L
Linus Torvalds 已提交
164 165 166 167
	{ 0 }
};
MODULE_DEVICE_TABLE(pci, isicom_pci_tbl);

168 169 170 171 172 173 174
static struct pci_driver isicom_driver = {
	.name		= "isicom",
	.id_table	= isicom_pci_tbl,
	.probe		= isicom_probe,
	.remove		= __devexit_p(isicom_remove)
};

L
Linus Torvalds 已提交
175 176 177 178
static int prev_card = 3;	/*	start servicing isi_card[0]	*/
static struct tty_driver *isicom_normal;

static void isicom_tx(unsigned long _data);
179
static void isicom_start(struct tty_struct *tty);
L
Linus Torvalds 已提交
180

J
Jiri Slaby 已提交
181 182
static DEFINE_TIMER(tx, isicom_tx, 0, 0);

L
Linus Torvalds 已提交
183 184 185
/*   baud index mappings from linux defns to isi */

static signed char linuxb_to_isib[] = {
186
	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21
L
Linus Torvalds 已提交
187 188 189
};

struct	isi_board {
190
	unsigned long		base;
191
	int			irq;
L
Linus Torvalds 已提交
192 193
	unsigned char		port_count;
	unsigned short		status;
194
	unsigned short		port_status; /* each bit for each port */
L
Linus Torvalds 已提交
195
	unsigned short		shift_count;
A
Alan Cox 已提交
196
	struct isi_port		*ports;
L
Linus Torvalds 已提交
197 198 199
	signed char		count;
	spinlock_t		card_lock; /* Card wide lock 11/5/00 -sameer */
	unsigned long		flags;
200
	unsigned int		index;
L
Linus Torvalds 已提交
201 202 203 204
};

struct	isi_port {
	unsigned short		magic;
A
Alan Cox 已提交
205
	struct tty_port		port;
206 207
	u16			channel;
	u16			status;
A
Alan Cox 已提交
208 209
	struct isi_board	*card;
	unsigned char		*xmit_buf;
L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222
	int			xmit_head;
	int			xmit_tail;
	int			xmit_cnt;
};

static struct isi_board isi_card[BOARD_COUNT];
static struct isi_port  isi_ports[PORT_COUNT];

/*
 *	Locking functions for card level locking. We need to own both
 *	the kernel lock for the card and have the card in a position that
 *	it wants to talk.
 */
223

224
static inline int WaitTillCardIsFree(unsigned long base)
225 226 227 228 229 230 231 232 233 234 235 236 237
{
	unsigned int count = 0;
	unsigned int a = in_atomic(); /* do we run under spinlock? */

	while (!(inw(base + 0xe) & 0x1) && count++ < 100)
		if (a)
			mdelay(1);
		else
			msleep(1);

	return !(inw(base + 0xe) & 0x1);
}

L
Linus Torvalds 已提交
238 239
static int lock_card(struct isi_board *card)
{
240
	unsigned long base = card->base;
J
Jiri Slaby 已提交
241
	unsigned int retries, a;
L
Linus Torvalds 已提交
242

J
Jiri Slaby 已提交
243
	for (retries = 0; retries < 10; retries++) {
L
Linus Torvalds 已提交
244
		spin_lock_irqsave(&card->card_lock, card->flags);
J
Jiri Slaby 已提交
245 246 247 248
		for (a = 0; a < 10; a++) {
			if (inw(base + 0xe) & 0x1)
				return 1;
			udelay(10);
L
Linus Torvalds 已提交
249
		}
J
Jiri Slaby 已提交
250 251
		spin_unlock_irqrestore(&card->card_lock, card->flags);
		msleep(10);
L
Linus Torvalds 已提交
252
	}
253
	pr_warning("Failed to lock Card (0x%lx)\n", card->base);
254

A
Adrian Bunk 已提交
255
	return 0;	/* Failed to acquire the card! */
L
Linus Torvalds 已提交
256 257 258 259 260 261 262 263 264 265
}

static void unlock_card(struct isi_board *card)
{
	spin_unlock_irqrestore(&card->card_lock, card->flags);
}

/*
 *  ISI Card specific ops ...
 */
266

267
/* card->lock HAS to be held */
268
static void raise_dtr(struct isi_port *port)
L
Linus Torvalds 已提交
269
{
270
	struct isi_board *card = port->card;
271 272
	unsigned long base = card->base;
	u16 channel = port->channel;
L
Linus Torvalds 已提交
273

274
	if (WaitTillCardIsFree(base))
L
Linus Torvalds 已提交
275 276
		return;

277
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
L
Linus Torvalds 已提交
278 279 280 281 282
	outw(0x0504, base);
	InterruptTheCard(base);
	port->status |= ISI_DTR;
}

283
/* card->lock HAS to be held */
284 285 286
static inline void drop_dtr(struct isi_port *port)
{
	struct isi_board *card = port->card;
287 288
	unsigned long base = card->base;
	u16 channel = port->channel;
L
Linus Torvalds 已提交
289

290
	if (WaitTillCardIsFree(base))
L
Linus Torvalds 已提交
291 292
		return;

293
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
L
Linus Torvalds 已提交
294
	outw(0x0404, base);
295
	InterruptTheCard(base);
L
Linus Torvalds 已提交
296 297 298
	port->status &= ~ISI_DTR;
}

299
/* card->lock HAS to be held */
300
static inline void raise_rts(struct isi_port *port)
L
Linus Torvalds 已提交
301
{
302
	struct isi_board *card = port->card;
303 304
	unsigned long base = card->base;
	u16 channel = port->channel;
L
Linus Torvalds 已提交
305

306
	if (WaitTillCardIsFree(base))
L
Linus Torvalds 已提交
307 308
		return;

309
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
L
Linus Torvalds 已提交
310
	outw(0x0a04, base);
311
	InterruptTheCard(base);
L
Linus Torvalds 已提交
312 313
	port->status |= ISI_RTS;
}
314 315

/* card->lock HAS to be held */
316
static inline void drop_rts(struct isi_port *port)
L
Linus Torvalds 已提交
317
{
318
	struct isi_board *card = port->card;
319 320
	unsigned long base = card->base;
	u16 channel = port->channel;
L
Linus Torvalds 已提交
321

322
	if (WaitTillCardIsFree(base))
L
Linus Torvalds 已提交
323 324
		return;

325
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
L
Linus Torvalds 已提交
326
	outw(0x0804, base);
327
	InterruptTheCard(base);
L
Linus Torvalds 已提交
328 329 330
	port->status &= ~ISI_RTS;
}

331
/* card->lock MUST NOT be held */
A
Alan Cox 已提交
332

333
static void isicom_dtr_rts(struct tty_port *port, int on)
L
Linus Torvalds 已提交
334
{
A
Alan Cox 已提交
335 336
	struct isi_port *ip = container_of(port, struct isi_port, port);
	struct isi_board *card = ip->card;
337
	unsigned long base = card->base;
A
Alan Cox 已提交
338
	u16 channel = ip->channel;
L
Linus Torvalds 已提交
339 340 341 342

	if (!lock_card(card))
		return;

343 344 345 346 347 348 349 350 351 352 353
	if (on) {
		outw(0x8000 | (channel << card->shift_count) | 0x02, base);
		outw(0x0f04, base);
		InterruptTheCard(base);
		ip->status |= (ISI_DTR | ISI_RTS);
	} else {
		outw(0x8000 | (channel << card->shift_count) | 0x02, base);
		outw(0x0C04, base);
		InterruptTheCard(base);
		ip->status &= ~(ISI_DTR | ISI_RTS);
	}
L
Linus Torvalds 已提交
354 355 356
	unlock_card(card);
}

357
/* card->lock HAS to be held */
358
static void drop_dtr_rts(struct isi_port *port)
L
Linus Torvalds 已提交
359
{
360
	struct isi_board *card = port->card;
361 362
	unsigned long base = card->base;
	u16 channel = port->channel;
L
Linus Torvalds 已提交
363

364
	if (WaitTillCardIsFree(base))
L
Linus Torvalds 已提交
365 366
		return;

367
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
L
Linus Torvalds 已提交
368
	outw(0x0c04, base);
369
	InterruptTheCard(base);
L
Linus Torvalds 已提交
370 371 372 373 374 375 376
	port->status &= ~(ISI_RTS | ISI_DTR);
}

/*
 *	ISICOM Driver specific routines ...
 *
 */
377

378 379
static inline int __isicom_paranoia_check(struct isi_port const *port,
	char *name, const char *routine)
L
Linus Torvalds 已提交
380 381
{
	if (!port) {
382 383
		pr_warning("Warning: bad isicom magic for dev %s in %s.\n",
			   name, routine);
L
Linus Torvalds 已提交
384 385 386
		return 1;
	}
	if (port->magic != ISICOM_MAGIC) {
387 388
		pr_warning("Warning: NULL isicom port for dev %s in %s.\n",
			   name, routine);
L
Linus Torvalds 已提交
389
		return 1;
390
	}
391

L
Linus Torvalds 已提交
392 393
	return 0;
}
394

L
Linus Torvalds 已提交
395
/*
396
 *	Transmitter.
L
Linus Torvalds 已提交
397 398 399 400 401 402 403
 *
 *	We shovel data into the card buffers on a regular basis. The card
 *	will do the rest of the work for us.
 */

static void isicom_tx(unsigned long _data)
{
404
	unsigned long flags, base;
J
Jiri Slaby 已提交
405
	unsigned int retries;
406
	short count = (BOARD_COUNT-1), card;
L
Linus Torvalds 已提交
407
	short txcount, wrd, residue, word_count, cnt;
408 409 410
	struct isi_port *port;
	struct tty_struct *tty;

L
Linus Torvalds 已提交
411 412
	/*	find next active board	*/
	card = (prev_card + 1) & 0x0003;
A
Alan Cox 已提交
413
	while (count-- > 0) {
414
		if (isi_card[card].status & BOARD_ACTIVE)
L
Linus Torvalds 已提交
415
			break;
416
		card = (card + 1) & 0x0003;
L
Linus Torvalds 已提交
417 418 419
	}
	if (!(isi_card[card].status & BOARD_ACTIVE))
		goto sched_again;
420

L
Linus Torvalds 已提交
421
	prev_card = card;
422

L
Linus Torvalds 已提交
423 424 425
	count = isi_card[card].port_count;
	port = isi_card[card].ports;
	base = isi_card[card].base;
J
Jiri Slaby 已提交
426 427 428 429 430 431 432 433 434 435

	spin_lock_irqsave(&isi_card[card].card_lock, flags);
	for (retries = 0; retries < 100; retries++) {
		if (inw(base + 0xe) & 0x1)
			break;
		udelay(2);
	}
	if (retries >= 100)
		goto unlock;

A
Alan Cox 已提交
436 437 438 439
	tty = tty_port_tty_get(&port->port);
	if (tty == NULL)
		goto put_unlock;

A
Alan Cox 已提交
440
	for (; count > 0; count--, port++) {
L
Linus Torvalds 已提交
441
		/* port not active or tx disabled to force flow control */
A
Alan Cox 已提交
442
		if (!(port->port.flags & ASYNC_INITIALIZED) ||
443
				!(port->status & ISI_TXOK))
L
Linus Torvalds 已提交
444
			continue;
445

L
Linus Torvalds 已提交
446
		txcount = min_t(short, TX_SIZE, port->xmit_cnt);
J
Jiri Slaby 已提交
447
		if (txcount <= 0 || tty->stopped || tty->hw_stopped)
L
Linus Torvalds 已提交
448
			continue;
J
Jiri Slaby 已提交
449 450

		if (!(inw(base + 0x02) & (1 << port->channel)))
451
			continue;
J
Jiri Slaby 已提交
452

453 454
		pr_debug("txing %d bytes, port%d.\n",
			 txcount, port->channel + 1);
455 456
		outw((port->channel << isi_card[card].shift_count) | txcount,
			base);
L
Linus Torvalds 已提交
457
		residue = NO;
458
		wrd = 0;
L
Linus Torvalds 已提交
459
		while (1) {
460 461
			cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE
					- port->xmit_tail));
L
Linus Torvalds 已提交
462 463 464
			if (residue == YES) {
				residue = NO;
				if (cnt > 0) {
A
Alan Cox 已提交
465
					wrd |= (port->port.xmit_buf[port->xmit_tail]
466 467 468
									<< 8);
					port->xmit_tail = (port->xmit_tail + 1)
						& (SERIAL_XMIT_SIZE - 1);
L
Linus Torvalds 已提交
469 470 471
					port->xmit_cnt--;
					txcount--;
					cnt--;
472
					outw(wrd, base);
473
				} else {
L
Linus Torvalds 已提交
474 475 476
					outw(wrd, base);
					break;
				}
477
			}
A
Alan Cox 已提交
478 479
			if (cnt <= 0)
				break;
L
Linus Torvalds 已提交
480
			word_count = cnt >> 1;
A
Alan Cox 已提交
481
			outsw(base, port->port.xmit_buf+port->xmit_tail, word_count);
482 483
			port->xmit_tail = (port->xmit_tail
				+ (word_count << 1)) & (SERIAL_XMIT_SIZE - 1);
L
Linus Torvalds 已提交
484 485 486 487
			txcount -= (word_count << 1);
			port->xmit_cnt -= (word_count << 1);
			if (cnt & 0x0001) {
				residue = YES;
A
Alan Cox 已提交
488
				wrd = port->port.xmit_buf[port->xmit_tail];
489 490
				port->xmit_tail = (port->xmit_tail + 1)
					& (SERIAL_XMIT_SIZE - 1);
L
Linus Torvalds 已提交
491 492 493 494 495 496 497 498 499
				port->xmit_cnt--;
				txcount--;
			}
		}

		InterruptTheCard(base);
		if (port->xmit_cnt <= 0)
			port->status &= ~ISI_TXOK;
		if (port->xmit_cnt <= WAKEUP_CHARS)
500
			tty_wakeup(tty);
501
	}
L
Linus Torvalds 已提交
502

A
Alan Cox 已提交
503 504
put_unlock:
	tty_kref_put(tty);
J
Jiri Slaby 已提交
505 506
unlock:
	spin_unlock_irqrestore(&isi_card[card].card_lock, flags);
507 508
	/*	schedule another tx for hopefully in about 10ms	*/
sched_again:
J
Jiri Slaby 已提交
509
	mod_timer(&tx, jiffies + msecs_to_jiffies(10));
510 511
}

L
Linus Torvalds 已提交
512
/*
513
 *	Main interrupt handler routine
L
Linus Torvalds 已提交
514
 */
515

516
static irqreturn_t isicom_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
517
{
518
	struct isi_board *card = dev_id;
519 520
	struct isi_port *port;
	struct tty_struct *tty;
521 522
	unsigned long base;
	u16 header, word_count, count, channel;
L
Linus Torvalds 已提交
523
	short byte_count;
A
Alan Cox 已提交
524
	unsigned char *rp;
525

L
Linus Torvalds 已提交
526 527
	if (!card || !(card->status & FIRMWARE_LOADED))
		return IRQ_NONE;
528

L
Linus Torvalds 已提交
529
	base = card->base;
530 531 532 533 534

	/* did the card interrupt us? */
	if (!(inw(base + 0x0e) & 0x02))
		return IRQ_NONE;

L
Linus Torvalds 已提交
535
	spin_lock(&card->card_lock);
536

J
Jiri Slaby 已提交
537 538 539 540 541 542
	/*
	 * disable any interrupts from the PCI card and lower the
	 * interrupt line
	 */
	outw(0x8000, base+0x04);
	ClearInterrupt(base);
543

L
Linus Torvalds 已提交
544 545 546 547 548 549
	inw(base);		/* get the dummy word out */
	header = inw(base);
	channel = (header & 0x7800) >> card->shift_count;
	byte_count = header & 0xff;

	if (channel + 1 > card->port_count) {
550 551
		pr_warning("%s(0x%lx): %d(channel) > port_count.\n",
			   __func__, base, channel+1);
J
Jiri Slaby 已提交
552
		outw(0x0000, base+0x04); /* enable interrupts */
L
Linus Torvalds 已提交
553
		spin_unlock(&card->card_lock);
554
		return IRQ_HANDLED;
L
Linus Torvalds 已提交
555 556
	}
	port = card->ports + channel;
A
Alan Cox 已提交
557
	if (!(port->port.flags & ASYNC_INITIALIZED)) {
J
Jiri Slaby 已提交
558
		outw(0x0000, base+0x04); /* enable interrupts */
559
		spin_unlock(&card->card_lock);
L
Linus Torvalds 已提交
560
		return IRQ_HANDLED;
561 562
	}

A
Alan Cox 已提交
563
	tty = tty_port_tty_get(&port->port);
L
Linus Torvalds 已提交
564 565
	if (tty == NULL) {
		word_count = byte_count >> 1;
A
Alan Cox 已提交
566
		while (byte_count > 1) {
L
Linus Torvalds 已提交
567 568 569 570 571
			inw(base);
			byte_count -= 2;
		}
		if (byte_count & 0x01)
			inw(base);
J
Jiri Slaby 已提交
572
		outw(0x0000, base+0x04); /* enable interrupts */
L
Linus Torvalds 已提交
573 574 575
		spin_unlock(&card->card_lock);
		return IRQ_HANDLED;
	}
576

L
Linus Torvalds 已提交
577 578
	if (header & 0x8000) {		/* Status Packet */
		header = inw(base);
A
Alan Cox 已提交
579
		switch (header & 0xff) {
580
		case 0:	/* Change in EIA signals */
A
Alan Cox 已提交
581
			if (port->port.flags & ASYNC_CHECK_CD) {
582 583 584
				if (port->status & ISI_DCD) {
					if (!(header & ISI_DCD)) {
					/* Carrier has been lost  */
585 586
						pr_debug("%s: DCD->low.\n",
							 __func__);
587
						port->status &= ~ISI_DCD;
588
						tty_hangup(tty);
L
Linus Torvalds 已提交
589
					}
590 591
				} else if (header & ISI_DCD) {
				/* Carrier has been detected */
592 593
					pr_debug("%s: DCD->high.\n",
						__func__);
594
					port->status |= ISI_DCD;
A
Alan Cox 已提交
595
					wake_up_interruptible(&port->port.open_wait);
L
Linus Torvalds 已提交
596
				}
597
			} else {
598 599 600 601 602 603
				if (header & ISI_DCD)
					port->status |= ISI_DCD;
				else
					port->status &= ~ISI_DCD;
			}

A
Alan Cox 已提交
604
			if (port->port.flags & ASYNC_CTS_FLOW) {
A
Alan Cox 已提交
605
				if (tty->hw_stopped) {
606
					if (header & ISI_CTS) {
A
Alan Cox 已提交
607
						port->port.tty->hw_stopped = 0;
608
						/* start tx ing */
609 610
						port->status |= (ISI_TXOK
							| ISI_CTS);
611
						tty_wakeup(tty);
L
Linus Torvalds 已提交
612
					}
613
				} else if (!(header & ISI_CTS)) {
A
Alan Cox 已提交
614
					tty->hw_stopped = 1;
615 616
					/* stop tx ing */
					port->status &= ~(ISI_TXOK | ISI_CTS);
L
Linus Torvalds 已提交
617
				}
618
			} else {
619 620
				if (header & ISI_CTS)
					port->status |= ISI_CTS;
L
Linus Torvalds 已提交
621
				else
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
					port->status &= ~ISI_CTS;
			}

			if (header & ISI_DSR)
				port->status |= ISI_DSR;
			else
				port->status &= ~ISI_DSR;

			if (header & ISI_RI)
				port->status |= ISI_RI;
			else
				port->status &= ~ISI_RI;

			break;

637
		case 1:	/* Received Break !!! */
638
			tty_insert_flip_char(tty, 0, TTY_BREAK);
A
Alan Cox 已提交
639
			if (port->port.flags & ASYNC_SAK)
640 641 642 643 644
				do_SAK(tty);
			tty_flip_buffer_push(tty);
			break;

		case 2:	/* Statistics		 */
645
			pr_debug("%s: stats!!!\n", __func__);
646 647 648
			break;

		default:
649 650
			pr_debug("%s: Unknown code in status packet.\n",
				 __func__);
651 652
			break;
		}
653
	} else {				/* Data   Packet */
A
Alan Cox 已提交
654 655

		count = tty_prepare_flip_string(tty, &rp, byte_count & ~1);
656 657
		pr_debug("%s: Can rx %d of %d bytes.\n",
			 __func__, count, byte_count);
L
Linus Torvalds 已提交
658
		word_count = count >> 1;
A
Alan Cox 已提交
659
		insw(base, rp, word_count);
L
Linus Torvalds 已提交
660 661
		byte_count -= (word_count << 1);
		if (count & 0x0001) {
662 663
			tty_insert_flip_char(tty,  inw(base) & 0xff,
				TTY_NORMAL);
L
Linus Torvalds 已提交
664
			byte_count -= 2;
665
		}
L
Linus Torvalds 已提交
666
		if (byte_count > 0) {
667 668
			pr_debug("%s(0x%lx:%d): Flip buffer overflow! dropping bytes...\n",
				 __func__, base, channel + 1);
A
Alan Cox 已提交
669 670
		/* drain out unread xtra data */
		while (byte_count > 0) {
L
Linus Torvalds 已提交
671 672 673 674
				inw(base);
				byte_count -= 2;
			}
		}
A
Alan Cox 已提交
675
		tty_flip_buffer_push(tty);
L
Linus Torvalds 已提交
676
	}
J
Jiri Slaby 已提交
677
	outw(0x0000, base+0x04); /* enable interrupts */
678
	spin_unlock(&card->card_lock);
A
Alan Cox 已提交
679
	tty_kref_put(tty);
680

L
Linus Torvalds 已提交
681
	return IRQ_HANDLED;
682
}
L
Linus Torvalds 已提交
683

A
Alan Cox 已提交
684
static void isicom_config_port(struct tty_struct *tty)
L
Linus Torvalds 已提交
685
{
A
Alan Cox 已提交
686
	struct isi_port *port = tty->driver_data;
687
	struct isi_board *card = port->card;
L
Linus Torvalds 已提交
688
	unsigned long baud;
689 690 691
	unsigned long base = card->base;
	u16 channel_setup, channel = port->channel,
		shift_count = card->shift_count;
L
Linus Torvalds 已提交
692
	unsigned char flow_ctrl;
693

A
Alan Cox 已提交
694
	/* FIXME: Switch to new tty baud API */
L
Linus Torvalds 已提交
695 696 697
	baud = C_BAUD(tty);
	if (baud & CBAUDEX) {
		baud &= ~CBAUDEX;
698

L
Linus Torvalds 已提交
699 700 701
		/*  if CBAUDEX bit is on and the baud is set to either 50 or 75
		 *  then the card is programmed for 57.6Kbps or 115Kbps
		 *  respectively.
702 703
		 */

704 705
		/* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
		if (baud < 1 || baud > 4)
A
Alan Cox 已提交
706
			tty->termios->c_cflag &= ~CBAUDEX;
L
Linus Torvalds 已提交
707 708
		else
			baud += 15;
709
	}
L
Linus Torvalds 已提交
710
	if (baud == 15) {
711 712

		/*  the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set
L
Linus Torvalds 已提交
713 714
		 *  by the set_serial_info ioctl ... this is done by
		 *  the 'setserial' utility.
715 716
		 */

A
Alan Cox 已提交
717
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
718
			baud++; /*  57.6 Kbps */
A
Alan Cox 已提交
719
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
A
Alan Cox 已提交
720
			baud += 2; /*  115  Kbps */
A
Alan Cox 已提交
721
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
722
			baud += 3; /* 230 kbps*/
A
Alan Cox 已提交
723
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
724
			baud += 4; /* 460 kbps*/
L
Linus Torvalds 已提交
725 726 727
	}
	if (linuxb_to_isib[baud] == -1) {
		/* hang up */
728 729
		drop_dtr(port);
		return;
A
Alan Cox 已提交
730
	} else
L
Linus Torvalds 已提交
731
		raise_dtr(port);
732

733
	if (WaitTillCardIsFree(base) == 0) {
A
Alan Cox 已提交
734
		outw(0x8000 | (channel << shift_count) | 0x03, base);
L
Linus Torvalds 已提交
735 736
		outw(linuxb_to_isib[baud] << 8 | 0x03, base);
		channel_setup = 0;
A
Alan Cox 已提交
737
		switch (C_CSIZE(tty)) {
738 739 740 741 742 743 744 745 746 747 748 749
		case CS5:
			channel_setup |= ISICOM_CS5;
			break;
		case CS6:
			channel_setup |= ISICOM_CS6;
			break;
		case CS7:
			channel_setup |= ISICOM_CS7;
			break;
		case CS8:
			channel_setup |= ISICOM_CS8;
			break;
L
Linus Torvalds 已提交
750
		}
751

L
Linus Torvalds 已提交
752 753 754 755 756
		if (C_CSTOPB(tty))
			channel_setup |= ISICOM_2SB;
		if (C_PARENB(tty)) {
			channel_setup |= ISICOM_EVPAR;
			if (C_PARODD(tty))
757
				channel_setup |= ISICOM_ODPAR;
L
Linus Torvalds 已提交
758
		}
759
		outw(channel_setup, base);
L
Linus Torvalds 已提交
760
		InterruptTheCard(base);
761
	}
L
Linus Torvalds 已提交
762
	if (C_CLOCAL(tty))
A
Alan Cox 已提交
763
		port->port.flags &= ~ASYNC_CHECK_CD;
L
Linus Torvalds 已提交
764
	else
A
Alan Cox 已提交
765
		port->port.flags |= ASYNC_CHECK_CD;
766

L
Linus Torvalds 已提交
767 768
	/* flow control settings ...*/
	flow_ctrl = 0;
A
Alan Cox 已提交
769
	port->port.flags &= ~ASYNC_CTS_FLOW;
L
Linus Torvalds 已提交
770
	if (C_CRTSCTS(tty)) {
A
Alan Cox 已提交
771
		port->port.flags |= ASYNC_CTS_FLOW;
L
Linus Torvalds 已提交
772
		flow_ctrl |= ISICOM_CTSRTS;
773 774
	}
	if (I_IXON(tty))
L
Linus Torvalds 已提交
775 776
		flow_ctrl |= ISICOM_RESPOND_XONXOFF;
	if (I_IXOFF(tty))
777 778
		flow_ctrl |= ISICOM_INITIATE_XONXOFF;

779
	if (WaitTillCardIsFree(base) == 0) {
A
Alan Cox 已提交
780
		outw(0x8000 | (channel << shift_count) | 0x04, base);
L
Linus Torvalds 已提交
781 782 783 784
		outw(flow_ctrl << 8 | 0x05, base);
		outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
		InterruptTheCard(base);
	}
785

L
Linus Torvalds 已提交
786 787 788 789 790 791 792
	/*	rx enabled -> enable port for rx on the card	*/
	if (C_CREAD(tty)) {
		card->port_status |= (1 << channel);
		outw(card->port_status, base + 0x02);
	}
}

793 794 795
/* open et all */

static inline void isicom_setup_board(struct isi_board *bp)
L
Linus Torvalds 已提交
796 797
{
	int channel;
798 799
	struct isi_port *port;

800
	bp->count++;
801 802 803 804 805 806
	if (!(bp->status & BOARD_INIT)) {
		port = bp->ports;
		for (channel = 0; channel < bp->port_count; channel++, port++)
			drop_dtr_rts(port);
	}
	bp->status |= BOARD_ACTIVE | BOARD_INIT;
L
Linus Torvalds 已提交
807
}
808

809 810 811
/* Activate and thus setup board are protected from races against shutdown
   by the tty_port mutex */

812
static int isicom_activate(struct tty_port *tport, struct tty_struct *tty)
L
Linus Torvalds 已提交
813
{
814
	struct isi_port *port = container_of(tport, struct isi_port, port);
815
	struct isi_board *card = port->card;
L
Linus Torvalds 已提交
816
	unsigned long flags;
817

818
	if (tty_port_alloc_xmit_buf(tport) < 0)
A
Alan Cox 已提交
819
		return -ENOMEM;
L
Linus Torvalds 已提交
820 821

	spin_lock_irqsave(&card->card_lock, flags);
822
	isicom_setup_board(card);
823

L
Linus Torvalds 已提交
824
	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
825

L
Linus Torvalds 已提交
826
	/*	discard any residual data	*/
827 828 829 830 831 832
	if (WaitTillCardIsFree(card->base) == 0) {
		outw(0x8000 | (port->channel << card->shift_count) | 0x02,
				card->base);
		outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base);
		InterruptTheCard(card->base);
	}
A
Alan Cox 已提交
833
	isicom_config_port(tty);
L
Linus Torvalds 已提交
834
	spin_unlock_irqrestore(&card->card_lock, flags);
835 836 837 838

	return 0;
}

839 840 841 842 843 844
static int isicom_carrier_raised(struct tty_port *port)
{
	struct isi_port *ip = container_of(port, struct isi_port, port);
	return (ip->status & ISI_DCD)?1 : 0;
}

845
static struct tty_port *isicom_find_port(struct tty_struct *tty)
L
Linus Torvalds 已提交
846
{
847 848
	struct isi_port *port;
	struct isi_board *card;
849
	unsigned int board;
850
	int line = tty->index;
L
Linus Torvalds 已提交
851 852

	if (line < 0 || line > PORT_COUNT-1)
853
		return NULL;
L
Linus Torvalds 已提交
854 855
	board = BOARD(line);
	card = &isi_card[board];
856

L
Linus Torvalds 已提交
857
	if (!(card->status & FIRMWARE_LOADED))
858
		return NULL;
859

L
Linus Torvalds 已提交
860 861
	/*  open on a port greater than the port count for the card !!! */
	if (line > ((board * 16) + card->port_count - 1))
862
		return NULL;
L
Linus Torvalds 已提交
863

864
	port = &isi_ports[line];
L
Linus Torvalds 已提交
865
	if (isicom_paranoia_check(port, tty->name, "isicom_open"))
866 867 868 869
		return NULL;

	return &port->port;
}
870

871 872 873 874
static int isicom_open(struct tty_struct *tty, struct file *filp)
{
	struct isi_port *port;
	struct tty_port *tport;
875

876 877 878 879
	tport = isicom_find_port(tty);
	if (tport == NULL)
		return -ENODEV;
	port = container_of(tport, struct isi_port, port);
880

881
	tty->driver_data = port;
882
	return tty_port_open(tport, tty, filp);
L
Linus Torvalds 已提交
883
}
884

L
Linus Torvalds 已提交
885 886
/* close et all */

887
/* card->lock HAS to be held */
888
static void isicom_shutdown_port(struct isi_port *port)
L
Linus Torvalds 已提交
889
{
890
	struct isi_board *card = port->card;
L
Linus Torvalds 已提交
891 892

	if (--card->count < 0) {
893 894
		pr_debug("%s: bad board(0x%lx) count %d.\n",
			 __func__, card->base, card->count);
895
		card->count = 0;
L
Linus Torvalds 已提交
896
	}
897
	/* last port was closed, shutdown that board too */
898 899
	if (!card->count)
		card->status &= BOARD_ACTIVE;
L
Linus Torvalds 已提交
900 901
}

902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
static void isicom_flush_buffer(struct tty_struct *tty)
{
	struct isi_port *port = tty->driver_data;
	struct isi_board *card = port->card;
	unsigned long flags;

	if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer"))
		return;

	spin_lock_irqsave(&card->card_lock, flags);
	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
	spin_unlock_irqrestore(&card->card_lock, flags);

	tty_wakeup(tty);
}

918
static void isicom_shutdown(struct tty_port *port)
L
Linus Torvalds 已提交
919
{
920 921
	struct isi_port *ip = container_of(port, struct isi_port, port);
	struct isi_board *card = ip->card;
L
Linus Torvalds 已提交
922
	unsigned long flags;
923 924

	/* indicate to the card that no more data can be received
L
Linus Torvalds 已提交
925 926
	   on this port */
	spin_lock_irqsave(&card->card_lock, flags);
927 928
	card->port_status &= ~(1 << ip->channel);
	outw(card->port_status, card->base + 0x02);
929
	isicom_shutdown_port(ip);
L
Linus Torvalds 已提交
930
	spin_unlock_irqrestore(&card->card_lock, flags);
A
Alan Cox 已提交
931
	tty_port_free_xmit_buf(port);
932
}
933

934 935 936
static void isicom_close(struct tty_struct *tty, struct file *filp)
{
	struct isi_port *ip = tty->driver_data;
937 938 939 940 941 942
	struct tty_port *port;

	if (ip == NULL)
		return;

	port = &ip->port;
943 944
	if (isicom_paranoia_check(ip, tty->name, "isicom_close"))
		return;
945
	tty_port_close(port, tty, filp);
L
Linus Torvalds 已提交
946 947 948
}

/* write et all */
949 950
static int isicom_write(struct tty_struct *tty,	const unsigned char *buf,
	int count)
L
Linus Torvalds 已提交
951
{
952
	struct isi_port *port = tty->driver_data;
953
	struct isi_board *card = port->card;
L
Linus Torvalds 已提交
954 955 956 957 958
	unsigned long flags;
	int cnt, total = 0;

	if (isicom_paranoia_check(port, tty->name, "isicom_write"))
		return 0;
959

L
Linus Torvalds 已提交
960
	spin_lock_irqsave(&card->card_lock, flags);
961

A
Alan Cox 已提交
962
	while (1) {
963 964
		cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt
				- 1, SERIAL_XMIT_SIZE - port->xmit_head));
965
		if (cnt <= 0)
L
Linus Torvalds 已提交
966
			break;
967

A
Alan Cox 已提交
968
		memcpy(port->port.xmit_buf + port->xmit_head, buf, cnt);
969 970
		port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE
			- 1);
L
Linus Torvalds 已提交
971 972 973 974
		port->xmit_cnt += cnt;
		buf += cnt;
		count -= cnt;
		total += cnt;
975
	}
L
Linus Torvalds 已提交
976 977 978
	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped)
		port->status |= ISI_TXOK;
	spin_unlock_irqrestore(&card->card_lock, flags);
979
	return total;
L
Linus Torvalds 已提交
980 981 982
}

/* put_char et all */
A
Alan Cox 已提交
983
static int isicom_put_char(struct tty_struct *tty, unsigned char ch)
L
Linus Torvalds 已提交
984
{
985
	struct isi_port *port = tty->driver_data;
986
	struct isi_board *card = port->card;
L
Linus Torvalds 已提交
987
	unsigned long flags;
988

L
Linus Torvalds 已提交
989
	if (isicom_paranoia_check(port, tty->name, "isicom_put_char"))
A
Alan Cox 已提交
990
		return 0;
991

L
Linus Torvalds 已提交
992
	spin_lock_irqsave(&card->card_lock, flags);
A
Alan Cox 已提交
993 994 995 996
	if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
		spin_unlock_irqrestore(&card->card_lock, flags);
		return 0;
	}
997

A
Alan Cox 已提交
998
	port->port.xmit_buf[port->xmit_head++] = ch;
L
Linus Torvalds 已提交
999 1000 1001
	port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
	port->xmit_cnt++;
	spin_unlock_irqrestore(&card->card_lock, flags);
A
Alan Cox 已提交
1002
	return 1;
L
Linus Torvalds 已提交
1003 1004 1005
}

/* flush_chars et all */
1006
static void isicom_flush_chars(struct tty_struct *tty)
L
Linus Torvalds 已提交
1007
{
1008
	struct isi_port *port = tty->driver_data;
1009

L
Linus Torvalds 已提交
1010 1011
	if (isicom_paranoia_check(port, tty->name, "isicom_flush_chars"))
		return;
1012

1013
	if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
A
Alan Cox 已提交
1014
			!port->port.xmit_buf)
L
Linus Torvalds 已提交
1015
		return;
1016

L
Linus Torvalds 已提交
1017 1018
	/* this tells the transmitter to consider this port for
	   data output to the card ... that's the best we can do. */
1019
	port->status |= ISI_TXOK;
L
Linus Torvalds 已提交
1020 1021 1022
}

/* write_room et all */
1023
static int isicom_write_room(struct tty_struct *tty)
L
Linus Torvalds 已提交
1024
{
1025
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1026 1027 1028 1029
	int free;

	if (isicom_paranoia_check(port, tty->name, "isicom_write_room"))
		return 0;
1030

L
Linus Torvalds 已提交
1031 1032 1033 1034 1035 1036 1037
	free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
	if (free < 0)
		free = 0;
	return free;
}

/* chars_in_buffer et all */
1038
static int isicom_chars_in_buffer(struct tty_struct *tty)
L
Linus Torvalds 已提交
1039
{
1040
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1041 1042 1043 1044 1045 1046
	if (isicom_paranoia_check(port, tty->name, "isicom_chars_in_buffer"))
		return 0;
	return port->xmit_cnt;
}

/* ioctl et all */
1047
static int isicom_send_break(struct tty_struct *tty, int length)
L
Linus Torvalds 已提交
1048
{
1049
	struct isi_port *port = tty->driver_data;
1050
	struct isi_board *card = port->card;
1051
	unsigned long base = card->base;
1052

1053 1054 1055
	if (length == -1)
		return -EOPNOTSUPP;

1056
	if (!lock_card(card))
1057
		return -EINVAL;
1058

L
Linus Torvalds 已提交
1059 1060 1061 1062 1063 1064
	outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
	outw((length & 0xff) << 8 | 0x00, base);
	outw((length & 0xff00), base);
	InterruptTheCard(base);

	unlock_card(card);
1065
	return 0;
L
Linus Torvalds 已提交
1066 1067
}

1068
static int isicom_tiocmget(struct tty_struct *tty)
L
Linus Torvalds 已提交
1069
{
1070
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1071
	/* just send the port status */
1072
	u16 status = port->status;
L
Linus Torvalds 已提交
1073 1074 1075

	if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
		return -ENODEV;
1076

L
Linus Torvalds 已提交
1077 1078 1079 1080 1081 1082 1083 1084
	return  ((status & ISI_RTS) ? TIOCM_RTS : 0) |
		((status & ISI_DTR) ? TIOCM_DTR : 0) |
		((status & ISI_DCD) ? TIOCM_CAR : 0) |
		((status & ISI_DSR) ? TIOCM_DSR : 0) |
		((status & ISI_CTS) ? TIOCM_CTS : 0) |
		((status & ISI_RI ) ? TIOCM_RI  : 0);
}

1085 1086
static int isicom_tiocmset(struct tty_struct *tty,
					unsigned int set, unsigned int clear)
L
Linus Torvalds 已提交
1087
{
1088
	struct isi_port *port = tty->driver_data;
1089
	unsigned long flags;
1090

L
Linus Torvalds 已提交
1091 1092
	if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
		return -ENODEV;
1093

1094
	spin_lock_irqsave(&port->card->card_lock, flags);
L
Linus Torvalds 已提交
1095 1096 1097 1098 1099 1100 1101 1102 1103
	if (set & TIOCM_RTS)
		raise_rts(port);
	if (set & TIOCM_DTR)
		raise_dtr(port);

	if (clear & TIOCM_RTS)
		drop_rts(port);
	if (clear & TIOCM_DTR)
		drop_dtr(port);
1104
	spin_unlock_irqrestore(&port->card->card_lock, flags);
L
Linus Torvalds 已提交
1105 1106

	return 0;
1107
}
L
Linus Torvalds 已提交
1108

A
Alan Cox 已提交
1109 1110
static int isicom_set_serial_info(struct tty_struct *tty,
					struct serial_struct __user *info)
L
Linus Torvalds 已提交
1111
{
A
Alan Cox 已提交
1112
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1113 1114 1115
	struct serial_struct newinfo;
	int reconfig_port;

1116
	if (copy_from_user(&newinfo, info, sizeof(newinfo)))
L
Linus Torvalds 已提交
1117
		return -EFAULT;
1118

A
Alan Cox 已提交
1119
	mutex_lock(&port->port.mutex);
A
Alan Cox 已提交
1120
	reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) !=
1121 1122
		(newinfo.flags & ASYNC_SPD_MASK));

L
Linus Torvalds 已提交
1123
	if (!capable(CAP_SYS_ADMIN)) {
A
Alan Cox 已提交
1124 1125
		if ((newinfo.close_delay != port->port.close_delay) ||
				(newinfo.closing_wait != port->port.closing_wait) ||
1126
				((newinfo.flags & ~ASYNC_USR_MASK) !=
A
Alan Cox 已提交
1127
				(port->port.flags & ~ASYNC_USR_MASK))) {
A
Alan Cox 已提交
1128
			mutex_unlock(&port->port.mutex);
L
Linus Torvalds 已提交
1129
			return -EPERM;
1130
		}
A
Alan Cox 已提交
1131
		port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
L
Linus Torvalds 已提交
1132
				(newinfo.flags & ASYNC_USR_MASK));
A
Alan Cox 已提交
1133
	} else {
A
Alan Cox 已提交
1134 1135
		port->port.close_delay = newinfo.close_delay;
		port->port.closing_wait = newinfo.closing_wait;
A
Alan Cox 已提交
1136
		port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
L
Linus Torvalds 已提交
1137 1138 1139
				(newinfo.flags & ASYNC_FLAGS));
	}
	if (reconfig_port) {
1140 1141
		unsigned long flags;
		spin_lock_irqsave(&port->card->card_lock, flags);
A
Alan Cox 已提交
1142
		isicom_config_port(tty);
1143
		spin_unlock_irqrestore(&port->card->card_lock, flags);
L
Linus Torvalds 已提交
1144
	}
A
Alan Cox 已提交
1145
	mutex_unlock(&port->port.mutex);
1146 1147
	return 0;
}
L
Linus Torvalds 已提交
1148

1149 1150
static int isicom_get_serial_info(struct isi_port *port,
	struct serial_struct __user *info)
L
Linus Torvalds 已提交
1151 1152
{
	struct serial_struct out_info;
1153

A
Alan Cox 已提交
1154
	mutex_lock(&port->port.mutex);
L
Linus Torvalds 已提交
1155 1156 1157 1158 1159
	memset(&out_info, 0, sizeof(out_info));
/*	out_info.type = ? */
	out_info.line = port - isi_ports;
	out_info.port = port->card->base;
	out_info.irq = port->card->irq;
A
Alan Cox 已提交
1160
	out_info.flags = port->port.flags;
L
Linus Torvalds 已提交
1161
/*	out_info.baud_base = ? */
A
Alan Cox 已提交
1162 1163
	out_info.close_delay = port->port.close_delay;
	out_info.closing_wait = port->port.closing_wait;
A
Alan Cox 已提交
1164
	mutex_unlock(&port->port.mutex);
1165
	if (copy_to_user(info, &out_info, sizeof(out_info)))
L
Linus Torvalds 已提交
1166 1167
		return -EFAULT;
	return 0;
1168
}
L
Linus Torvalds 已提交
1169

1170
static int isicom_ioctl(struct tty_struct *tty,
1171
	unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
1172
{
1173
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1174 1175 1176 1177 1178
	void __user *argp = (void __user *)arg;

	if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
		return -ENODEV;

A
Alan Cox 已提交
1179
	switch (cmd) {
1180 1181 1182 1183
	case TIOCGSERIAL:
		return isicom_get_serial_info(port, argp);

	case TIOCSSERIAL:
A
Alan Cox 已提交
1184
		return isicom_set_serial_info(tty, argp);
1185 1186 1187

	default:
		return -ENOIOCTLCMD;
L
Linus Torvalds 已提交
1188 1189 1190 1191 1192
	}
	return 0;
}

/* set_termios et all */
1193
static void isicom_set_termios(struct tty_struct *tty,
A
Alan Cox 已提交
1194
	struct ktermios *old_termios)
L
Linus Torvalds 已提交
1195
{
1196
	struct isi_port *port = tty->driver_data;
1197
	unsigned long flags;
1198

L
Linus Torvalds 已提交
1199 1200
	if (isicom_paranoia_check(port, tty->name, "isicom_set_termios"))
		return;
1201

L
Linus Torvalds 已提交
1202
	if (tty->termios->c_cflag == old_termios->c_cflag &&
1203
			tty->termios->c_iflag == old_termios->c_iflag)
L
Linus Torvalds 已提交
1204
		return;
1205

1206
	spin_lock_irqsave(&port->card->card_lock, flags);
A
Alan Cox 已提交
1207
	isicom_config_port(tty);
1208
	spin_unlock_irqrestore(&port->card->card_lock, flags);
1209

L
Linus Torvalds 已提交
1210
	if ((old_termios->c_cflag & CRTSCTS) &&
1211
			!(tty->termios->c_cflag & CRTSCTS)) {
L
Linus Torvalds 已提交
1212
		tty->hw_stopped = 0;
1213 1214
		isicom_start(tty);
	}
L
Linus Torvalds 已提交
1215 1216 1217
}

/* throttle et all */
1218
static void isicom_throttle(struct tty_struct *tty)
L
Linus Torvalds 已提交
1219
{
1220
	struct isi_port *port = tty->driver_data;
1221 1222
	struct isi_board *card = port->card;

L
Linus Torvalds 已提交
1223 1224
	if (isicom_paranoia_check(port, tty->name, "isicom_throttle"))
		return;
1225

L
Linus Torvalds 已提交
1226 1227 1228 1229 1230 1231
	/* tell the card that this port cannot handle any more data for now */
	card->port_status &= ~(1 << port->channel);
	outw(card->port_status, card->base + 0x02);
}

/* unthrottle et all */
1232
static void isicom_unthrottle(struct tty_struct *tty)
L
Linus Torvalds 已提交
1233
{
1234
	struct isi_port *port = tty->driver_data;
1235 1236
	struct isi_board *card = port->card;

L
Linus Torvalds 已提交
1237 1238
	if (isicom_paranoia_check(port, tty->name, "isicom_unthrottle"))
		return;
1239

L
Linus Torvalds 已提交
1240 1241 1242 1243 1244 1245
	/* tell the card that this port is ready to accept more data */
	card->port_status |= (1 << port->channel);
	outw(card->port_status, card->base + 0x02);
}

/* stop et all */
1246
static void isicom_stop(struct tty_struct *tty)
L
Linus Torvalds 已提交
1247
{
1248
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1249 1250 1251

	if (isicom_paranoia_check(port, tty->name, "isicom_stop"))
		return;
1252

L
Linus Torvalds 已提交
1253 1254 1255 1256 1257 1258
	/* this tells the transmitter not to consider this port for
	   data output to the card. */
	port->status &= ~ISI_TXOK;
}

/* start et all */
1259
static void isicom_start(struct tty_struct *tty)
L
Linus Torvalds 已提交
1260
{
1261
	struct isi_port *port = tty->driver_data;
1262

L
Linus Torvalds 已提交
1263 1264
	if (isicom_paranoia_check(port, tty->name, "isicom_start"))
		return;
1265

L
Linus Torvalds 已提交
1266 1267 1268 1269 1270
	/* this tells the transmitter to consider this port for
	   data output to the card. */
	port->status |= ISI_TXOK;
}

1271
static void isicom_hangup(struct tty_struct *tty)
L
Linus Torvalds 已提交
1272
{
1273
	struct isi_port *port = tty->driver_data;
1274

L
Linus Torvalds 已提交
1275 1276
	if (isicom_paranoia_check(port, tty->name, "isicom_hangup"))
		return;
1277
	tty_port_hangup(&port->port);
L
Linus Torvalds 已提交
1278 1279 1280
}


1281 1282 1283
/*
 * Driver init and deinit functions
 */
L
Linus Torvalds 已提交
1284

J
Jeff Dike 已提交
1285
static const struct tty_operations isicom_ops = {
1286 1287 1288 1289 1290 1291
	.open			= isicom_open,
	.close			= isicom_close,
	.write			= isicom_write,
	.put_char		= isicom_put_char,
	.flush_chars		= isicom_flush_chars,
	.write_room		= isicom_write_room,
L
Linus Torvalds 已提交
1292
	.chars_in_buffer	= isicom_chars_in_buffer,
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302
	.ioctl			= isicom_ioctl,
	.set_termios		= isicom_set_termios,
	.throttle		= isicom_throttle,
	.unthrottle		= isicom_unthrottle,
	.stop			= isicom_stop,
	.start			= isicom_start,
	.hangup			= isicom_hangup,
	.flush_buffer		= isicom_flush_buffer,
	.tiocmget		= isicom_tiocmget,
	.tiocmset		= isicom_tiocmset,
1303
	.break_ctl		= isicom_send_break,
L
Linus Torvalds 已提交
1304 1305
};

1306 1307
static const struct tty_port_operations isicom_port_ops = {
	.carrier_raised		= isicom_carrier_raised,
1308
	.dtr_rts		= isicom_dtr_rts,
1309 1310
	.activate		= isicom_activate,
	.shutdown		= isicom_shutdown,
1311 1312
};

1313 1314
static int __devinit reset_card(struct pci_dev *pdev,
	const unsigned int card, unsigned int *signature)
L
Linus Torvalds 已提交
1315
{
1316 1317
	struct isi_board *board = pci_get_drvdata(pdev);
	unsigned long base = board->base;
1318
	unsigned int sig, portcount = 0;
1319
	int retval = 0;
1320

1321 1322
	dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1,
		base);
1323

1324
	inw(base + 0x8);
1325

1326
	msleep(10);
1327 1328 1329

	outw(0, base + 0x8); /* Reset */

1330
	msleep(1000);
1331

1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
	sig = inw(base + 0x4) & 0xff;

	if (sig != 0xa5 && sig != 0xbb && sig != 0xcc && sig != 0xdd &&
			sig != 0xee) {
		dev_warn(&pdev->dev, "ISILoad:Card%u reset failure (Possible "
			"bad I/O Port Address 0x%lx).\n", card + 1, base);
		dev_dbg(&pdev->dev, "Sig=0x%x\n", sig);
		retval = -EIO;
		goto end;
	}

	msleep(10);
1344

J
Jiri Slaby 已提交
1345
	portcount = inw(base + 0x2);
1346
	if (!(inw(base + 0xe) & 0x1) || (portcount != 0 && portcount != 4 &&
1347
				portcount != 8 && portcount != 16)) {
1348
		dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.\n",
1349
			card + 1);
J
Jiri Slaby 已提交
1350 1351
		retval = -EIO;
		goto end;
1352 1353
	}

1354
	switch (sig) {
1355 1356 1357
	case 0xa5:
	case 0xbb:
	case 0xdd:
J
Jiri Slaby 已提交
1358
		board->port_count = (portcount == 4) ? 4 : 8;
1359 1360 1361
		board->shift_count = 12;
		break;
	case 0xcc:
1362
	case 0xee:
1363 1364 1365
		board->port_count = 16;
		board->shift_count = 11;
		break;
1366
	}
1367
	dev_info(&pdev->dev, "-Done\n");
1368
	*signature = sig;
1369

1370 1371
end:
	return retval;
L
Linus Torvalds 已提交
1372 1373
}

1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410
static int __devinit load_firmware(struct pci_dev *pdev,
	const unsigned int index, const unsigned int signature)
{
	struct isi_board *board = pci_get_drvdata(pdev);
	const struct firmware *fw;
	unsigned long base = board->base;
	unsigned int a;
	u16 word_count, status;
	int retval = -EIO;
	char *name;
	u8 *data;

	struct stframe {
		u16	addr;
		u16	count;
		u8	data[0];
	} *frame;

	switch (signature) {
	case 0xa5:
		name = "isi608.bin";
		break;
	case 0xbb:
		name = "isi608em.bin";
		break;
	case 0xcc:
		name = "isi616em.bin";
		break;
	case 0xdd:
		name = "isi4608.bin";
		break;
	case 0xee:
		name = "isi4616.bin";
		break;
	default:
		dev_err(&pdev->dev, "Unknown signature.\n");
		goto end;
A
Alan Cox 已提交
1411
	}
1412 1413 1414 1415 1416

	retval = request_firmware(&fw, name, &pdev->dev);
	if (retval)
		goto end;

1417 1418
	retval = -EIO;

1419 1420
	for (frame = (struct stframe *)fw->data;
			frame < (struct stframe *)(fw->data + fw->size);
1421 1422
			frame = (struct stframe *)((u8 *)(frame + 1) +
				frame->count)) {
1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438
		if (WaitTillCardIsFree(base))
			goto errrelfw;

		outw(0xf0, base);	/* start upload sequence */
		outw(0x00, base);
		outw(frame->addr, base); /* lsb of address */

		word_count = frame->count / 2 + frame->count % 2;
		outw(word_count, base);
		InterruptTheCard(base);

		udelay(100); /* 0x2f */

		if (WaitTillCardIsFree(base))
			goto errrelfw;

A
Alan Cox 已提交
1439 1440
		status = inw(base + 0x4);
		if (status != 0) {
1441
			dev_warn(&pdev->dev, "Card%d rejected load header:\n"
1442 1443 1444 1445
				 "Address:0x%x\n"
				 "Count:0x%x\n"
				 "Status:0x%x\n",
				 index + 1, frame->addr, frame->count, status);
1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456
			goto errrelfw;
		}
		outsw(base, frame->data, word_count);

		InterruptTheCard(base);

		udelay(50); /* 0x0f */

		if (WaitTillCardIsFree(base))
			goto errrelfw;

A
Alan Cox 已提交
1457 1458
		status = inw(base + 0x4);
		if (status != 0) {
1459 1460 1461 1462
			dev_err(&pdev->dev, "Card%d got out of sync.Card "
				"Status:0x%x\n", index + 1, status);
			goto errrelfw;
		}
A
Alan Cox 已提交
1463
	}
1464 1465 1466

/* XXX: should we test it by reading it back and comparing with original like
 * in load firmware package? */
1467 1468 1469 1470
	for (frame = (struct stframe *)fw->data;
			frame < (struct stframe *)(fw->data + fw->size);
			frame = (struct stframe *)((u8 *)(frame + 1) +
				frame->count)) {
1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486
		if (WaitTillCardIsFree(base))
			goto errrelfw;

		outw(0xf1, base); /* start download sequence */
		outw(0x00, base);
		outw(frame->addr, base); /* lsb of address */

		word_count = (frame->count >> 1) + frame->count % 2;
		outw(word_count + 1, base);
		InterruptTheCard(base);

		udelay(50); /* 0xf */

		if (WaitTillCardIsFree(base))
			goto errrelfw;

A
Alan Cox 已提交
1487 1488
		status = inw(base + 0x4);
		if (status != 0) {
1489
			dev_warn(&pdev->dev, "Card%d rejected verify header:\n"
1490 1491 1492 1493
				 "Address:0x%x\n"
				 "Count:0x%x\n"
				 "Status: 0x%x\n",
				 index + 1, frame->addr, frame->count, status);
1494 1495 1496 1497
			goto errrelfw;
		}

		data = kmalloc(word_count * 2, GFP_KERNEL);
1498 1499 1500 1501 1502
		if (data == NULL) {
			dev_err(&pdev->dev, "Card%d, firmware upload "
				"failed, not enough memory\n", index + 1);
			goto errrelfw;
		}
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520
		inw(base);
		insw(base, data, word_count);
		InterruptTheCard(base);

		for (a = 0; a < frame->count; a++)
			if (data[a] != frame->data[a]) {
				kfree(data);
				dev_err(&pdev->dev, "Card%d, firmware upload "
					"failed\n", index + 1);
				goto errrelfw;
			}
		kfree(data);

		udelay(50); /* 0xf */

		if (WaitTillCardIsFree(base))
			goto errrelfw;

A
Alan Cox 已提交
1521 1522
		status = inw(base + 0x4);
		if (status != 0) {
1523 1524 1525 1526 1527 1528
			dev_err(&pdev->dev, "Card%d verify got out of sync. "
				"Card Status:0x%x\n", index + 1, status);
			goto errrelfw;
		}
	}

1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
	/* xfer ctrl */
	if (WaitTillCardIsFree(base))
		goto errrelfw;

	outw(0xf2, base);
	outw(0x800, base);
	outw(0x0, base);
	outw(0x0, base);
	InterruptTheCard(base);
	outw(0x0, base + 0x4); /* for ISI4608 cards */

1540 1541 1542 1543 1544 1545 1546 1547 1548
	board->status |= FIRMWARE_LOADED;
	retval = 0;

errrelfw:
	release_firmware(fw);
end:
	return retval;
}

L
Linus Torvalds 已提交
1549 1550 1551
/*
 *	Insmod can set static symbols so keep these static
 */
1552
static unsigned int card_count;
1553 1554 1555 1556

static int __devinit isicom_probe(struct pci_dev *pdev,
	const struct pci_device_id *ent)
{
J
Jiri Slaby 已提交
1557
	unsigned int uninitialized_var(signature), index;
1558 1559 1560
	int retval = -EPERM;
	struct isi_board *board = NULL;

1561
	if (card_count >= BOARD_COUNT)
1562 1563
		goto err;

1564 1565 1566 1567 1568 1569
	retval = pci_enable_device(pdev);
	if (retval) {
		dev_err(&pdev->dev, "failed to enable\n");
		goto err;
	}

1570 1571 1572
	dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device);

	/* allot the first empty slot in the array */
1573
	for (index = 0; index < BOARD_COUNT; index++) {
1574 1575 1576 1577
		if (isi_card[index].base == 0) {
			board = &isi_card[index];
			break;
		}
1578 1579 1580 1581 1582
	}
	if (index == BOARD_COUNT) {
		retval = -ENODEV;
		goto err_disable;
	}
1583

1584
	board->index = index;
1585 1586
	board->base = pci_resource_start(pdev, 3);
	board->irq = pdev->irq;
1587
	card_count++;
1588 1589 1590

	pci_set_drvdata(pdev, board);

1591 1592
	retval = pci_request_region(pdev, 3, ISICOM_NAME);
	if (retval) {
J
Jiri Slaby 已提交
1593 1594 1595 1596
		dev_err(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d "
			"will be disabled.\n", board->base, board->base + 15,
			index + 1);
		retval = -EBUSY;
1597
		goto errdec;
A
Alan Cox 已提交
1598
	}
1599

J
Jiri Slaby 已提交
1600 1601 1602 1603 1604
	retval = request_irq(board->irq, isicom_interrupt,
			IRQF_SHARED | IRQF_DISABLED, ISICOM_NAME, board);
	if (retval < 0) {
		dev_err(&pdev->dev, "Could not install handler at Irq %d. "
			"Card%d will be disabled.\n", board->irq, index + 1);
1605
		goto errunrr;
J
Jiri Slaby 已提交
1606
	}
1607 1608 1609 1610 1611

	retval = reset_card(pdev, index, &signature);
	if (retval < 0)
		goto errunri;

1612 1613 1614 1615
	retval = load_firmware(pdev, index, signature);
	if (retval < 0)
		goto errunri;

1616 1617 1618 1619
	for (index = 0; index < board->port_count; index++)
		tty_register_device(isicom_normal, board->index * 16 + index,
				&pdev->dev);

1620 1621 1622 1623 1624
	return 0;

errunri:
	free_irq(board->irq, board);
errunrr:
1625
	pci_release_region(pdev, 3);
1626
errdec:
1627
	board->base = 0;
1628
	card_count--;
1629
err_disable:
1630
	pci_disable_device(pdev);
1631
err:
1632 1633 1634 1635 1636 1637
	return retval;
}

static void __devexit isicom_remove(struct pci_dev *pdev)
{
	struct isi_board *board = pci_get_drvdata(pdev);
1638 1639 1640 1641
	unsigned int i;

	for (i = 0; i < board->port_count; i++)
		tty_unregister_device(isicom_normal, board->index * 16 + i);
1642 1643

	free_irq(board->irq, board);
1644
	pci_release_region(pdev, 3);
1645 1646
	board->base = 0;
	card_count--;
1647
	pci_disable_device(pdev);
1648
}
L
Linus Torvalds 已提交
1649

1650
static int __init isicom_init(void)
L
Linus Torvalds 已提交
1651
{
1652 1653
	int retval, idx, channel;
	struct isi_port *port;
1654

A
Alan Cox 已提交
1655
	for (idx = 0; idx < BOARD_COUNT; idx++) {
1656 1657 1658 1659
		port = &isi_ports[idx * 16];
		isi_card[idx].ports = port;
		spin_lock_init(&isi_card[idx].card_lock);
		for (channel = 0; channel < 16; channel++, port++) {
A
Alan Cox 已提交
1660
			tty_port_init(&port->port);
1661
			port->port.ops = &isicom_port_ops;
1662 1663 1664
			port->magic = ISICOM_MAGIC;
			port->card = &isi_card[idx];
			port->channel = channel;
A
Alan Cox 已提交
1665 1666
			port->port.close_delay = 50 * HZ/100;
			port->port.closing_wait = 3000 * HZ/100;
1667 1668
			port->status = 0;
			/*  . . .  */
A
Alan Cox 已提交
1669
		}
1670 1671
		isi_card[idx].base = 0;
		isi_card[idx].irq = 0;
L
Linus Torvalds 已提交
1672
	}
1673

J
Jiri Slaby 已提交
1674 1675 1676 1677
	/* tty driver structure initialization */
	isicom_normal = alloc_tty_driver(PORT_COUNT);
	if (!isicom_normal) {
		retval = -ENOMEM;
1678
		goto error;
J
Jiri Slaby 已提交
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689
	}

	isicom_normal->owner			= THIS_MODULE;
	isicom_normal->name 			= "ttyM";
	isicom_normal->major			= ISICOM_NMAJOR;
	isicom_normal->minor_start		= 0;
	isicom_normal->type			= TTY_DRIVER_TYPE_SERIAL;
	isicom_normal->subtype			= SERIAL_TYPE_NORMAL;
	isicom_normal->init_termios		= tty_std_termios;
	isicom_normal->init_termios.c_cflag	= B9600 | CS8 | CREAD | HUPCL |
		CLOCAL;
1690
	isicom_normal->flags			= TTY_DRIVER_REAL_RAW |
1691
		TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK;
J
Jiri Slaby 已提交
1692 1693 1694 1695
	tty_set_operations(isicom_normal, &isicom_ops);

	retval = tty_register_driver(isicom_normal);
	if (retval) {
1696
		pr_debug("Couldn't register the dialin driver\n");
J
Jiri Slaby 已提交
1697 1698
		goto err_puttty;
	}
L
Linus Torvalds 已提交
1699

1700
	retval = pci_register_driver(&isicom_driver);
L
Linus Torvalds 已提交
1701
	if (retval < 0) {
1702
		pr_err("Unable to register pci driver.\n");
J
Jiri Slaby 已提交
1703
		goto err_unrtty;
L
Linus Torvalds 已提交
1704
	}
1705

J
Jiri Slaby 已提交
1706
	mod_timer(&tx, jiffies + 1);
1707

L
Linus Torvalds 已提交
1708
	return 0;
J
Jiri Slaby 已提交
1709 1710 1711 1712
err_unrtty:
	tty_unregister_driver(isicom_normal);
err_puttty:
	put_tty_driver(isicom_normal);
1713 1714
error:
	return retval;
L
Linus Torvalds 已提交
1715 1716 1717 1718
}

static void __exit isicom_exit(void)
{
J
Jiri Slaby 已提交
1719
	del_timer_sync(&tx);
1720

1721
	pci_unregister_driver(&isicom_driver);
J
Jiri Slaby 已提交
1722 1723
	tty_unregister_driver(isicom_normal);
	put_tty_driver(isicom_normal);
L
Linus Torvalds 已提交
1724 1725
}

1726
module_init(isicom_init);
L
Linus Torvalds 已提交
1727
module_exit(isicom_exit);
1728 1729 1730 1731

MODULE_AUTHOR("MultiTech");
MODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech");
MODULE_LICENSE("GPL");
1732 1733 1734 1735 1736
MODULE_FIRMWARE("isi608.bin");
MODULE_FIRMWARE("isi608em.bin");
MODULE_FIRMWARE("isi616em.bin");
MODULE_FIRMWARE("isi4608.bin");
MODULE_FIRMWARE("isi4616.bin");