isicom.c 45.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 *	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
 *	1/9/98	alan@redhat.com		Merge to 2.0.x kernel tree
 *					Obtain and use official major/minors
 *					Loader switched to a misc device
 *					(fixed range check bug as a side effect)
 *					Printk clean up
 *	9/12/98	alan@redhat.com		Rough port to 2.1.x
 *
 *	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.
23
 *					This is to prevent the firmware
L
Linus Torvalds 已提交
24 25 26 27 28 29 30 31 32 33
 *					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
34
 *					when the last port on the card is
L
Linus Torvalds 已提交
35 36 37
 *					closed.
 *
 *	10/5/00  sameer			Signal mask setup command added
38
 *					to  isicom_setup_port and
L
Linus Torvalds 已提交
39 40 41
 *					isicom_shutdown_port.
 *
 *	24/5/00  sameer			The driver is now SMP aware.
42 43
 *
 *
L
Linus Torvalds 已提交
44
 *	27/11/00 Vinayak P Risbud	Fixed the Driver Crash Problem
45 46
 *
 *
L
Linus Torvalds 已提交
47 48 49 50 51 52
 *	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
 *
53
 *	11/04/01  Kevin			Fixed firmware load problem with
L
Linus Torvalds 已提交
54
 *					ISIHP-4X card
55
 *
L
Linus Torvalds 已提交
56 57 58 59 60 61 62 63 64
 *	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
65
 *					info during insmod as well as module
L
Linus Torvalds 已提交
66
 *					listing by lsmod.
67
 *
L
Linus Torvalds 已提交
68 69 70 71 72 73 74 75
 *	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.
76
 *
L
Linus Torvalds 已提交
77 78 79 80 81
 *	09/06/01 acme@conectiva.com.br	use capable, not suser, do
 *					restore_flags on failure in
 *					isicom_send_break, verify put_user
 *					result
 *
82 83 84 85 86 87 88
 *	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 已提交
89 90 91 92 93 94 95
 *					Red Hat Distribution
 *
 *	06/01/05  Alan Cox 		Merged the ISI and base kernel strands
 *					into a single 2.6 driver
 *
 *	***********************************************************
 *
96
 *	To use this driver you also need the support package. You
L
Linus Torvalds 已提交
97 98
 *	can find this in RPM format on
 *		ftp://ftp.linux.org.uk/pub/linux/alan
99
 *
L
Linus Torvalds 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
 *	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
 */

#include <linux/module.h>
115
#include <linux/firmware.h>
L
Linus Torvalds 已提交
116 117
#include <linux/kernel.h>
#include <linux/tty.h>
A
Alan Cox 已提交
118
#include <linux/tty_flip.h>
L
Linus Torvalds 已提交
119 120 121 122 123 124 125 126 127 128
#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>

A
Alan Cox 已提交
129 130
#include <linux/uaccess.h>
#include <linux/io.h>
L
Linus Torvalds 已提交
131 132 133 134 135 136
#include <asm/system.h>

#include <linux/pci.h>

#include <linux/isicom.h>

137 138 139
#define InterruptTheCard(base) outw(0, (base) + 0xc)
#define ClearInterrupt(base) inw((base) + 0x0a)

J
Jiri Slaby 已提交
140
#define pr_dbg(str...) pr_debug("ISICOM: " str)
141 142 143 144 145 146
#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

147 148 149
static int isicom_probe(struct pci_dev *, const struct pci_device_id *);
static void __devexit isicom_remove(struct pci_dev *);

