si.c revision 12624
110015Speter/*
212496Speter * Device driver for Specialix range (SI/XIO) of serial line multiplexors.
310015Speter *
410015Speter * Copyright (C) 1990, 1992 Specialix International,
510015Speter * Copyright (C) 1993, Andy Rutter <andy@acronym.co.uk>
610015Speter * Copyright (C) 1995, Peter Wemm <peter@haywire.dialix.com>
710015Speter *
810015Speter * Originally derived from:	SunOS 4.x version
910015Speter * Ported from BSDI version to FreeBSD by Peter Wemm.
1010015Speter *
1110015Speter * Redistribution and use in source and binary forms, with or without
1210015Speter * modification, are permitted provided that the following conditions
1310015Speter * are met:
1410015Speter * 1. Redistributions of source code must retain the above copyright
1510015Speter *    notices, this list of conditions and the following disclaimer.
1610015Speter * 2. Redistributions in binary form must reproduce the above copyright
1710015Speter *    notices, this list of conditions and the following disclaimer in the
1810015Speter *    documentation and/or other materials provided with the distribution.
1910015Speter * 3. All advertising materials mentioning features or use of this software
2010015Speter *    must display the following acknowledgement:
2110015Speter *	This product includes software developed by Andy Rutter of
2210015Speter *	Advanced Methods and Tools Ltd. based on original information
2310015Speter *	from Specialix International.
2410015Speter * 4. Neither the name of Advanced Methods and Tools, nor Specialix
2510015Speter *    International may be used to endorse or promote products derived from
2610015Speter *    this software without specific prior written permission.
2710015Speter *
2810015Speter * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
2910015Speter * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
3010015Speter * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
3110015Speter * NO EVENT SHALL THE AUTHORS BE LIABLE.
3210015Speter *
3312624Speter *	$Id: si.c,v 1.19 1995/11/29 14:39:55 julian Exp $
3410015Speter */
3510015Speter
3610015Speter#ifndef lint
3710015Speterstatic char si_copyright1[] =  "@(#) (C) Specialix International, 1990,1992",
3810015Speter            si_copyright2[] =  "@(#) (C) Andy Rutter 1993",
3910015Speter            si_copyright3[] =  "@(#) (C) Peter Wemm 1995";
4010015Speter#endif	/* not lint */
4110015Speter
4210015Speter#include <sys/param.h>
4310015Speter#include <sys/systm.h>
4410015Speter#include <sys/ioctl.h>
4510015Speter#include <sys/tty.h>
4610015Speter#include <sys/ttydefaults.h>
4710015Speter#include <sys/proc.h>
4810015Speter#include <sys/user.h>
4910015Speter#include <sys/conf.h>
5010015Speter#include <sys/file.h>
5110015Speter#include <sys/uio.h>
5210015Speter#include <sys/dkstat.h>
5310015Speter#include <sys/kernel.h>
5410015Speter#include <sys/syslog.h>
5510015Speter#include <sys/malloc.h>
5610015Speter#include <sys/devconf.h>
5710015Speter
5810015Speter#include <machine/clock.h>
5910015Speter
6010015Speter#include <i386/isa/icu.h>
6110015Speter#include <i386/isa/isa.h>
6210015Speter#include <i386/isa/isa_device.h>
6310015Speter
6410015Speter#include <i386/isa/sireg.h>
6510015Speter#include <machine/si.h>
6610015Speter
6710015Speter#include "si.h"
6810015Speter
6910015Speter/*
7010015Speter * This device driver is designed to interface the Specialix International
7112496Speter * range of serial multiplexor cards (SI/XIO) to BSDI/386 on an ISA bus machine.
7210015Speter *
7310015Speter * The controller is interfaced to the host via dual port ram
7410015Speter * and a (programmable - SIHOST2) interrupt at IRQ 11,12 or 15.
7510015Speter */
7610015Speter
7710015Speter#define	POLL		/* turn on poller to generate buffer empty interrupt */
7812174Speter#undef	FASTPOLL	/* turn on 100Hz poller, (XXX: NOTYET!) */
7910047Speter#define SI_DEF_HWFLOW	/* turn on default CRTSCTS flow control */
8012496Speter#define SI_I_HIGH_WATER	(TTYHOG - 2 * SI_BUFFERSIZE)
8110015Speter
8210015Speterenum si_mctl { GET, SET, BIS, BIC };
8310015Speter
8412502Sjulian#ifdef JREMOD
8512517Sjulian#ifdef DEVFS
8612517Sjulian#include <sys/devfsext.h>
8712517Sjulian#endif /*DEVFS*/
8812502Sjulian#define CDEV_MAJOR 68
8912502Sjulian#endif /*JREMOD*/
9012502Sjulian
9112502Sjulian
9210015Speterstatic void si_command __P((struct si_port *, int, int));
9310015Speterstatic int si_modem __P((struct si_port *, enum si_mctl, int));
9410015Speterstatic void si_write_enable __P((struct si_port *, int));
9510015Speterstatic int si_Sioctl __P((dev_t, int, caddr_t, int, struct proc *));
9610015Speterstatic void si_start __P((struct tty *));
9710015Speterstatic void si_lstart __P((struct si_port *));
9810015Speterstatic void si_disc_optim __P((struct tty *tp, struct termios *t,
9910015Speter					struct si_port *pp));
10010015Speterstatic void sihardclose __P((struct si_port *pp));
10110015Speterstatic void sidtrwakeup __P((void *chan));
10210015Speter
10310015Speterint	siparam __P((struct tty *, struct termios *));
10410015Speter
10510708Speterextern	void	si_registerdev __P((struct isa_device *id));
10610708Speterextern	int	siprobe __P((struct isa_device *id));
10710708Speterextern	int	siattach __P((struct isa_device *id));
10810708Speterstatic	void	si_modem_state __P((struct si_port *pp, struct tty *tp, int hi_ip));
10910708Speter
11012174Speter#ifdef SI_DEBUG		/* use: ``options "SI_DEBUG"'' in your config file */
11112174Speter/* XXX: should be varargs, I know.. but where's vprintf()? */
11212174Speterstatic	void	si_dprintf __P((/* struct si_port *pp, int flags, char *str, int a1, int a2, int a3, int a4, int a5, int a6 */));
11310708Speterstatic	char	*si_mctl2str __P((enum si_mctl cmd));
11410708Speter#define	DPRINT(x)	si_dprintf x
11510708Speter#else
11610708Speter#define	DPRINT(x)	/* void */
11710708Speter#endif
11810708Speter
11910962Speterstatic int si_Nports;
12010962Speterstatic int si_Nmodules;
12110962Speterstatic int si_debug = 0;	/* data, not bss, so it's patchable */
12210015Speter
12310962Speterstatic struct tty *si_tty;
12410962Speter
12512174Speter/* where the firmware lives; defined in si_code.c */
12610015Speterextern int si_dsize;
12710015Speterextern unsigned char si_download[];
12810015Speter
12910044Speterstruct si_softc {
13010044Speter	int 		sc_type;	/* adapter type */
13110044Speter	char 		*sc_typename;	/* adapter type string */
13210044Speter
13310044Speter	struct si_port	*sc_ports;	/* port structures for this card */
13410044Speter
13510044Speter	caddr_t		sc_paddr;	/* physical addr of iomem */
13610044Speter	caddr_t		sc_maddr;	/* kvaddr of iomem */
13710044Speter	int		sc_nport;	/* # ports on this card */
13810044Speter	int		sc_irq;		/* copy of attach irq */
13910044Speter	int		sc_eisa_iobase;	/* EISA io port address */
14010044Speter	int		sc_eisa_irqbits;
14110044Speter	struct kern_devconf sc_kdc;
14210044Speter};
14310044Speterstruct si_softc si_softc[NSI];		/* up to 4 elements */
14410044Speter
14512174Speter#ifndef B2000	/* not standard, but the hardware knows it. */
14610015Speter# define B2000 2000
14710015Speter#endif
14810015Speterstatic struct speedtab bdrates[] = {
14910015Speter	B75,	CLK75,		/* 0x0 */
15010015Speter	B110,	CLK110,		/* 0x1 */
15110015Speter	B150,	CLK150,		/* 0x3 */
15210015Speter	B300,	CLK300,		/* 0x4 */
15310015Speter	B600,	CLK600,		/* 0x5 */
15410015Speter	B1200,	CLK1200,	/* 0x6 */
15510015Speter	B2000,	CLK2000,	/* 0x7 */
15610015Speter	B2400,	CLK2400,	/* 0x8 */
15710015Speter	B4800,	CLK4800,	/* 0x9 */
15810015Speter	B9600,	CLK9600,	/* 0xb */
15910015Speter	B19200,	CLK19200,	/* 0xc */
16010015Speter	B38400, CLK38400,	/* 0x2 (out of order!) */
16110015Speter	B57600, CLK57600,	/* 0xd */
16210015Speter	B115200, CLK110,	/* 0x1 (dupe!, 110 baud on "si") */
16310015Speter	-1,	-1
16410015Speter};
16510015Speter
16610015Speter
16710015Speter/* populated with approx character/sec rates - translated at card
16810015Speter * initialisation time to chars per tick of the clock */
16910015Speterstatic int done_chartimes = 0;
17010015Speterstatic struct speedtab chartimes[] = {
17110015Speter	B75,	8,
17210015Speter	B110,	11,
17310015Speter	B150,	15,
17410015Speter	B300,	30,
17510015Speter	B600,	60,
17610015Speter	B1200,	120,
17710015Speter	B2000,	200,
17810015Speter	B2400,	240,
17910015Speter	B4800,	480,
18010015Speter	B9600,	960,
18110015Speter	B19200,	1920,
18210015Speter	B38400, 3840,
18310015Speter	B57600, 5760,
18410015Speter	B115200, 11520,
18510015Speter	-1,	-1
18610015Speter};
18710015Speterstatic volatile int in_intr = 0;	/* Inside interrupt handler? */
18810015Speter
18910047Speterstatic int si_default_rate =	TTYDEF_SPEED;
19010047Speterstatic int si_default_iflag =	0;
19110047Speterstatic int si_default_oflag =	0;
19210047Speterstatic int si_default_lflag =	0;
19310047Speter#ifdef SI_DEF_HWFLOW
19410047Speterstatic int si_default_cflag =	TTYDEF_CFLAG | CRTSCTS;
19510047Speter#else
19610047Speterstatic int si_default_cflag =	TTYDEF_CFLAG;
19710047Speter#endif
19810047Speter
19910015Speter#ifdef POLL
20010015Speter#define	POLL_INTERVAL	(hz/2)
20110015Speterstatic int init_finished = 0;
20212174Speterstatic int fastpoll = 0;
20310015Speterstatic void si_poll __P((void *));
20410015Speter#endif
20510015Speter
20610015Speter/*
20710015Speter * Array of adapter types and the corresponding RAM size. The order of
20810015Speter * entries here MUST match the ordinal of the adapter type.
20910015Speter */
21010015Speterstatic char *si_type[] = {
21110015Speter	"EMPTY",
21210015Speter	"SIHOST",
21310015Speter	"SI2",				/* MCA */
21410015Speter	"SIHOST2",
21510015Speter	"SIEISA",
21610015Speter};
21710015Speter
21810015Speter
21910015Speterstatic struct kern_devconf si_kdc[NSI] = { {
22010015Speter	0, 0, 0,		/* filled in by dev_attach */
22110015Speter	"si", 0, { MDDT_ISA, 0, "tty" },
22210015Speter	isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
22310015Speter	&kdc_isa0,		/* parent */
22410015Speter	0,			/* parent data */
22510015Speter	DC_UNCONFIGURED,	/* state */
22610015Speter	"Specialix SI/XIO Host adapter",
22710015Speter	DC_CLS_SERIAL,		/* class */
22810015Speter} };
22910015Speter
23010015Spetervoid
23110015Spetersi_registerdev(id)
23210015Speter	struct isa_device *id;
23310015Speter{
23410015Speter	if (id->id_unit != 0) {
23510015Speter		si_kdc[id->id_unit] = si_kdc[0];	/* struct copy */
23610015Speter	}
23710015Speter	si_kdc[id->id_unit].kdc_unit = id->id_unit;
23810015Speter	si_kdc[id->id_unit].kdc_isa = id;
23912174Speter	si_kdc[id->id_unit].kdc_state = DC_UNCONFIGURED;
24010015Speter	dev_attach(&si_kdc[id->id_unit]);
24110015Speter}
24210015Speter
24310015Speter/* Look for a valid board at the given mem addr */
24410015Speterint
24510015Spetersiprobe(id)
24610015Speter	struct isa_device *id;
24710015Speter{
24810015Speter	struct si_softc *sc;
24910015Speter	int type;
25010015Speter	u_int i, ramsize;
25110015Speter	volatile BYTE was, *ux;
25210015Speter	volatile unsigned char *maddr;
25310015Speter	unsigned char *paddr;
25410015Speter
25510015Speter	si_registerdev(id);
25610015Speter
25710015Speter	maddr = id->id_maddr;		/* virtual address... */
25810015Speter	paddr = (caddr_t)vtophys(id->id_maddr);	/* physical address... */
25910015Speter
26012496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: probe at virtual=0x%x physical=0x%x\n",
26112496Speter		id->id_unit, id->id_maddr, paddr));
26210015Speter
26310015Speter	/*
26410015Speter	 * this is a lie, but it's easier than trying to handle caching
26510015Speter	 * and ram conflicts in the >1M and <16M region.
26610015Speter	 */
26710015Speter	if ((caddr_t)paddr < (caddr_t)IOM_BEGIN ||
26810015Speter	    (caddr_t)paddr >= (caddr_t)IOM_END) {
26912174Speter		printf("si%d: iomem (%lx) out of range\n",
27012174Speter			id->id_unit, (long)paddr);
27110015Speter		return(0);
27210015Speter	}
27310015Speter
27410015Speter	if (id->id_unit >= NSI) {
27510015Speter		/* THIS IS IMPOSSIBLE */
27610015Speter		return(0);
27710015Speter	}
27810015Speter
27910015Speter	if (((u_int)paddr & 0x7fff) != 0) {
28010015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
28110015Speter			"si%d: iomem (%x) not on 32k boundary\n",
28210015Speter			id->id_unit, paddr));
28310015Speter		return(0);
28410015Speter	}
28510015Speter
28610015Speter
28710015Speter	for (i=0; i < NSI; i++) {
28810015Speter		if ((sc = &si_softc[i]) == NULL)
28910015Speter			continue;
29010015Speter		if ((caddr_t)sc->sc_paddr == (caddr_t)paddr) {
29110015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
29210015Speter				"si%d: iomem (%x) already configured to si%d\n",
29310015Speter				id->id_unit, sc->sc_paddr, i));
29410015Speter			return(0);
29510015Speter		}
29610015Speter	}
29710015Speter
29810015Speter#if NEISA > 0
29910015Speter	if (id->id_iobase > 0x0fff) {	/* EISA card */
30010015Speter		int irq, port;
30110015Speter		unsigned long base;
30210015Speter		int eisa_irqs[] = { 0,IRQ1,IRQ2,IRQ3,IRQ4,IRQ5,IRQ6,IRQ7,
30310015Speter			IRQ8,IRQ9,IRQ10,IRQ11,IRQ12,IRQ13,IRQ14,IRQ15 };
30410015Speter
30510015Speter		port = id->id_iobase;
30610015Speter		base = (inb(port+1) << 24) | (inb(port) << 16);
30710015Speter		irq  = ((inb(port+2) >> 4) & 0xf);
30810015Speter
30910015Speter		id->id_irq = eisa_irqs[irq];
31010015Speter
31110015Speter		DPRINT((0, DBG_AUTOBOOT,
31212496Speter		    "si%d: EISA base %x, irq %x, id_irq %x, port %x\n",
31310015Speter		    id->id_unit, base, irq, id->id_irq, port));
31410015Speter
31510015Speter		if ((id->id_irq&(IRQ1|IRQ2|IRQ8|IRQ13)) != 0)
31610015Speter			goto bad_irq;
31710015Speter
31810015Speter		id->id_iobase &= 0xf000;
31910015Speter		id->id_iosize  = 0x0fff;
32010015Speter
32110015Speter		type = EISA;
32210015Speter		outb(p+2, (BYTE)irq << 4);
32310015Speter
32410015Speter		sc->sc_eisa_iobase = p;
32510015Speter		sc->sc_eisa_irqbits = irq << 4;
32610015Speter		ramsize = SIEISA_RAMSIZE;
32710015Speter		goto got_card;
32810015Speter	}
32910015Speter#endif
33010015Speter
33110015Speter	/* Is there anything out there? (0x17 is just an arbitrary number) */
33210015Speter	*maddr = 0x17;
33310015Speter	if (*maddr != 0x17) {
33410015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
33510015Speter			"si%d: 0x17 check fail at phys 0x%x\n",
33610015Speter			id->id_unit, paddr));
33710015Speterfail:
33810015Speter		return(0);
33910015Speter	}
34010015Speter	/*
34110015Speter	 * OK, now to see if whatever responded is really an SI card.
34210015Speter	 * Try for a MK II first (SIHOST2)
34310015Speter	 */
34410015Speter	for (i=SIPLSIG; i<SIPLSIG+8; i++)
34510015Speter		if ((*(maddr+i) & 7) != (~(BYTE)i & 7))
34610015Speter			goto try_mk1;
34710015Speter
34810015Speter	/* It must be an SIHOST2 */
34910015Speter	*(maddr + SIPLRESET) = 0;
35010015Speter	*(maddr + SIPLIRQCLR) = 0;
35110015Speter	*(maddr + SIPLIRQSET) = 0x10;
35210015Speter	type = SIHOST2;
35310015Speter	ramsize = SIHOST2_RAMSIZE;
35410015Speter	goto got_card;
35510015Speter
35610015Speter	/*
35710015Speter	 * Its not a MK II, so try for a MK I (SIHOST)
35810015Speter	 */
35910015Spetertry_mk1:
36010015Speter	*(maddr+SIRESET) = 0x0;		/* reset the card */
36110015Speter	*(maddr+SIINTCL) = 0x0;		/* clear int */
36210015Speter	*(maddr+SIRAM) = 0x17;
36310015Speter	if (*(maddr+SIRAM) != (BYTE)0x17)
36410015Speter		goto fail;
36510015Speter	*(maddr+0x7ff8) = 0x17;
36610015Speter	if (*(maddr+0x7ff8) != (BYTE)0x17) {
36710015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
36810015Speter			"si%d: 0x17 check fail at phys 0x%x = 0x%x\n",
36910015Speter			id->id_unit, paddr+0x77f8, *(maddr+0x77f8)));
37010015Speter		goto fail;
37110015Speter	}
37210015Speter
37310015Speter	/* It must be an SIHOST (maybe?) - there must be a better way XXXX */
37410015Speter	type = SIHOST;
37510015Speter	ramsize = SIHOST_RAMSIZE;
37610015Speter
37710015Spetergot_card:
37812496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: found type %d card, try memory test\n",
37912496Speter		id->id_unit, type));
38010015Speter	/* Try the acid test */
38110015Speter	ux = (BYTE *)(maddr + SIRAM);
38210015Speter	for (i=0; i<ramsize; i++, ux++)
38310015Speter		*ux = (BYTE)(i&0xff);
38410015Speter	ux = (BYTE *)(maddr + SIRAM);
38510015Speter	for (i=0; i<ramsize; i++, ux++) {
38610015Speter		if ((was = *ux) != (BYTE)(i&0xff)) {
38710015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
38812174Speter				"si%d: match fail at phys 0x%x, was %x should be %x\n",
38910015Speter				id->id_unit, paddr+i, was, i&0xff));
39010015Speter			goto fail;
39110015Speter		}
39210015Speter	}
39310015Speter
39410015Speter	/* clear out the RAM */
39510015Speter	ux = (BYTE *)(maddr + SIRAM);
39610015Speter	for (i=0; i<ramsize; i++)
39710015Speter		*ux++ = 0;
39810015Speter	ux = (BYTE *)(maddr + SIRAM);
39910015Speter	for (i=0; i<ramsize; i++) {
40010015Speter		if ((was = *ux++) != 0) {
40110015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
40212174Speter				"si%d: clear fail at phys 0x%x, was %x\n",
40310015Speter				id->id_unit, paddr+i, was));
40410015Speter			goto fail;
40510015Speter		}
40610015Speter	}
40710015Speter
40810015Speter	/*
40910015Speter	 * Success, we've found a valid board, now fill in
41010015Speter	 * the adapter structure.
41110015Speter	 */
41210015Speter	switch (type) {
41310015Speter	case SIHOST2:
41410015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
41510015Speterbad_irq:
41610015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
41710015Speter				"si%d: bad IRQ value - %d\n",
41810015Speter				id->id_unit, id->id_irq));
41910015Speter			return(0);
42010015Speter		}
42110015Speter		id->id_msize = SIHOST2_MEMSIZE;
42210015Speter		break;
42310015Speter	case SIHOST:
42410015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
42510015Speter			goto bad_irq;
42610015Speter		}
42710015Speter		id->id_msize = SIHOST_MEMSIZE;
42810015Speter		break;
42910015Speter	case SIEISA:
43010015Speter		id->id_msize = SIEISA_MEMSIZE;
43110015Speter		break;
43210015Speter	case SI2:		/* MCA */
43310015Speter	default:
43410015Speter		printf("si%d: %s not supported\n", id->id_unit, si_type[type]);
43510015Speter		return(0);
43610015Speter	}
43710015Speter	si_softc[id->id_unit].sc_type = type;
43810015Speter	si_softc[id->id_unit].sc_typename = si_type[type];
43910015Speter	return(-1);	/* -1 == found */
44010015Speter}
44110015Speter
44210015Speter/*
44310015Speter * Attach the device.  Initialize the card.
44410015Speter */
44510015Speterint
44610015Spetersiattach(id)
44710015Speter	struct isa_device *id;
44810015Speter{
44910015Speter	int unit = id->id_unit;
45010015Speter	struct si_softc *sc = &si_softc[unit];
45110015Speter	struct si_port *pp;
45210015Speter	volatile struct si_channel *ccbp;
45310015Speter	volatile struct si_reg *regp;
45410015Speter	volatile caddr_t maddr;
45510015Speter	struct si_module *modp;
45610015Speter	struct tty *tp;
45710015Speter	struct speedtab *spt;
45810015Speter	int nmodule, nport, x, y;
45912174Speter	int uart_type;
46010015Speter
46112496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", id->id_unit));
46210015Speter
46310015Speter	sc->sc_paddr = (caddr_t)vtophys(id->id_maddr);
46410015Speter	sc->sc_maddr = id->id_maddr;
46510015Speter	sc->sc_irq = id->id_irq;
46610015Speter
46710015Speter	sc->sc_ports = NULL;			/* mark as uninitialised */
46810015Speter
46910015Speter	maddr = sc->sc_maddr;
47010015Speter
47110015Speter	/*
47210015Speter	 * OK, now lets download the firmware and try and boot the CPU..
47310015Speter	 */
47410015Speter
47512496Speter	DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n",
47612496Speter		id->id_unit, si_dsize));
47710015Speter	bcopy(si_download, maddr, si_dsize);
47810015Speter
47910015Speter	switch (sc->sc_type) {
48010015Speter	case SIEISA:
48110015Speter#if NEISA > 0
48210015Speter		/* modify the Z280 firmware to tell it that it's on an EISA */
48310015Speter		*(maddr+0x42) = 1;
48410015Speter		outb(sc->sc_eisa_iobase+2, sc->sc_eisa_irqbits | 4);
48510015Speter		(void)inb(sc->sc_eisa_iobase+3); /* reset interrupt */
48610015Speter		break;
48710015Speter#endif	/* fall-through if not EISA */
48810015Speter	case SI2:
48912174Speter		/*
49012174Speter		 * must get around to converting the code for
49112174Speter		 * these one day, if FreeBSD ever supports it.
49212174Speter		 */
49310015Speter		return 0;
49410015Speter	case SIHOST:
49510015Speter		*(maddr+SIRESET_CL) = 0;
49610015Speter		*(maddr+SIINTCL_CL) = 0;
49710015Speter		break;
49810015Speter	case SIHOST2:
49910015Speter		*(maddr+SIPLRESET) = 0x10;
50010015Speter		switch (sc->sc_irq) {
50110015Speter		case IRQ11:
50210015Speter			*(maddr+SIPLIRQ11) = 0x10;
50310015Speter			break;
50410015Speter		case IRQ12:
50510015Speter			*(maddr+SIPLIRQ12) = 0x10;
50610015Speter			break;
50710015Speter		case IRQ15:
50810015Speter			*(maddr+SIPLIRQ15) = 0x10;
50910015Speter			break;
51010015Speter		}
51110015Speter		*(maddr+SIPLIRQCLR) = 0x10;
51210015Speter		break;
51310015Speter	}
51410015Speter
51510015Speter	DELAY(1000000);			/* wait around for a second */
51610015Speter
51710015Speter	regp = (struct si_reg *)maddr;
51810015Speter	y = 0;
51910015Speter					/* wait max of 5 sec for init OK */
52010015Speter	while (regp->initstat == 0 && y++ < 10) {
52110015Speter		DELAY(500000);
52210015Speter	}
52310015Speter	switch (regp->initstat) {
52410015Speter	case 0:
52510015Speter		printf("si%d: startup timeout - aborting\n", unit);
52612174Speter		sc->sc_type = SIEMPTY;
52710015Speter		return 0;
52810015Speter	case 1:
52912174Speter			/* set throttle to 125 intr per second */
53010015Speter		regp->int_count = 25000;
53110015Speter			/* rx intr max of 25 timer per second */
53210015Speter		regp->rx_int_count = 4;
53310015Speter		regp->int_pending = 0;		/* no intr pending */
53410015Speter		regp->int_scounter = 0;	/* reset counter */
53510015Speter		break;
53610015Speter	case 0xff:
53710015Speter		/*
53810015Speter		 * No modules found, so give up on this one.
53910015Speter		 */
54010015Speter		printf("si%d: %s - no ports found\n", unit,
54110015Speter			si_type[sc->sc_type]);
54210015Speter		return 0;
54310015Speter	default:
54410015Speter		printf("si%d: Z280 version error - initstat %x\n",
54510015Speter			unit, regp->initstat);
54610015Speter		return 0;
54710015Speter	}
54810015Speter
54910015Speter	/*
55010015Speter	 * First time around the ports just count them in order
55110015Speter	 * to allocate some memory.
55210015Speter	 */
55310015Speter	nport = 0;
55410015Speter	modp = (struct si_module *)(maddr + 0x80);
55510015Speter	for (;;) {
55612174Speter		DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp));
55710015Speter		switch (modp->sm_type & (~MMASK)) {
55810015Speter		case M232:
55910015Speter		case M422:
56010015Speter			DPRINT((0, DBG_DOWNLOAD,
56112174Speter				"si%d: Found 232/422 module, %d ports\n",
56210015Speter				unit, (int)(modp->sm_type & MMASK)));
56310015Speter
56410015Speter			/* this is a firmware issue */
56510015Speter			if (si_Nports == SI_MAXPORTPERCARD) {
56610015Speter				printf("si%d: extra ports ignored\n", unit);
56710015Speter				continue;
56810015Speter			}
56910015Speter
57010015Speter			x = modp->sm_type & MMASK;
57110015Speter			nport += x;
57210015Speter			si_Nports += x;
57310015Speter			si_Nmodules++;
57410015Speter			break;
57510015Speter		default:
57610015Speter			printf("si%d: unknown module type %d\n",
57710015Speter				unit, modp->sm_type);
57810015Speter			break;
57910015Speter		}
58010015Speter		if (modp->sm_next == 0)
58110015Speter			break;
58210015Speter		modp = (struct si_module *)
58310015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
58410015Speter	}
58510015Speter	sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport,
58610015Speter		M_DEVBUF, M_NOWAIT);
58710015Speter	if (sc->sc_ports == 0) {
58810015Spetermem_fail:
58910015Speter		printf("si%d: fail to malloc memory for port structs\n",
59010015Speter			unit);
59110015Speter		return 0;
59210015Speter	}
59310015Speter	bzero(sc->sc_ports, sizeof(struct si_port) * nport);
59410015Speter	sc->sc_nport = nport;
59510015Speter
59610015Speter	/*
59710015Speter	 * allocate tty structures for ports
59810015Speter	 */
59910015Speter	tp = (struct tty *)malloc(sizeof(*tp) * nport, M_DEVBUF, M_NOWAIT);
60010015Speter	if (tp == 0)
60110015Speter		goto mem_fail;
60210015Speter	bzero(tp, sizeof(*tp) * nport);
60310962Speter	si_tty = tp;
60410015Speter
60510015Speter	/* mark the device state as attached */
60610015Speter	si_kdc[unit].kdc_state = DC_BUSY;
60710015Speter
60810015Speter	/*
60910015Speter	 * Scan round the ports again, this time initialising.
61010015Speter	 */
61110015Speter	pp = sc->sc_ports;
61210015Speter	nmodule = 0;
61310015Speter	modp = (struct si_module *)(maddr + 0x80);
61412174Speter	uart_type = 0;
61510015Speter	for (;;) {
61610015Speter		switch (modp->sm_type & (~MMASK)) {
61710015Speter		case M232:
61810015Speter		case M422:
61910015Speter			nmodule++;
62010015Speter			nport = (modp->sm_type & MMASK);
62110015Speter			ccbp = (struct si_channel *)((char *)modp+0x100);
62212174Speter			if (uart_type == 0)
62312174Speter				uart_type = ccbp->type;
62410015Speter			for (x = 0; x < nport; x++, pp++, ccbp++) {
62510015Speter				pp->sp_ccb = ccbp;	/* save the address */
62610015Speter				pp->sp_tty = tp++;
62710015Speter				pp->sp_pend = IDLE_CLOSE;
62810015Speter				pp->sp_state = 0;	/* internal flag */
62910015Speter				pp->sp_dtr_wait = 3 * hz;
63010047Speter				pp->sp_iin.c_iflag = si_default_iflag;
63110047Speter				pp->sp_iin.c_oflag = si_default_oflag;
63210047Speter				pp->sp_iin.c_cflag = si_default_cflag;
63310047Speter				pp->sp_iin.c_lflag = si_default_lflag;
63410015Speter				termioschars(&pp->sp_iin);
63510015Speter				pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed =
63610047Speter					si_default_rate;
63710015Speter				pp->sp_iout = pp->sp_iin;
63810015Speter			}
63910015Speter			break;
64010015Speter		default:
64110015Speter			break;
64210015Speter		}
64310015Speter		if (modp->sm_next == 0) {
64412174Speter			printf("si%d: card: %s, ports: %d, modules: %d (type: %d)\n",
64510015Speter				unit,
64610015Speter				sc->sc_typename,
64710015Speter				sc->sc_nport,
64812174Speter				nmodule,
64912174Speter				uart_type);
65010015Speter			break;
65110015Speter		}
65210015Speter		modp = (struct si_module *)
65310015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
65410015Speter	}
65510015Speter	if (done_chartimes == 0) {
65610015Speter		for (spt = chartimes ; spt->sp_speed != -1; spt++) {
65710015Speter			if ((spt->sp_code /= hz) == 0)
65810015Speter				spt->sp_code = 1;
65910015Speter		}
66010015Speter		done_chartimes = 1;
66110015Speter	}
66212502Sjulian
66310015Speter	return (1);
66410015Speter}
66510015Speter
66610015Speterstruct isa_driver sidriver =
66710015Speter	{ siprobe, siattach, "si" };
66810015Speter
66910015Speter
67010015Speterint
67110015Spetersiopen(dev, flag, mode, p)
67210015Speter	dev_t dev;
67310015Speter	int flag, mode;
67410015Speter	struct proc *p;
67510015Speter{
67610015Speter	int oldspl, error;
67710015Speter	int card, port;
67810015Speter	register struct si_softc *sc;
67910015Speter	register struct tty *tp;
68010015Speter	volatile struct si_channel *ccbp;
68110015Speter	struct si_port *pp;
68210015Speter	int mynor = minor(dev);
68310015Speter
68410015Speter	/* quickly let in /dev/si_control */
68510015Speter	if (IS_CONTROLDEV(mynor)) {
68610015Speter		if (error = suser(p->p_ucred, &p->p_acflag))
68710015Speter			return(error);
68810015Speter		return(0);
68910015Speter	}
69010015Speter
69110015Speter	card = SI_CARD(mynor);
69210015Speter	if (card >= NSI)
69310015Speter		return (ENXIO);
69410015Speter	sc = &si_softc[card];
69510015Speter
69612174Speter	if (sc->sc_type == SIEMPTY) {
69712174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n",
69810015Speter			card, sc->sc_typename));
69910015Speter		return(ENXIO);
70010015Speter	}
70110015Speter
70210015Speter	port = SI_PORT(mynor);
70310015Speter	if (port >= sc->sc_nport) {
70412174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n",
70510015Speter			card, sc->sc_nport));
70610015Speter		return(ENXIO);
70710015Speter	}
70810015Speter
70910015Speter#ifdef	POLL
71010015Speter	/*
71110015Speter	 * We've now got a device, so start the poller.
71210015Speter	 */
71310015Speter	if (init_finished == 0) {
71410015Speter		timeout(si_poll, (caddr_t)0L, POLL_INTERVAL);
71510015Speter		init_finished = 1;
71610015Speter	}
71710015Speter#endif
71810015Speter
71910015Speter	/* initial/lock device */
72010015Speter	if (IS_STATE(mynor)) {
72110015Speter		return(0);
72210015Speter	}
72310015Speter
72410015Speter	pp = sc->sc_ports + port;
72510015Speter	tp = pp->sp_tty;			/* the "real" tty */
72610015Speter	ccbp = pp->sp_ccb;			/* Find control block */
72710015Speter	DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%x,%x,%x,%x)\n",
72810015Speter		dev, flag, mode, p));
72910015Speter
73010015Speter	oldspl = spltty();			/* Keep others out */
73110015Speter	error = 0;
73210015Speter
73310015Speteropen_top:
73410015Speter	while (pp->sp_state & SS_DTR_OFF) {
73510015Speter		error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sidtr", 0);
73610015Speter		if (error != 0)
73710015Speter			goto out;
73810015Speter	}
73910015Speter
74010015Speter	if (tp->t_state & TS_ISOPEN) {
74110015Speter		/*
74210015Speter		 * The device is open, so everything has been initialised.
74310015Speter		 * handle conflicts.
74410015Speter		 */
74510015Speter		if (IS_CALLOUT(mynor)) {
74610015Speter			if (!pp->sp_active_out) {
74710015Speter				error = EBUSY;
74810015Speter				goto out;
74910015Speter			}
75010015Speter		} else {
75110015Speter			if (pp->sp_active_out) {
75210015Speter				if (flag & O_NONBLOCK) {
75310015Speter					error = EBUSY;
75410015Speter					goto out;
75510015Speter				}
75610015Speter				error = tsleep(&pp->sp_active_out,
75710015Speter						TTIPRI|PCATCH, "sibi", 0);
75810015Speter				if (error != 0)
75910015Speter					goto out;
76010015Speter				goto open_top;
76110015Speter			}
76210015Speter		}
76310015Speter		if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
76410015Speter			DPRINT((pp, DBG_OPEN|DBG_FAIL,
76510015Speter				"already open and EXCLUSIVE set\n"));
76610015Speter			error = EBUSY;
76710015Speter			goto out;
76810015Speter		}
76910015Speter	} else {
77010015Speter		/*
77110015Speter		 * The device isn't open, so there are no conflicts.
77210015Speter		 * Initialize it. Avoid sleep... :-)
77310015Speter		 */
77410015Speter		DPRINT((pp, DBG_OPEN, "first open\n"));
77510015Speter		tp->t_oproc = si_start;
77610015Speter		tp->t_param = siparam;
77710015Speter		tp->t_dev = dev;
77810015Speter		tp->t_termios = mynor & SI_CALLOUT_MASK
77910015Speter				? pp->sp_iout : pp->sp_iin;
78010015Speter
78110015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
78210015Speter
78310015Speter		++pp->sp_wopeners;	/* in case of sleep in siparam */
78410015Speter
78510015Speter		error = siparam(tp, &tp->t_termios);
78610015Speter
78710015Speter		--pp->sp_wopeners;
78810015Speter		if (error != 0)
78910015Speter			goto out;
79010015Speter		/* XXX: we should goto_top if siparam slept */
79110015Speter
79210015Speter		ttsetwater(tp);
79310015Speter
79410015Speter		/* set initial DCD state */
79510015Speter		pp->sp_last_hi_ip = ccbp->hi_ip;
79610015Speter		if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) {
79710015Speter			(*linesw[tp->t_line].l_modem)(tp, 1);
79810015Speter		}
79910015Speter	}
80010015Speter
80110015Speter	/* whoops! we beat the close! */
80210015Speter	if (pp->sp_state & SS_CLOSING) {
80310015Speter		/* try and stop it from proceeding to bash the hardware */
80410015Speter		pp->sp_state &= ~SS_CLOSING;
80510015Speter	}
80610015Speter
80710015Speter	/*
80810015Speter	 * Wait for DCD if necessary
80910015Speter	 */
81010015Speter	if (!(tp->t_state & TS_CARR_ON)
81110015Speter	    && !IS_CALLOUT(mynor)
81210015Speter	    && !(tp->t_cflag & CLOCAL)
81310015Speter	    && !(flag & O_NONBLOCK)) {
81410015Speter		++pp->sp_wopeners;
81510015Speter		DPRINT((pp, DBG_OPEN, "sleeping for carrier\n"));
81610015Speter		error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0);
81710015Speter		--pp->sp_wopeners;
81810015Speter		if (error != 0)
81910015Speter			goto out;
82010015Speter		goto open_top;
82110015Speter	}
82210015Speter
82310015Speter	error = (*linesw[tp->t_line].l_open)(dev, tp);
82410015Speter	si_disc_optim(tp, &tp->t_termios, pp);
82510015Speter	if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor))
82610015Speter		pp->sp_active_out = TRUE;
82710015Speter
82810015Speter	pp->sp_state |= SS_OPEN;	/* made it! */
82910015Speter
83010015Speterout:
83110015Speter	splx(oldspl);
83210015Speter
83310015Speter	DPRINT((pp, DBG_OPEN, "leaving siopen\n"));
83410015Speter
83510015Speter	if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0)
83610015Speter		sihardclose(pp);
83710015Speter
83810015Speter	return(error);
83910015Speter}
84010015Speter
84110015Speterint
84210015Spetersiclose(dev, flag, mode, p)
84310015Speter	dev_t dev;
84410015Speter	int flag, mode;
84510015Speter	struct proc *p;
84610015Speter{
84710015Speter	register struct si_port *pp;
84810015Speter	register struct tty *tp;
84910015Speter	int oldspl;
85010015Speter	int error = 0;
85110015Speter	int mynor = minor(dev);
85210015Speter
85310015Speter	if (IS_SPECIAL(mynor))
85410015Speter		return(0);
85510015Speter
85610015Speter	oldspl = spltty();
85710015Speter
85810015Speter	pp = MINOR2PP(mynor);
85910015Speter	tp = pp->sp_tty;
86010015Speter
86110015Speter	DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%x,%x,%x,%x) sp_state:%x\n",
86210015Speter		dev, flag, mode, p, pp->sp_state));
86310015Speter
86410015Speter	/* did we sleep and loose a race? */
86510015Speter	if (pp->sp_state & SS_CLOSING) {
86610015Speter		/* error = ESOMETING? */
86710015Speter		goto out;
86810015Speter	}
86910015Speter
87010015Speter	/* begin race detection.. */
87110015Speter	pp->sp_state |= SS_CLOSING;
87210015Speter
87310015Speter	si_write_enable(pp, 0);		/* block writes for ttywait() */
87410015Speter
87510015Speter	/* THIS MAY SLEEP IN TTYWAIT!!! */
87610015Speter	(*linesw[tp->t_line].l_close)(tp, flag);
87710015Speter
87810015Speter	si_write_enable(pp, 1);
87910015Speter
88010015Speter	/* did we sleep and somebody started another open? */
88110015Speter	if (!(pp->sp_state & SS_CLOSING)) {
88210015Speter		/* error = ESOMETING? */
88310015Speter		goto out;
88410015Speter	}
88510015Speter	/* ok. we are now still on the right track.. nuke the hardware */
88610015Speter
88710015Speter	if (pp->sp_state & SS_LSTART) {
88810015Speter		untimeout((timeout_func_t)si_lstart, (caddr_t)pp);
88910015Speter		pp->sp_state &= ~SS_LSTART;
89010015Speter	}
89110015Speter
89210015Speter	sistop(tp, FREAD | FWRITE);
89310015Speter
89410015Speter	sihardclose(pp);
89510015Speter	ttyclose(tp);
89610015Speter	pp->sp_state &= ~SS_OPEN;
89710015Speter
89810015Speterout:
89910015Speter	DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n"));
90010015Speter	splx(oldspl);
90110015Speter	return(error);
90210015Speter}
90310015Speter
90410015Speterstatic void
90510015Spetersihardclose(pp)
90610015Speter	struct si_port *pp;
90710015Speter{
90810015Speter	int oldspl;
90910015Speter	struct tty *tp;
91010015Speter	volatile struct si_channel *ccbp;
91110015Speter
91210015Speter	oldspl = spltty();
91310015Speter
91410015Speter	tp = pp->sp_tty;
91510015Speter	ccbp = pp->sp_ccb;			/* Find control block */
91610015Speter	if (tp->t_cflag & HUPCL
91710015Speter	    || !pp->sp_active_out
91810015Speter	       && !(ccbp->hi_ip & IP_DCD)
91910015Speter	       && !(pp->sp_iin.c_cflag && CLOCAL)
92010015Speter	    || !(tp->t_state & TS_ISOPEN)) {
92110015Speter
92210015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
92310015Speter		(void) si_command(pp, FCLOSE, SI_NOWAIT);
92410015Speter
92510015Speter		if (pp->sp_dtr_wait != 0) {
92610015Speter			timeout(sidtrwakeup, pp, pp->sp_dtr_wait);
92710015Speter			pp->sp_state |= SS_DTR_OFF;
92810015Speter		}
92910015Speter
93010015Speter	}
93110015Speter	pp->sp_active_out = FALSE;
93210015Speter	wakeup((caddr_t)&pp->sp_active_out);
93310015Speter	wakeup(TSA_CARR_ON(tp));
93410015Speter
93510015Speter	splx(oldspl);
93610015Speter}
93710015Speter
93810015Speter
93910015Speter/*
94010015Speter * called at splsoftclock()...
94110015Speter */
94210015Speterstatic void
94310015Spetersidtrwakeup(chan)
94410015Speter	void *chan;
94510015Speter{
94610015Speter	struct si_port *pp;
94710015Speter	int oldspl;
94810015Speter
94910015Speter	oldspl = spltty();
95010015Speter
95110015Speter	pp = (struct si_port *)chan;
95210015Speter	pp->sp_state &= ~SS_DTR_OFF;
95310015Speter	wakeup(&pp->sp_dtr_wait);
95410015Speter
95510015Speter	splx(oldspl);
95610015Speter}
95710015Speter
95810015Speter/*
95910015Speter * User level stuff - read and write
96010015Speter */
96110015Speterint
96210015Spetersiread(dev, uio, flag)
96310015Speter	register dev_t dev;
96410015Speter	struct uio *uio;
96510015Speter	int flag;
96610015Speter{
96710015Speter	register struct tty *tp;
96810015Speter	int mynor = minor(dev);
96910015Speter
97010015Speter	if (IS_SPECIAL(mynor)) {
97110015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_READ, "siread(CONTROLDEV!!)\n"));
97210015Speter		return(ENODEV);
97310015Speter	}
97410015Speter	tp = MINOR2TP(mynor);
97510015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_READ,
97610015Speter		"siread(%x,%x,%x)\n", dev, uio, flag));
97710015Speter	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
97810015Speter}
97910015Speter
98010015Speter
98110015Speterint
98210015Spetersiwrite(dev, uio, flag)
98310015Speter	dev_t dev;
98410015Speter	struct uio *uio;
98510015Speter	int flag;
98610015Speter{
98710015Speter	register struct si_port *pp;
98810015Speter	register struct tty *tp;
98910015Speter	int error = 0;
99010015Speter	int mynor = minor(dev);
99110015Speter	int oldspl;
99210015Speter
99310015Speter	if (IS_SPECIAL(mynor)) {
99410015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n"));
99510015Speter		return(ENODEV);
99610015Speter	}
99710015Speter	pp = MINOR2PP(mynor);
99810015Speter	tp = pp->sp_tty;
99910015Speter	DPRINT((pp, DBG_WRITE, "siwrite(%x,%x,%x)\n", dev, uio, flag));
100010015Speter
100110015Speter	oldspl = spltty();
100210015Speter	/*
100310015Speter	 * If writes are currently blocked, wait on the "real" tty
100410015Speter	 */
100510015Speter	while (pp->sp_state & SS_BLOCKWRITE) {
100610015Speter		pp->sp_state |= SS_WAITWRITE;
100710015Speter		DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n"));
100810015Speter		if (error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH,
100910015Speter				     "siwrite", 0))
101010015Speter			goto out;
101110015Speter	}
101210015Speter
101310015Speter	error = (*linesw[tp->t_line].l_write)(tp, uio, flag);
101410015Speterout:
101510015Speter	splx(oldspl);
101610015Speter	return (error);
101710015Speter}
101810015Speter
101910015Speter
102010015Speterstruct tty *
102110015Spetersidevtotty(dev_t dev)
102210015Speter{
102310015Speter	struct si_port *pp;
102410015Speter	int mynor = minor(dev);
102510015Speter	struct si_softc *sc = &si_softc[SI_CARD(mynor)];
102610015Speter
102710015Speter	if (IS_SPECIAL(mynor))
102810015Speter		return(NULL);
102910015Speter	if (SI_PORT(mynor) >= sc->sc_nport)
103010015Speter		return(NULL);
103110015Speter	pp = MINOR2PP(mynor);
103210015Speter	return (pp->sp_tty);
103310015Speter}
103410015Speter
103510015Speterint
103610015Spetersiioctl(dev, cmd, data, flag, p)
103710015Speter	dev_t dev;
103810015Speter	int cmd;
103910015Speter	caddr_t data;
104010015Speter	int flag;
104110015Speter	struct proc *p;
104210015Speter{
104310015Speter	struct si_port *pp;
104410015Speter	register struct tty *tp;
104510015Speter	int error;
104610015Speter	int mynor = minor(dev);
104710015Speter	int oldspl;
104810015Speter	int blocked = 0;
104910015Speter#if defined(COMPAT_43)
105010015Speter	int oldcmd;
105110015Speter	struct termios term;
105210015Speter#endif
105310015Speter
105410015Speter	if (IS_SI_IOCTL(cmd))
105510015Speter		return(si_Sioctl(dev, cmd, data, flag, p));
105610015Speter
105710015Speter	pp = MINOR2PP(mynor);
105810015Speter	tp = pp->sp_tty;
105910015Speter
106010015Speter	DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%x,%x,%x,%x)\n",
106110015Speter		dev, cmd, data, flag));
106210015Speter	if (IS_STATE(mynor)) {
106310015Speter		struct termios *ct;
106410015Speter
106510015Speter		switch (mynor & SI_STATE_MASK) {
106610015Speter		case SI_INIT_STATE_MASK:
106710015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
106810015Speter			break;
106910015Speter		case SI_LOCK_STATE_MASK:
107010015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
107110015Speter			break;
107210015Speter		default:
107310015Speter			return (ENODEV);
107410015Speter		}
107510015Speter		switch (cmd) {
107610015Speter		case TIOCSETA:
107710015Speter			error = suser(p->p_ucred, &p->p_acflag);
107810015Speter			if (error != 0)
107910015Speter				return (error);
108010015Speter			*ct = *(struct termios *)data;
108110015Speter			return (0);
108210015Speter		case TIOCGETA:
108310015Speter			*(struct termios *)data = *ct;
108410015Speter			return (0);
108510015Speter		case TIOCGETD:
108610015Speter			*(int *)data = TTYDISC;
108710015Speter			return (0);
108810015Speter		case TIOCGWINSZ:
108910015Speter			bzero(data, sizeof(struct winsize));
109010015Speter			return (0);
109110015Speter		default:
109210015Speter			return (ENOTTY);
109310015Speter		}
109410015Speter	}
109510015Speter	/*
109610015Speter	 * Do the old-style ioctl compat routines...
109710015Speter	 */
109810015Speter#if defined(COMPAT_43)
109910015Speter	term = tp->t_termios;
110010015Speter	oldcmd = cmd;
110110015Speter	error = ttsetcompat(tp, &cmd, data, &term);
110210015Speter	if (error != 0)
110310015Speter		return (error);
110410015Speter	if (cmd != oldcmd)
110510015Speter		data = (caddr_t)&term;
110610015Speter#endif
110710015Speter	/*
110810015Speter	 * Do the initial / lock state business
110910015Speter	 */
111010015Speter	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
111110015Speter		int     cc;
111210015Speter		struct termios *dt = (struct termios *)data;
111310015Speter		struct termios *lt = mynor & SI_CALLOUT_MASK
111410015Speter				     ? &pp->sp_lout : &pp->sp_lin;
111510015Speter
111610015Speter		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
111710015Speter			| (dt->c_iflag & ~lt->c_iflag);
111810015Speter		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
111910015Speter			| (dt->c_oflag & ~lt->c_oflag);
112010015Speter		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
112110015Speter			| (dt->c_cflag & ~lt->c_cflag);
112210015Speter		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
112310015Speter			| (dt->c_lflag & ~lt->c_lflag);
112410015Speter		for (cc = 0; cc < NCCS; ++cc)
112510015Speter			if (lt->c_cc[cc] != 0)
112610015Speter				dt->c_cc[cc] = tp->t_cc[cc];
112710015Speter		if (lt->c_ispeed != 0)
112810015Speter			dt->c_ispeed = tp->t_ispeed;
112910015Speter		if (lt->c_ospeed != 0)
113010015Speter			dt->c_ospeed = tp->t_ospeed;
113110015Speter	}
113210015Speter
113310015Speter	/*
113410015Speter	 * Block user-level writes to give the ttywait()
113510015Speter	 * a chance to completely drain for commands
113610015Speter	 * that require the port to be in a quiescent state.
113710015Speter	 */
113810015Speter	switch (cmd) {
113910015Speter	case TIOCSETAW: case TIOCSETAF:
114010015Speter	case TIOCDRAIN: case TIOCSETP:
114110015Speter		blocked++;	/* block writes for ttywait() and siparam() */
114210015Speter		si_write_enable(pp, 0);
114310015Speter	}
114410015Speter
114510015Speter	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
114610015Speter	if (error >= 0)
114710015Speter		goto out;
114810015Speter
114910015Speter	oldspl = spltty();
115010015Speter
115110015Speter	error = ttioctl(tp, cmd, data, flag);
115210015Speter	si_disc_optim(tp, &tp->t_termios, pp);
115310015Speter	if (error >= 0)
115410015Speter		goto outspl;
115510015Speter
115610015Speter	switch (cmd) {
115710015Speter	case TIOCSBRK:
115810015Speter		si_command(pp, SBREAK, SI_NOWAIT);
115910015Speter		break;
116010015Speter	case TIOCCBRK:
116110015Speter		si_command(pp, EBREAK, SI_NOWAIT);
116210015Speter		break;
116310015Speter	case TIOCSDTR:
116410015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
116510015Speter		break;
116610015Speter	case TIOCCDTR:
116710015Speter		(void) si_modem(pp, SET, 0);
116810015Speter		break;
116910015Speter	case TIOCMSET:
117010015Speter		(void) si_modem(pp, SET, *(int *)data);
117110015Speter		break;
117210015Speter	case TIOCMBIS:
117310015Speter		(void) si_modem(pp, BIS, *(int *)data);
117410015Speter		break;
117510015Speter	case TIOCMBIC:
117610015Speter		(void) si_modem(pp, BIC, *(int *)data);
117710015Speter		break;
117810015Speter	case TIOCMGET:
117910015Speter		*(int *)data = si_modem(pp, GET, 0);
118010015Speter		break;
118110015Speter	case TIOCMSDTRWAIT:
118210015Speter		/* must be root since the wait applies to following logins */
118310015Speter		error = suser(p->p_ucred, &p->p_acflag);
118410015Speter		if (error != 0) {
118510015Speter			goto outspl;
118610015Speter		}
118710015Speter		pp->sp_dtr_wait = *(int *)data * hz / 100;
118810015Speter		break;
118910015Speter	case TIOCMGDTRWAIT:
119010015Speter		*(int *)data = pp->sp_dtr_wait * 100 / hz;
119110015Speter		break;
119210015Speter
119310015Speter	default:
119410015Speter		error = ENOTTY;
119510015Speter	}
119610015Speter	error = 0;
119710015Speteroutspl:
119810015Speter	splx(oldspl);
119910015Speterout:
120010015Speter	DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error));
120110015Speter	if (blocked)
120210015Speter		si_write_enable(pp, 1);
120310015Speter	return(error);
120410015Speter}
120510015Speter
120610015Speter/*
120710015Speter * Handle the Specialix ioctls. All MUST be called via the CONTROL device
120810015Speter */
120910015Speterstatic int
121010015Spetersi_Sioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
121110015Speter{
121210015Speter	struct si_softc *xsc;
121310015Speter	register struct si_port *xpp;
121410015Speter	volatile struct si_reg *regp;
121510015Speter	struct si_tcsi *dp;
121610044Speter	struct si_pstat *sps;
121711872Sphk	int *ip, error = 0;
121810015Speter	int oldspl;
121910015Speter	int card, port;
122010015Speter	int mynor = minor(dev);
122110015Speter
122210015Speter	DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%x,%x,%x,%x)\n",
122310015Speter		dev, cmd, data, flag));
122410015Speter
122510044Speter#if 1
122610044Speter	DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT));
122710044Speter	DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB));
122810044Speter	DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY));
122910044Speter#endif
123010044Speter
123110015Speter	if (!IS_CONTROLDEV(mynor)) {
123210015Speter		DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n"));
123310015Speter		return(ENODEV);
123410015Speter	}
123510015Speter
123610015Speter	oldspl = spltty();	/* better safe than sorry */
123710015Speter
123810015Speter	ip = (int *)data;
123910015Speter
124010015Speter#define SUCHECK if (error = suser(p->p_ucred, &p->p_acflag)) goto out
124110015Speter
124210015Speter	switch (cmd) {
124310015Speter	case TCSIPORTS:
124410015Speter		*ip = si_Nports;
124510015Speter		goto out;
124610015Speter	case TCSIMODULES:
124710015Speter		*ip = si_Nmodules;
124810015Speter		goto out;
124910015Speter	case TCSISDBG_ALL:
125010015Speter		SUCHECK;
125110015Speter		si_debug = *ip;
125210015Speter		goto out;
125310015Speter	case TCSIGDBG_ALL:
125410015Speter		*ip = si_debug;
125510015Speter		goto out;
125610015Speter	default:
125710015Speter		/*
125810015Speter		 * Check that a controller for this port exists
125910015Speter		 */
126010044Speter
126110044Speter		/* may also be a struct si_pstat, a superset of si_tcsi */
126210044Speter
126310015Speter		dp = (struct si_tcsi *)data;
126410044Speter		sps = (struct si_pstat *)data;
126510015Speter		card = dp->tc_card;
126610015Speter		xsc = &si_softc[card];	/* check.. */
126712174Speter		if (card < 0 || card >= NSI || xsc->sc_type == SIEMPTY) {
126810015Speter			error = ENOENT;
126910015Speter			goto out;
127010015Speter		}
127110015Speter		/*
127210015Speter		 * And check that a port exists
127310015Speter		 */
127410015Speter		port = dp->tc_port;
127510015Speter		if (port < 0 || port >= xsc->sc_nport) {
127610015Speter			error = ENOENT;
127710015Speter			goto out;
127810015Speter		}
127910015Speter		xpp = xsc->sc_ports + port;
128010015Speter		regp = (struct si_reg *)xsc->sc_maddr;
128110015Speter	}
128210015Speter
128310015Speter	switch (cmd) {
128410015Speter	case TCSIDEBUG:
128510015Speter#ifdef	SI_DEBUG
128610015Speter		SUCHECK;
128710015Speter		if (xpp->sp_debug)
128810015Speter			xpp->sp_debug = 0;
128910015Speter		else {
129010015Speter			xpp->sp_debug = DBG_ALL;
129110015Speter			DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n",
129210015Speter				(xpp->sp_debug&DBG_ALL)?"ON":"OFF"));
129310015Speter		}
129410015Speter		break;
129510015Speter#else
129610015Speter		error = ENODEV;
129710015Speter		goto out;
129810015Speter#endif
129910015Speter	case TCSISDBG_LEVEL:
130010015Speter	case TCSIGDBG_LEVEL:
130110015Speter#ifdef	SI_DEBUG
130210015Speter		if (cmd == TCSIGDBG_LEVEL) {
130310015Speter			dp->tc_dbglvl = xpp->sp_debug;
130410015Speter		} else {
130510015Speter			SUCHECK;
130610015Speter			xpp->sp_debug = dp->tc_dbglvl;
130710015Speter		}
130810015Speter		break;
130910015Speter#else
131010015Speter		error = ENODEV;
131110015Speter		goto out;
131210015Speter#endif
131310015Speter	case TCSIGRXIT:
131410015Speter		dp->tc_int = regp->rx_int_count;
131510015Speter		break;
131610015Speter	case TCSIRXIT:
131710015Speter		SUCHECK;
131810015Speter		regp->rx_int_count = dp->tc_int;
131910015Speter		break;
132010015Speter	case TCSIGIT:
132110015Speter		dp->tc_int = regp->int_count;
132210015Speter		break;
132310015Speter	case TCSIIT:
132410015Speter		SUCHECK;
132510015Speter		regp->int_count = dp->tc_int;
132610015Speter		break;
132710044Speter	case TCSISTATE:
132810044Speter		dp->tc_int = xpp->sp_ccb->hi_ip;
132910015Speter		break;
133010044Speter	/* these next three use a different structure */
133110044Speter	case TCSI_PORT:
133210015Speter		SUCHECK;
133310044Speter		sps->tc_siport = *xpp;
133410015Speter		break;
133510044Speter	case TCSI_CCB:
133610044Speter		SUCHECK;
133710044Speter		sps->tc_ccb = *xpp->sp_ccb;
133810015Speter		break;
133910044Speter	case TCSI_TTY:
134010044Speter		SUCHECK;
134110044Speter		sps->tc_tty = *xpp->sp_tty;
134210015Speter		break;
134310015Speter	default:
134410015Speter		error = EINVAL;
134510015Speter		goto out;
134610015Speter	}
134710015Speterout:
134810015Speter	splx(oldspl);
134910015Speter	return(error);		/* success */
135010015Speter}
135110015Speter
135210015Speter/*
135310015Speter *	siparam()	: Configure line params
135410015Speter *	called at spltty();
135510015Speter *	this may sleep, does not flush, nor wait for drain, nor block writes
135610015Speter *	caller must arrange this if it's important..
135710015Speter */
135810015Speterint
135910015Spetersiparam(tp, t)
136010015Speter	register struct tty *tp;
136110015Speter	register struct termios *t;
136210015Speter{
136310015Speter	register struct si_port *pp = TP2PP(tp);
136410015Speter	volatile struct si_channel *ccbp;
136510015Speter	int oldspl, cflag, iflag, oflag, lflag;
136610015Speter	int error = 0;		/* shutup gcc */
136710015Speter	int ispeed = 0;		/* shutup gcc */
136810015Speter	int ospeed = 0;		/* shutup gcc */
136910161Speter	BYTE val;
137010015Speter
137110015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t));
137210015Speter	cflag = t->c_cflag;
137310015Speter	iflag = t->c_iflag;
137410015Speter	oflag = t->c_oflag;
137510015Speter	lflag = t->c_lflag;
137610044Speter	DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n",
137710044Speter		oflag, cflag, iflag, lflag));
137810015Speter
137910015Speter
138010015Speter	/* if not hung up.. */
138110015Speter	if (t->c_ospeed != 0) {
138210015Speter		/* translate baud rate to firmware values */
138310015Speter		ospeed = ttspeedtab(t->c_ospeed, bdrates);
138410015Speter		ispeed = t->c_ispeed ?
138510015Speter			 ttspeedtab(t->c_ispeed, bdrates) : ospeed;
138610015Speter
138710015Speter		/* enforce legit baud rate */
138810015Speter		if (ospeed < 0 || ispeed < 0)
138910015Speter			return (EINVAL);
139010015Speter	}
139110015Speter
139210015Speter
139310015Speter	oldspl = spltty();
139410015Speter
139510015Speter	ccbp = pp->sp_ccb;
139610015Speter
139710161Speter	/* ========== set hi_break ========== */
139810161Speter	val = 0;
139910161Speter	if (iflag & IGNBRK)		/* Breaks */
140010161Speter		val |= BR_IGN;
140110161Speter	if (iflag & BRKINT)		/* Interrupt on break? */
140210161Speter		val |= BR_INT;
140310161Speter	if (iflag & PARMRK)		/* Parity mark? */
140410161Speter		val |= BR_PARMRK;
140510161Speter	if (iflag & IGNPAR)		/* Ignore chars with parity errors? */
140610161Speter		val |= BR_PARIGN;
140710161Speter	ccbp->hi_break = val;
140810161Speter
140910161Speter	/* ========== set hi_csr ========== */
141010015Speter	/* if not hung up.. */
141110015Speter	if (t->c_ospeed != 0) {
141210015Speter		/* Set I/O speeds */
141310161Speter		 val = (ispeed << 4) | ospeed;
141410015Speter	}
141510161Speter	ccbp->hi_csr = val;
141610015Speter
141710161Speter	/* ========== set hi_mr2 ========== */
141810161Speter	val = 0;
141910015Speter	if (cflag & CSTOPB)				/* Stop bits */
142010161Speter		val |= MR2_2_STOP;
142110015Speter	else
142210161Speter		val |= MR2_1_STOP;
142310161Speter	/*
142410161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
142510161Speter	 * a DCE, hence the reverse sense of RTS and CTS
142610161Speter	 */
142710161Speter	/* Output Flow - RTS must be raised before data can be sent */
142810161Speter	if (cflag & CCTS_OFLOW)
142910161Speter		val |= MR2_RTSCONT;
143010161Speter
143110161Speter	ccbp->hi_mr1 = val;
143210161Speter
143310161Speter	/* ========== set hi_mr1 ========== */
143410161Speter	val = 0;
143510015Speter	if (!(cflag & PARENB))				/* Parity */
143610161Speter		val |= MR1_NONE;
143710015Speter	else
143810161Speter		val |= MR1_WITH;
143910015Speter	if (cflag & PARODD)
144010161Speter		val |= MR1_ODD;
144110015Speter
144210015Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
144310161Speter		val |= MR1_8_BITS;
144410015Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
144510161Speter		val |= MR1_7_BITS;
144610015Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
144710161Speter		val |= MR1_6_BITS;
144810015Speter	} else {					/* Must be 5 */
144910161Speter		val |= MR1_5_BITS;
145010015Speter	}
145110161Speter	/*
145210161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
145310161Speter	 * a DCE, hence the reverse sense of RTS and CTS
145410161Speter	 */
145510161Speter	/* Input Flow - CTS is raised when port is ready to receive data */
145610161Speter	if (cflag & CRTS_IFLOW)
145710161Speter		val |= MR1_CTSCONT;
145810015Speter
145910161Speter	ccbp->hi_mr1 = val;
146010161Speter
146110161Speter	/* ========== set hi_mask ========== */
146210161Speter	val = 0xff;
146310161Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
146410161Speter		val &= 0xFF;
146510161Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
146610161Speter		val &= 0x7F;
146710161Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
146810161Speter		val &= 0x3F;
146910161Speter	} else {					/* Must be 5 */
147010161Speter		val &= 0x1F;
147110161Speter	}
147210015Speter	if (iflag & ISTRIP)
147310161Speter		val &= 0x7F;
147410015Speter
147510161Speter	ccbp->hi_mask = val;
147610161Speter
147710161Speter	/* ========== set hi_prtcl ========== */
147810161Speter	val = 0;
147910015Speter				/* Monitor DCD etc. if a modem */
148010015Speter	if (!(cflag & CLOCAL))
148110161Speter		val |= SP_DCEN;
148210161Speter	if (iflag & IXANY)
148310161Speter		val |= SP_TANY;
148410161Speter	if (iflag & IXON)
148510161Speter		val |= SP_TXEN;
148610161Speter	if (iflag & IXOFF)
148710161Speter		val |= SP_RXEN;
148810161Speter	if (iflag & INPCK)
148910161Speter		val |= SP_PAEN;
149010015Speter
149110161Speter	ccbp->hi_prtcl = val;
149210161Speter
149310161Speter
149410161Speter	/* ========== set hi_{rx|tx}{on|off} ========== */
149510161Speter	/* XXX: the card TOTALLY shields us from the flow control... */
149610015Speter	ccbp->hi_txon = t->c_cc[VSTART];
149710015Speter	ccbp->hi_txoff = t->c_cc[VSTOP];
149810015Speter
149910015Speter	ccbp->hi_rxon = t->c_cc[VSTART];
150010015Speter	ccbp->hi_rxoff = t->c_cc[VSTOP];
150110015Speter
150210161Speter	/* ========== send settings to the card ========== */
150310015Speter	/* potential sleep here */
150410015Speter	if (ccbp->hi_stat == IDLE_CLOSE)		/* Not yet open */
150510015Speter		si_command(pp, LOPEN, SI_WAIT);		/* open it */
150610015Speter	else
150710015Speter		si_command(pp, CONFIG, SI_WAIT);	/* change params */
150810015Speter
150910161Speter	/* ========== set DTR etc ========== */
151010015Speter	/* Hangup if ospeed == 0 */
151110015Speter	if (t->c_ospeed == 0) {
151210015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
151310015Speter	} else {
151410015Speter		/*
151510015Speter		 * If the previous speed was 0, may need to re-enable
151610015Speter	 	 * the modem signals
151710015Speter	 	 */
151810015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
151910015Speter	}
152010015Speter
152110044Speter	DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n",
152210044Speter		ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break));
152310015Speter
152410015Speter	splx(oldspl);
152510015Speter	return(error);
152610015Speter}
152710015Speter
152810015Speter/*
152910015Speter * Enable or Disable the writes to this channel...
153010015Speter * "state" ->  enabled = 1; disabled = 0;
153110015Speter */
153210015Speterstatic void
153310015Spetersi_write_enable(pp, state)
153410015Speter	register struct si_port *pp;
153510015Speter	int state;
153610015Speter{
153710015Speter	int oldspl;
153810015Speter
153910015Speter	oldspl = spltty();
154010015Speter
154110015Speter	if (state) {
154210015Speter		pp->sp_state &= ~SS_BLOCKWRITE;
154310015Speter		if (pp->sp_state & SS_WAITWRITE) {
154410015Speter			pp->sp_state &= ~SS_WAITWRITE;
154510015Speter			/* thunder away! */
154610015Speter			wakeup((caddr_t)pp);
154710015Speter		}
154810015Speter	} else {
154910015Speter		pp->sp_state |= SS_BLOCKWRITE;
155010015Speter	}
155110015Speter
155210015Speter	splx(oldspl);
155310015Speter}
155410015Speter
155510015Speter/*
155610015Speter * Set/Get state of modem control lines.
155710015Speter * Due to DCE-like behaviour of the adapter, some signals need translation:
155810015Speter *	TIOCM_DTR	DSR
155910015Speter *	TIOCM_RTS	CTS
156010015Speter */
156110015Speterstatic int
156210015Spetersi_modem(pp, cmd, bits)
156310015Speter	struct si_port *pp;
156410015Speter	enum si_mctl cmd;
156510015Speter	int bits;
156610015Speter{
156710015Speter	volatile struct si_channel *ccbp;
156810015Speter	int x;
156910015Speter
157010015Speter	DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits));
157110015Speter	ccbp = pp->sp_ccb;		/* Find channel address */
157210015Speter	switch (cmd) {
157310015Speter	case GET:
157410015Speter		x = ccbp->hi_ip;
157510015Speter		bits = TIOCM_LE;
157610015Speter		if (x & IP_DCD)		bits |= TIOCM_CAR;
157710015Speter		if (x & IP_DTR)		bits |= TIOCM_DTR;
157810015Speter		if (x & IP_RTS)		bits |= TIOCM_RTS;
157910015Speter		if (x & IP_RI)		bits |= TIOCM_RI;
158010015Speter		return(bits);
158110015Speter	case SET:
158210015Speter		ccbp->hi_op &= ~(OP_DSR|OP_CTS);
158310015Speter		/* fall through */
158410015Speter	case BIS:
158510015Speter		x = 0;
158610015Speter		if (bits & TIOCM_DTR)
158710015Speter			x |= OP_DSR;
158810015Speter		if (bits & TIOCM_RTS)
158910015Speter			x |= OP_CTS;
159010015Speter		ccbp->hi_op |= x;
159110015Speter		break;
159210015Speter	case BIC:
159310015Speter		if (bits & TIOCM_DTR)
159410015Speter			ccbp->hi_op &= ~OP_DSR;
159510015Speter		if (bits & TIOCM_RTS)
159610015Speter			ccbp->hi_op &= ~OP_CTS;
159710015Speter	}
159810015Speter	return 0;
159910015Speter}
160010015Speter
160110015Speter/*
160210015Speter * Handle change of modem state
160310015Speter */
160410015Speterstatic void
160510015Spetersi_modem_state(pp, tp, hi_ip)
160610015Speter	register struct si_port *pp;
160710015Speter	register struct tty *tp;
160810015Speter	register int hi_ip;
160910015Speter{
161010015Speter							/* if a modem dev */
161110015Speter	if (hi_ip & IP_DCD) {
161210015Speter		if ( !(pp->sp_last_hi_ip & IP_DCD)) {
161310015Speter			DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n",
161410015Speter				tp->t_line));
161510015Speter			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
161610015Speter		}
161710015Speter	} else {
161810015Speter		if (pp->sp_last_hi_ip & IP_DCD) {
161910015Speter			DPRINT((pp, DBG_INTR, "modem carr off\n"));
162010015Speter			if ((*linesw[tp->t_line].l_modem)(tp, 0))
162110015Speter				(void) si_modem(pp, SET, 0);
162210015Speter		}
162310015Speter	}
162410015Speter	pp->sp_last_hi_ip = hi_ip;
162510015Speter
162610015Speter}
162710015Speter
162810015Speter/*
162910015Speter * Poller to catch missed interrupts.
163012174Speter *
163112496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get
163212496Speter * better response.  We could really use a "periodic" version timeout(). :-)
163310015Speter */
163410015Speter#ifdef POLL
163510708Speterstatic void
163610015Spetersi_poll(void *nothing)
163710015Speter{
163810015Speter	register struct si_softc *sc;
163910015Speter	register int i;
164010015Speter	volatile struct si_reg *regp;
164112174Speter	register struct si_port *pp;
164212174Speter	int lost, oldspl, port;
164310015Speter
164410015Speter	DPRINT((0, DBG_POLL, "si_poll()\n"));
164511609Speter	oldspl = spltty();
164610015Speter	if (in_intr)
164710015Speter		goto out;
164810015Speter	lost = 0;
164910015Speter	for (i=0; i<NSI; i++) {
165010015Speter		sc = &si_softc[i];
165112174Speter		if (sc->sc_type == SIEMPTY)
165210015Speter			continue;
165310015Speter		regp = (struct si_reg *)sc->sc_maddr;
165410015Speter		/*
165510015Speter		 * See if there has been a pending interrupt for 2 seconds
165610015Speter		 * or so. The test <int_scounter >= 200) won't correspond
165710015Speter		 * to 2 seconds if int_count gets changed.
165810015Speter		 */
165910015Speter		if (regp->int_pending != 0) {
166010015Speter			if (regp->int_scounter >= 200 &&
166110015Speter			    regp->initstat == 1) {
166212174Speter				printf("si%d: lost intr\n", i);
166310015Speter				lost++;
166410015Speter			}
166510015Speter		} else {
166610015Speter			regp->int_scounter = 0;
166710015Speter		}
166810015Speter
166912174Speter		/*
167012174Speter		 * gripe about no input flow control..
167112174Speter		 */
167212174Speter		pp = sc->sc_ports;
167312174Speter		for (port = 0; port < sc->sc_nport; pp++, port++) {
167412174Speter			if (pp->sp_delta_overflows > 0) {
167512174Speter				printf("si%d: %d tty level buffer overflows\n",
167612174Speter					i, pp->sp_delta_overflows);
167712174Speter				pp->sp_delta_overflows = 0;
167812174Speter			}
167912174Speter		}
168010015Speter	}
168110015Speter	if (lost)
168210015Speter		siintr(-1);	/* call intr with fake vector */
168311609Speterout:
168410015Speter	splx(oldspl);
168510015Speter
168610015Speter	timeout(si_poll, (caddr_t)0L, POLL_INTERVAL);
168710015Speter}
168810015Speter#endif	/* ifdef POLL */
168910015Speter
169010015Speter/*
169110015Speter * The interrupt handler polls ALL ports on ALL adapters each time
169210015Speter * it is called.
169310015Speter */
169410015Speter
169512496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE];	/* input staging area */
169610015Speter
169710708Spetervoid
169811609Spetersiintr(int unit)
169910015Speter{
170010015Speter	register struct si_softc *sc;
170110015Speter
170210015Speter	register struct si_port *pp;
170310015Speter	volatile struct si_channel *ccbp;
170410015Speter	register struct tty *tp;
170510015Speter	volatile caddr_t maddr;
170611872Sphk	BYTE op, ip;
170712174Speter	int x, card, port, n, i, isopen;
170810015Speter	volatile BYTE *z;
170910015Speter	BYTE c;
171010015Speter
171111609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "siintr(%d)\n", unit));
171211609Speter	if (in_intr) {
171311609Speter		if (unit < 0)	/* should never happen */
171410708Speter			return;
171512174Speter		printf("si%d: Warning interrupt handler re-entered\n",
171611609Speter			unit);
171710708Speter		return;
171810015Speter	}
171910015Speter	in_intr = 1;
172010015Speter
172110015Speter	/*
172210015Speter	 * When we get an int we poll all the channels and do ALL pending
172310015Speter	 * work, not just the first one we find. This allows all cards to
172410015Speter	 * share the same vector.
172510015Speter	 */
172610015Speter	for (card=0; card < NSI; card++) {
172710015Speter		sc = &si_softc[card];
172812174Speter		if (sc->sc_type == SIEMPTY)
172910015Speter			continue;
173012174Speter
173112174Speter		/*
173212174Speter		 * First, clear the interrupt
173312174Speter		 */
173410015Speter		switch(sc->sc_type) {
173510015Speter		case SIHOST :
173610015Speter			maddr = sc->sc_maddr;
173710015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
173810015Speter							/* flag nothing pending */
173910015Speter			*(maddr+SIINTCL) = 0x00;	/* Set IRQ clear */
174010015Speter			*(maddr+SIINTCL_CL) = 0x00;	/* Clear IRQ clear */
174110015Speter			break;
174210015Speter		case SIHOST2:
174310015Speter			maddr = sc->sc_maddr;
174410015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
174510015Speter			*(maddr+SIPLIRQCLR) = 0x00;
174610015Speter			*(maddr+SIPLIRQCLR) = 0x10;
174710015Speter			break;
174810015Speter		case SIEISA:
174910015Speter#if NEISA > 0
175010015Speter			maddr = sc->sc_maddr;
175110015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
175210015Speter			(void)inb(sc->sc_eisa_iobase+3);
175310015Speter			break;
175410015Speter#endif	/* fall through if not EISA kernel */
175510015Speter		case SIEMPTY:
175610015Speter		default:
175710015Speter			continue;
175810015Speter		}
175910015Speter		((volatile struct si_reg *)maddr)->int_scounter = 0;
176010015Speter
176112174Speter		/*
176212174Speter		 * check each port
176312174Speter		 */
176412174Speter		for (pp=sc->sc_ports,port=0; port < sc->sc_nport; pp++,port++) {
176510015Speter			ccbp = pp->sp_ccb;
176610015Speter			tp = pp->sp_tty;
176710015Speter
176812174Speter
176910015Speter			/*
177010015Speter			 * See if a command has completed ?
177110015Speter			 */
177210015Speter			if (ccbp->hi_stat != pp->sp_pend) {
177310015Speter				DPRINT((pp, DBG_INTR,
177410015Speter					"siintr hi_stat = 0x%x, pend = %d\n",
177510015Speter					ccbp->hi_stat, pp->sp_pend));
177610015Speter				switch(pp->sp_pend) {
177710015Speter				case LOPEN:
177810015Speter				case MPEND:
177910015Speter				case MOPEN:
178010015Speter				case CONFIG:
178110015Speter					pp->sp_pend = ccbp->hi_stat;
178210015Speter						/* sleeping in si_command */
178310015Speter					wakeup(&pp->sp_state);
178410015Speter					break;
178510015Speter				default:
178610015Speter					pp->sp_pend = ccbp->hi_stat;
178710015Speter				}
178810015Speter	 		}
178910015Speter
179010015Speter			/*
179110015Speter			 * Continue on if it's closed
179210015Speter			 */
179310015Speter			if (ccbp->hi_stat == IDLE_CLOSE) {
179410015Speter				continue;
179510015Speter			}
179610015Speter
179710015Speter			/*
179810015Speter			 * Do modem state change if not a local device
179910015Speter			 */
180010015Speter			si_modem_state(pp, tp, ccbp->hi_ip);
180110015Speter
180210015Speter			/*
180312174Speter			 * Check to see if there's we should 'receive'
180412174Speter			 * characters.
180512174Speter			 */
180612174Speter			if (tp->t_state & TS_CONNECTED &&
180712174Speter			    tp->t_state & TS_ISOPEN)
180812174Speter				isopen = 1;
180912174Speter			else
181012174Speter				isopen = 0;
181112174Speter
181212174Speter			/*
181310015Speter			 * Do break processing
181410015Speter			 */
181510015Speter			if (ccbp->hi_state & ST_BREAK) {
181612174Speter				if (isopen) {
181712174Speter				    (*linesw[tp->t_line].l_rint)(TTY_BI, tp);
181810015Speter				}
181910015Speter				ccbp->hi_state &= ~ST_BREAK;   /* A Bit iffy this */
182010015Speter				DPRINT((pp, DBG_INTR, "si_intr break\n"));
182110015Speter			}
182210015Speter
182310015Speter			/*
182412174Speter			 * Do RX stuff - if not open then dump any characters.
182512174Speter			 * XXX: This is VERY messy and needs to be cleaned up.
182612174Speter			 *
182712174Speter			 * XXX: can we leave data in the host adapter buffer
182812174Speter			 * when the clists are full?  That may be dangerous
182912174Speter			 * if the user cannot get an interrupt signal through.
183010015Speter			 */
183110015Speter
183212174Speter	more_rx:	/* XXX Sorry. the nesting was driving me bats! :-( */
183312174Speter
183412174Speter			if (!isopen) {
183510015Speter				ccbp->hi_rxopos = ccbp->hi_rxipos;
183612174Speter				goto end_rx;
183712174Speter			}
183810015Speter
183912174Speter			/*
184012174Speter			 * Process read characters if not skipped above
184112174Speter			 */
184212174Speter			c = ccbp->hi_rxipos - ccbp->hi_rxopos;
184312174Speter			if (c == 0) {
184412174Speter				goto end_rx;
184512174Speter			}
184610015Speter
184712174Speter			op = ccbp->hi_rxopos;
184812174Speter			ip = ccbp->hi_rxipos;
184912174Speter			n = c & 0xff;
185012174Speter
185112174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
185210015Speter						n, op, ip));
185310015Speter
185412174Speter			/*
185512174Speter			 * Suck characters out of host card buffer into the
185612174Speter			 * "input staging buffer" - so that we dont leave the
185712174Speter			 * host card in limbo while we're possibly echoing
185812174Speter			 * characters and possibly flushing input inside the
185912174Speter			 * ldisc l_rint() routine.
186012174Speter			 */
186112496Speter			if (n <= SI_BUFFERSIZE - op) {
186210015Speter
186312174Speter				DPRINT((pp, DBG_INTR, "\tsingle copy\n"));
186412174Speter				z = ccbp->hi_rxbuf + op;
186512174Speter				bcopy((caddr_t)z, si_rxbuf, n);
186610015Speter
186712174Speter				op += n;
186812174Speter			} else {
186912496Speter				x = SI_BUFFERSIZE - op;
187010015Speter
187112174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x));
187212174Speter				z = ccbp->hi_rxbuf + op;
187312174Speter				bcopy((caddr_t)z, si_rxbuf, x);
187410015Speter
187512174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n-x));
187612174Speter				z = ccbp->hi_rxbuf;
187712174Speter				bcopy((caddr_t)z, si_rxbuf+x, n-x);
187810015Speter
187912174Speter				op += n;
188012174Speter			}
188110015Speter
188212174Speter			/* clear collected characters from buffer */
188312174Speter			ccbp->hi_rxopos = op;
188412174Speter
188512174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
188610015Speter						n, op, ip));
188710015Speter
188812174Speter			/*
188912174Speter			 * at this point...
189012174Speter			 * n = number of chars placed in si_rxbuf
189112174Speter			 */
189210015Speter
189312174Speter			/*
189412174Speter			 * Avoid the grotesquely inefficient lineswitch
189512174Speter			 * routine (ttyinput) in "raw" mode. It usually
189612174Speter			 * takes about 450 instructions (that's without
189712174Speter			 * canonical processing or echo!). slinput is
189812174Speter			 * reasonably fast (usually 40 instructions
189912174Speter			 * plus call overhead).
190012174Speter			 */
190112174Speter			if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
190210015Speter
190312174Speter				/* block if the driver supports it */
190412174Speter				if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER
190512174Speter				    && (tp->t_cflag & CRTS_IFLOW
190612174Speter					|| tp->t_iflag & IXOFF)
190712174Speter				    && !(tp->t_state & TS_TBLOCK))
190812174Speter					ttyblock(tp);
190910015Speter
191012174Speter				tk_nin += n;
191112174Speter				tk_rawcc += n;
191212174Speter				tp->t_rawcc += n;
191312174Speter
191412174Speter				pp->sp_delta_overflows +=
191512174Speter				    b_to_q((char *)si_rxbuf, n, &tp->t_rawq);
191612174Speter
191712174Speter				ttwakeup(tp);
191812174Speter				if (tp->t_state & TS_TTSTOP
191912174Speter				    && (tp->t_iflag & IXANY
192012174Speter					|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
192112174Speter					tp->t_state &= ~TS_TTSTOP;
192212174Speter					tp->t_lflag &= ~FLUSHO;
192312174Speter					si_start(tp);
192412174Speter				}
192512174Speter			} else {
192612174Speter				/*
192712174Speter				 * It'd be nice to not have to go through the
192812174Speter				 * function call overhead for each char here.
192912174Speter				 * It'd be nice to block input it, saving a
193012174Speter				 * loop here and the call/return overhead.
193112174Speter				 */
193212174Speter				for(x = 0; x < n; x++) {
193312174Speter					i = si_rxbuf[x];
193412174Speter					if ((*linesw[tp->t_line].l_rint)(i, tp)
193512174Speter					     == -1) {
193612174Speter						pp->sp_delta_overflows++;
193710015Speter					}
193812174Speter					/*
193912174Speter					 * doesn't seem to be much point doing
194012174Speter					 * this here.. this driver has no
194112174Speter					 * softtty processing! ??
194212174Speter					 */
194312174Speter					if (pp->sp_hotchar && i == pp->sp_hotchar) {
194412174Speter						setsofttty();
194512174Speter					}
194612174Speter				}
194712174Speter			}
194812174Speter			goto more_rx;	/* try for more until RXbuf is empty */
194910015Speter
195012174Speter	end_rx:		/* XXX: Again, sorry about the gotos.. :-) */
195110015Speter
195210015Speter			/*
195310015Speter			 * Do TX stuff
195410015Speter			 */
195510015Speter			(*linesw[tp->t_line].l_start)(tp);
195610015Speter
195710015Speter		} /* end of for (all ports on this controller) */
195810015Speter	} /* end of for (all controllers) */
195910015Speter
196011609Speter	in_intr = 0;
196111609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "end siintr(%d)\n", unit));
196210015Speter}
196310015Speter
196410015Speter/*
196510015Speter * Nudge the transmitter...
196612174Speter *
196712174Speter * XXX: I inherited some funny code here.  It implies the host card only
196812174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does
196912174Speter * not interrupt when it's actually hits empty.  In some cases, we have
197012174Speter * processes waiting for complete drain, and we need to simulate an interrupt
197112174Speter * about when we think the buffer is going to be empty (and retry if not).
197212174Speter * I really am not certain about this...  I *need* the hardware manuals.
197310015Speter */
197410015Speterstatic void
197510015Spetersi_start(tp)
197610015Speter	register struct tty *tp;
197710015Speter{
197810015Speter	struct si_port *pp;
197910015Speter	volatile struct si_channel *ccbp;
198010015Speter	register struct clist *qp;
198110015Speter	register char *dptr;
198210015Speter	BYTE ipos;
198310015Speter	int nchar;
198410015Speter	int oldspl, count, n, amount, buffer_full;
198510015Speter	int do_exitproc;
198610015Speter
198710015Speter	oldspl = spltty();
198810015Speter
198910015Speter	qp = &tp->t_outq;
199010015Speter	pp = TP2PP(tp);
199110015Speter
199210015Speter	DPRINT((pp, DBG_ENTRY|DBG_START,
199310015Speter		"si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n",
199410015Speter		tp, tp->t_state, pp->sp_state, qp->c_cc));
199510015Speter
199610015Speter	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
199710015Speter		goto out;
199810015Speter
199910015Speter	do_exitproc = 0;
200010015Speter	buffer_full = 0;
200110015Speter	ccbp = pp->sp_ccb;
200210015Speter
200310015Speter	/*
200410015Speter	 * Handle the case where ttywait() is called on process exit
200510015Speter	 * this may be BSDI specific, I dont know...
200610015Speter	 */
200710015Speter	if (tp->t_session != NULL && tp->t_session->s_leader != NULL &&
200810015Speter	    (tp->t_session->s_leader->p_flag & P_WEXIT)) {
200910015Speter		do_exitproc++;
201010015Speter	}
201110015Speter
201210015Speter	count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
201310015Speter	DPRINT((pp, DBG_START, "count %d\n", (BYTE)count));
201410015Speter
201510015Speter	dptr = (char *)ccbp->hi_txbuf;	/* data buffer */
201610015Speter
201710015Speter	while ((nchar = qp->c_cc) > 0) {
201810015Speter		if ((BYTE)count >= 255) {
201910015Speter			buffer_full++;
202010015Speter			break;
202110015Speter		}
202210015Speter		amount = min(nchar, (255 - (BYTE)count));
202310015Speter		ipos = (unsigned int)ccbp->hi_txipos;
202410015Speter		/* will it fit in one lump? */
202512496Speter		if ((SI_BUFFERSIZE - ipos) >= amount) {
202610015Speter			n = q_to_b(&tp->t_outq,
202710015Speter				(char *)&ccbp->hi_txbuf[ipos], amount);
202810015Speter		} else {
202910015Speter			n = q_to_b(&tp->t_outq,
203010015Speter				(char *)&ccbp->hi_txbuf[ipos],
203112496Speter				SI_BUFFERSIZE-ipos);
203212496Speter			if (n == SI_BUFFERSIZE-ipos) {
203310015Speter				n += q_to_b(&tp->t_outq,
203410015Speter					(char *)&ccbp->hi_txbuf[0],
203512496Speter					amount - (SI_BUFFERSIZE-ipos));
203610015Speter			}
203710015Speter		}
203810015Speter		ccbp->hi_txipos += n;
203910015Speter		count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
204010015Speter	}
204110015Speter
204210015Speter	if (count != 0 && nchar == 0) {
204310015Speter		tp->t_state |= TS_BUSY;
204410015Speter	} else {
204510015Speter		tp->t_state &= ~TS_BUSY;
204610015Speter	}
204710015Speter
204810015Speter	/* wakeup time? */
204910015Speter	ttwwakeup(tp);
205010015Speter
205110015Speter	DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n",
205210015Speter		(BYTE)count, nchar, tp->t_state));
205310015Speter
205410015Speter	if ((tp->t_state & TS_BUSY) || do_exitproc)
205510015Speter	{
205610015Speter		int time;
205710015Speter
205810015Speter		if (do_exitproc != 0) {
205910015Speter			time = hz / 10;
206010015Speter		} else {
206110015Speter			time = ttspeedtab(tp->t_ospeed, chartimes);
206210015Speter
206310015Speter			if (time > 0) {
206410015Speter				if (time < nchar)
206510015Speter					time = nchar / time;
206610015Speter				else
206710015Speter					time = 2;
206810015Speter			} else {
206912174Speter				printf("si%d: bad char time value!!\n",
207012174Speter					(int)SI_CARD(tp->t_dev));
207110015Speter				goto out;
207210015Speter			}
207310015Speter		}
207410015Speter
207510015Speter		if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) {
207610015Speter			untimeout((timeout_func_t)si_lstart, (caddr_t)pp);
207710015Speter		} else {
207810015Speter			pp->sp_state |= SS_LSTART;
207910015Speter		}
208010015Speter		DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time));
208110015Speter		timeout((timeout_func_t)si_lstart, (caddr_t)pp, time);
208210015Speter	}
208310015Speter
208410015Speterout:
208510015Speter	splx(oldspl);
208610015Speter	DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n"));
208710015Speter}
208810015Speter
208910015Speter/*
209010015Speter * Note: called at splsoftclock from the timeout code
209110015Speter * This has to deal with two things...  cause wakeups while waiting for
209210015Speter * tty drains on last process exit, and call l_start at about the right
209310015Speter * time for protocols like ppp.
209410015Speter */
209510015Speterstatic void
209610015Spetersi_lstart(pp)
209710015Speter	register struct si_port *pp;
209810015Speter{
209910015Speter	register struct tty *tp;
210010015Speter	int oldspl;
210110015Speter
210210015Speter	DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n",
210310015Speter		pp, pp->sp_state));
210410015Speter
210510015Speter	oldspl = spltty();
210610015Speter
210710015Speter	if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) {
210810015Speter		splx(oldspl);
210910015Speter		return;
211010015Speter	}
211110015Speter	pp->sp_state &= ~SS_LSTART;
211210015Speter	pp->sp_state |= SS_INLSTART;
211310015Speter
211410015Speter	tp = pp->sp_tty;
211510015Speter
211610015Speter	/* deal with the process exit case */
211710015Speter	ttwwakeup(tp);
211810015Speter
211912174Speter	/* nudge protocols - eg: ppp */
212010015Speter	(*linesw[tp->t_line].l_start)(tp);
212110015Speter
212210015Speter	pp->sp_state &= ~SS_INLSTART;
212310015Speter	splx(oldspl);
212410015Speter}
212510015Speter
212610015Speter/*
212710015Speter * Stop output on a line. called at spltty();
212810015Speter */
212910015Spetervoid
213010015Spetersistop(tp, rw)
213110015Speter	register struct tty *tp;
213210015Speter	int rw;
213310015Speter{
213410015Speter	volatile struct si_channel *ccbp;
213510015Speter	struct si_port *pp;
213610015Speter
213710015Speter	pp = TP2PP(tp);
213810015Speter	ccbp = pp->sp_ccb;
213910015Speter
214010015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "sistop(%x,%x)\n", tp, rw));
214110015Speter
214210015Speter	/* XXX: must check (rw & FWRITE | FREAD) etc flushing... */
214310015Speter	if (rw & FWRITE) {
214410015Speter		/* what level are we meant to be flushing anyway? */
214510015Speter		if (tp->t_state & TS_BUSY) {
214610015Speter			si_command(TP2PP(tp), WFLUSH, SI_NOWAIT);
214710015Speter			tp->t_state &= ~TS_BUSY;
214810015Speter			ttwwakeup(tp);	/* Bruce???? */
214910015Speter		}
215010015Speter	}
215112174Speter#if 1	/* XXX: this doesn't work right yet.. */
215212174Speter	/* XXX: this may have been failing because we used to call l_rint()
215312174Speter	 * while we were looping based on these two counters. Now, we collect
215412174Speter	 * the data and then loop stuffing it into l_rint(), making this
215512174Speter	 * useless.  Should we cause this to blow away the staging buffer?
215612174Speter	 */
215710015Speter	if (rw & FREAD) {
215810015Speter		ccbp->hi_rxopos = ccbp->hi_rxipos;
215910015Speter	}
216010015Speter#endif
216110015Speter}
216210015Speter
216310015Speter/*
216410015Speter * Issue a command to the Z280 host card CPU.
216510015Speter */
216610015Speter
216710015Speterstatic void
216810015Spetersi_command(pp, cmd, waitflag)
216910015Speter	struct si_port *pp;		/* port control block (local) */
217010015Speter	int cmd;
217110015Speter	int waitflag;
217210015Speter{
217310015Speter	int oldspl;
217410015Speter	volatile struct si_channel *ccbp = pp->sp_ccb;
217510015Speter	int x;
217610015Speter
217710015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n",
217810015Speter		pp, cmd, waitflag, ccbp->hi_stat));
217910015Speter
218010015Speter	oldspl = spltty();		/* Keep others out */
218110015Speter
218210015Speter	/* wait until it's finished what it was doing.. */
218310015Speter	while((x = ccbp->hi_stat) != IDLE_OPEN &&
218410015Speter			x != IDLE_CLOSE &&
218510015Speter			x != cmd) {
218610015Speter		if (in_intr) {			/* Prevent sleep in intr */
218710015Speter			DPRINT((pp, DBG_PARAM,
218810015Speter				"cmd intr collision - completing %d\trequested %d\n",
218910015Speter				x, cmd));
219010015Speter			splx(oldspl);
219110015Speter			return;
219210015Speter		} else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
219310015Speter				"sicmd1", 1)) {
219410015Speter			splx(oldspl);
219510015Speter			return;
219610015Speter		}
219710015Speter	}
219810015Speter	/* it should now be in IDLE_OPEN, IDLE_CLOSE, or "cmd" */
219910015Speter
220010015Speter	/* if there was a pending command, cause a state-change wakeup */
220110015Speter	if (pp->sp_pend != IDLE_OPEN) {
220210015Speter		switch(pp->sp_pend) {
220310015Speter		case LOPEN:
220410015Speter		case MPEND:
220510015Speter		case MOPEN:
220610015Speter		case CONFIG:
220710015Speter			wakeup(&pp->sp_state);
220810015Speter			break;
220910015Speter		default:
221010015Speter			break;
221110015Speter		}
221210015Speter	}
221310015Speter
221410015Speter	pp->sp_pend = cmd;		/* New command pending */
221510015Speter	ccbp->hi_stat = cmd;		/* Post it */
221610015Speter
221710015Speter	if (waitflag) {
221810015Speter		if (in_intr) {		/* If in interrupt handler */
221910015Speter			DPRINT((pp, DBG_PARAM,
222010015Speter				"attempt to sleep in si_intr - cmd req %d\n",
222110015Speter				cmd));
222210015Speter			splx(oldspl);
222310015Speter			return;
222410015Speter		} else while(ccbp->hi_stat != IDLE_OPEN) {
222510015Speter			if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
222610015Speter			    "sicmd2", 0))
222710015Speter				break;
222810015Speter		}
222910015Speter	}
223010015Speter	splx(oldspl);
223110015Speter}
223210015Speter
223310015Speterstatic void
223410015Spetersi_disc_optim(tp, t, pp)
223510015Speter	struct tty	*tp;
223610015Speter	struct termios	*t;
223710015Speter	struct si_port	*pp;
223810015Speter{
223910015Speter	/*
224010015Speter	 * XXX can skip a lot more cases if Smarts.  Maybe
224110015Speter	 * (IGNCR | ISTRIP | IXON) in c_iflag.  But perhaps we
224210015Speter	 * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state.
224310015Speter	 */
224410015Speter	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
224510015Speter	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
224610015Speter	    && (!(t->c_iflag & PARMRK)
224710015Speter		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
224810015Speter	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
224910015Speter	    && linesw[tp->t_line].l_rint == ttyinput)
225010015Speter		tp->t_state |= TS_CAN_BYPASS_L_RINT;
225110015Speter	else
225210015Speter		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
225310161Speter
225410015Speter	/*
225510015Speter	 * Prepare to reduce input latency for packet
225610015Speter	 * discplines with a end of packet character.
225710015Speter	 */
225810015Speter	if (tp->t_line == SLIPDISC)
225910015Speter		pp->sp_hotchar = 0xc0;
226010015Speter	else if (tp->t_line == PPPDISC)
226110015Speter		pp->sp_hotchar = 0x7e;
226210015Speter	else
226310015Speter		pp->sp_hotchar = 0;
226410161Speter
226510161Speter	DPRINT((pp, DBG_OPTIM, "bypass: %s, hotchar: %x\n",
226610161Speter		(tp->t_state & TS_CAN_BYPASS_L_RINT) ? "on" : "off",
226710161Speter		pp->sp_hotchar));
226810015Speter}
226910015Speter
227010015Speter
227110015Speter#ifdef	SI_DEBUG
227210015Speterstatic void
227310015Spetersi_dprintf(pp, flags, str, a1, a2, a3, a4, a5, a6)
227410015Speter	struct si_port *pp;
227510015Speter	int flags;
227610015Speter	char *str;
227710015Speter	int a1, a2, a3, a4, a5, a6;
227810015Speter{
227910015Speter	if ((pp == NULL && (si_debug&flags)) ||
228010015Speter	    (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) {
228110015Speter	    	if (pp != NULL)
228212496Speter	    		printf("%ci%d(%d): ", 's',
228312174Speter	    			(int)SI_CARD(pp->sp_tty->t_dev),
228412174Speter	    			(int)SI_PORT(pp->sp_tty->t_dev));
228510015Speter		printf(str, a1, a2, a3, a4, a5, a6);
228610015Speter	}
228710015Speter}
228810015Speter
228910015Speterstatic char *
229010015Spetersi_mctl2str(cmd)
229110015Speter	enum si_mctl cmd;
229210015Speter{
229310015Speter	switch (cmd) {
229410015Speter	case GET:	return("GET");
229510015Speter	case SET:	return("SET");
229610015Speter	case BIS:	return("BIS");
229710015Speter	case BIC:	return("BIC");
229810015Speter	}
229910015Speter	return("BAD");
230010015Speter}
230112502Sjulian
230212624Speter#endif	/* DEBUG */
230312502Sjulian
230412624Speter
230512502Sjulian#ifdef JREMOD
230612502Sjulianstruct cdevsw si_cdevsw =
230712502Sjulian	{ siopen,	siclose,	siread,		siwrite,	/*68*/
230812502Sjulian	  siioctl,	sistop,		nxreset,	sidevtotty,/* si */
230912502Sjulian	  ttselect,	nxmmap,		NULL };
231012502Sjulian
231112502Sjulianstatic si_devsw_installed = 0;
231212502Sjulian
231312517Sjulianstatic void 	si_drvinit(void *unused)
231412502Sjulian{
231512517Sjulian	dev_t dev;
231612517Sjulian	dev_t dev_chr;
231712517Sjulian
231812502Sjulian	if( ! si_devsw_installed ) {
231912517Sjulian		dev = makedev(CDEV_MAJOR,0);
232012517Sjulian		cdevsw_add(&dev,&si_cdevsw,NULL);
232112517Sjulian		dev_chr = dev;
232212502Sjulian#if defined(BDEV_MAJOR)
232312517Sjulian		dev = makedev(BDEV_MAJOR,0);
232412517Sjulian		bdevsw_add(&dev,&si_bdevsw,NULL);
232512502Sjulian#endif /*BDEV_MAJOR*/
232612502Sjulian		si_devsw_installed = 1;
232712517Sjulian#ifdef DEVFS
232812517Sjulian		{
232912517Sjulian			int x;
233012517Sjulian/* default for a simple device with no probe routine (usually delete this) */
233112517Sjulian			x=devfs_add_devsw(
233212517Sjulian/*	path	name	devsw		minor	type   uid gid perm*/
233312517Sjulian	"/",	"si",	major(dev_chr),	0,	DV_CHR,	0,  0, 0600);
233412517Sjulian		}
233512521Sjulian#endif
233612517Sjulian    	}
233712502Sjulian}
233812517Sjulian
233912517SjulianSYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,si_drvinit,NULL)
234012517Sjulian
234112502Sjulian#endif /* JREMOD */
2342