isicom.c 41.2 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
37
38
39
 *					closed.
 *
 *	10/5/00  sameer			Signal mask setup command added
40
 *					to  isicom_setup_port and
Linus Torvalds's avatar
Linus Torvalds committed
41
42
43
 *					isicom_shutdown_port.
 *
 *	24/5/00  sameer			The driver is now SMP aware.
44
45
 *
 *
Linus Torvalds's avatar
Linus Torvalds committed
46
 *	27/11/00 Vinayak P Risbud	Fixed the Driver Crash Problem
47
48
 *
 *
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
56
 *					ISIHP-4X card
57
 *
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
68
 *					listing by lsmod.
69
 *
Linus Torvalds's avatar
Linus Torvalds committed
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
 *
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
99
100
 *	can find this in RPM format on
 *		ftp://ftp.linux.org.uk/pub/linux/alan
101
 *
Linus Torvalds's avatar
Linus Torvalds committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
 *	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>
117
#include <linux/firmware.h>
Linus Torvalds's avatar
Linus Torvalds committed
118
119
#include <linux/kernel.h>
#include <linux/tty.h>
Alan Cox's avatar
Alan Cox committed
120
#include <linux/tty_flip.h>
Linus Torvalds's avatar
Linus Torvalds committed
121
122
123
124
#include <linux/termios.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/serial.h>
Alexey Dobriyan's avatar
Alexey Dobriyan committed
125
#include <linux/smp_lock.h>
Linus Torvalds's avatar
Linus Torvalds committed
126
127
128
129
130
131
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/ioport.h>

Alan Cox's avatar
Alan Cox committed
132
133
#include <linux/uaccess.h>
#include <linux/io.h>
Linus Torvalds's avatar
Linus Torvalds committed
134
135
136
137
138
139
#include <asm/system.h>

#include <linux/pci.h>

#include <linux/isicom.h>

140
141
142
#define InterruptTheCard(base) outw(0, (base) + 0xc)
#define ClearInterrupt(base) inw((base) + 0x0a)

Jiri Slaby's avatar
Jiri Slaby committed
143
#define pr_dbg(str...) pr_debug("ISICOM: " str)
144
145
146
147
148
149
#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

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

Linus Torvalds's avatar
Linus Torvalds committed
153
static struct pci_device_id isicom_pci_tbl[] = {
154
155
156
157
158
159
160
161
162
	{ 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) },
Linus Torvalds's avatar
Linus Torvalds committed
163
164
165
166
	{ 0 }
};
MODULE_DEVICE_TABLE(pci, isicom_pci_tbl);

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

Linus Torvalds's avatar
Linus Torvalds committed
174
175
176
177
static int prev_card = 3;	/*	start servicing isi_card[0]	*/
static struct tty_driver *isicom_normal;

static void isicom_tx(unsigned long _data);
178
static void isicom_start(struct tty_struct *tty);
Linus Torvalds's avatar
Linus Torvalds committed
179

180
181
static DEFINE_TIMER(tx, isicom_tx, 0, 0);

Linus Torvalds's avatar
Linus Torvalds committed
182
183
184
/*   baud index mappings from linux defns to isi */

static signed char linuxb_to_isib[] = {
185
	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21
Linus Torvalds's avatar
Linus Torvalds committed
186
187
188
};

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

struct	isi_port {
	unsigned short		magic;
Alan Cox's avatar
Alan Cox committed
204
	struct tty_port		port;
205
206
	u16			channel;
	u16			status;
Alan Cox's avatar
Alan Cox committed
207
208
	struct isi_board	*card;
	unsigned char		*xmit_buf;
Linus Torvalds's avatar
Linus Torvalds committed
209
210
211
212
213
214
215
216
217
218
219
220
221
	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.
 */
222

223
static inline int WaitTillCardIsFree(unsigned long base)
224
225
226
227
228
229
230
231
232
233
234
235
236
{
	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);
}