L
Linus Torvalds 已提交
150
static struct pci_device_id isicom_pci_tbl[] = {
151 152 153 154 155 156 157 158 159
	{ 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 已提交
160 161 162 163
	{ 0 }
};
MODULE_DEVICE_TABLE(pci, isicom_pci_tbl);

164 165 166 167 168 169 170
static struct pci_driver isicom_driver = {
	.name		= "isicom",
	.id_table	= isicom_pci_tbl,
	.probe		= isicom_probe,
	.remove		= __devexit_p(isicom_remove)
};

L
Linus Torvalds 已提交
171 172 173 174
static int prev_card = 3;	/*	start servicing isi_card[0]	*/
static struct tty_driver *isicom_normal;

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

J
Jiri Slaby 已提交
177 178
static DEFINE_TIMER(tx, isicom_tx, 0, 0);

L
Linus Torvalds 已提交
179 180 181
/*   baud index mappings from linux defns to isi */

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

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

struct	isi_port {
	unsigned short		magic;
A
Alan Cox 已提交
201
	struct tty_port		port;
L
Linus Torvalds 已提交
202
	int			close_delay;
203 204 205
	u16			channel;
	u16			status;
	u16			closing_wait;
A
Alan Cox 已提交
206 207
	struct isi_board	*card;
	unsigned char		*xmit_buf;
L
Linus Torvalds 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220
	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.
 */
221

222
static inline int WaitTillCardIsFree(unsigned long base)
223 224 225 226 227 228 229 230 231 232 233 234 235
{
	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 已提交
236 237
static int lock_card(struct isi_board *card)
{
238
	unsigned long base = card->base;
J
Jiri Slaby 已提交
239
	unsigned int retries, a;
L
Linus Torvalds 已提交
240

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

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

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

/*
 *  ISI Card specific ops ...
 */
265

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

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

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

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

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

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

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

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

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

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

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

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

330
/* card->lock MUST NOT be held */
331
static inline void raise_dtr_rts(struct isi_port *port)
L
Linus Torvalds 已提交
332
{
333
	struct isi_board *card = port->card;
334 335
	unsigned long base = card->base;
	u16 channel = port->channel;
L
Linus Torvalds 已提交
336 337 338 339

	if (!lock_card(card))
		return;

340
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
L
Linus Torvalds 已提交
341 342 343 344 345 346
	outw(0x0f04, base);
	InterruptTheCard(base);
	port->status |= (ISI_DTR | ISI_RTS);
	unlock_card(card);
}

347
/* card->lock HAS to be held */
348
static void drop_dtr_rts(struct isi_port *port)
L
Linus Torvalds 已提交
349
{
350
	struct isi_board *card = port->card;
351 352
	unsigned long base = card->base;
	u16 channel = port->channel;
L
Linus Torvalds 已提交
353

354
	if (WaitTillCardIsFree(base))
L
Linus Torvalds 已提交
355 356
		return;

357
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
L
Linus Torvalds 已提交
358
	outw(0x0c04, base);
359
	InterruptTheCard(base);
L
Linus Torvalds 已提交
360 361 362 363 364 365 366
	port->status &= ~(ISI_RTS | ISI_DTR);
}

/*
 *	ISICOM Driver specific routines ...
 *
 */
367

368 369
static inline int __isicom_paranoia_check(struct isi_port const *port,
	char *name, const char *routine)
L
Linus Torvalds 已提交
370 371
{
	if (!port) {
372 373
		printk(KERN_WARNING "ISICOM: Warning: bad isicom magic for "
			"dev %s in %s.\n", name, routine);
L
Linus Torvalds 已提交
374 375 376
		return 1;
	}
	if (port->magic != ISICOM_MAGIC) {
377 378
		printk(KERN_WARNING "ISICOM: Warning: NULL isicom port for "
			"dev %s in %s.\n", name, routine);
L
Linus Torvalds 已提交
379
		return 1;
380
	}
381

L
Linus Torvalds 已提交
382 383
	return 0;
}
384

L
Linus Torvalds 已提交
385
/*
386
 *	Transmitter.
L
Linus Torvalds 已提交
387 388 389 390 391 392 393
 *
 *	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)
{
394
	unsigned long flags, base;
J
Jiri Slaby 已提交
395
	unsigned int retries;
396
	short count = (BOARD_COUNT-1), card;
L
Linus Torvalds 已提交
397
	short txcount, wrd, residue, word_count, cnt;
398 399 400
	struct isi_port *port;
	struct tty_struct *tty;

L
Linus Torvalds 已提交
401 402
	/*	find next active board	*/
	card = (prev_card + 1) & 0x0003;
A
Alan Cox 已提交
403
	while (count-- > 0) {
404
		if (isi_card[card].status & BOARD_ACTIVE)
L
Linus Torvalds 已提交
405
			break;
406
		card = (card + 1) & 0x0003;
L
Linus Torvalds 已提交
407 408 409
	}
	if (!(isi_card[card].status & BOARD_ACTIVE))
		goto sched_again;
410

L
Linus Torvalds 已提交
411
	prev_card = card;
412

L
Linus Torvalds 已提交
413 414 415
	count = isi_card[card].port_count;
	port = isi_card[card].ports;
	base = isi_card[card].base;
J
Jiri Slaby 已提交
416 417 418 419 420 421 422 423 424 425

	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 已提交
426
	for (; count > 0; count--, port++) {
L
Linus Torvalds 已提交
427
		/* port not active or tx disabled to force flow control */
A
Alan Cox 已提交
428
		if (!(port->port.flags & ASYNC_INITIALIZED) ||
429
				!(port->status & ISI_TXOK))
L
Linus Torvalds 已提交
430
			continue;
431

A
Alan Cox 已提交
432
		tty = port->port.tty;
433

J
Jiri Slaby 已提交
434
		if (tty == NULL)
L
Linus Torvalds 已提交
435
			continue;
436

L
Linus Torvalds 已提交
437
		txcount = min_t(short, TX_SIZE, port->xmit_cnt);
J
Jiri Slaby 已提交
438
		if (txcount <= 0 || tty->stopped || tty->hw_stopped)
L
Linus Torvalds 已提交
439
			continue;
J
Jiri Slaby 已提交
440 441

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

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

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

J
Jiri Slaby 已提交
494 495
unlock:
	spin_unlock_irqrestore(&isi_card[card].card_lock, flags);
496 497
	/*	schedule another tx for hopefully in about 10ms	*/
sched_again:
J
Jiri Slaby 已提交
498
	mod_timer(&tx, jiffies + msecs_to_jiffies(10));
499 500
}

L
Linus Torvalds 已提交
501
/*
502
 *	Main interrupt handler routine
L
Linus Torvalds 已提交
503
 */
504

505
static irqreturn_t isicom_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
506
{
507
	struct isi_board *card = dev_id;
508 509
	struct isi_port *port;
	struct tty_struct *tty;
510 511
	unsigned long base;
	u16 header, word_count, count, channel;
L
Linus Torvalds 已提交
512
	short byte_count;
A
Alan Cox 已提交
513
	unsigned char *rp;
514

L
Linus Torvalds 已提交
515 516
	if (!card || !(card->status & FIRMWARE_LOADED))
		return IRQ_NONE;
517

L
Linus Torvalds 已提交
518
	base = card->base;
519 520 521 522 523

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

L
Linus Torvalds 已提交
524
	spin_lock(&card->card_lock);
525

J
Jiri Slaby 已提交
526 527 528 529 530 531
	/*
	 * disable any interrupts from the PCI card and lower the
	 * interrupt line
	 */
	outw(0x8000, base+0x04);
	ClearInterrupt(base);
532

L
Linus Torvalds 已提交
533 534 535 536 537 538
	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) {
539 540
		printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%lx): "
			"%d(channel) > port_count.\n", base, channel+1);
J
Jiri Slaby 已提交
541
		outw(0x0000, base+0x04); /* enable interrupts */
L
Linus Torvalds 已提交
542
		spin_unlock(&card->card_lock);
543
		return IRQ_HANDLED;
L
Linus Torvalds 已提交
544 545
	}
	port = card->ports + channel;
A
Alan Cox 已提交
546
	if (!(port->port.flags & ASYNC_INITIALIZED)) {
J
Jiri Slaby 已提交
547
		outw(0x0000, base+0x04); /* enable interrupts */
548
		spin_unlock(&card->card_lock);
L
Linus Torvalds 已提交
549
		return IRQ_HANDLED;
550 551
	}

A
Alan Cox 已提交
552
	tty = port->port.tty;
L
Linus Torvalds 已提交
553 554
	if (tty == NULL) {
		word_count = byte_count >> 1;
A
Alan Cox 已提交
555
		while (byte_count > 1) {
L
Linus Torvalds 已提交
556 557 558 559 560
			inw(base);
			byte_count -= 2;
		}
		if (byte_count & 0x01)
			inw(base);
J
Jiri Slaby 已提交
561
		outw(0x0000, base+0x04); /* enable interrupts */
L
Linus Torvalds 已提交
562 563 564
		spin_unlock(&card->card_lock);
		return IRQ_HANDLED;
	}
565

L
Linus Torvalds 已提交
566 567
	if (header & 0x8000) {		/* Status Packet */
		header = inw(base);
A
Alan Cox 已提交
568
		switch (header & 0xff) {
569
		case 0:	/* Change in EIA signals */
A
Alan Cox 已提交
570
			if (port->port.flags & ASYNC_CHECK_CD) {
571 572 573
				if (port->status & ISI_DCD) {
					if (!(header & ISI_DCD)) {
					/* Carrier has been lost  */
574 575
						pr_dbg("interrupt: DCD->low.\n"
							);
576
						port->status &= ~ISI_DCD;
577
						tty_hangup(tty);
L
Linus Torvalds 已提交
578
					}
579 580 581 582
				} else if (header & ISI_DCD) {
				/* Carrier has been detected */
					pr_dbg("interrupt: DCD->high.\n");
					port->status |= ISI_DCD;
A
Alan Cox 已提交
583
					wake_up_interruptible(&port->port.open_wait);
L
Linus Torvalds 已提交
584
				}
585
			} else {
586 587 588 589 590 591
				if (header & ISI_DCD)
					port->status |= ISI_DCD;
				else
					port->status &= ~ISI_DCD;
			}

A
Alan Cox 已提交
592 593
			if (port->port.flags & ASYNC_CTS_FLOW) {
				if (port->port.tty->hw_stopped) {
594
					if (header & ISI_CTS) {
A
Alan Cox 已提交
595
						port->port.tty->hw_stopped = 0;
596
						/* start tx ing */
597 598
						port->status |= (ISI_TXOK
							| ISI_CTS);
599
						tty_wakeup(tty);
L
Linus Torvalds 已提交
600
					}
601
				} else if (!(header & ISI_CTS)) {
A
Alan Cox 已提交
602
					port->port.tty->hw_stopped = 1;
603 604
					/* stop tx ing */
					port->status &= ~(ISI_TXOK | ISI_CTS);
L
Linus Torvalds 已提交
605
				}
606
			} else {
607 608
				if (header & ISI_CTS)
					port->status |= ISI_CTS;
L
Linus Torvalds 已提交
609
				else
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
					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;

625
		case 1:	/* Received Break !!! */
626
			tty_insert_flip_char(tty, 0, TTY_BREAK);
A
Alan Cox 已提交
627
			if (port->port.flags & ASYNC_SAK)
628 629 630 631 632
				do_SAK(tty);
			tty_flip_buffer_push(tty);
			break;

		case 2:	/* Statistics		 */
633
			pr_dbg("isicom_interrupt: stats!!!.\n");
634 635 636
			break;

		default:
637
			pr_dbg("Intr: Unknown code in status packet.\n");
638 639
			break;
		}
640
	} else {				/* Data   Packet */
A
Alan Cox 已提交
641 642

		count = tty_prepare_flip_string(tty, &rp, byte_count & ~1);
643
		pr_dbg("Intr: Can rx %d of %d bytes.\n", count, byte_count);
L
Linus Torvalds 已提交
644
		word_count = count >> 1;
A
Alan Cox 已提交
645
		insw(base, rp, word_count);
L
Linus Torvalds 已提交
646 647
		byte_count -= (word_count << 1);
		if (count & 0x0001) {
648 649
			tty_insert_flip_char(tty,  inw(base) & 0xff,
				TTY_NORMAL);
L
Linus Torvalds 已提交
650
			byte_count -= 2;
651
		}
L
Linus Torvalds 已提交
652
		if (byte_count > 0) {
653 654
			pr_dbg("Intr(0x%lx:%d): Flip buffer overflow! dropping "
				"bytes...\n", base, channel + 1);
A
Alan Cox 已提交
655 656
		/* drain out unread xtra data */
		while (byte_count > 0) {
L
Linus Torvalds 已提交
657 658 659 660
				inw(base);
				byte_count -= 2;
			}
		}
A
Alan Cox 已提交
661
		tty_flip_buffer_push(tty);
L
Linus Torvalds 已提交
662
	}
J
Jiri Slaby 已提交
663
	outw(0x0000, base+0x04); /* enable interrupts */
664
	spin_unlock(&card->card_lock);
665

L
Linus Torvalds 已提交
666
	return IRQ_HANDLED;
667
}
L
Linus Torvalds 已提交
668

669
static void isicom_config_port(struct isi_port *port)
L
Linus Torvalds 已提交
670
{
671 672
	struct isi_board *card = port->card;
	struct tty_struct *tty;
L
Linus Torvalds 已提交
673
	unsigned long baud;
674 675 676
	unsigned long base = card->base;
	u16 channel_setup, channel = port->channel,
		shift_count = card->shift_count;
L
Linus Torvalds 已提交
677
	unsigned char flow_ctrl;
678

A
Alan Cox 已提交
679
	tty = port->port.tty;
A
Alan Cox 已提交
680 681

	if (tty == NULL)
L
Linus Torvalds 已提交
682
		return;
A
Alan Cox 已提交
683
	/* FIXME: Switch to new tty baud API */
L
Linus Torvalds 已提交
684 685 686
	baud = C_BAUD(tty);
	if (baud & CBAUDEX) {
		baud &= ~CBAUDEX;
687

L
Linus Torvalds 已提交
688 689 690
		/*  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.
691 692
		 */

693 694
		/* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
		if (baud < 1 || baud > 4)
A
Alan Cox 已提交
695
			port->port.tty->termios->c_cflag &= ~CBAUDEX;
L
Linus Torvalds 已提交
696 697
		else
			baud += 15;
698
	}
L
Linus Torvalds 已提交
699
	if (baud == 15) {
700 701

		/*  the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set
L
Linus Torvalds 已提交
702 703
		 *  by the set_serial_info ioctl ... this is done by
		 *  the 'setserial' utility.
704 705
		 */

A
Alan Cox 已提交
706
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
707
			baud++; /*  57.6 Kbps */
A
Alan Cox 已提交
708
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
A
Alan Cox 已提交
709
			baud += 2; /*  115  Kbps */
A
Alan Cox 已提交
710
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
711
			baud += 3; /* 230 kbps*/
A
Alan Cox 已提交
712
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
713
			baud += 4; /* 460 kbps*/
L
Linus Torvalds 已提交
714 715 716
	}
	if (linuxb_to_isib[baud] == -1) {
		/* hang up */
717 718
		drop_dtr(port);
		return;
A
Alan Cox 已提交
719
	} else
L
Linus Torvalds 已提交
720
		raise_dtr(port);
721

722
	if (WaitTillCardIsFree(base) == 0) {
A
Alan Cox 已提交
723
		outw(0x8000 | (channel << shift_count) | 0x03, base);
L
Linus Torvalds 已提交
724 725
		outw(linuxb_to_isib[baud] << 8 | 0x03, base);
		channel_setup = 0;
A
Alan Cox 已提交
726
		switch (C_CSIZE(tty)) {
727 728 729 730 731 732 733 734 735 736 737 738
		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 已提交
739
		}
740

L
Linus Torvalds 已提交
741 742 743 744 745
		if (C_CSTOPB(tty))
			channel_setup |= ISICOM_2SB;
		if (C_PARENB(tty)) {
			channel_setup |= ISICOM_EVPAR;
			if (C_PARODD(tty))
746
				channel_setup |= ISICOM_ODPAR;
L
Linus Torvalds 已提交
747
		}
748
		outw(channel_setup, base);
L
Linus Torvalds 已提交
749
		InterruptTheCard(base);
750
	}
L
Linus Torvalds 已提交
751
	if (C_CLOCAL(tty))
A
Alan Cox 已提交
752
		port->port.flags &= ~ASYNC_CHECK_CD;
L
Linus Torvalds 已提交
753
	else
A
Alan Cox 已提交
754
		port->port.flags |= ASYNC_CHECK_CD;
755

L
Linus Torvalds 已提交
756 757
	/* flow control settings ...*/
	flow_ctrl = 0;
A
Alan Cox 已提交
758
	port->port.flags &= ~ASYNC_CTS_FLOW;
L
Linus Torvalds 已提交
759
	if (C_CRTSCTS(tty)) {
A
Alan Cox 已提交
760
		port->port.flags |= ASYNC_CTS_FLOW;
L
Linus Torvalds 已提交
761
		flow_ctrl |= ISICOM_CTSRTS;
762 763
	}
	if (I_IXON(tty))
L
Linus Torvalds 已提交
764 765
		flow_ctrl |= ISICOM_RESPOND_XONXOFF;
	if (I_IXOFF(tty))
766 767
		flow_ctrl |= ISICOM_INITIATE_XONXOFF;

768
	if (WaitTillCardIsFree(base) == 0) {
A
Alan Cox 已提交
769
		outw(0x8000 | (channel << shift_count) | 0x04, base);
L
Linus Torvalds 已提交
770 771 772 773
		outw(flow_ctrl << 8 | 0x05, base);
		outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
		InterruptTheCard(base);
	}
774

L
Linus Torvalds 已提交
775 776 777 778 779 780 781
	/*	rx enabled -> enable port for rx on the card	*/
	if (C_CREAD(tty)) {
		card->port_status |= (1 << channel);
		outw(card->port_status, base + 0x02);
	}
}

782 783 784
/* open et all */

static inline void isicom_setup_board(struct isi_board *bp)
L
Linus Torvalds 已提交
785 786
{
	int channel;
787
	struct isi_port *port;
L
Linus Torvalds 已提交
788
	unsigned long flags;
789

L
Linus Torvalds 已提交
790 791 792 793 794 795 796
	spin_lock_irqsave(&bp->card_lock, flags);
	if (bp->status & BOARD_ACTIVE) {
		spin_unlock_irqrestore(&bp->card_lock, flags);
		return;
	}
	port = bp->ports;
	bp->status |= BOARD_ACTIVE;
797
	for (channel = 0; channel < bp->port_count; channel++, port++)
L
Linus Torvalds 已提交
798
		drop_dtr_rts(port);
799
	spin_unlock_irqrestore(&bp->card_lock, flags);
L
Linus Torvalds 已提交
800
}
801 802

static int isicom_setup_port(struct isi_port *port)
L
Linus Torvalds 已提交
803
{
804
	struct isi_board *card = port->card;
L
Linus Torvalds 已提交
805
	unsigned long flags;
806

A
Alan Cox 已提交
807
	if (port->port.flags & ASYNC_INITIALIZED)
L
Linus Torvalds 已提交
808
		return 0;
A
Alan Cox 已提交
809 810
	if (tty_port_alloc_xmit_buf(&port->port) < 0)
		return -ENOMEM;
L
Linus Torvalds 已提交
811 812

	spin_lock_irqsave(&card->card_lock, flags);
A
Alan Cox 已提交
813 814 815
	if (port->port.tty)
		clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
	if (port->port.count == 1)
L
Linus Torvalds 已提交
816
		card->count++;
817

L
Linus Torvalds 已提交
818
	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
819

L
Linus Torvalds 已提交
820
	/*	discard any residual data	*/
821 822 823 824 825 826
	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);
	}
827

L
Linus Torvalds 已提交
828
	isicom_config_port(port);
A
Alan Cox 已提交
829
	port->port.flags |= ASYNC_INITIALIZED;
L
Linus Torvalds 已提交
830
	spin_unlock_irqrestore(&card->card_lock, flags);
831 832 833 834

