epca.c 75.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
/*
	Copyright (C) 1996  Digi International.
3

Linus Torvalds's avatar
Linus Torvalds committed
4
5
6
	For technical support please email digiLinux@dgii.com or
	call Digi tech support at (612) 912-3456

7
8
	** This driver is no longer supported by Digi **

9
10
11
12
13
14
15
16
	Much of this design and code came from epca.c which was
	copyright (C) 1994, 1995 Troy De Jongh, and subsquently
	modified by David Nugent, Christoph Lameter, Mike McLagan.

	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.
Linus Torvalds's avatar
Linus Torvalds committed
17

18
19
20
21
22
23
24
25
26
27
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* See README.epca for change history --DAT*/
Linus Torvalds's avatar
Linus Torvalds committed
28
29
30
31
32

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
33
#include <linux/sched.h>
Linus Torvalds's avatar
Linus Torvalds committed
34
35
36
37
38
39
#include <linux/serial.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
Alexey Dobriyan's avatar
Alexey Dobriyan committed
40
#include <linux/smp_lock.h>
Linus Torvalds's avatar
Linus Torvalds committed
41
42
#include <linux/ioport.h>
#include <linux/interrupt.h>
Alan Cox's avatar
Alan Cox committed
43
44
#include <linux/uaccess.h>
#include <linux/io.h>
45
#include <linux/spinlock.h>
Linus Torvalds's avatar
Linus Torvalds committed
46
47
#include <linux/pci.h>
#include "digiPCI.h"
48

Linus Torvalds's avatar
Linus Torvalds committed
49
50
51
52
53
54

#include "digi1.h"
#include "digiFep1.h"
#include "epca.h"
#include "epcaconfig.h"

55
#define VERSION            "1.3.0.1-LK2.6"
Linus Torvalds's avatar
Linus Torvalds committed
56
57

/* This major needs to be submitted to Linux to join the majors list */
58
#define DIGIINFOMAJOR       35  /* For Digi specific ioctl */
Linus Torvalds's avatar
Linus Torvalds committed
59
60
61
62
63
64
65
66
67
68
69
70
71


#define MAXCARDS 7
#define epcaassert(x, msg)  if (!(x)) epca_error(__LINE__, msg)

#define PFX "epca: "

static int nbdevs, num_cards, liloconfig;
static int digi_poller_inhibited = 1 ;

static int setup_error_code;
static int invalid_lilo_config;

72
73
/*
 * The ISA boards do window flipping into the same spaces so its only sane with
Alan Cox's avatar
Alan Cox committed
74
75
76
 * a single lock. It's still pretty efficient. This lock guards the hardware
 * and the tty_port lock guards the kernel side stuff like use counts. Take
 * this lock inside the port lock if you must take both.
77
 */
78
static DEFINE_SPINLOCK(epca_lock);
79

Alan Cox's avatar
Alan Cox committed
80
81
/* MAXBOARDS is typically 12, but ISA and EISA cards are restricted
   to 7 below. */
Linus Torvalds's avatar
Linus Torvalds committed
82
83
84
85
86
87
88
static struct board_info boards[MAXBOARDS];

static struct tty_driver *pc_driver;
static struct tty_driver *pc_info;

/* ------------------ Begin Digi specific structures -------------------- */

89
90
91
92
93
94
95
/*
 * digi_channels represents an array of structures that keep track of each
 * channel of the Digi product. Information such as transmit and receive
 * pointers, termio data, and signal definitions (DTR, CTS, etc ...) are stored
 * here. This structure is NOT used to overlay the cards physical channel
 * structure.
 */
Linus Torvalds's avatar
Linus Torvalds committed
96
97
static struct channel digi_channels[MAX_ALLOC];

98
99
100
101
102
/*
 * card_ptr is an array used to hold the address of the first channel structure
 * of each card. This array will hold the addresses of various channels located
 * in digi_channels.
 */
Linus Torvalds's avatar
Linus Torvalds committed
103
104
105
106
static struct channel *card_ptr[MAXCARDS];

static struct timer_list epca_timer;

107
108
109
110
/*
 * Begin generic memory functions. These functions will be alias (point at)
 * more specific functions dependent on the board being configured.
 */
111
112
113
114
115
116
117
118
static void memwinon(struct board_info *b, unsigned int win);
static void memwinoff(struct board_info *b, unsigned int win);
static void globalwinon(struct channel *ch);
static void rxwinon(struct channel *ch);
static void txwinon(struct channel *ch);
static void memoff(struct channel *ch);
static void assertgwinon(struct channel *ch);
static void assertmemoff(struct channel *ch);
Linus Torvalds's avatar
Linus Torvalds committed
119
120
121

/* ---- Begin more 'specific' memory functions for cx_like products --- */

122
123
124
125
126
127
static void pcxem_memwinon(struct board_info *b, unsigned int win);
static void pcxem_memwinoff(struct board_info *b, unsigned int win);
static void pcxem_globalwinon(struct channel *ch);
static void pcxem_rxwinon(struct channel *ch);
static void pcxem_txwinon(struct channel *ch);
static void pcxem_memoff(struct channel *ch);
Linus Torvalds's avatar
Linus Torvalds committed
128
129
130

/* ------ Begin more 'specific' memory functions for the pcxe ------- */

131
132
133
134
135
136
static void pcxe_memwinon(struct board_info *b, unsigned int win);
static void pcxe_memwinoff(struct board_info *b, unsigned int win);
static void pcxe_globalwinon(struct channel *ch);
static void pcxe_rxwinon(struct channel *ch);
static void pcxe_txwinon(struct channel *ch);
static void pcxe_memoff(struct channel *ch);
Linus Torvalds's avatar
Linus Torvalds committed
137
138
139
140

/* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */
/* Note : pc64xe and pcxi share the same windowing routines */