Linus Torvalds's avatar
Linus Torvalds committed
237
238
static int lock_card(struct isi_board *card)
{
239
	unsigned long base = card->base;
Jiri Slaby's avatar
Jiri Slaby committed
240
	unsigned int retries, a;
Linus Torvalds's avatar
Linus Torvalds committed
241

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

Adrian Bunk's avatar
Adrian Bunk committed
255
	return 0;	/* Failed to acquire the card! */
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
269
{
270
	struct isi_board *card = port->card;
271
272
	unsigned long base = card->base;
	u16 channel = port->channel;
Linus Torvalds's avatar
Linus Torvalds committed
273

274
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
275
276
		return;

277
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
289

290
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
291
292
		return;

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

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

306
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
307
308
		return;

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

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

322
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
323
324
		return;

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

331
/* card->lock MUST NOT be held */
332

333
static void isicom_dtr_rts(struct tty_port *port, int on)
Linus Torvalds's avatar
Linus Torvalds committed
334
{
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;
338
	u16 channel = ip->channel;
Linus Torvalds's avatar
Linus Torvalds committed
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);
	}
Linus Torvalds's avatar
Linus Torvalds committed
354
355
356
	unlock_card(card);
}

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

364
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
365
366
		return;

367
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds's avatar
Linus Torvalds committed
368
	outw(0x0c04, base);
369
	InterruptTheCard(base);
Linus Torvalds's avatar
Linus Torvalds committed
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)
Linus Torvalds's avatar
Linus Torvalds committed
380
381
{
	if (!port) {
382
383
		printk(KERN_WARNING "ISICOM: Warning: bad isicom magic for "
			"dev %s in %s.\n", name, routine);
Linus Torvalds's avatar
Linus Torvalds committed
384
385
386
		return 1;
	}
	if (port->magic != ISICOM_MAGIC) {
387
388
		printk(KERN_WARNING "ISICOM: Warning: NULL isicom port for "
			"dev %s in %s.\n", name, routine);
Linus Torvalds's avatar
Linus Torvalds committed
389
		return 1;
390
	}
391

Linus Torvalds's avatar
Linus Torvalds committed
392
393
	return 0;
}
394

