isicom.c 45 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
35
36
37
 *					closed.
 *
 *	10/5/00  sameer			Signal mask setup command added
38
 *					to  isicom_setup_port and
Linus Torvalds's avatar
Linus Torvalds committed
39
40
41
 *					isicom_shutdown_port.
 *
 *	24/5/00  sameer			The driver is now SMP aware.
42
43
 *
 *
Linus Torvalds's avatar
Linus Torvalds committed
44
 *	27/11/00 Vinayak P Risbud	Fixed the Driver Crash Problem
45
46
 *
 *
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
54
 *					ISIHP-4X card
55
 *
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
66
 *					listing by lsmod.
67
 *
Linus Torvalds's avatar
Linus Torvalds committed
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
 *
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
97
98
 *	can find this in RPM format on
 *		ftp://ftp.linux.org.uk/pub/linux/alan
99
 *
Linus Torvalds's avatar
Linus Torvalds committed
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>
Linus Torvalds's avatar
Linus Torvalds committed
116
117
#include <linux/kernel.h>
#include <linux/tty.h>
Alan Cox's avatar
Alan Cox committed
118
#include <linux/tty_flip.h>
Linus Torvalds's avatar
Linus Torvalds committed
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>

Alan Cox's avatar
Alan Cox committed
129
130
#include <linux/uaccess.h>
#include <linux/io.h>
Linus Torvalds's avatar
Linus Torvalds committed
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)

Jiri Slaby's avatar
Jiri Slaby committed
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 *);

Linus Torvalds's avatar
Linus Torvalds committed
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) },
Linus Torvalds's avatar
Linus Torvalds committed
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)
};

Linus Torvalds's avatar
Linus Torvalds committed
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);
Linus Torvalds's avatar
Linus Torvalds committed
176

177
178
static DEFINE_TIMER(tx, isicom_tx, 0, 0);

Linus Torvalds's avatar
Linus Torvalds committed
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
Linus Torvalds's avatar
Linus Torvalds committed
183
184
185
};

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

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