141
142
143
144
145
146
static void pcxi_memwinon(struct board_info *b, unsigned int win);
static void pcxi_memwinoff(struct board_info *b, unsigned int win);
static void pcxi_globalwinon(struct channel *ch);
static void pcxi_rxwinon(struct channel *ch);
static void pcxi_txwinon(struct channel *ch);
static void pcxi_memoff(struct channel *ch);
Linus Torvalds's avatar
Linus Torvalds committed
147
148
149

/* - Begin 'specific' do nothing memory functions needed for some cards - */

150
151
152
153
154
155
156
157
static void dummy_memwinon(struct board_info *b, unsigned int win);
static void dummy_memwinoff(struct board_info *b, unsigned int win);
static void dummy_globalwinon(struct channel *ch);
static void dummy_rxwinon(struct channel *ch);
static void dummy_txwinon(struct channel *ch);
static void dummy_memoff(struct channel *ch);
static void dummy_assertgwinon(struct channel *ch);
static void dummy_assertmemoff(struct channel *ch);
Linus Torvalds's avatar
Linus Torvalds committed
158

159
160
static struct channel *verifyChannel(struct tty_struct *);
static void pc_sched_event(struct channel *, int);
Linus Torvalds's avatar
Linus Torvalds committed
161
162
static void epca_error(int, char *);
static void pc_close(struct tty_struct *, struct file *);
Alan Cox's avatar
Alan Cox committed
163
static void shutdown(struct channel *, struct tty_struct *tty);
Linus Torvalds's avatar
Linus Torvalds committed
164
165
166
167
168
169
170
171
172
173
174
175
176
177
static void pc_hangup(struct tty_struct *);
static int pc_write_room(struct tty_struct *);
static int pc_chars_in_buffer(struct tty_struct *);
static void pc_flush_buffer(struct tty_struct *);
static void pc_flush_chars(struct tty_struct *);
static int pc_open(struct tty_struct *, struct file *);
static void post_fep_init(unsigned int crd);
static void epcapoll(unsigned long);
static void doevent(int);
static void fepcmd(struct channel *, int, int, int, int, int);
static unsigned termios2digi_h(struct channel *ch, unsigned);
static unsigned termios2digi_i(struct channel *ch, unsigned);
static unsigned termios2digi_c(struct channel *ch, unsigned);
static void epcaparam(struct tty_struct *, struct channel *);
Alan Cox's avatar
Alan Cox committed
178
static void receive_data(struct channel *, struct tty_struct *tty);
Linus Torvalds's avatar
Linus Torvalds committed
179
static int pc_ioctl(struct tty_struct *, struct file *,
Alan Cox's avatar
Alan Cox committed
180
			unsigned int, unsigned long);
Linus Torvalds's avatar
Linus Torvalds committed
181
static int info_ioctl(struct tty_struct *, struct file *,
Alan Cox's avatar
Alan Cox committed
182
			unsigned int, unsigned long);
Alan Cox's avatar
Alan Cox committed
183
static void pc_set_termios(struct tty_struct *, struct ktermios *);
David Howells's avatar
David Howells committed
184
static void do_softint(struct work_struct *work);
Linus Torvalds's avatar
Linus Torvalds committed
185
186
static void pc_stop(struct tty_struct *);
static void pc_start(struct tty_struct *);
Alan Cox's avatar
Alan Cox committed
187
static void pc_throttle(struct tty_struct *tty);
Linus Torvalds's avatar
Linus Torvalds committed
188
static void pc_unthrottle(struct tty_struct *tty);
Alan Cox's avatar
Alan Cox committed
189
static int pc_send_break(struct tty_struct *tty, int msec);
Linus Torvalds's avatar
Linus Torvalds committed
190
191
192
static void setup_empty_event(struct tty_struct *tty, struct channel *ch);

static int pc_write(struct tty_struct *, const unsigned char *, int);
193
static int pc_init(void);
Linus Torvalds's avatar
Linus Torvalds committed
194
195
static int init_PCI(void);

196
197
198
199
200
201
202
203
204
/*
 * Table of functions for each board to handle memory. Mantaining parallelism
 * is a *very* good idea here. The idea is for the runtime code to blindly call
 * these functions, not knowing/caring about the underlying hardware. This
 * stuff should contain no conditionals; if more functionality is needed a
 * different entry should be established. These calls are the interface calls
 * and are the only functions that should be accessed. Anyone caught making
 * direct calls deserves what they get.
 */