Linus Torvalds's avatar
Linus Torvalds committed
395
/*
396
 *	Transmitter.
Linus Torvalds's avatar
Linus Torvalds committed
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;
Jiri Slaby's avatar
Jiri Slaby committed
405
	unsigned int retries;
406
	short count = (BOARD_COUNT-1), card;
Linus Torvalds's avatar
Linus Torvalds committed
407
	short txcount, wrd, residue, word_count, cnt;
408
409
410
	struct isi_port *port;
	struct tty_struct *tty;

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

Linus Torvalds's avatar
Linus Torvalds committed
421
	prev_card = card;
422

Linus Torvalds's avatar
Linus Torvalds committed
423
424
425
	count = isi_card[card].port_count;
	port = isi_card[card].ports;
	base = isi_card[card].base;
Jiri Slaby's avatar
Jiri Slaby committed
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;

Alan Cox's avatar
Alan Cox committed
436
437
438
439
	tty = tty_port_tty_get(&port->port);
	if (tty == NULL)
		goto put_unlock;

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

Linus Torvalds's avatar
Linus Torvalds committed
446
		txcount = min_t(short, TX_SIZE, port->xmit_cnt);
Jiri Slaby's avatar
Jiri Slaby committed
447
		if (txcount <= 0 || tty->stopped || tty->hw_stopped)
Linus Torvalds's avatar
Linus Torvalds committed
448
			continue;
Jiri Slaby's avatar
Jiri Slaby committed
449
450

		if (!(inw(base + 0x02) & (1 << port->channel)))
451
			continue;
Jiri Slaby's avatar
Jiri Slaby committed
452

453
454
455
456
		pr_dbg("txing %d bytes, port%d.\n", txcount,
			port->channel + 1);
		outw((port->channel << isi_card[card].shift_count) | txcount,
			base);
Linus Torvalds's avatar
Linus Torvalds committed
457
		residue = NO;
458
		wrd = 0;
Linus Torvalds's avatar
Linus Torvalds committed
459
		while (1) {
460
461
			cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE
					- port->xmit_tail));
Linus Torvalds's avatar
Linus Torvalds committed
462
463
464
			if (residue == YES) {
				residue = NO;
				if (cnt > 0) {
Alan Cox's avatar
Alan Cox committed
465
					wrd |= (port->port.xmit_buf[port->xmit_tail]
466
467
468
									<< 8);
					port->xmit_tail = (port->xmit_tail + 1)
						& (SERIAL_XMIT_SIZE - 1);
Linus Torvalds's avatar
Linus Torvalds committed
469
470
471
					port->xmit_cnt--;
					txcount--;
					cnt--;
472
					outw(wrd, base);
473
				} else {
Linus Torvalds's avatar
Linus Torvalds committed
474
475
476
					outw(wrd, base);
					break;
				}
477
			}
Alan Cox's avatar
Alan Cox committed
478
479
			if (cnt <= 0)
				break;
Linus Torvalds's avatar
Linus Torvalds committed
480
			word_count = cnt >> 1;
Alan Cox's avatar
Alan Cox committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
484
485
486
487
			txcount -= (word_count << 1);
			port->xmit_cnt -= (word_count << 1);
			if (cnt & 0x0001) {
				residue = YES;
Alan Cox's avatar
Alan Cox committed
488
				wrd = port->port.xmit_buf[port->xmit_tail];
489
490
				port->xmit_tail = (port->xmit_tail + 1)
					& (SERIAL_XMIT_SIZE - 1);
Linus Torvalds's avatar
Linus Torvalds committed
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
	}
Linus Torvalds's avatar
Linus Torvalds committed
502

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

Linus Torvalds's avatar
Linus Torvalds committed
512
/*
513
 *	Main interrupt handler routine
Linus Torvalds's avatar
Linus Torvalds committed
514
 */
515

516
static irqreturn_t isicom_interrupt(int irq, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
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;
Linus Torvalds's avatar
Linus Torvalds committed
523
	short byte_count;
Alan Cox's avatar
Alan Cox committed
524
	unsigned char *rp;
525

Linus Torvalds's avatar
Linus Torvalds committed
526
527
	if (!card || !(card->status & FIRMWARE_LOADED))
		return IRQ_NONE;
528

Linus Torvalds's avatar
Linus Torvalds committed
529
	base = card->base;
530
531
532
533
534

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

Linus Torvalds's avatar
Linus Torvalds committed
535
	spin_lock(&card->card_lock);
536

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

Linus Torvalds's avatar
Linus Torvalds committed
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
		printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%lx): "
			"%d(channel) > port_count.\n", base, channel+1);
552
		outw(0x0000, base+0x04); /* enable interrupts */
Linus Torvalds's avatar
Linus Torvalds committed
553
		spin_unlock(&card->card_lock);
554
		return IRQ_HANDLED;
Linus Torvalds's avatar
Linus Torvalds committed
555
556
	}
	port = card->ports + channel;
Alan Cox's avatar
Alan Cox committed
557
	if (!(port->port.flags & ASYNC_INITIALIZED)) {
558
		outw(0x0000, base+0x04); /* enable interrupts */
559
		spin_unlock(&card->card_lock);
Linus Torvalds's avatar
Linus Torvalds committed
560
		return IRQ_HANDLED;
561
562
	}

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