	return 0;
}

835 836
static int block_til_ready(struct tty_struct *tty, struct file *filp,
	struct isi_port *port)
L
Linus Torvalds 已提交
837
{
838
	struct isi_board *card = port->card;
L
Linus Torvalds 已提交
839 840 841 842 843 844
	int do_clocal = 0, retval;
	unsigned long flags;
	DECLARE_WAITQUEUE(wait, current);

	/* block if port is in the process of being closed */

A
Alan Cox 已提交
845
	if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) {
846
		pr_dbg("block_til_ready: close in progress.\n");
A
Alan Cox 已提交
847 848
		interruptible_sleep_on(&port->port.close_wait);
		if (port->port.flags & ASYNC_HUP_NOTIFY)
L
Linus Torvalds 已提交
849 850 851 852
			return -EAGAIN;
		else
			return -ERESTARTSYS;
	}
853

L
Linus Torvalds 已提交
854
	/* if non-blocking mode is set ... */
855

856 857
	if ((filp->f_flags & O_NONBLOCK) ||
			(tty->flags & (1 << TTY_IO_ERROR))) {
858
		pr_dbg("block_til_ready: non-block mode.\n");
A
Alan Cox 已提交
859
		port->port.flags |= ASYNC_NORMAL_ACTIVE;
860 861 862
		return 0;
	}