220
static inline int WaitTillCardIsFree(unsigned long base)
221
222
223
224
225
226
227
228
229
230
231
232
233
{
	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
234
235
static int lock_card(struct isi_board *card)
{
236
	unsigned long base = card->base;
Jiri Slaby's avatar
Jiri Slaby committed
237
	unsigned int retries, a;
Linus Torvalds's avatar
Linus Torvalds committed
238

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

Adrian Bunk's avatar
Adrian Bunk committed
252
	return 0;	/* Failed to acquire the card! */
Linus Torvalds's avatar
Linus Torvalds committed
253
254
255
256
257
258
259
260
261
262
}

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

/*
 *  ISI Card specific ops ...
 */
263

264
/* card->lock HAS to be held */
265
static void raise_dtr(struct isi_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
266
{
267
	struct isi_board *card = port->card;
268
269
	unsigned long base = card->base;
	u16 channel = port->channel;
Linus Torvalds's avatar
Linus Torvalds committed
270

271
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
272
273
		return;

274
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds's avatar
Linus Torvalds committed
275
276
277
278
279
	outw(0x0504, base);
	InterruptTheCard(base);
	port->status |= ISI_DTR;
}

280
/* card->lock HAS to be held */
281
282
283
static inline void drop_dtr(struct isi_port *port)
{
	struct isi_board *card = port->card;
284
285
	unsigned long base = card->base;
	u16 channel = port->channel;
Linus Torvalds's avatar
Linus Torvalds committed
286

287
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
288
289
		return;

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

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

303
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
304
305
		return;

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

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

319
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
320
321
		return;

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

328
/* card->lock MUST NOT be held */
329
static inline void raise_dtr_rts(struct isi_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
330
{
331
	struct isi_board *card = port->card;
332
333
	unsigned long base = card->base;
	u16 channel = port->channel;
Linus Torvalds's avatar
Linus Torvalds committed
334
335
336
337

	if (!lock_card(card))
		return;

338
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds's avatar
Linus Torvalds committed
339
340
341
342
343
344
	outw(0x0f04, base);
	InterruptTheCard(base);
	port->status |= (ISI_DTR | ISI_RTS);
	unlock_card(card);
}

345
/* card->lock HAS to be held */
346
static void drop_dtr_rts(struct isi_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
347
{
348
	struct isi_board *card = port->card;
349
350
	unsigned long base = card->base;
	u16 channel = port->channel;
Linus Torvalds's avatar
Linus Torvalds committed
351

352
	if (WaitTillCardIsFree(base))
Linus Torvalds's avatar
Linus Torvalds committed
353
354
		return;

355
	outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds's avatar
Linus Torvalds committed
356
	outw(0x0c04, base);
357
	InterruptTheCard(base);
Linus Torvalds's avatar
Linus Torvalds committed
358
359
360
361
362
363
364
	port->status &= ~(ISI_RTS | ISI_DTR);
}

/*
 *	ISICOM Driver specific routines ...
 *
 */
365

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

Linus Torvalds's avatar
Linus Torvalds committed
380
381
	return 0;
}
382

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

Linus Torvalds's avatar
Linus Torvalds committed
399
400
	/*	find next active board	*/
	card = (prev_card + 1) & 0x0003;
Alan Cox's avatar
Alan Cox committed
401
	while (count-- > 0) {
402
		if (isi_card[card].status & BOARD_ACTIVE)
Linus Torvalds's avatar
Linus Torvalds committed
403
			break;
404
		card = (card + 1) & 0x0003;
Linus Torvalds's avatar
Linus Torvalds committed
405
406
407
	}
	if (!(isi_card[card].status & BOARD_ACTIVE))
		goto sched_again;
408

Linus Torvalds's avatar
Linus Torvalds committed
409
	prev_card = card;
410

Linus Torvalds's avatar
Linus Torvalds committed
411
412
413
	count = isi_card[card].port_count;
	port = isi_card[card].ports;
	base = isi_card[card].base;
Jiri Slaby's avatar
Jiri Slaby committed
414
415
416
417
418
419
420
421
422
423

	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
424
425
426
427
	tty = tty_port_tty_get(&port->port);
	if (tty == NULL)
		goto put_unlock;

Alan Cox's avatar
Alan Cox committed
428
	for (; count > 0; count--, port++) {
Linus Torvalds's avatar
Linus Torvalds committed
429
		/* port not active or tx disabled to force flow control */
Alan Cox's avatar
Alan Cox committed
430
		if (!(port->port.flags & ASYNC_INITIALIZED) ||
431
				!(port->status & ISI_TXOK))
Linus Torvalds's avatar
Linus Torvalds committed
432
			continue;
433

Linus Torvalds's avatar
Linus Torvalds committed
434
		txcount = min_t(short, TX_SIZE, port->xmit_cnt);
Jiri Slaby's avatar
Jiri Slaby committed
435
		if (txcount <= 0 || tty->stopped || tty->hw_stopped)
Linus Torvalds's avatar
Linus Torvalds committed
436
			continue;
Jiri Slaby's avatar
Jiri Slaby committed
437
438

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

441
442
443
444
		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
445
		residue = NO;
446
		wrd = 0;
Linus Torvalds's avatar
Linus Torvalds committed
447
		while (1) {
448
449
			cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE
					- port->xmit_tail));
Linus Torvalds's avatar
Linus Torvalds committed
450
451
452
			if (residue == YES) {
				residue = NO;
				if (cnt > 0) {
Alan Cox's avatar
Alan Cox committed
453
					wrd |= (port->port.xmit_buf[port->xmit_tail]
454
455
456
									<< 8);
					port->xmit_tail = (port->xmit_tail + 1)
						& (SERIAL_XMIT_SIZE - 1);
Linus Torvalds's avatar
Linus Torvalds committed
457
458
459
					port->xmit_cnt--;
					txcount--;
					cnt--;
460
					outw(wrd, base);
461
				} else {
Linus Torvalds's avatar
Linus Torvalds committed
462
463
464
					outw(wrd, base);
					break;
				}
465
			}
Alan Cox's avatar
Alan Cox committed
466
467
			if (cnt <= 0)
				break;
Linus Torvalds's avatar
Linus Torvalds committed
468
			word_count = cnt >> 1;
Alan Cox's avatar
Alan Cox committed
469
			outsw(base, port->port.xmit_buf+port->xmit_tail, word_count);
470
471
			port->xmit_tail = (port->xmit_tail
				+ (word_count << 1)) & (SERIAL_XMIT_SIZE - 1);
Linus Torvalds's avatar
Linus Torvalds committed
472
473
474
475
			txcount -= (word_count << 1);
			port->xmit_cnt -= (word_count << 1);
			if (cnt & 0x0001) {
				residue = YES;
Alan Cox's avatar
Alan Cox committed
476
				wrd = port->port.xmit_buf[port->xmit_tail];
477
478
				port->xmit_tail = (port->xmit_tail + 1)
					& (SERIAL_XMIT_SIZE - 1);
Linus Torvalds's avatar
Linus Torvalds committed
479
480
481
482
483
484
485
486
487
				port->xmit_cnt--;
				txcount--;
			}
		}

		InterruptTheCard(base);
		if (port->xmit_cnt <= 0)
			port->status &= ~ISI_TXOK;
		if (port->xmit_cnt <= WAKEUP_CHARS)
488
			tty_wakeup(tty);
489
	}
Linus Torvalds's avatar
Linus Torvalds committed
490

Alan Cox's avatar
Alan Cox committed
491
492
put_unlock:
	tty_kref_put(tty);
Jiri Slaby's avatar
Jiri Slaby committed
493
494
unlock:
	spin_unlock_irqrestore(&isi_card[card].card_lock, flags);
495
496
	/*	schedule another tx for hopefully in about 10ms	*/
sched_again:
497
	mod_timer(&tx, jiffies + msecs_to_jiffies(10));
498
499
}

Linus Torvalds's avatar
Linus Torvalds committed
500
/*
501
 *	Main interrupt handler routine
Linus Torvalds's avatar
Linus Torvalds committed
502
 */
503

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

Linus Torvalds's avatar
Linus Torvalds committed
514
515
	if (!card || !(card->status & FIRMWARE_LOADED))
		return IRQ_NONE;
516

Linus Torvalds's avatar
Linus Torvalds committed
517
	base = card->base;
518
519
520
521
522

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

Linus Torvalds's avatar
Linus Torvalds committed
523
	spin_lock(&card->card_lock);
524

525
526
527
528
529
530
	/*
	 * disable any interrupts from the PCI card and lower the
	 * interrupt line
	 */
	outw(0x8000, base+0x04);
	ClearInterrupt(base);
531

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

Alan Cox's avatar
Alan Cox committed
551
	tty = tty_port_tty_get(&port->port);
Linus Torvalds's avatar
Linus Torvalds committed
552
553
	if (tty == NULL) {
		word_count = byte_count >> 1;
Alan Cox's avatar
Alan Cox committed
554
		while (byte_count > 1) {
Linus Torvalds's avatar
Linus Torvalds committed
555
556
557
558
559
			inw(base);
			byte_count -= 2;
		}
		if (byte_count & 0x01)
			inw(base);
560
		outw(0x0000, base+0x04); /* enable interrupts */
Linus Torvalds's avatar
Linus Torvalds committed
561
562
563
		spin_unlock(&card->card_lock);
		return IRQ_HANDLED;
	}
564

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

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

624
		case 1:	/* Received Break !!! */
625
			tty_insert_flip_char(tty, 0, TTY_BREAK);
Alan Cox's avatar
Alan Cox committed
626
			if (port->port.flags & ASYNC_SAK)
627
628
629
630
631
				do_SAK(tty);
			tty_flip_buffer_push(tty);
			break;

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

		default:
636
			pr_dbg("Intr: Unknown code in status packet.\n");
637
638
			break;
		}