Linus Torvalds's avatar
Linus Torvalds committed
577
578
	if (header & 0x8000) {		/* Status Packet */
		header = inw(base);
Alan Cox's avatar
Alan Cox committed
579
		switch (header & 0xff) {
580
		case 0:	/* Change in EIA signals */
Alan Cox's avatar
Alan Cox committed
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_dbg("interrupt: DCD->low.\n"
							);
587
						port->status &= ~ISI_DCD;
588
						tty_hangup(tty);
Linus Torvalds's avatar
Linus Torvalds committed
589
					}
590
591
592
593
				} else if (header & ISI_DCD) {
				/* Carrier has been detected */
					pr_dbg("interrupt: DCD->high.\n");
					port->status |= ISI_DCD;
Alan Cox's avatar
Alan Cox committed
594
					wake_up_interruptible(&port->port.open_wait);
Linus Torvalds's avatar
Linus Torvalds committed
595
				}
596
			} else {
597
598
599
600
601
602
				if (header & ISI_DCD)
					port->status |= ISI_DCD;
				else
					port->status &= ~ISI_DCD;
			}

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

636
		case 1:	/* Received Break !!! */
637
			tty_insert_flip_char(tty, 0, TTY_BREAK);
Alan Cox's avatar
Alan Cox committed
638
			if (port->port.flags & ASYNC_SAK)
639
640
641
642
643
				do_SAK(tty);
			tty_flip_buffer_push(tty);
			break;

		case 2:	/* Statistics		 */
644
			pr_dbg("isicom_interrupt: stats!!!.\n");
645
646
647
			break;

		default:
648
			pr_dbg("Intr: Unknown code in status packet.\n");
649
650
			break;
		}
651
	} else {				/* Data   Packet */
Alan Cox's avatar
Alan Cox committed
652
653

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

Linus Torvalds's avatar
Linus Torvalds committed
678
	return IRQ_HANDLED;
679
}
Linus Torvalds's avatar
Linus Torvalds committed
680