L
Linus Torvalds 已提交
863 864
	if (C_CLOCAL(tty))
		do_clocal = 1;
865 866

	/* block waiting for DCD to be asserted, and while
L
Linus Torvalds 已提交
867 868
						callout dev is busy */
	retval = 0;
A
Alan Cox 已提交
869
	add_wait_queue(&port->port.open_wait, &wait);
L
Linus Torvalds 已提交
870 871 872

	spin_lock_irqsave(&card->card_lock, flags);
	if (!tty_hung_up_p(filp))
A
Alan Cox 已提交
873 874
		port->port.count--;
	port->port.blocked_open++;
L
Linus Torvalds 已提交
875
	spin_unlock_irqrestore(&card->card_lock, flags);
876

L
Linus Torvalds 已提交
877 878 879 880
	while (1) {
		raise_dtr_rts(port);

		set_current_state(TASK_INTERRUPTIBLE);
A
Alan Cox 已提交
881 882
		if (tty_hung_up_p(filp) || !(port->port.flags & ASYNC_INITIALIZED)) {
			if (port->port.flags & ASYNC_HUP_NOTIFY)
L
Linus Torvalds 已提交
883 884 885 886
				retval = -EAGAIN;
			else
				retval = -ERESTARTSYS;
			break;
887
		}
A
Alan Cox 已提交
888
		if (!(port->port.flags & ASYNC_CLOSING) &&
889
				(do_clocal || (port->status & ISI_DCD))) {
L
Linus Torvalds 已提交
890
			break;
891
		}
L
Linus Torvalds 已提交
892 893 894 895
		if (signal_pending(current)) {
			retval = -ERESTARTSYS;
			break;
		}
896
		schedule();
L
Linus Torvalds 已提交
897 898
	}
	set_current_state(TASK_RUNNING);
A
Alan Cox 已提交
899
	remove_wait_queue(&port->port.open_wait, &wait);
L
Linus Torvalds 已提交
900 901
	spin_lock_irqsave(&card->card_lock, flags);
	if (!tty_hung_up_p(filp))
A
Alan Cox 已提交
902 903
		port->port.count++;
	port->port.blocked_open--;
L
Linus Torvalds 已提交
904 905 906
	spin_unlock_irqrestore(&card->card_lock, flags);
	if (retval)
		return retval;
A
Alan Cox 已提交
907
	port->port.flags |= ASYNC_NORMAL_ACTIVE;
L
Linus Torvalds 已提交
908 909
	return 0;
}
910 911

static int isicom_open(struct tty_struct *tty, struct file *filp)
L
Linus Torvalds 已提交
912
{
913 914
	struct isi_port *port;
	struct isi_board *card;
915 916
	unsigned int board;
	int error, line;
L
Linus Torvalds 已提交
917 918 919 920 921 922

	line = tty->index;
	if (line < 0 || line > PORT_COUNT-1)
		return -ENODEV;
	board = BOARD(line);
	card = &isi_card[board];
923

L
Linus Torvalds 已提交
924 925
	if (!(card->status & FIRMWARE_LOADED))
		return -ENODEV;
926

L
Linus Torvalds 已提交
927 928 929 930
	/*  open on a port greater than the port count for the card !!! */
	if (line > ((board * 16) + card->port_count - 1))
		return -ENODEV;

931
	port = &isi_ports[line];
L
Linus Torvalds 已提交
932 933
	if (isicom_paranoia_check(port, tty->name, "isicom_open"))
		return -ENODEV;
934 935 936

	isicom_setup_board(card);

A
Alan Cox 已提交
937
	port->port.count++;
L
Linus Torvalds 已提交
938
	tty->driver_data = port;
A
Alan Cox 已提交
939
	port->port.tty = tty;
A
Alan Cox 已提交
940 941 942 943
	error = isicom_setup_port(port);
	if (error == 0)
		error = block_til_ready(tty, filp, port);
	return error;
L
Linus Torvalds 已提交
944
}
945

L
Linus Torvalds 已提交
946 947
/* close et all */

948
static inline void isicom_shutdown_board(struct isi_board *bp)
L
Linus Torvalds 已提交
949
{
A
Alan Cox 已提交
950
	if (bp->status & BOARD_ACTIVE)
L
Linus Torvalds 已提交
951 952 953
		bp->status &= ~BOARD_ACTIVE;
}