639
	} else {				/* Data   Packet */
Alan Cox's avatar
Alan Cox committed
640
641

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

Linus Torvalds's avatar
Linus Torvalds committed
666
	return IRQ_HANDLED;
667
}
Linus Torvalds's avatar
Linus Torvalds committed
668

Alan Cox's avatar
Alan Cox committed
669
static void isicom_config_port(struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
670
{
Alan Cox's avatar
Alan Cox committed
671
	struct isi_port *port = tty->driver_data;
672
	struct isi_board *card = port->card;
Linus Torvalds's avatar
Linus Torvalds committed
673
	unsigned long baud;
674
675
676
	unsigned long base = card->base;
	u16 channel_setup, channel = port->channel,
		shift_count = card->shift_count;
Linus Torvalds's avatar
Linus Torvalds committed
677
	unsigned char flow_ctrl;
678

Alan Cox's avatar
Alan Cox committed
679
	/* FIXME: Switch to new tty baud API */
Linus Torvalds's avatar
Linus Torvalds committed
680
681
682
	baud = C_BAUD(tty);
	if (baud & CBAUDEX) {
		baud &= ~CBAUDEX;
683

Linus Torvalds's avatar
Linus Torvalds committed
684
685
686
		/*  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.
687
688
		 */

689
690
		/* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
		if (baud < 1 || baud > 4)
Alan Cox's avatar
Alan Cox committed
691
			tty->termios->c_cflag &= ~CBAUDEX;
Linus Torvalds's avatar
Linus Torvalds committed
692
693
		else
			baud += 15;
694
	}
Linus Torvalds's avatar
Linus Torvalds committed
695
	if (baud == 15) {
696
697

		/*  the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set
Linus Torvalds's avatar
Linus Torvalds committed
698
699
		 *  by the set_serial_info ioctl ... this is done by
		 *  the 'setserial' utility.
700
701
		 */

Alan Cox's avatar
Alan Cox committed
702
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
703
			baud++; /*  57.6 Kbps */
Alan Cox's avatar
Alan Cox committed
704
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Alan Cox's avatar
Alan Cox committed
705
			baud += 2; /*  115  Kbps */
Alan Cox's avatar
Alan Cox committed
706
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
707
			baud += 3; /* 230 kbps*/
Alan Cox's avatar
Alan Cox committed
708
		if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
709
			baud += 4; /* 460 kbps*/
Linus Torvalds's avatar
Linus Torvalds committed
710
711
712
	}
	if (linuxb_to_isib[baud] == -1) {
		/* hang up */
713
714
		drop_dtr(port);
		return;
Alan Cox's avatar
Alan Cox committed
715
	} else
Linus Torvalds's avatar
Linus Torvalds committed
716
		raise_dtr(port);
717

718
	if (WaitTillCardIsFree(base) == 0) {
Alan Cox's avatar
Alan Cox committed
719
		outw(0x8000 | (channel << shift_count) | 0x03, base);
Linus Torvalds's avatar
Linus Torvalds committed
720
721
		outw(linuxb_to_isib[baud] << 8 | 0x03, base);
		channel_setup = 0;
Alan Cox's avatar
Alan Cox committed
722
		switch (C_CSIZE(tty)) {
723
724
725
726
727
728
729
730
731
732
733
734
		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
735
		}
736

Linus Torvalds's avatar
Linus Torvalds committed
737
738
739
740
741
		if (C_CSTOPB(tty))
			channel_setup |= ISICOM_2SB;
		if (C_PARENB(tty)) {
			channel_setup |= ISICOM_EVPAR;
			if (C_PARODD(tty))
742
				channel_setup |= ISICOM_ODPAR;
Linus Torvalds's avatar
Linus Torvalds committed
743
		}
744
		outw(channel_setup, base);
Linus Torvalds's avatar
Linus Torvalds committed
745
		InterruptTheCard(base);
746
	}
Linus Torvalds's avatar
Linus Torvalds committed
747
	if (C_CLOCAL(tty))
Alan Cox's avatar
Alan Cox committed
748
		port->port.flags &= ~ASYNC_CHECK_CD;
Linus Torvalds's avatar
Linus Torvalds committed
749
	else
Alan Cox's avatar
Alan Cox committed
750
		port->port.flags |= ASYNC_CHECK_CD;
751

Linus Torvalds's avatar
Linus Torvalds committed
752
753
	/* flow control settings ...*/
	flow_ctrl = 0;
Alan Cox's avatar
Alan Cox committed
754
	port->port.flags &= ~ASYNC_CTS_FLOW;
Linus Torvalds's avatar
Linus Torvalds committed
755
	if (C_CRTSCTS(tty)) {
Alan Cox's avatar
Alan Cox committed
756
		port->port.flags |= ASYNC_CTS_FLOW;
Linus Torvalds's avatar
Linus Torvalds committed
757
		flow_ctrl |= ISICOM_CTSRTS;
758
759
	}
	if (I_IXON(tty))
Linus Torvalds's avatar
Linus Torvalds committed
760
761
		flow_ctrl |= ISICOM_RESPOND_XONXOFF;
	if (I_IXOFF(tty))
762
763
		flow_ctrl |= ISICOM_INITIATE_XONXOFF;

764
	if (WaitTillCardIsFree(base) == 0) {
Alan Cox's avatar
Alan Cox committed
765
		outw(0x8000 | (channel << shift_count) | 0x04, base);
Linus Torvalds's avatar
Linus Torvalds committed
766
767
768
769
		outw(flow_ctrl << 8 | 0x05, base);
		outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
		InterruptTheCard(base);
	}
770

Linus Torvalds's avatar
Linus Torvalds committed
771
772
773
774
775
776
777
	/*	rx enabled -> enable port for rx on the card	*/
	if (C_CREAD(tty)) {
		card->port_status |= (1 << channel);
		outw(card->port_status, base + 0x02);
	}
}

778
779
780
/* open et all */

static inline void isicom_setup_board(struct isi_board *bp)
Linus Torvalds's avatar
Linus Torvalds committed
781
782
{
	int channel;
783
	struct isi_port *port;
Linus Torvalds's avatar
Linus Torvalds committed
784
	unsigned long flags;
785

Linus Torvalds's avatar
Linus Torvalds committed
786
787
788
789
790
791
792
	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;
793
	for (channel = 0; channel < bp->port_count; channel++, port++)
Linus Torvalds's avatar
Linus Torvalds committed
794
		drop_dtr_rts(port);
795
	spin_unlock_irqrestore(&bp->card_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
796
}
797

Alan Cox's avatar
Alan Cox committed
798
static int isicom_setup_port(struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
799
{
Alan Cox's avatar
Alan Cox committed
800
	struct isi_port *port = tty->driver_data;
801
	struct isi_board *card = port->card;
Linus Torvalds's avatar
Linus Torvalds committed
802
	unsigned long flags;
803

Alan Cox's avatar
Alan Cox committed
804
	if (port->port.flags & ASYNC_INITIALIZED)
Linus Torvalds's avatar
Linus Torvalds committed
805
		return 0;
Alan Cox's avatar
Alan Cox committed
806
807
	if (tty_port_alloc_xmit_buf(&port->port) < 0)
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
808
809

	spin_lock_irqsave(&card->card_lock, flags);
Alan Cox's avatar
Alan Cox committed
810
	clear_bit(TTY_IO_ERROR, &tty->flags);
Alan Cox's avatar
Alan Cox committed
811
	if (port->port.count == 1)
Linus Torvalds's avatar
Linus Torvalds committed
812
		card->count++;
813

Linus Torvalds's avatar
Linus Torvalds committed
814
	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
815

Linus Torvalds's avatar
Linus Torvalds committed
816
	/*	discard any residual data	*/
817
818
819
820
821
822
	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);
	}
823

Alan Cox's avatar
Alan Cox committed
824
	isicom_config_port(tty);
Alan Cox's avatar
Alan Cox committed
825
	port->port.flags |= ASYNC_INITIALIZED;
Linus Torvalds's avatar
Linus Torvalds committed
826
	spin_unlock_irqrestore(&card->card_lock, flags);
827
828
829
830

	return 0;
}

831
832
static int block_til_ready(struct tty_struct *tty, struct file *filp,
	struct isi_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
833
{
834
	struct isi_board *card = port->card;
Linus Torvalds's avatar
Linus Torvalds committed
835
836
837
838
839
840
	int do_clocal = 0, retval;
	unsigned long flags;
	DECLARE_WAITQUEUE(wait, current);

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

Alan Cox's avatar
Alan Cox committed
841
	if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) {
842
		pr_dbg("block_til_ready: close in progress.\n");
Alan Cox's avatar
Alan Cox committed
843
844
		interruptible_sleep_on(&port->port.close_wait);
		if (port->port.flags & ASYNC_HUP_NOTIFY)
Linus Torvalds's avatar
Linus Torvalds committed
845
846
847
848
			return -EAGAIN;
		else
			return -ERESTARTSYS;
	}
849

Linus Torvalds's avatar
Linus Torvalds committed
850
	/* if non-blocking mode is set ... */
851

852
853
	if ((filp->f_flags & O_NONBLOCK) ||
			(tty->flags & (1 << TTY_IO_ERROR))) {
854
		pr_dbg("block_til_ready: non-block mode.\n");
Alan Cox's avatar
Alan Cox committed
855
		port->port.flags |= ASYNC_NORMAL_ACTIVE;
856
857
858
		return 0;
	}

Linus Torvalds's avatar
Linus Torvalds committed
859
860
	if (C_CLOCAL(tty))
		do_clocal = 1;
861
862

	/* block waiting for DCD to be asserted, and while
Linus Torvalds's avatar
Linus Torvalds committed
863
864
						callout dev is busy */
	retval = 0;
Alan Cox's avatar
Alan Cox committed
865
	add_wait_queue(&port->port.open_wait, &wait);
Linus Torvalds's avatar
Linus Torvalds committed
866
867
868

	spin_lock_irqsave(&card->card_lock, flags);
	if (!tty_hung_up_p(filp))
Alan Cox's avatar
Alan Cox committed
869
870
		port->port.count--;
	port->port.blocked_open++;
Linus Torvalds's avatar
Linus Torvalds committed
871
	spin_unlock_irqrestore(&card->card_lock, flags);
872

Linus Torvalds's avatar
Linus Torvalds committed
873
874
875
876
	while (1) {
		raise_dtr_rts(port);

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

static int isicom_open(struct tty_struct *tty, struct file *filp)
Linus Torvalds's avatar
Linus Torvalds committed
908
{
909
910
	struct isi_port *port;
	struct isi_board *card;
911
912
	unsigned int board;
	int error, line;
Linus Torvalds's avatar
Linus Torvalds committed
913
914
915
916
917
918

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

Linus Torvalds's avatar
Linus Torvalds committed
920
921
	if (!(card->status & FIRMWARE_LOADED))
		return -ENODEV;
922

Linus Torvalds's avatar
Linus Torvalds committed
923
924
925
926
	/*  open on a port greater than the port count for the card !!! */
	if (line > ((board * 16) + card->port_count - 1))
		return -ENODEV;

927
	port = &isi_ports[line];
Linus Torvalds's avatar
Linus Torvalds committed
928
929
	if (isicom_paranoia_check(port, tty->name, "isicom_open"))
		return -ENODEV;
930
931
932

	isicom_setup_board(card);

Alan Cox's avatar
Alan Cox committed
933
	port->port.count++;
Linus Torvalds's avatar
Linus Torvalds committed
934
	tty->driver_data = port;
Alan Cox's avatar
Alan Cox committed
935
936
	tty_port_tty_set(&port->port, tty);
	error = isicom_setup_port(tty);
Alan Cox's avatar
Alan Cox committed
937
938
939
	if (error == 0)
		error = block_til_ready(tty, filp, port);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
940
}
941

Linus Torvalds's avatar
Linus Torvalds committed
942
943
/* close et all */

944
static inline void isicom_shutdown_board(struct isi_board *bp)
Linus Torvalds's avatar
Linus Torvalds committed
945
{
Alan Cox's avatar
Alan Cox committed
946
	if (bp->status & BOARD_ACTIVE)
Linus Torvalds's avatar
Linus Torvalds committed
947
948
949
		bp->status &= ~BOARD_ACTIVE;
}

950
/* card->lock HAS to be held */
951
static void isicom_shutdown_port(struct isi_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
952
{
953
954
955
	struct isi_board *card = port->card;
	struct tty_struct *tty;

Alan Cox's avatar
Alan Cox committed
956
	tty = tty_port_tty_get(&port->port);
Linus Torvalds's avatar
Linus Torvalds committed
957

Alan Cox's avatar
Alan Cox committed
958
959
	if (!(port->port.flags & ASYNC_INITIALIZED)) {
		tty_kref_put(tty);
Linus Torvalds's avatar
Linus Torvalds committed
960
		return;
Alan Cox's avatar
Alan Cox committed
961
	}
962

Alan Cox's avatar
Alan Cox committed
963
964
	tty_port_free_xmit_buf(&port->port);
	port->port.flags &= ~ASYNC_INITIALIZED;
Linus Torvalds's avatar
Linus Torvalds committed
965
	/* 3rd October 2000 : Vinayak P Risbud */
Alan Cox's avatar
Alan Cox committed
966
	tty_port_tty_set(&port->port, NULL);
967

Linus Torvalds's avatar
Linus Torvalds committed
968
969
970
	/*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
971
	appears on the remote end. Now we drop the dtr only if the
Linus Torvalds's avatar
Linus Torvalds committed
972
	HUPCL(Hangup on close) flag is set for the tty*/