Alan Cox's avatar
Alan Cox committed
681
static void isicom_config_port(struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
682
{
Alan Cox's avatar
Alan Cox committed
683
	struct isi_port *port = tty->driver_data;
684
	struct isi_board *card = port->card;
Linus Torvalds's avatar
Linus Torvalds committed
685
	unsigned long baud;
686
687
688
	unsigned long base = card->base;
	u16 channel_setup, channel = port->channel,
		shift_count = card->shift_count;
Linus Torvalds's avatar
Linus Torvalds committed
689
	unsigned char flow_ctrl;
690

Alan Cox's avatar
Alan Cox committed
691
	/* FIXME: Switch to new tty baud API */
Linus Torvalds's avatar
Linus Torvalds committed
692
693
694
	baud = C_BAUD(tty);
	if (baud & CBAUDEX) {
		baud &= ~CBAUDEX;
695

Linus Torvalds's avatar
Linus Torvalds committed
696
697
698
		/*  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.
699
700
		 */

701
702
		/* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
		if (baud < 1 || baud > 4)
Alan Cox's avatar
Alan Cox committed
703
			tty->termios->c_cflag &= ~CBAUDEX;
Linus Torvalds's avatar
Linus Torvalds committed
704
705
		else
			baud += 15;
706
	}
Linus Torvalds's avatar
Linus Torvalds committed
707
	if (baud == 15) {
708
709

		/*  the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set
Linus Torvalds's avatar
Linus Torvalds committed
710
711
		 *  by the set_serial_info ioctl ... this is done by
		 *  the 'setserial' utility.
712
713
		 */

Alan Cox's avatar
Alan Cox committed
714
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
715
			baud++; /*  57.6 Kbps */
Alan Cox's avatar
Alan Cox committed
716
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Alan Cox's avatar
Alan Cox committed
717
			baud += 2; /*  115  Kbps */
Alan Cox's avatar
Alan Cox committed
718
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
719
			baud += 3; /* 230 kbps*/
Alan Cox's avatar
Alan Cox committed
720
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
721
			baud += 4; /* 460 kbps*/
Linus Torvalds's avatar
Linus Torvalds committed
722
723
724
	}
	if (linuxb_to_isib[baud] == -1) {
		/* hang up */
725
726
		drop_dtr(port);
		return;
Alan Cox's avatar
Alan Cox committed
727
	} else
Linus Torvalds's avatar
Linus Torvalds committed
728
		raise_dtr(port);
729

730
	if (WaitTillCardIsFree(base) == 0) {
Alan Cox's avatar
Alan Cox committed
731
		outw(0x8000 | (channel << shift_count) | 0x03, base);
Linus Torvalds's avatar
Linus Torvalds committed
732
733
		outw(linuxb_to_isib[baud] << 8 | 0x03, base);
		channel_setup = 0;
Alan Cox's avatar
Alan Cox committed
734
		switch (C_CSIZE(tty)) {
735
736
737
738
739
740
741
742
743
744
745
746
		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;
Linus Torvalds's avatar
Linus Torvalds committed
747
		}
748

Linus Torvalds's avatar
Linus Torvalds committed
749
750
751
752
753
		if (C_CSTOPB(tty))
			channel_setup |= ISICOM_2SB;
		if (C_PARENB(tty)) {
			channel_setup |= ISICOM_EVPAR;
			if (C_PARODD(tty))
754
				channel_setup |= ISICOM_ODPAR;
Linus Torvalds's avatar
Linus Torvalds committed
755
		}
756
		outw(channel_setup, base);
Linus Torvalds's avatar
Linus Torvalds committed
757
		InterruptTheCard(base);
758
	}
Linus Torvalds's avatar
Linus Torvalds committed
759
	if (C_CLOCAL(tty))
Alan Cox's avatar
Alan Cox committed
760
		port->port.flags &= ~ASYNC_CHECK_CD;
Linus Torvalds's avatar
Linus Torvalds committed
761
	else
Alan Cox's avatar
Alan Cox committed
762
		port->port.flags |= ASYNC_CHECK_CD;
763

Linus Torvalds's avatar
Linus Torvalds committed
764
765
	/* flow control settings ...*/
	flow_ctrl = 0;
Alan Cox's avatar
Alan Cox committed
766
	port->port.flags &= ~ASYNC_CTS_FLOW;
Linus Torvalds's avatar
Linus Torvalds committed
767
	if (C_CRTSCTS(tty)) {
Alan Cox's avatar
Alan Cox committed
768
		port->port.flags |= ASYNC_CTS_FLOW;
Linus Torvalds's avatar
Linus Torvalds committed
769
		flow_ctrl |= ISICOM_CTSRTS;
770
771
	}
	if (I_IXON(tty))
Linus Torvalds's avatar
Linus Torvalds committed
772
773
		flow_ctrl |= ISICOM_RESPOND_XONXOFF;
	if (I_IXOFF(tty))
774
775
		flow_ctrl |= ISICOM_INITIATE_XONXOFF;

776
	if (WaitTillCardIsFree(base) == 0) {
Alan Cox's avatar
Alan Cox committed
777
		outw(0x8000 | (channel << shift_count) | 0x04, base);
Linus Torvalds's avatar
Linus Torvalds committed
778
779
780
781
		outw(flow_ctrl << 8 | 0x05, base);
		outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
		InterruptTheCard(base);
	}
782

Linus Torvalds's avatar
Linus Torvalds committed
783
784
785
786
787
788
789
	/*	rx enabled -> enable port for rx on the card	*/
	if (C_CREAD(tty)) {
		card->port_status |= (1 << channel);
		outw(card->port_status, base + 0x02);
	}
}

790
791
792
/* open et all */

static inline void isicom_setup_board(struct isi_board *bp)
Linus Torvalds's avatar
Linus Torvalds committed
793
794
{
	int channel;
795
796
	struct isi_port *port;

797
	bp->count++;
798
799
800
801
802
803
	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;
Linus Torvalds's avatar
Linus Torvalds committed
804
}
805

806
807
808
/* Activate and thus setup board are protected from races against shutdown
   by the tty_port mutex */

809
static int isicom_activate(struct tty_port *tport, struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
810
{
811
	struct isi_port *port = container_of(tport, struct isi_port, port);
812
	struct isi_board *card = port->card;
Linus Torvalds's avatar
Linus Torvalds committed
813
	unsigned long flags;
814

815
	if (tty_port_alloc_xmit_buf(tport) < 0)
Alan Cox's avatar
Alan Cox committed
816
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
817
818

	spin_lock_irqsave(&card->card_lock, flags);
819
	isicom_setup_board(card);
820

Linus Torvalds's avatar
Linus Torvalds committed
821
	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
822

Linus Torvalds's avatar
Linus Torvalds committed
823
	/*	discard any residual data	*/
824
825
826
827
828
829
	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);
	}
Alan Cox's avatar
Alan Cox committed
830
	isicom_config_port(tty);
Linus Torvalds's avatar
Linus Torvalds committed
831
	spin_unlock_irqrestore(&card->card_lock, flags);
832
833
834
835

	return 0;
}