954
/* card->lock HAS to be held */
955
static void isicom_shutdown_port(struct isi_port *port)
L
Linus Torvalds 已提交
956
{
957 958 959
	struct isi_board *card = port->card;
	struct tty_struct *tty;

A
Alan Cox 已提交
960
	tty = port->port.tty;
L
Linus Torvalds 已提交
961

A
Alan Cox 已提交
962
	if (!(port->port.flags & ASYNC_INITIALIZED))
L
Linus Torvalds 已提交
963
		return;
964

A
Alan Cox 已提交
965 966
	tty_port_free_xmit_buf(&port->port);
	port->port.flags &= ~ASYNC_INITIALIZED;
L
Linus Torvalds 已提交
967
	/* 3rd October 2000 : Vinayak P Risbud */
A
Alan Cox 已提交
968
	port->port.tty = NULL;
969

L
Linus Torvalds 已提交
970 971 972
	/*Fix done by Anil .S on 30-04-2001
	remote login through isi port has dtr toggle problem
	due to which the carrier drops before the password prompt
973
	appears on the remote end. Now we drop the dtr only if the
L
Linus Torvalds 已提交
974
	HUPCL(Hangup on close) flag is set for the tty*/
975 976

	if (C_HUPCL(tty))
L
Linus Torvalds 已提交
977 978
		/* drop dtr on this port */
		drop_dtr(port);
979 980

	/* any other port uninits  */
L
Linus Torvalds 已提交
981 982
	if (tty)
		set_bit(TTY_IO_ERROR, &tty->flags);
983

L
Linus Torvalds 已提交
984
	if (--card->count < 0) {
985
		pr_dbg("isicom_shutdown_port: bad board(0x%lx) count %d.\n",
L
Linus Torvalds 已提交
986
			card->base, card->count);
987
		card->count = 0;
L
Linus Torvalds 已提交
988
	}
989

990
	/* last port was closed, shutdown that boad too */
991
	if (C_HUPCL(tty)) {
L
Linus Torvalds 已提交
992 993 994 995 996
		if (!card->count)
			isicom_shutdown_board(card);
	}
}

997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
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);
}

1013
static void isicom_close(struct tty_struct *tty, struct file *filp)
L
Linus Torvalds 已提交
1014
{
1015
	struct isi_port *port = tty->driver_data;
J
Jiri Slaby 已提交
1016
	struct isi_board *card;
L
Linus Torvalds 已提交
1017
	unsigned long flags;
1018

L
Linus Torvalds 已提交
1019 1020
	if (!port)
		return;
J
Jiri Slaby 已提交
1021
	card = port->card;
L
Linus Torvalds 已提交
1022 1023
	if (isicom_paranoia_check(port, tty->name, "isicom_close"))
		return;
1024

1025
	pr_dbg("Close start!!!.\n");
1026

L
Linus Torvalds 已提交
1027 1028 1029 1030 1031
	spin_lock_irqsave(&card->card_lock, flags);
	if (tty_hung_up_p(filp)) {
		spin_unlock_irqrestore(&card->card_lock, flags);
		return;
	}
1032

A
Alan Cox 已提交
1033
	if (tty->count == 1 && port->port.count != 1) {
1034 1035
		printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port "
			"count tty->count = 1 port count = %d.\n",
A
Alan Cox 已提交
1036 1037
			card->base, port->port.count);
		port->port.count = 1;
L
Linus Torvalds 已提交
1038
	}
A
Alan Cox 已提交
1039
	if (--port->port.count < 0) {
1040 1041
		printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port "
			"count for channel%d = %d", card->base, port->channel,
A
Alan Cox 已提交
1042 1043
			port->port.count);
		port->port.count = 0;
L
Linus Torvalds 已提交
1044
	}
1045

A
Alan Cox 已提交
1046
	if (port->port.count) {
L
Linus Torvalds 已提交
1047 1048
		spin_unlock_irqrestore(&card->card_lock, flags);
		return;
1049
	}
A
Alan Cox 已提交
1050
	port->port.flags |= ASYNC_CLOSING;
L
Linus Torvalds 已提交
1051 1052
	tty->closing = 1;
	spin_unlock_irqrestore(&card->card_lock, flags);
1053

L
Linus Torvalds 已提交
1054 1055
	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
		tty_wait_until_sent(tty, port->closing_wait);
1056
	/* indicate to the card that no more data can be received
L
Linus Torvalds 已提交
1057 1058
	   on this port */
	spin_lock_irqsave(&card->card_lock, flags);
A
Alan Cox 已提交
1059
	if (port->port.flags & ASYNC_INITIALIZED) {
L
Linus Torvalds 已提交
1060 1061
		card->port_status &= ~(1 << port->channel);
		outw(card->port_status, card->base + 0x02);
1062
	}
L
Linus Torvalds 已提交
1063 1064
	isicom_shutdown_port(port);
	spin_unlock_irqrestore(&card->card_lock, flags);
1065

1066
	isicom_flush_buffer(tty);
L
Linus Torvalds 已提交
1067 1068 1069 1070 1071
	tty_ldisc_flush(tty);

	spin_lock_irqsave(&card->card_lock, flags);
	tty->closing = 0;

A
Alan Cox 已提交
1072
	if (port->port.blocked_open) {
L
Linus Torvalds 已提交
1073 1074
		spin_unlock_irqrestore(&card->card_lock, flags);
		if (port->close_delay) {
1075
			pr_dbg("scheduling until time out.\n");
1076 1077
			msleep_interruptible(
				jiffies_to_msecs(port->close_delay));
L
Linus Torvalds 已提交
1078 1079
		}
		spin_lock_irqsave(&card->card_lock, flags);
A
Alan Cox 已提交
1080
		wake_up_interruptible(&port->port.open_wait);
1081
	}
A
Alan Cox 已提交
1082 1083
	port->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
	wake_up_interruptible(&port->port.close_wait);
L
Linus Torvalds 已提交
1084 1085 1086 1087
	spin_unlock_irqrestore(&card->card_lock, flags);
}

/* write et all */
1088 1089
static int isicom_write(struct tty_struct *tty,	const unsigned char *buf,
	int count)
L
Linus Torvalds 已提交
1090
{
1091
	struct isi_port *port = tty->driver_data;
1092
	struct isi_board *card = port->card;
L
Linus Torvalds 已提交
1093 1094 1095 1096 1097
	unsigned long flags;
	int cnt, total = 0;

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

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

A
Alan Cox 已提交
1101
	while (1) {
1102 1103
		cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt
				- 1, SERIAL_XMIT_SIZE - port->xmit_head));
1104
		if (cnt <= 0)
L
Linus Torvalds 已提交
1105
			break;
1106

A
Alan Cox 已提交
1107
		memcpy(port->port.xmit_buf + port->xmit_head, buf, cnt);
1108 1109
		port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE
			- 1);
L
Linus Torvalds 已提交
1110 1111 1112 1113
		port->xmit_cnt += cnt;
		buf += cnt;
		count -= cnt;
		total += cnt;
1114
	}
L
Linus Torvalds 已提交
1115 1116 1117
	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped)
		port->status |= ISI_TXOK;
	spin_unlock_irqrestore(&card->card_lock, flags);
1118
	return total;
L
Linus Torvalds 已提交
1119 1120 1121
}

/* put_char et all */
A
Alan Cox 已提交
1122
static int isicom_put_char(struct tty_struct *tty, unsigned char ch)
L
Linus Torvalds 已提交
1123
{
1124
	struct isi_port *port = tty->driver_data;
1125
	struct isi_board *card = port->card;
L
Linus Torvalds 已提交
1126
	unsigned long flags;
1127

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

L
Linus Torvalds 已提交
1131
	spin_lock_irqsave(&card->card_lock, flags);
A
Alan Cox 已提交
1132 1133 1134 1135
	if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
		spin_unlock_irqrestore(&card->card_lock, flags);
		return 0;
	}