205
static void memwinon(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
206
{
207
	b->memwinon(b, win);
Linus Torvalds's avatar
Linus Torvalds committed
208
209
}

210
static void memwinoff(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
211
{
212
	b->memwinoff(b, win);
Linus Torvalds's avatar
Linus Torvalds committed
213
214
}

215
static void globalwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
216
{
217
	ch->board->globalwinon(ch);
Linus Torvalds's avatar
Linus Torvalds committed
218
219
}

220
static void rxwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
221
{
222
	ch->board->rxwinon(ch);
Linus Torvalds's avatar
Linus Torvalds committed
223
224
}

225
static void txwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
226
{
227
	ch->board->txwinon(ch);
Linus Torvalds's avatar
Linus Torvalds committed
228
229
}

230
static void memoff(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
231
{
232
	ch->board->memoff(ch);
Linus Torvalds's avatar
Linus Torvalds committed
233
}
234
static void assertgwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
235
{
236
	ch->board->assertgwinon(ch);
Linus Torvalds's avatar
Linus Torvalds committed
237
238
}

239
static void assertmemoff(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
240
{
241
	ch->board->assertmemoff(ch);
Linus Torvalds's avatar
Linus Torvalds committed
242
243
}

244
/* PCXEM windowing is the same as that used in the PCXR and CX series cards. */
245
static void pcxem_memwinon(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
246
{
Alan Cox's avatar
Alan Cox committed
247
	outb_p(FEPWIN | win, b->port + 1);
Linus Torvalds's avatar
Linus Torvalds committed
248
249
}

250
static void pcxem_memwinoff(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
251
{
252
	outb_p(0, b->port + 1);
Linus Torvalds's avatar
Linus Torvalds committed
253
254
}

255
static void pcxem_globalwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
256
{
Alan Cox's avatar
Alan Cox committed
257
	outb_p(FEPWIN, (int)ch->board->port + 1);
Linus Torvalds's avatar
Linus Torvalds committed
258
259
}

260
static void pcxem_rxwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
261
262
263
264
{
	outb_p(ch->rxwin, (int)ch->board->port + 1);
}

265
static void pcxem_txwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
266
267
268
269
{
	outb_p(ch->txwin, (int)ch->board->port + 1);
}

270
static void pcxem_memoff(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
271
272
273
274
275
{
	outb_p(0, (int)ch->board->port + 1);
}

/* ----------------- Begin pcxe memory window stuff ------------------ */
276
static void pcxe_memwinon(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
277
{
278
	outb_p(FEPWIN | win, b->port + 1);
Linus Torvalds's avatar
Linus Torvalds committed
279
280
}

281
static void pcxe_memwinoff(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
282
{
283
	outb_p(inb(b->port) & ~FEPMEM, b->port + 1);
284
	outb_p(0, b->port + 1);
Linus Torvalds's avatar
Linus Torvalds committed
285
286
}

287
static void pcxe_globalwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
288
{
289
	outb_p(FEPWIN, (int)ch->board->port + 1);
Linus Torvalds's avatar
Linus Torvalds committed
290
291
}

292
static void pcxe_rxwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
293
{
294
	outb_p(ch->rxwin, (int)ch->board->port + 1);
Linus Torvalds's avatar
Linus Torvalds committed
295
296
}

297
static void pcxe_txwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
298
{
299
	outb_p(ch->txwin, (int)ch->board->port + 1);
Linus Torvalds's avatar
Linus Torvalds committed
300
301
}

302
static void pcxe_memoff(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
303
304
305
306
307
308
{
	outb_p(0, (int)ch->board->port);
	outb_p(0, (int)ch->board->port + 1);
}

/* ------------- Begin pc64xe and pcxi memory window stuff -------------- */
309
static void pcxi_memwinon(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
310
{
311
	outb_p(inb(b->port) | FEPMEM, b->port);
Linus Torvalds's avatar
Linus Torvalds committed
312
313
}

314
static void pcxi_memwinoff(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
315
{
316
	outb_p(inb(b->port) & ~FEPMEM, b->port);
Linus Torvalds's avatar
Linus Torvalds committed
317
318
}

319
static void pcxi_globalwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
320
{
321
	outb_p(FEPMEM, ch->board->port);
Linus Torvalds's avatar
Linus Torvalds committed
322
323
}

324
static void pcxi_rxwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
325
{
326
	outb_p(FEPMEM, ch->board->port);
Linus Torvalds's avatar
Linus Torvalds committed
327
328
}

329
static void pcxi_txwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
330
{
331
	outb_p(FEPMEM, ch->board->port);
Linus Torvalds's avatar
Linus Torvalds committed
332
333
}

334
static void pcxi_memoff(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
335
{
336
	outb_p(0, ch->board->port);
Linus Torvalds's avatar
Linus Torvalds committed
337
338
}

339
static void pcxi_assertgwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
340
{
341
	epcaassert(inb(ch->board->port) & FEPMEM, "Global memory off");
Linus Torvalds's avatar
Linus Torvalds committed
342
343
}

344
static void pcxi_assertmemoff(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
345
{
346
	epcaassert(!(inb(ch->board->port) & FEPMEM), "Memory on");
Linus Torvalds's avatar
Linus Torvalds committed
347
348
}

349
350
351
352
353
354
355
/*
 * Not all of the cards need specific memory windowing routines. Some cards
 * (Such as PCI) needs no windowing routines at all. We provide these do
 * nothing routines so that the same code base can be used. The driver will
 * ALWAYS call a windowing routine if it thinks it needs to; regardless of the
 * card. However, dependent on the card the routine may or may not do anything.
 */
356
static void dummy_memwinon(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
357
358
359
{
}

360
static void dummy_memwinoff(struct board_info *b, unsigned int win)
Linus Torvalds's avatar
Linus Torvalds committed
361
362
363
{
}

364
static void dummy_globalwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
365
366
367
{
}

368
static void dummy_rxwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
369
370
371
{
}

372
static void dummy_txwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
373
374
375
{
}

376
static void dummy_memoff(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
377
378
379
{
}

380
static void dummy_assertgwinon(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
381
382
383
{
}

384
static void dummy_assertmemoff(struct channel *ch)
Linus Torvalds's avatar
Linus Torvalds committed
385
386
387
{
}

388
static struct channel *verifyChannel(struct tty_struct *tty)
389
390
391
392
393
394
395
{
	/*
	 * This routine basically provides a sanity check. It insures that the
	 * channel returned is within the proper range of addresses as well as
	 * properly initialized. If some bogus info gets passed in
	 * through tty->driver_data this should catch it.
	 */
396
	if (tty) {
Alan Cox's avatar
Alan Cox committed
397
		struct channel *ch = tty->driver_data;
Alan Cox's avatar
Alan Cox committed
398
		if (ch >= &digi_channels[0] && ch < &digi_channels[nbdevs]) {
Linus Torvalds's avatar
Linus Torvalds committed
399
400
401
			if (ch->magic == EPCA_MAGIC)
				return ch;
		}
402
	}
Linus Torvalds's avatar
Linus Torvalds committed
403
	return NULL;
404
}
Linus Torvalds's avatar
Linus Torvalds committed
405

406
407
static void pc_sched_event(struct channel *ch, int event)
{
408
409
410
411
	/*
	 * We call this to schedule interrupt processing on some event. The
	 * kernel sees our request and calls the related routine in OUR driver.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
412
413
	ch->event |= 1 << event;
	schedule_work(&ch->tqueue);
414
}
Linus Torvalds's avatar
Linus Torvalds committed
415
416

static void epca_error(int line, char *msg)
417
{
Alan Cox's avatar
Alan Cox committed
418
	printk(KERN_ERR "epca_error (Digi): line = %d %s\n", line, msg);
419
}
Linus Torvalds's avatar
Linus Torvalds committed
420

421
static void pc_close(struct tty_struct *tty, struct file *filp)
422
{
Linus Torvalds's avatar
Linus Torvalds committed
423
	struct channel *ch;
Alan Cox's avatar
Alan Cox committed
424
	struct tty_port *port;
425
426
427
428
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
Alan Cox's avatar
Alan Cox committed
429
	ch = verifyChannel(tty);
Alan Cox's avatar
Alan Cox committed
430
431
432
	if (ch == NULL)
		return;
	port = &ch->port;
Linus Torvalds's avatar
Linus Torvalds committed
433

Alan Cox's avatar
Alan Cox committed
434
	if (tty_port_close_start(port, tty, filp) == 0)
Alan Cox's avatar
Alan Cox committed
435
		return;
Linus Torvalds's avatar
Linus Torvalds committed
436

Alan Cox's avatar
Alan Cox committed
437
438
439
	pc_flush_buffer(tty);
	shutdown(ch, tty);

Alan Cox's avatar
Alan Cox committed
440
441
	tty_port_close_end(port, tty);
	ch->event = 0;	/* FIXME: review ch->event locking */
Alan Cox's avatar
Alan Cox committed
442
	tty_port_tty_set(port, NULL);
443
}
Linus Torvalds's avatar
Linus Torvalds committed
444

Alan Cox's avatar
Alan Cox committed
445
static void shutdown(struct channel *ch, struct tty_struct *tty)
446
{
Linus Torvalds's avatar
Linus Torvalds committed
447
	unsigned long flags;
448
	struct board_chan __iomem *bc;
Alan Cox's avatar
Alan Cox committed
449
	struct tty_port *port = &ch->port;
Linus Torvalds's avatar
Linus Torvalds committed
450

Alan Cox's avatar
Alan Cox committed
451
	if (!(port->flags & ASYNC_INITIALIZED))
Linus Torvalds's avatar
Linus Torvalds committed
452
453
		return;

454
	spin_lock_irqsave(&epca_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
455

456
	globalwinon(ch);
Linus Torvalds's avatar
Linus Torvalds committed
457
458
	bc = ch->brdchan;

459
460
461
462
463
	/*
	 * In order for an event to be generated on the receipt of data the
	 * idata flag must be set. Since we are shutting down, this is not
	 * necessary clear this flag.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
464
	if (bc)
465
		writeb(0, &bc->idata);
Linus Torvalds's avatar
Linus Torvalds committed
466

467
	/* If we're a modem control device and HUPCL is on, drop RTS & DTR. */
468
	if (tty->termios->c_cflag & HUPCL)  {
Linus Torvalds's avatar
Linus Torvalds committed
469
470
471
472
473
		ch->omodem &= ~(ch->m_rts | ch->m_dtr);
		fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1);
	}
	memoff(ch);

474
475
476
477
	/*
	 * The channel has officialy been closed. The next time it is opened it
	 * will have to reinitialized. Set a flag to indicate this.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
478
	/* Prevent future Digi programmed interrupts from coming active */
Alan Cox's avatar
Alan Cox committed
479
	port->flags &= ~ASYNC_INITIALIZED;
480
	spin_unlock_irqrestore(&epca_lock, flags);
481
}
Linus Torvalds's avatar
Linus Torvalds committed
482
483

static void pc_hangup(struct tty_struct *tty)
484
{
Linus Torvalds's avatar
Linus Torvalds committed
485
	struct channel *ch;
Alan Cox's avatar
Alan Cox committed
486

487
488
489
490
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
Alan Cox's avatar
Alan Cox committed
491
492
	ch = verifyChannel(tty);
	if (ch != NULL) {
493
		pc_flush_buffer(tty);
Linus Torvalds's avatar
Linus Torvalds committed
494
		tty_ldisc_flush(tty);
Alan Cox's avatar
Alan Cox committed
495
496
497
		shutdown(ch, tty);

		ch->event = 0;	/* FIXME: review locking of ch->event */
Alan Cox's avatar
Alan Cox committed
498
		tty_port_hangup(&ch->port);
499
500
	}
}
Linus Torvalds's avatar
Linus Torvalds committed
501

502
static int pc_write(struct tty_struct *tty,
Alan Cox's avatar
Alan Cox committed
503
			const unsigned char *buf, int bytesAvailable)
504
{
505
506
507
508
	unsigned int head, tail;
	int dataLen;
	int size;
	int amountCopied;
Linus Torvalds's avatar
Linus Torvalds committed
509
510
511
	struct channel *ch;
	unsigned long flags;
	int remain;
512
	struct board_chan __iomem *bc;
Linus Torvalds's avatar
Linus Torvalds committed
513

514
515
516
517
518
519
520
521
	/*
	 * pc_write is primarily called directly by the kernel routine
	 * tty_write (Though it can also be called by put_char) found in
	 * tty_io.c. pc_write is passed a line discipline buffer where the data
	 * to be written out is stored. The line discipline implementation
	 * itself is done at the kernel level and is not brought into the
	 * driver.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
522

523
524
525
526
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
Alan Cox's avatar
Alan Cox committed
527
528
	ch = verifyChannel(tty);
	if (ch == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
529
530
531
532
533
534
535
		return 0;

	/* Make a pointer to the channel data structure found on the board. */
	bc   = ch->brdchan;
	size = ch->txbufsize;
	amountCopied = 0;

536
	spin_lock_irqsave(&epca_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
537
538
	globalwinon(ch);

539
540
	head = readw(&bc->tin) & (size - 1);
	tail = readw(&bc->tout);
Linus Torvalds's avatar
Linus Torvalds committed
541

542
543
	if (tail != readw(&bc->tout))
		tail = readw(&bc->tout);
Linus Torvalds's avatar
Linus Torvalds committed
544
545
	tail &= (size - 1);

546
547
548
549
550
551
552
553
554
555
	if (head >= tail) {
		/* head has not wrapped */
		/*
		 * remain (much like dataLen above) represents the total amount
		 * of space available on the card for data. Here dataLen
		 * represents the space existing between the head pointer and
		 * the end of buffer. This is important because a memcpy cannot
		 * be told to automatically wrap around when it hits the buffer
		 * end.
		 */
Linus Torvalds's avatar
Linus Torvalds committed
556
557
		dataLen = size - head;
		remain = size - (head - tail) - 1;
558
559
	} else {
		/* head has wrapped around */
Linus Torvalds's avatar
Linus Torvalds committed
560
561
		remain = tail - head - 1;
		dataLen = remain;
562
563
564
565
566
	}
	/*
	 * Check the space on the card. If we have more data than space; reduce
	 * the amount of data to fit the space.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
567
568
	bytesAvailable = min(remain, bytesAvailable);
	txwinon(ch);
569
570
	while (bytesAvailable > 0) {
		/* there is data to copy onto card */
Linus Torvalds's avatar
Linus Torvalds committed
571

572
573
574
575
		/*
		 * If head is not wrapped, the below will make sure the first
		 * data copy fills to the end of card buffer.
		 */
Linus Torvalds's avatar
Linus Torvalds committed
576
		dataLen = min(bytesAvailable, dataLen);
577
		memcpy_toio(ch->txptr + head, buf, dataLen);
Linus Torvalds's avatar
Linus Torvalds committed
578
579
580
581
582
		buf += dataLen;
		head += dataLen;
		amountCopied += dataLen;
		bytesAvailable -= dataLen;

583
		if (head >= size) {
Linus Torvalds's avatar
Linus Torvalds committed
584
585
586
			head = 0;
			dataLen = tail;
		}
587
	}
Linus Torvalds's avatar
Linus Torvalds committed
588
589
	ch->statusflags |= TXBUSY;
	globalwinon(ch);
590
	writew(head, &bc->tin);
Linus Torvalds's avatar
Linus Torvalds committed
591

592
	if ((ch->statusflags & LOWWAIT) == 0)  {
Linus Torvalds's avatar
Linus Torvalds committed
593
		ch->statusflags |= LOWWAIT;
594
		writeb(1, &bc->ilow);
Linus Torvalds's avatar
Linus Torvalds committed
595
596
	}
	memoff(ch);
597
	spin_unlock_irqrestore(&epca_lock, flags);
598
599
	return amountCopied;
}
Linus Torvalds's avatar
Linus Torvalds committed
600
601

static int pc_write_room(struct tty_struct *tty)
602
{
Alan Cox's avatar
Alan Cox committed
603
	int remain = 0;
Linus Torvalds's avatar
Linus Torvalds committed
604
605
606
	struct channel *ch;
	unsigned long flags;
	unsigned int head, tail;
607
	struct board_chan __iomem *bc;
608
609
610
611
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
Alan Cox's avatar
Alan Cox committed
612
613
	ch = verifyChannel(tty);
	if (ch != NULL) {
614
		spin_lock_irqsave(&epca_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
615
616
617
		globalwinon(ch);

		bc   = ch->brdchan;
618
619
		head = readw(&bc->tin) & (ch->txbufsize - 1);
		tail = readw(&bc->tout);
Linus Torvalds's avatar
Linus Torvalds committed
620

621
622
		if (tail != readw(&bc->tout))
			tail = readw(&bc->tout);
Linus Torvalds's avatar
Linus Torvalds committed
623
624
		/* Wrap tail if necessary */
		tail &= (ch->txbufsize - 1);
Alan Cox's avatar
Alan Cox committed
625
626
		remain = tail - head - 1;
		if (remain < 0)
Linus Torvalds's avatar
Linus Torvalds committed
627
628
			remain += ch->txbufsize;

629
		if (remain && (ch->statusflags & LOWWAIT) == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
630
			ch->statusflags |= LOWWAIT;
631
			writeb(1, &bc->ilow);
Linus Torvalds's avatar
Linus Torvalds committed
632
633
		}
		memoff(ch);
634
		spin_unlock_irqrestore(&epca_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
635
636
637
	}
	/* Return how much room is left on card */
	return remain;
638
}
Linus Torvalds's avatar
Linus Torvalds committed
639
640

static int pc_chars_in_buffer(struct tty_struct *tty)
641
{
Linus Torvalds's avatar
Linus Torvalds committed
642
643
644
645
646
	int chars;
	unsigned int ctail, head, tail;
	int remain;
	unsigned long flags;
	struct channel *ch;
647
	struct board_chan __iomem *bc;
648
649
650
651
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
Alan Cox's avatar
Alan Cox committed
652
653
	ch = verifyChannel(tty);
	if (ch == NULL)
654
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
655

656
	spin_lock_irqsave(&epca_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
657
658
659
	globalwinon(ch);

	bc = ch->brdchan;
660
661
662
	tail = readw(&bc->tout);
	head = readw(&bc->tin);
	ctail = readw(&ch->mailbox->cout);
Linus Torvalds's avatar
Linus Torvalds committed
663

Alan Cox's avatar
Alan Cox committed
664
665
	if (tail == head && readw(&ch->mailbox->cin) == ctail &&
						readb(&bc->tbusy) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
666
		chars = 0;
667
668
	else  { /* Begin if some space on the card has been used */
		head = readw(&bc->tin) & (ch->txbufsize - 1);
Linus Torvalds's avatar
Linus Torvalds committed
669
		tail &= (ch->txbufsize - 1);
670
671
672
673
674
		/*
		 * The logic here is basically opposite of the above
		 * pc_write_room here we are finding the amount of bytes in the
		 * buffer filled. Not the amount of bytes empty.
		 */
Alan Cox's avatar
Alan Cox committed
675
676
		remain = tail - head - 1;
		if (remain < 0)
Linus Torvalds's avatar
Linus Torvalds committed
677
678
			remain += ch->txbufsize;
		chars = (int)(ch->txbufsize - remain);
679
680
681
682
683
684
685
		/*
		 * Make it possible to wakeup anything waiting for output in
		 * tty_ioctl.c, etc.
		 *
		 * If not already set. Setup an event to indicate when the
		 * transmit buffer empties.
		 */
Linus Torvalds's avatar
Linus Torvalds committed
686
		if (!(ch->statusflags & EMPTYWAIT))
Alan Cox's avatar
Alan Cox committed
687
			setup_empty_event(tty, ch);
Linus Torvalds's avatar
Linus Torvalds committed
688
689
	} /* End if some space on the card has been used */
	memoff(ch);
690
	spin_unlock_irqrestore(&epca_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
691
	/* Return number of characters residing on card. */
692
693
	return chars;
}
Linus Torvalds's avatar
Linus Torvalds committed
694
695

static void pc_flush_buffer(struct tty_struct *tty)
696
{
Linus Torvalds's avatar
Linus Torvalds committed
697
698
699
	unsigned int tail;
	unsigned long flags;
	struct channel *ch;
700
	struct board_chan __iomem *bc;
701
702
703
704
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
Alan Cox's avatar
Alan Cox committed
705
706
	ch = verifyChannel(tty);
	if (ch == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
707
708
		return;

709
	spin_lock_irqsave(&epca_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
710
711
	globalwinon(ch);
	bc   = ch->brdchan;
712
	tail = readw(&bc->tout);
Linus Torvalds's avatar
Linus Torvalds committed
713
714
715
	/* Have FEP move tout pointer; effectively flushing transmit buffer */
	fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0);
	memoff(ch);
716
	spin_unlock_irqrestore(&epca_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
717
	tty_wakeup(tty);
718
}
Linus Torvalds's avatar
Linus Torvalds committed
719
720

static void pc_flush_chars(struct tty_struct *tty)
721
722
723
724
725
726
{
	struct channel *ch;
	/*
	 * verifyChannel returns the channel from the tty struct if it is
	 * valid. This serves as a sanity check.
	 */
Alan Cox's avatar
Alan Cox committed
727
728
	ch = verifyChannel(tty);
	if (ch != NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
729
		unsigned long flags;
730
		spin_lock_irqsave(&epca_lock, flags);
731
732
733
734
		/*
		 * If not already set and the transmitter is busy setup an
		 * event to indicate when the transmit empties.
		 */
Alan Cox's avatar
Alan Cox committed
735
736
737
		if ((ch->statusflags & TXBUSY) &&
				!(ch->statusflags & EMPTYWAIT))
			setup_empty_event(tty, ch);
738
		spin_unlock_irqrestore(&epca_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
739
	}
740
}
Linus Torvalds's avatar
Linus Torvalds committed
741

Alan Cox's avatar
Alan Cox committed
742
static int epca_carrier_raised(struct tty_port *port)
743
{
Alan Cox's avatar
Alan Cox committed
744
745
746
	struct channel *ch = container_of(port, struct channel, port);
	if (ch->imodem & ch->dcd)
		return 1;
Linus Torvalds's avatar
Linus Torvalds committed
747
	return 0;
748
}
Linus Torvalds's avatar
Linus Torvalds committed
749

750
static void epca_dtr_rts(struct tty_port *port, int onoff)
Alan Cox's avatar
Alan Cox committed
751
752
753
{
}

Alan Cox's avatar
Alan Cox committed
754
static int pc_open(struct tty_struct *tty, struct file *filp)
755
{
Linus Torvalds's avatar
Linus Torvalds committed
756
	struct channel *ch;
Alan Cox's avatar
Alan Cox committed
757
	struct tty_port *port;
Linus Torvalds's avatar
Linus Torvalds committed
758
759
	unsigned long flags;
	int line, retval, boardnum;
760
	struct board_chan __iomem *bc;
761
	unsigned int head;
Linus Torvalds's avatar
Linus Torvalds committed
762
763

	line = tty->index;
764
765
	if (line < 0 || line >= nbdevs)
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
766
767

	ch = &digi_channels[line];
Alan Cox's avatar
Alan Cox committed
768
	port = &ch->port;
Linus Torvalds's avatar
Linus Torvalds committed
769
770
771
772
	boardnum = ch->boardnum;

	/* Check status of board configured in system.  */

773
	/*
774
	 * I check to see if the epca_setup routine detected a user error. It
775
776
777
	 * might be better to put this in pc_init, but for the moment it goes
	 * here.
	 */
778
	if (invalid_lilo_config) {
Linus Torvalds's avatar
Linus Torvalds committed
779
		if (setup_error_code & INVALID_BOARD_TYPE)
780
			printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n");
Linus Torvalds's avatar
Linus Torvalds committed
781
		if (setup_error_code & INVALID_NUM_PORTS)
782
			printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n");
Linus Torvalds's avatar
Linus Torvalds committed
783
		if (setup_error_code & INVALID_MEM_BASE)
784
			printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n");
Linus Torvalds's avatar
Linus Torvalds committed
785
		if (setup_error_code & INVALID_PORT_BASE)
786
			printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n");
Linus Torvalds's avatar
Linus Torvalds committed
787
		if (setup_error_code & INVALID_BOARD_STATUS)
788
			printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n");
Linus Torvalds's avatar
Linus Torvalds committed
789
		if (setup_error_code & INVALID_ALTPIN)
790
			printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n");
Linus Torvalds's avatar
Linus Torvalds committed
791
		tty->driver_data = NULL;   /* Mark this device as 'down' */
792
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
793
	}
794
	if (boardnum >= num_cards || boards[boardnum].status == DISABLED)  {
Linus Torvalds's avatar
Linus Torvalds committed
795
796
797
		tty->driver_data = NULL;   /* Mark this device as 'down' */
		return(-ENODEV);
	}
798

799
800
	bc = ch->brdchan;
	if (bc == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
801
		tty->driver_data = NULL;
802
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
803
804
	}

Alan Cox's avatar
Alan Cox committed
805
	spin_lock_irqsave(&port->lock, flags);
806
807
808
809
810
	/*
	 * Every time a channel is opened, increment a counter. This is
	 * necessary because we do not wish to flush and shutdown the channel
	 * until the last app holding the channel open, closes it.
	 */
Alan Cox's avatar
Alan Cox committed
811
	port->count++;
812
813
814
815
	/*
	 * Set a kernel structures pointer to our local channel structure. This
	 * way we can get to it when passed only a tty struct.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
816
	tty->driver_data = ch;
Alan Cox's avatar
Alan Cox committed
817
	port->tty = tty;
818
819
820
821
	/*
	 * If this is the first time the channel has been opened, initialize
	 * the tty->termios struct otherwise let pc_close handle it.
	 */
Alan Cox's avatar
Alan Cox committed
822
	spin_lock(&epca_lock);
Linus Torvalds's avatar
Linus Torvalds committed
823
824
825
826
	globalwinon(ch);
	ch->statusflags = 0;

	/* Save boards current modem status */
827
	ch->imodem = readb(&bc->mstat);
Linus Torvalds's avatar
Linus Torvalds committed
828

829
830
831
832
	/*
	 * Set receive head and tail ptrs to each other. This indicates no data
	 * available to read.
	 */
833
834
	head = readw(&bc->rin);
	writew(head, &bc->rout);
Linus Torvalds's avatar
Linus Torvalds committed
835
836
837

	/* Set the channels associated tty structure */

838
839
840
841
	/*
	 * The below routine generally sets up parity, baud, flow control
	 * issues, etc.... It effect both control flags and input flags.
	 */
Alan Cox's avatar
Alan Cox committed
842
	epcaparam(tty, ch);
Linus Torvalds's avatar
Linus Torvalds committed
843
	memoff(ch);
Alan Cox's avatar
Alan Cox committed
844
845
846
	spin_unlock(&epca_lock);
	port->flags |= ASYNC_INITIALIZED;
	spin_unlock_irqrestore(&port->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
847

Alan Cox's avatar
Alan Cox committed
848
	retval = tty_port_block_til_ready(port, tty, filp);
Linus Torvalds's avatar
Linus Torvalds committed
849
850
	if (retval)
		return retval;
851
852
853
854
	/*
	 * Set this again in case a hangup set it to zero while this open() was
	 * waiting for the line...
	 */
Alan Cox's avatar
Alan Cox committed
855
856
857
	spin_lock_irqsave(&port->lock, flags);
	port->tty = tty;
	spin_lock(&epca_lock);
Linus Torvalds's avatar
Linus Torvalds committed
858
859
	globalwinon(ch);
	/* Enable Digi Data events */
860
	writeb(1, &bc->idata);
Linus Torvalds's avatar
Linus Torvalds committed
861
	memoff(ch);
Alan Cox's avatar
Alan Cox committed
862
863
	spin_unlock(&epca_lock);
	spin_unlock_irqrestore(&port->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
864
	return 0;
865
}
Linus Torvalds's avatar
Linus Torvalds committed
866
867

static int __init epca_module_init(void)
868
{
869
	return pc_init();
Linus Torvalds's avatar
Linus Torvalds committed
870
871
872
873
874
875
876
877
878
879
880
881
882
}
module_init(epca_module_init);

static struct pci_driver epca_driver;

static void __exit epca_module_exit(void)
{
	int               count, crd;
	struct board_info *bd;
	struct channel    *ch;

	del_timer_sync(&epca_timer);

Alan Cox's avatar
Alan Cox committed
883
884
	if (tty_unregister_driver(pc_driver) ||
				tty_unregister_driver(pc_info)) {
885
		printk(KERN_WARNING "epca: cleanup_module failed to un-register tty driver\n");
Linus Torvalds's avatar
Linus Torvalds committed
886
887
888
889
890
		return;
	}
	put_tty_driver(pc_driver);
	put_tty_driver(pc_info);

891
	for (crd = 0; crd < num_cards; crd++) {
Linus Torvalds's avatar
Linus Torvalds committed
892
		bd = &boards[crd];
893
		if (!bd) { /* sanity check */
Linus Torvalds's avatar
Linus Torvalds committed
894
895
			printk(KERN_ERR "<Error> - Digi : cleanup_module failed\n");
			return;
896
		}
897
		ch = card_ptr[crd];
898
		for (count = 0; count < bd->numports; count++, ch++) {
Alan Cox's avatar
Alan Cox committed
899
900
901
902
903
			struct tty_struct *tty = tty_port_tty_get(&ch->port);
			if (tty) {
				tty_hangup(tty);
				tty_kref_put(tty);
			}
904
905
906
		}
	}
	pci_unregister_driver(&epca_driver);
Linus Torvalds's avatar
Linus Torvalds committed
907
908
909
}
module_exit(epca_module_exit);

910
static const struct tty_operations pc_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
911
912
913
914
915
916
917
918
919
920
921
922
923
924
	.open = pc_open,
	.close = pc_close,
	.write = pc_write,
	.write_room = pc_write_room,
	.flush_buffer = pc_flush_buffer,
	.chars_in_buffer = pc_chars_in_buffer,
	.flush_chars = pc_flush_chars,
	.ioctl = pc_ioctl,
	.set_termios = pc_set_termios,
	.stop = pc_stop,
	.start = pc_start,
	.throttle = pc_throttle,
	.unthrottle = pc_unthrottle,
	.hangup = pc_hangup,
Alan Cox's avatar
Alan Cox committed
925
	.break_ctl = pc_send_break
Linus Torvalds's avatar
Linus Torvalds committed
926
927
};

Alan Cox's avatar
Alan Cox committed
928
929
static const struct tty_port_operations epca_port_ops = {
	.carrier_raised = epca_carrier_raised,
930
	.dtr_rts = epca_dtr_rts,
Alan Cox's avatar
Alan Cox committed
931
932
};

Alan Cox's avatar
Alan Cox committed
933
static int info_open(struct tty_struct *tty, struct file *filp)
Linus Torvalds's avatar
Linus Torvalds committed
934
935
936
937
{
	return 0;
}

938
static const struct tty_operations info_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
939
940
941
942
	.open = info_open,
	.ioctl = info_ioctl,
};

943
static int __init pc_init(void)
944
{
Linus Torvalds's avatar
Linus Torvalds committed
945
946
947
	int crd;
	struct board_info *bd;
	unsigned char board_id = 0;
948
	int err = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
949
950
951
952
953
954
955

	int pci_boards_found, pci_count;

	pci_count = 0;

	pc_driver = alloc_tty_driver(MAX_ALLOC);
	if (!pc_driver)
956
		goto out1;
Linus Torvalds's avatar
Linus Torvalds committed
957
958

	pc_info = alloc_tty_driver(MAX_ALLOC);
959
960
	if (!pc_info)
		goto out2;
Linus Torvalds's avatar
Linus Torvalds committed
961

962
963
964
965
966
967
968
969
970
	/*
	 * If epca_setup has not been ran by LILO set num_cards to defaults;
	 * copy board structure defined by digiConfig into drivers board
	 * structure. Note : If LILO has ran epca_setup then epca_setup will
	 * handle defining num_cards as well as copying the data into the board
	 * structure.
	 */
	if (!liloconfig) {
		/* driver has been configured via. epcaconfig */
Linus Torvalds's avatar
Linus Torvalds committed
971
972
		nbdevs = NBDEVS;
		num_cards = NUMCARDS;
973
974
975
		memcpy(&boards, &static_boards,
		       sizeof(struct board_info) * NUMCARDS);
	}
Linus Torvalds's avatar
Linus Torvalds committed
976

977
978
979
980
981
982
	/*
	 * Note : If lilo was used to configure the driver and the ignore
	 * epcaconfig option was choosen (digiepca=2) then nbdevs and num_cards
	 * will equal 0 at this point. This is okay; PCI cards will still be
	 * picked up if detected.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
983

984
985
986
987
	/*
	 * Set up interrupt, we will worry about memory allocation in
	 * post_fep_init.
	 */
Alan Cox's avatar
Alan Cox committed
988
	printk(KERN_INFO "DIGI epca driver version %s loaded.\n", VERSION);
Linus Torvalds's avatar
Linus Torvalds committed
989

990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
	/*
	 * NOTE : This code assumes that the number of ports found in the
	 * boards array is correct. This could be wrong if the card in question
	 * is PCI (And therefore has no ports entry in the boards structure.)
	 * The rest of the information will be valid for PCI because the
	 * beginning of pc_init scans for PCI and determines i/o and base
	 * memory addresses. I am not sure if it is possible to read the number
	 * of ports supported by the card prior to it being booted (Since that
	 * is the state it is in when pc_init is run). Because it is not
	 * possible to query the number of supported ports until after the card
	 * has booted; we are required to calculate the card_ptrs as the card
	 * is initialized (Inside post_fep_init). The negative thing about this
	 * approach is that digiDload's call to GET_INFO will have a bad port
	 * value. (Since this is called prior to post_fep_init.)
	 */
Linus Torvalds's avatar
Linus Torvalds committed
1005
	pci_boards_found = 0;
1006
	if (num_cards < MAXBOARDS)
Linus Torvalds's avatar
Linus Torvalds committed
1007
1008
1009
1010
		pci_boards_found += init_PCI();
	num_cards += pci_boards_found;

	pc_driver->owner = THIS_MODULE;
1011
1012
	pc_driver->name = "ttyD";
	pc_driver->major = DIGI_MAJOR;
Linus Torvalds's avatar
Linus Torvalds committed
1013
1014
1015
1016
1017
1018
1019
1020
	pc_driver->minor_start = 0;
	pc_driver->type = TTY_DRIVER_TYPE_SERIAL;
	pc_driver->subtype = SERIAL_TYPE_NORMAL;
	pc_driver->init_termios = tty_std_termios;
	pc_driver->init_termios.c_iflag = 0;
	pc_driver->init_termios.c_oflag = 0;
	pc_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
	pc_driver->init_termios.c_lflag = 0;
Alan Cox's avatar
Alan Cox committed
1021
1022
	pc_driver->init_termios.c_ispeed = 9600;
	pc_driver->init_termios.c_ospeed = 9600;
Alan Cox's avatar
Alan Cox committed
1023
	pc_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_HARDWARE_BREAK;
Linus Torvalds's avatar
Linus Torvalds committed
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
	tty_set_operations(pc_driver, &pc_ops);

	pc_info->owner = THIS_MODULE;
	pc_info->name = "digi_ctl";
	pc_info->major = DIGIINFOMAJOR;
	pc_info->minor_start = 0;
	pc_info->type = TTY_DRIVER_TYPE_SERIAL;
	pc_info->subtype = SERIAL_TYPE_INFO;
	pc_info->init_termios = tty_std_termios;
	pc_info->init_termios.c_iflag = 0;
	pc_info->init_termios.c_oflag = 0;
	pc_info->init_termios.c_lflag = 0;
	pc_info->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
Alan Cox's avatar