836
837
838
839
840
841
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;
}

842
static struct tty_port *isicom_find_port(struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
843
{
844
845
	struct isi_port *port;
	struct isi_board *card;
846
	unsigned int board;
847
	int line = tty->index;
Linus Torvalds's avatar
Linus Torvalds committed
848
849

	if (line < 0 || line > PORT_COUNT-1)
850
		return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
851
852
	board = BOARD(line);
	card = &isi_card[board];
853

Linus Torvalds's avatar
Linus Torvalds committed
854
	if (!(card->status & FIRMWARE_LOADED))
855
		return NULL;
856

Linus Torvalds's avatar
Linus Torvalds committed
857
858
	/*  open on a port greater than the port count for the card !!! */
	if (line > ((board * 16) + card->port_count - 1))
859
		return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
860

861
	port = &isi_ports[line];
Linus Torvalds's avatar
Linus Torvalds committed
862
	if (isicom_paranoia_check(port, tty->name, "isicom_open"))
863
864
865
866
		return NULL;

	return &port->port;
}
867

868
869
870
871
872
static int isicom_open(struct tty_struct *tty, struct file *filp)
{
	struct isi_port *port;
	struct isi_board *card;
	struct tty_port *tport;
873

874
875
876
877
878
	tport = isicom_find_port(tty);
	if (tport == NULL)
		return -ENODEV;
	port = container_of(tport, struct isi_port, port);
	card = &isi_card[BOARD(tty->index)];
879

880
	return tty_port_open(tport, tty, filp);
Linus Torvalds's avatar
Linus Torvalds committed
881
}
882

Linus Torvalds's avatar
Linus Torvalds committed
883
884
/* close et all */

885
/* card->lock HAS to be held */
886
static void isicom_shutdown_port(struct isi_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
887
{
888
	struct isi_board *card = port->card;
Linus Torvalds's avatar
Linus Torvalds committed
889
890

	if (--card->count < 0) {
891
		pr_dbg("isicom_shutdown_port: bad board(0x%lx) count %d.\n",
Linus Torvalds's avatar
Linus Torvalds committed
892
			card->base, card->count);
893
		card->count = 0;
Linus Torvalds's avatar
Linus Torvalds committed
894
	}
895
	/* last port was closed, shutdown that board too */
896
897
	if (!card->count)
		card->status &= BOARD_ACTIVE;
Linus Torvalds's avatar
Linus Torvalds committed
898
899
}

900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
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);
}