1136

A
Alan Cox 已提交
1137
	port->port.xmit_buf[port->xmit_head++] = ch;
L
Linus Torvalds 已提交
1138 1139 1140
	port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
	port->xmit_cnt++;
	spin_unlock_irqrestore(&card->card_lock, flags);
A
Alan Cox 已提交
1141
	return 1;
L
Linus Torvalds 已提交
1142 1143 1144
}

/* flush_chars et all */
1145
static void isicom_flush_chars(struct tty_struct *tty)
L
Linus Torvalds 已提交
1146
{
1147
	struct isi_port *port = tty->driver_data;
1148

L
Linus Torvalds 已提交
1149 1150
	if (isicom_paranoia_check(port, tty->name, "isicom_flush_chars"))
		return;
1151

1152
	if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
A
Alan Cox 已提交
1153
			!port->port.xmit_buf)
L
Linus Torvalds 已提交
1154
		return;
1155

L
Linus Torvalds 已提交
1156 1157
	/* this tells the transmitter to consider this port for
	   data output to the card ... that's the best we can do. */
1158
	port->status |= ISI_TXOK;
L
Linus Torvalds 已提交
1159 1160 1161
}

/* write_room et all */
1162
static int isicom_write_room(struct tty_struct *tty)
L
Linus Torvalds 已提交
1163
{
1164
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1165 1166 1167 1168
	int free;

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

L
Linus Torvalds 已提交
1170 1171 1172 1173 1174 1175 1176
	free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
	if (free < 0)
		free = 0;
	return free;
}

/* chars_in_buffer et all */
1177
static int isicom_chars_in_buffer(struct tty_struct *tty)
L
Linus Torvalds 已提交
1178
{
1179
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1180 1181 1182 1183 1184 1185
	if (isicom_paranoia_check(port, tty->name, "isicom_chars_in_buffer"))
		return 0;
	return port->xmit_cnt;
}

/* ioctl et all */
1186 1187
static inline void isicom_send_break(struct isi_port *port,
	unsigned long length)
L
Linus Torvalds 已提交
1188
{
1189
	struct isi_board *card = port->card;
1190
	unsigned long base = card->base;
1191 1192

	if (!lock_card(card))
L
Linus Torvalds 已提交
1193
		return;
1194

L
Linus Torvalds 已提交
1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
	outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
	outw((length & 0xff) << 8 | 0x00, base);
	outw((length & 0xff00), base);
	InterruptTheCard(base);

	unlock_card(card);
}

static int isicom_tiocmget(struct tty_struct *tty, struct file *file)
{
1205
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1206
	/* just send the port status */
1207
	u16 status = port->status;
L
Linus Torvalds 已提交
1208 1209 1210

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

L
Linus Torvalds 已提交
1212 1213 1214 1215 1216 1217 1218 1219 1220
	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);
}

static int isicom_tiocmset(struct tty_struct *tty, struct file *file,
1221
	unsigned int set, unsigned int clear)
L
Linus Torvalds 已提交
1222
{
1223
	struct isi_port *port = tty->driver_data;
1224
	unsigned long flags;
1225

L
Linus Torvalds 已提交
1226 1227
	if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
		return -ENODEV;
1228

1229
	spin_lock_irqsave(&port->card->card_lock, flags);
L
Linus Torvalds 已提交
1230 1231 1232 1233 1234 1235 1236 1237 1238
	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);
1239
	spin_unlock_irqrestore(&port->card->card_lock, flags);
L
Linus Torvalds 已提交
1240 1241

	return 0;
1242
}
L
Linus Torvalds 已提交
1243

1244 1245
static int isicom_set_serial_info(struct isi_port *port,
	struct serial_struct __user *info)
L
Linus Torvalds 已提交
1246 1247 1248 1249
{
	struct serial_struct newinfo;
	int reconfig_port;

1250
	if (copy_from_user(&newinfo, info, sizeof(newinfo)))
L
Linus Torvalds 已提交
1251
		return -EFAULT;
1252

1253 1254
	lock_kernel();

A
Alan Cox 已提交
1255
	reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) !=
1256 1257
		(newinfo.flags & ASYNC_SPD_MASK));

L
Linus Torvalds 已提交
1258 1259
	if (!capable(CAP_SYS_ADMIN)) {
		if ((newinfo.close_delay != port->close_delay) ||
1260 1261
				(newinfo.closing_wait != port->closing_wait) ||
				((newinfo.flags & ~ASYNC_USR_MASK) !=
A
Alan Cox 已提交
1262
				(port->port.flags & ~ASYNC_USR_MASK))) {
1263
			unlock_kernel();
L
Linus Torvalds 已提交
1264
			return -EPERM;
1265
		}
A
Alan Cox 已提交
1266
		port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
L
Linus Torvalds 已提交
1267
				(newinfo.flags & ASYNC_USR_MASK));
A
Alan Cox 已提交
1268
	} else {
L
Linus Torvalds 已提交
1269
		port->close_delay = newinfo.close_delay;
1270
		port->closing_wait = newinfo.closing_wait;
A
Alan Cox 已提交
1271
		port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
L
Linus Torvalds 已提交
1272 1273 1274
				(newinfo.flags & ASYNC_FLAGS));
	}
	if (reconfig_port) {
1275 1276
		unsigned long flags;
		spin_lock_irqsave(&port->card->card_lock, flags);
L
Linus Torvalds 已提交
1277
		isicom_config_port(port);
1278
		spin_unlock_irqrestore(&port->card->card_lock, flags);
L
Linus Torvalds 已提交
1279
	}
1280
	unlock_kernel();
1281 1282
	return 0;
}
L
Linus Torvalds 已提交
1283

1284 1285
static int isicom_get_serial_info(struct isi_port *port,
	struct serial_struct __user *info)
L
Linus Torvalds 已提交
1286 1287
{
	struct serial_struct out_info;
1288

1289
	lock_kernel();
L
Linus Torvalds 已提交
1290 1291 1292 1293 1294
	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 已提交
1295
	out_info.flags = port->port.flags;
L
Linus Torvalds 已提交
1296 1297 1298
/*	out_info.baud_base = ? */
	out_info.close_delay = port->close_delay;
	out_info.closing_wait = port->closing_wait;
1299
	unlock_kernel();
1300
	if (copy_to_user(info, &out_info, sizeof(out_info)))
L
Linus Torvalds 已提交
1301 1302
		return -EFAULT;
	return 0;
1303
}
L
Linus Torvalds 已提交
1304

1305 1306
static int isicom_ioctl(struct tty_struct *tty, struct file *filp,
	unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
1307
{
1308
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1309 1310 1311 1312 1313 1314
	void __user *argp = (void __user *)arg;
	int retval;

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

A
Alan Cox 已提交
1315
	switch (cmd) {
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339
	case TCSBRK:
		retval = tty_check_change(tty);
		if (retval)
			return retval;
		tty_wait_until_sent(tty, 0);
		if (!arg)
			isicom_send_break(port, HZ/4);
		return 0;

	case TCSBRKP:
		retval = tty_check_change(tty);
		if (retval)
			return retval;
		tty_wait_until_sent(tty, 0);
		isicom_send_break(port, arg ? arg * (HZ/10) : HZ/4);
		return 0;
	case TIOCGSERIAL:
		return isicom_get_serial_info(port, argp);

	case TIOCSSERIAL:
		return isicom_set_serial_info(port, argp);

	default:
		return -ENOIOCTLCMD;
L
Linus Torvalds 已提交
1340 1341 1342 1343 1344
	}
	return 0;
}

/* set_termios et all */
1345
static void isicom_set_termios(struct tty_struct *tty,
A
Alan Cox 已提交
1346
	struct ktermios *old_termios)
L
Linus Torvalds 已提交
1347
{
1348
	struct isi_port *port = tty->driver_data;
1349
	unsigned long flags;
1350

L
Linus Torvalds 已提交
1351 1352
	if (isicom_paranoia_check(port, tty->name, "isicom_set_termios"))
		return;
1353

L
Linus Torvalds 已提交
1354
	if (tty->termios->c_cflag == old_termios->c_cflag &&
1355
			tty->termios->c_iflag == old_termios->c_iflag)
L
Linus Torvalds 已提交
1356
		return;
1357

1358
	spin_lock_irqsave(&port->card->card_lock, flags);
L
Linus Torvalds 已提交
1359
	isicom_config_port(port);
1360
	spin_unlock_irqrestore(&port->card->card_lock, flags);
1361

L
Linus Torvalds 已提交
1362
	if ((old_termios->c_cflag & CRTSCTS) &&
1363
			!(tty->termios->c_cflag & CRTSCTS)) {
L
Linus Torvalds 已提交
1364
		tty->hw_stopped = 0;
1365 1366
		isicom_start(tty);
	}
L
Linus Torvalds 已提交
1367 1368 1369
}

/* throttle et all */
1370
static void isicom_throttle(struct tty_struct *tty)
L
Linus Torvalds 已提交
1371
{
1372
	struct isi_port *port = tty->driver_data;
1373 1374
	struct isi_board *card = port->card;

L
Linus Torvalds 已提交
1375 1376
	if (isicom_paranoia_check(port, tty->name, "isicom_throttle"))
		return;
1377

L
Linus Torvalds 已提交
1378 1379 1380 1381 1382 1383
	/* 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 */
1384
static void isicom_unthrottle(struct tty_struct *tty)
L
Linus Torvalds 已提交
1385
{
1386
	struct isi_port *port = tty->driver_data;
1387 1388
	struct isi_board *card = port->card;

L
Linus Torvalds 已提交
1389 1390
	if (isicom_paranoia_check(port, tty->name, "isicom_unthrottle"))
		return;
1391

L
Linus Torvalds 已提交
1392 1393 1394 1395 1396 1397
	/* 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 */
1398
static void isicom_stop(struct tty_struct *tty)
L
Linus Torvalds 已提交
1399
{
1400
	struct isi_port *port = tty->driver_data;
L
Linus Torvalds 已提交
1401 1402 1403

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

L
Linus Torvalds 已提交
1405 1406 1407 1408 1409 1410
	/* this tells the transmitter not to consider this port for
	   data output to the card. */
	port->status &= ~ISI_TXOK;
}

/* start et all */
1411
static void isicom_start(struct tty_struct *tty)
L
Linus Torvalds 已提交
1412
{
1413
	struct isi_port *port = tty->driver_data;
1414

L
Linus Torvalds 已提交
1415 1416
	if (isicom_paranoia_check(port, tty->name, "isicom_start"))
		return;
1417

L
Linus Torvalds 已提交
1418 1419 1420 1421 1422
	/* this tells the transmitter to consider this port for
	   data output to the card. */
	port->status |= ISI_TXOK;
}

1423
static void isicom_hangup(struct tty_struct *tty)
L
Linus Torvalds 已提交
1424
{
1425
	struct isi_port *port = tty->driver_data;
1426
	unsigned long flags;
1427

L
Linus Torvalds 已提交
1428 1429
	if (isicom_paranoia_check(port, tty->name, "isicom_hangup"))
		return;
1430

1431
	spin_lock_irqsave(&port->card->card_lock, flags);
L
Linus Torvalds 已提交
1432
	isicom_shutdown_port(port);
1433 1434
	spin_unlock_irqrestore(&port->card->card_lock, flags);

A
Alan Cox 已提交
1435 1436 1437 1438
	port->port.count = 0;
	port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
	port->port.tty = NULL;
	wake_up_interruptible(&port->port.open_wait);
L
Linus Torvalds 已提交
1439 1440 1441
}


1442 1443 1444
/*
 * Driver init and deinit functions
 */
L
Linus Torvalds 已提交
1445

J
Jeff Dike 已提交
1446
static const struct tty_operations isicom_ops = {
1447 1448 1449 1450 1451 1452
	.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 已提交
1453
	.chars_in_buffer	= isicom_chars_in_buffer,
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463
	.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,
L
Linus Torvalds 已提交
1464 1465
};

1466 1467
static int __devinit reset_card(struct pci_dev *pdev,
	const unsigned int card, unsigned int *signature)
L
Linus Torvalds 已提交
1468
{
1469 1470
	struct isi_board *board = pci_get_drvdata(pdev);
	unsigned long base = board->base;
1471
	unsigned int sig, portcount = 0;
1472
	int retval = 0;
1473

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

1477
	inw(base + 0x8);
1478

1479
	msleep(10);
1480 1481 1482

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

1483
	msleep(1000);
1484

1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
	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);
1497

J
Jiri Slaby 已提交
1498
	portcount = inw(base + 0x2);
1499
	if (!(inw(base + 0xe) & 0x1) || (portcount != 0 && portcount != 4 &&
1500
				portcount != 8 && portcount != 16)) {
1501
		dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.\n",
1502
			card + 1);
J
Jiri Slaby 已提交
1503 1504
		retval = -EIO;
		goto end;
1505 1506
	}

1507
	switch (sig) {
1508 1509 1510
	case 0xa5:
	case 0xbb:
	case 0xdd:
J
Jiri Slaby 已提交
1511
		board->port_count = (portcount == 4) ? 4 : 8;
1512 1513 1514
		board->shift_count = 12;
		break;
	case 0xcc:
1515
	case 0xee:
1516 1517 1518
		board->port_count = 16;
		board->shift_count = 11;
		break;
1519
	}
1520
	dev_info(&pdev->dev, "-Done\n");
1521
	*signature = sig;
1522

1523 1524
end:
	return retval;
L
Linus Torvalds 已提交
1525 1526
}

1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563
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 已提交
1564
	}
1565 1566 1567 1568 1569

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

1570 1571
	retval = -EIO;

1572 1573
	for (frame = (struct stframe *)fw->data;
			frame < (struct stframe *)(fw->data + fw->size);
1574 1575
			frame = (struct stframe *)((u8 *)(frame + 1) +
				frame->count)) {
1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591
		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 已提交
1592 1593
		status = inw(base + 0x4);
		if (status != 0) {
1594
			dev_warn(&pdev->dev, "Card%d rejected load header:\n"
1595 1596 1597
				KERN_WARNING "Address:0x%x\n"
				KERN_WARNING "Count:0x%x\n"
				KERN_WARNING "Status:0x%x\n",
1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609
				index + 1, frame->addr, frame->count, status);
			goto errrelfw;
		}
		outsw(base, frame->data, word_count);

		InterruptTheCard(base);

		udelay(50); /* 0x0f */

		if (WaitTillCardIsFree(base))
			goto errrelfw;

A
Alan Cox 已提交
1610 1611
		status = inw(base + 0x4);
		if (status != 0) {
1612 1613 1614 1615
			dev_err(&pdev->dev, "Card%d got out of sync.Card "
				"Status:0x%x\n", index + 1, status);
			goto errrelfw;
		}
A
Alan Cox 已提交
1616
	}