916
static void isicom_shutdown(struct tty_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
917
{
918
919
	struct isi_port *ip = container_of(port, struct isi_port, port);
	struct isi_board *card = ip->card;
Linus Torvalds's avatar
Linus Torvalds committed
920
	unsigned long flags;
921
922

	/* indicate to the card that no more data can be received
Linus Torvalds's avatar
Linus Torvalds committed
923
924
	   on this port */
	spin_lock_irqsave(&card->card_lock, flags);
925
926
	card->port_status &= ~(1 << ip->channel);
	outw(card->port_status, card->base + 0x02);
927
	isicom_shutdown_port(ip);
Linus Torvalds's avatar
Linus Torvalds committed
928
	spin_unlock_irqrestore(&card->card_lock, flags);
929
	tty_port_free_xmit_buf(port);
930
}
931

932
933
934
935
936
937
static void isicom_close(struct tty_struct *tty, struct file *filp)
{
	struct isi_port *ip = tty->driver_data;
	struct tty_port *port = &ip->port;
	if (isicom_paranoia_check(ip, tty->name, "isicom_close"))
		return;
938
	tty_port_close(port, tty, filp);
Linus Torvalds's avatar
Linus Torvalds committed
939
940
941
}

/* write et all */
942
943
static int isicom_write(struct tty_struct *tty,	const unsigned char *buf,
	int count)
Linus Torvalds's avatar
Linus Torvalds committed
944
{
945
	struct isi_port *port = tty->driver_data;
946
	struct isi_board *card = port->card;
Linus Torvalds's avatar
Linus Torvalds committed
947
948
949
950
951
	unsigned long flags;
	int cnt, total = 0;

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

Linus Torvalds's avatar
Linus Torvalds committed
953
	spin_lock_irqsave(&card->card_lock, flags);
954

Alan Cox's avatar
Alan Cox committed
955
	while (1) {
956
957
		cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt
				- 1, SERIAL_XMIT_SIZE - port->xmit_head));
958
		if (cnt <= 0)
Linus Torvalds's avatar
Linus Torvalds committed
959
			break;
960

Alan Cox's avatar
Alan Cox committed
961
		memcpy(port->port.xmit_buf + port->xmit_head, buf, cnt);
962
963
		port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE
			- 1);
Linus Torvalds's avatar
Linus Torvalds committed
964
965
966
967
		port->xmit_cnt += cnt;
		buf += cnt;
		count -= cnt;
		total += cnt;
968
	}
Linus Torvalds's avatar
Linus Torvalds committed
969
970
971
	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped)
		port->status |= ISI_TXOK;
	spin_unlock_irqrestore(&card->card_lock, flags);
972
	return total;
Linus Torvalds's avatar
Linus Torvalds committed
973
974
975
}

/* put_char et all */
Alan Cox's avatar
Alan Cox committed
976
static int isicom_put_char(struct tty_struct *tty, unsigned char ch)
Linus Torvalds's avatar
Linus Torvalds committed
977
{
978
	struct isi_port *port = tty->driver_data;
979
	struct isi_board *card = port->card;
Linus Torvalds's avatar
Linus Torvalds committed
980
	unsigned long flags;
981

Linus Torvalds's avatar
Linus Torvalds committed
982
	if (isicom_paranoia_check(port, tty->name, "isicom_put_char"))
Alan Cox's avatar
Alan Cox committed
983
		return 0;
984

Linus Torvalds's avatar
Linus Torvalds committed
985
	spin_lock_irqsave(&card->card_lock, flags);
Alan Cox's avatar
Alan Cox committed
986
987
988
989
	if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
		spin_unlock_irqrestore(&card->card_lock, flags);
		return 0;
	}
990

Alan Cox's avatar
Alan Cox committed
991
	port->port.xmit_buf[port->xmit_head++] = ch;
Linus Torvalds's avatar
Linus Torvalds committed
992
993
994
	port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
	port->xmit_cnt++;
	spin_unlock_irqrestore(&card->card_lock, flags);
Alan Cox's avatar
Alan Cox committed
995
	return 1;
Linus Torvalds's avatar
Linus Torvalds committed
996
997
998
}

/* flush_chars et all */
999
static void isicom_flush_chars(struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
1000
{
1001
	struct isi_port *port = tty->driver_data;
Jiri Slaby's avatar