1617 1618 1619

/* XXX: should we test it by reading it back and comparing with original like
 * in load firmware package? */
1620 1621 1622 1623
	for (frame = (struct stframe *)fw->data;
			frame < (struct stframe *)(fw->data + fw->size);
			frame = (struct stframe *)((u8 *)(frame + 1) +
				frame->count)) {
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639
		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 已提交
1640 1641
		status = inw(base + 0x4);
		if (status != 0) {
1642
			dev_warn(&pdev->dev, "Card%d rejected verify header:\n"
1643 1644 1645
				KERN_WARNING "Address:0x%x\n"
				KERN_WARNING "Count:0x%x\n"
				KERN_WARNING "Status: 0x%x\n",
1646 1647 1648 1649 1650
				index + 1, frame->addr, frame->count, status);
			goto errrelfw;
		}

		data = kmalloc(word_count * 2, GFP_KERNEL);
1651 1652 1653 1654 1655
		if (data == NULL) {
			dev_err(&pdev->dev, "Card%d, firmware upload "
				"failed, not enough memory\n", index + 1);
			goto errrelfw;
		}
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673
		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 已提交
1674 1675
		status = inw(base + 0x4);
		if (status != 0) {
1676 1677 1678 1679 1680 1681
			dev_err(&pdev->dev, "Card%d verify got out of sync. "
				"Card Status:0x%x\n", index + 1, status);
			goto errrelfw;
		}
	}

1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692
	/* 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 */

1693 1694 1695 1696 1697 1698 1699 1700 1701
	board->status |= FIRMWARE_LOADED;
	retval = 0;

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

L
Linus Torvalds 已提交
1702 1703 1704
/*
 *	Insmod can set static symbols so keep these static
 */
1705
static unsigned int card_count;
1706 1707 1708 1709

static int __devinit isicom_probe(struct pci_dev *pdev,
	const struct pci_device_id *ent)
{
1710
	unsigned int signature, index;
1711 1712 1713
	int retval = -EPERM;
	struct isi_board *board = NULL;

1714
	if (card_count >= BOARD_COUNT)
1715 1716
		goto err;

1717 1718 1719 1720 1721 1722
	retval = pci_enable_device(pdev);
	if (retval) {
		dev_err(&pdev->dev, "failed to enable\n");
		goto err;
	}

1723 1724 1725 1726 1727 1728 1729 1730 1731
	dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device);

	/* allot the first empty slot in the array */
	for (index = 0; index < BOARD_COUNT; index++)
		if (isi_card[index].base == 0) {
			board = &isi_card[index];
			break;
		}

1732
	board->index = index;
1733 1734
	board->base = pci_resource_start(pdev, 3);
	board->irq = pdev->irq;
1735
	card_count++;
1736 1737 1738

	pci_set_drvdata(pdev, board);

1739 1740
	retval = pci_request_region(pdev, 3, ISICOM_NAME);
	if (retval) {
J
Jiri Slaby 已提交
1741 1742 1743 1744
		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;
1745
		goto errdec;
A
Alan Cox 已提交
1746
	}
1747

J
Jiri Slaby 已提交
1748 1749 1750 1751 1752
	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);
1753
		goto errunrr;
J
Jiri Slaby 已提交
1754
	}
1755 1756 1757 1758 1759

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

1760 1761 1762 1763
	retval = load_firmware(pdev, index, signature);
	if (retval < 0)
		goto errunri;

1764 1765 1766 1767
	for (index = 0; index < board->port_count; index++)
		tty_register_device(isicom_normal, board->index * 16 + index,
				&pdev->dev);

1768 1769 1770 1771 1772
	return 0;

errunri:
	free_irq(board->irq, board);
errunrr:
1773
	pci_release_region(pdev, 3);
1774
errdec:
1775
	board->base = 0;
1776
	card_count--;
1777
	pci_disable_device(pdev);
1778
err:
1779 1780 1781 1782 1783 1784
	return retval;
}

static void __devexit isicom_remove(struct pci_dev *pdev)
{
	struct isi_board *board = pci_get_drvdata(pdev);
1785 1786 1787 1788
	unsigned int i;

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

	free_irq(board->irq, board);
1791
	pci_release_region(pdev, 3);
1792 1793
	board->base = 0;
	card_count--;
1794
	pci_disable_device(pdev);
1795
}
L
Linus Torvalds 已提交
1796

1797
static int __init isicom_init(void)
L
Linus Torvalds 已提交
1798
{
1799 1800
	int retval, idx, channel;
	struct isi_port *port;
1801

A
Alan Cox 已提交
1802
	for (idx = 0; idx < BOARD_COUNT; idx++) {
1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
		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++) {
			port->magic = ISICOM_MAGIC;
			port->card = &isi_card[idx];
			port->channel = channel;
			port->close_delay = 50 * HZ/100;
			port->closing_wait = 3000 * HZ/100;
			port->status = 0;
A
Alan Cox 已提交
1813
			tty_port_init(&port->port);
1814
			/*  . . .  */
A
Alan Cox 已提交
1815
		}
1816 1817
		isi_card[idx].base = 0;
		isi_card[idx].irq = 0;
L
Linus Torvalds 已提交
1818
	}
1819

J
Jiri Slaby 已提交
1820 1821 1822 1823
	/* tty driver structure initialization */
	isicom_normal = alloc_tty_driver(PORT_COUNT);
	if (!isicom_normal) {
		retval = -ENOMEM;
1824
		goto error;
J
Jiri Slaby 已提交
1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835
	}

	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;
1836 1837
	isicom_normal->flags			= TTY_DRIVER_REAL_RAW |
		TTY_DRIVER_DYNAMIC_DEV;
J
Jiri Slaby 已提交
1838 1839 1840 1841 1842 1843 1844
	tty_set_operations(isicom_normal, &isicom_ops);

	retval = tty_register_driver(isicom_normal);
	if (retval) {
		pr_dbg("Couldn't register the dialin driver\n");
		goto err_puttty;
	}
L
Linus Torvalds 已提交
1845

1846
	retval = pci_register_driver(&isicom_driver);
L
Linus Torvalds 已提交
1847
	if (retval < 0) {
1848
		printk(KERN_ERR "ISICOM: Unable to register pci driver.\n");
J
Jiri Slaby 已提交
1849
		goto err_unrtty;
L
Linus Torvalds 已提交
1850
	}
1851

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

L
Linus Torvalds 已提交
1854
	return 0;
J
Jiri Slaby 已提交
1855 1856 1857 1858
err_unrtty:
	tty_unregister_driver(isicom_normal);
err_puttty:
	put_tty_driver(isicom_normal);
1859 1860
error:
	return retval;
L
Linus Torvalds 已提交
1861 1862 1863 1864
}

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

1867
	pci_unregister_driver(&isicom_driver);
J
Jiri Slaby 已提交
1868 1869
	tty_unregister_driver(isicom_normal);
	put_tty_driver(isicom_normal);
L
Linus Torvalds 已提交
1870 1871
}

1872
module_init(isicom_init);
L
Linus Torvalds 已提交
1873
module_exit(isicom_exit);
1874 1875 1876 1877

MODULE_AUTHOR("MultiTech");
MODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech");
MODULE_LICENSE("GPL");