si.c revision 29677
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 *
3329677Sgibbs *	$Id: si.c,v 1.60 1997/09/14 03:19:18 peter Exp $
3410015Speter */
3510015Speter
3610015Speter#ifndef lint
3716322Sgpalmerstatic const char si_copyright1[] =  "@(#) (C) Specialix International, 1990,1992",
3816322Sgpalmer                  si_copyright2[] =  "@(#) (C) Andy Rutter 1993",
3916322Sgpalmer                  si_copyright3[] =  "@(#) (C) Peter Wemm 1995";
4010015Speter#endif	/* not lint */
4110015Speter
4210015Speter#include <sys/param.h>
4310015Speter#include <sys/systm.h>
4424207Sbde#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
4524207Sbde#include <sys/ioctl_compat.h>
4624207Sbde#endif
4710015Speter#include <sys/tty.h>
4810015Speter#include <sys/proc.h>
4910015Speter#include <sys/conf.h>
5024131Sbde#include <sys/fcntl.h>
5110015Speter#include <sys/dkstat.h>
5210015Speter#include <sys/kernel.h>
5310015Speter#include <sys/malloc.h>
5415683Speter#include <sys/sysctl.h>
5512675Sjulian#ifdef DEVFS
5612675Sjulian#include <sys/devfsext.h>
5712675Sjulian#endif /*DEVFS*/
5810015Speter
5910015Speter#include <machine/clock.h>
6010015Speter
6112659Sbde#include <vm/vm.h>
6212662Sdg#include <vm/pmap.h>
6312659Sbde
6410015Speter#include <i386/isa/icu.h>
6510015Speter#include <i386/isa/isa.h>
6610015Speter#include <i386/isa/isa_device.h>
6710015Speter
6810015Speter#include <i386/isa/sireg.h>
6910015Speter#include <machine/si.h>
7013353Speter#include <machine/stdarg.h>
7110015Speter
7210015Speter#include "si.h"
7310015Speter
7410015Speter/*
7510015Speter * This device driver is designed to interface the Specialix International
7612496Speter * range of serial multiplexor cards (SI/XIO) to BSDI/386 on an ISA bus machine.
7710015Speter *
7810015Speter * The controller is interfaced to the host via dual port ram
7910015Speter * and a (programmable - SIHOST2) interrupt at IRQ 11,12 or 15.
8010015Speter */
8110015Speter
8217547Speter#define	POLL		/* turn on poller to scan for lost interrupts */
8317547Speter#define REALPOLL	/* on each poll, scan for work regardless */
8417547Speter#define POLLHZ	(hz/10)	/* 10 times per second */
8510047Speter#define SI_DEF_HWFLOW	/* turn on default CRTSCTS flow control */
8612496Speter#define SI_I_HIGH_WATER	(TTYHOG - 2 * SI_BUFFERSIZE)
8715639Speter#define INT_COUNT 25000	/* max of 125 ints per second */
8815639Speter#define RXINT_COUNT 1	/* one rxint per 10 milliseconds */
8910015Speter
9010015Speterenum si_mctl { GET, SET, BIS, BIC };
9110015Speter
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 *));
9725047Sbdestatic timeout_t si_lstart;
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
10312724Sphkstatic int	siparam __P((struct tty *, struct termios *));
10410015Speter
10512724Sphkstatic	int	siprobe __P((struct isa_device *id));
10612724Sphkstatic	int	siattach __P((struct isa_device *id));
10710708Speterstatic	void	si_modem_state __P((struct si_port *pp, struct tty *tp, int hi_ip));
10810708Speter
10912675Sjulianstruct isa_driver sidriver =
11012675Sjulian	{ siprobe, siattach, "si" };
11112675Sjulian
11212675Sjulian
11312675Sjulianstatic	d_open_t	siopen;
11412675Sjulianstatic	d_close_t	siclose;
11512675Sjulianstatic	d_read_t	siread;
11612675Sjulianstatic	d_write_t	siwrite;
11712675Sjulianstatic	d_ioctl_t	siioctl;
11812675Sjulianstatic	d_stop_t	sistop;
11912731Sbdestatic	d_devtotty_t	sidevtotty;
12012675Sjulian
12112675Sjulian#define CDEV_MAJOR 68
12212678Sphkstatic struct cdevsw si_cdevsw =
12312675Sjulian	{ siopen,	siclose,	siread,		siwrite,	/*68*/
12412743Sbde	  siioctl,	sistop,		noreset,	sidevtotty,/* si */
12529368Speter	  ttpoll,	nommap,		NULL,	"si",	NULL,	-1 };
12612675Sjulian
12712675Sjulian
12812174Speter#ifdef SI_DEBUG		/* use: ``options "SI_DEBUG"'' in your config file */
12913353Speter
13013353Speterstatic	void	si_dprintf __P((struct si_port *pp, int flags, const char *fmt,
13113353Speter				...));
13210708Speterstatic	char	*si_mctl2str __P((enum si_mctl cmd));
13313353Speter
13410708Speter#define	DPRINT(x)	si_dprintf x
13513353Speter
13610708Speter#else
13710708Speter#define	DPRINT(x)	/* void */
13810708Speter#endif
13910708Speter
14010962Speterstatic int si_Nports;
14110962Speterstatic int si_Nmodules;
14210962Speterstatic int si_debug = 0;	/* data, not bss, so it's patchable */
14310015Speter
14410962Speterstatic struct tty *si_tty;
14510962Speter
14612174Speter/* where the firmware lives; defined in si_code.c */
14710015Speterextern int si_dsize;
14810015Speterextern unsigned char si_download[];
14910015Speter
15010044Speterstruct si_softc {
15110044Speter	int 		sc_type;	/* adapter type */
15210044Speter	char 		*sc_typename;	/* adapter type string */
15310044Speter
15410044Speter	struct si_port	*sc_ports;	/* port structures for this card */
15510044Speter
15610044Speter	caddr_t		sc_paddr;	/* physical addr of iomem */
15710044Speter	caddr_t		sc_maddr;	/* kvaddr of iomem */
15810044Speter	int		sc_nport;	/* # ports on this card */
15910044Speter	int		sc_irq;		/* copy of attach irq */
16010044Speter	int		sc_eisa_iobase;	/* EISA io port address */
16110044Speter	int		sc_eisa_irqbits;
16212675Sjulian#ifdef	DEVFS
16312675Sjulian	struct {
16412675Sjulian		void	*ttyd;
16512826Speter		void	*cuaa;
16612675Sjulian		void	*ttyl;
16712675Sjulian		void	*ttyi;
16812675Sjulian	} devfs_token[32]; /* what is the max per card? */
16913165Speter	void	*control_token;
17012675Sjulian#endif
17110044Speter};
17212724Sphkstatic struct si_softc si_softc[NSI];		/* up to 4 elements */
17310044Speter
17412174Speter#ifndef B2000	/* not standard, but the hardware knows it. */
17510015Speter# define B2000 2000
17610015Speter#endif
17710015Speterstatic struct speedtab bdrates[] = {
17810015Speter	B75,	CLK75,		/* 0x0 */
17910015Speter	B110,	CLK110,		/* 0x1 */
18010015Speter	B150,	CLK150,		/* 0x3 */
18110015Speter	B300,	CLK300,		/* 0x4 */
18210015Speter	B600,	CLK600,		/* 0x5 */
18310015Speter	B1200,	CLK1200,	/* 0x6 */
18410015Speter	B2000,	CLK2000,	/* 0x7 */
18510015Speter	B2400,	CLK2400,	/* 0x8 */
18610015Speter	B4800,	CLK4800,	/* 0x9 */
18710015Speter	B9600,	CLK9600,	/* 0xb */
18810015Speter	B19200,	CLK19200,	/* 0xc */
18910015Speter	B38400, CLK38400,	/* 0x2 (out of order!) */
19010015Speter	B57600, CLK57600,	/* 0xd */
19110015Speter	B115200, CLK110,	/* 0x1 (dupe!, 110 baud on "si") */
19210015Speter	-1,	-1
19310015Speter};
19410015Speter
19510015Speter
19610015Speter/* populated with approx character/sec rates - translated at card
19710015Speter * initialisation time to chars per tick of the clock */
19810015Speterstatic int done_chartimes = 0;
19910015Speterstatic struct speedtab chartimes[] = {
20010015Speter	B75,	8,
20110015Speter	B110,	11,
20210015Speter	B150,	15,
20310015Speter	B300,	30,
20410015Speter	B600,	60,
20510015Speter	B1200,	120,
20610015Speter	B2000,	200,
20710015Speter	B2400,	240,
20810015Speter	B4800,	480,
20910015Speter	B9600,	960,
21010015Speter	B19200,	1920,
21110015Speter	B38400, 3840,
21210015Speter	B57600, 5760,
21310015Speter	B115200, 11520,
21410015Speter	-1,	-1
21510015Speter};
21610015Speterstatic volatile int in_intr = 0;	/* Inside interrupt handler? */
21710015Speter
21810047Speterstatic int si_default_rate =	TTYDEF_SPEED;
21910047Speterstatic int si_default_iflag =	0;
22010047Speterstatic int si_default_oflag =	0;
22110047Speterstatic int si_default_lflag =	0;
22210047Speter#ifdef SI_DEF_HWFLOW
22310047Speterstatic int si_default_cflag =	TTYDEF_CFLAG | CRTSCTS;
22410047Speter#else
22510047Speterstatic int si_default_cflag =	TTYDEF_CFLAG;
22610047Speter#endif
22710047Speter
22810015Speter#ifdef POLL
22915683Speterstatic int si_pollrate;			/* in addition to irq */
23017547Speterstatic int si_realpoll;			/* poll HW on timer */
23115639Speter
23216403SpeterSYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, "");
23317547SpeterSYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, "");
23415639Speter
23510015Speterstatic int init_finished = 0;
23610015Speterstatic void si_poll __P((void *));
23710015Speter#endif
23810015Speter
23910015Speter/*
24010015Speter * Array of adapter types and the corresponding RAM size. The order of
24110015Speter * entries here MUST match the ordinal of the adapter type.
24210015Speter */
24310015Speterstatic char *si_type[] = {
24410015Speter	"EMPTY",
24510015Speter	"SIHOST",
24610015Speter	"SI2",				/* MCA */
24710015Speter	"SIHOST2",
24810015Speter	"SIEISA",
24910015Speter};
25010015Speter
25110015Speter/* Look for a valid board at the given mem addr */
25212724Sphkstatic int
25310015Spetersiprobe(id)
25410015Speter	struct isa_device *id;
25510015Speter{
25610015Speter	struct si_softc *sc;
25710015Speter	int type;
25810015Speter	u_int i, ramsize;
25910015Speter	volatile BYTE was, *ux;
26010015Speter	volatile unsigned char *maddr;
26110015Speter	unsigned char *paddr;
26210015Speter
26317547Speter	si_pollrate = POLLHZ;		/* default 10 per second */
26417547Speter#ifdef REALPOLL
26517547Speter	si_realpoll = 1;		/* scan always */
26617547Speter#endif
26715683Speter
26810015Speter	maddr = id->id_maddr;		/* virtual address... */
26910015Speter	paddr = (caddr_t)vtophys(id->id_maddr);	/* physical address... */
27010015Speter
27112496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: probe at virtual=0x%x physical=0x%x\n",
27212496Speter		id->id_unit, id->id_maddr, paddr));
27310015Speter
27410015Speter	/*
27510015Speter	 * this is a lie, but it's easier than trying to handle caching
27610015Speter	 * and ram conflicts in the >1M and <16M region.
27710015Speter	 */
27810015Speter	if ((caddr_t)paddr < (caddr_t)IOM_BEGIN ||
27910015Speter	    (caddr_t)paddr >= (caddr_t)IOM_END) {
28012174Speter		printf("si%d: iomem (%lx) out of range\n",
28112174Speter			id->id_unit, (long)paddr);
28210015Speter		return(0);
28310015Speter	}
28410015Speter
28510015Speter	if (id->id_unit >= NSI) {
28610015Speter		/* THIS IS IMPOSSIBLE */
28710015Speter		return(0);
28810015Speter	}
28910015Speter
29010015Speter	if (((u_int)paddr & 0x7fff) != 0) {
29110015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
29210015Speter			"si%d: iomem (%x) not on 32k boundary\n",
29310015Speter			id->id_unit, paddr));
29410015Speter		return(0);
29510015Speter	}
29610015Speter
29710015Speter
29810015Speter	for (i=0; i < NSI; i++) {
29910015Speter		if ((sc = &si_softc[i]) == NULL)
30010015Speter			continue;
30110015Speter		if ((caddr_t)sc->sc_paddr == (caddr_t)paddr) {
30210015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
30310015Speter				"si%d: iomem (%x) already configured to si%d\n",
30410015Speter				id->id_unit, sc->sc_paddr, i));
30510015Speter			return(0);
30610015Speter		}
30710015Speter	}
30810015Speter
30910015Speter#if NEISA > 0
31010015Speter	if (id->id_iobase > 0x0fff) {	/* EISA card */
31110015Speter		int irq, port;
31210015Speter		unsigned long base;
31310015Speter		int eisa_irqs[] = { 0,IRQ1,IRQ2,IRQ3,IRQ4,IRQ5,IRQ6,IRQ7,
31410015Speter			IRQ8,IRQ9,IRQ10,IRQ11,IRQ12,IRQ13,IRQ14,IRQ15 };
31510015Speter
31610015Speter		port = id->id_iobase;
31710015Speter		base = (inb(port+1) << 24) | (inb(port) << 16);
31810015Speter		irq  = ((inb(port+2) >> 4) & 0xf);
31910015Speter
32010015Speter		id->id_irq = eisa_irqs[irq];
32110015Speter
32210015Speter		DPRINT((0, DBG_AUTOBOOT,
32312496Speter		    "si%d: EISA base %x, irq %x, id_irq %x, port %x\n",
32410015Speter		    id->id_unit, base, irq, id->id_irq, port));
32510015Speter
32610015Speter		if ((id->id_irq&(IRQ1|IRQ2|IRQ8|IRQ13)) != 0)
32710015Speter			goto bad_irq;
32810015Speter
32910015Speter		id->id_iobase &= 0xf000;
33010015Speter		id->id_iosize  = 0x0fff;
33110015Speter
33210015Speter		type = EISA;
33310015Speter		outb(p+2, (BYTE)irq << 4);
33410015Speter
33510015Speter		sc->sc_eisa_iobase = p;
33610015Speter		sc->sc_eisa_irqbits = irq << 4;
33710015Speter		ramsize = SIEISA_RAMSIZE;
33810015Speter		goto got_card;
33910015Speter	}
34010015Speter#endif
34110015Speter
34210015Speter	/* Is there anything out there? (0x17 is just an arbitrary number) */
34310015Speter	*maddr = 0x17;
34410015Speter	if (*maddr != 0x17) {
34510015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
34610015Speter			"si%d: 0x17 check fail at phys 0x%x\n",
34710015Speter			id->id_unit, paddr));
34810015Speterfail:
34910015Speter		return(0);
35010015Speter	}
35110015Speter	/*
35210015Speter	 * OK, now to see if whatever responded is really an SI card.
35310015Speter	 * Try for a MK II first (SIHOST2)
35410015Speter	 */
35510015Speter	for (i=SIPLSIG; i<SIPLSIG+8; i++)
35610015Speter		if ((*(maddr+i) & 7) != (~(BYTE)i & 7))
35710015Speter			goto try_mk1;
35810015Speter
35910015Speter	/* It must be an SIHOST2 */
36010015Speter	*(maddr + SIPLRESET) = 0;
36110015Speter	*(maddr + SIPLIRQCLR) = 0;
36210015Speter	*(maddr + SIPLIRQSET) = 0x10;
36310015Speter	type = SIHOST2;
36410015Speter	ramsize = SIHOST2_RAMSIZE;
36510015Speter	goto got_card;
36610015Speter
36710015Speter	/*
36810015Speter	 * Its not a MK II, so try for a MK I (SIHOST)
36910015Speter	 */
37010015Spetertry_mk1:
37110015Speter	*(maddr+SIRESET) = 0x0;		/* reset the card */
37210015Speter	*(maddr+SIINTCL) = 0x0;		/* clear int */
37310015Speter	*(maddr+SIRAM) = 0x17;
37410015Speter	if (*(maddr+SIRAM) != (BYTE)0x17)
37510015Speter		goto fail;
37610015Speter	*(maddr+0x7ff8) = 0x17;
37710015Speter	if (*(maddr+0x7ff8) != (BYTE)0x17) {
37810015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
37910015Speter			"si%d: 0x17 check fail at phys 0x%x = 0x%x\n",
38010015Speter			id->id_unit, paddr+0x77f8, *(maddr+0x77f8)));
38110015Speter		goto fail;
38210015Speter	}
38310015Speter
38410015Speter	/* It must be an SIHOST (maybe?) - there must be a better way XXXX */
38510015Speter	type = SIHOST;
38610015Speter	ramsize = SIHOST_RAMSIZE;
38710015Speter
38810015Spetergot_card:
38912496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: found type %d card, try memory test\n",
39012496Speter		id->id_unit, type));
39110015Speter	/* Try the acid test */
39218515Speter	ux = maddr + SIRAM;
39310015Speter	for (i=0; i<ramsize; i++, ux++)
39410015Speter		*ux = (BYTE)(i&0xff);
39518515Speter	ux = maddr + SIRAM;
39610015Speter	for (i=0; i<ramsize; i++, ux++) {
39710015Speter		if ((was = *ux) != (BYTE)(i&0xff)) {
39810015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
39912174Speter				"si%d: match fail at phys 0x%x, was %x should be %x\n",
40010015Speter				id->id_unit, paddr+i, was, i&0xff));
40110015Speter			goto fail;
40210015Speter		}
40310015Speter	}
40410015Speter
40510015Speter	/* clear out the RAM */
40618515Speter	ux = maddr + SIRAM;
40710015Speter	for (i=0; i<ramsize; i++)
40810015Speter		*ux++ = 0;
40918515Speter	ux = maddr + SIRAM;
41010015Speter	for (i=0; i<ramsize; i++) {
41110015Speter		if ((was = *ux++) != 0) {
41210015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
41312174Speter				"si%d: clear fail at phys 0x%x, was %x\n",
41410015Speter				id->id_unit, paddr+i, was));
41510015Speter			goto fail;
41610015Speter		}
41710015Speter	}
41810015Speter
41910015Speter	/*
42010015Speter	 * Success, we've found a valid board, now fill in
42110015Speter	 * the adapter structure.
42210015Speter	 */
42310015Speter	switch (type) {
42410015Speter	case SIHOST2:
42510015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
42610015Speterbad_irq:
42710015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
42810015Speter				"si%d: bad IRQ value - %d\n",
42910015Speter				id->id_unit, id->id_irq));
43010015Speter			return(0);
43110015Speter		}
43210015Speter		id->id_msize = SIHOST2_MEMSIZE;
43310015Speter		break;
43410015Speter	case SIHOST:
43510015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
43610015Speter			goto bad_irq;
43710015Speter		}
43810015Speter		id->id_msize = SIHOST_MEMSIZE;
43910015Speter		break;
44010015Speter	case SIEISA:
44110015Speter		id->id_msize = SIEISA_MEMSIZE;
44210015Speter		break;
44310015Speter	case SI2:		/* MCA */
44410015Speter	default:
44510015Speter		printf("si%d: %s not supported\n", id->id_unit, si_type[type]);
44610015Speter		return(0);
44710015Speter	}
44810015Speter	si_softc[id->id_unit].sc_type = type;
44910015Speter	si_softc[id->id_unit].sc_typename = si_type[type];
45010015Speter	return(-1);	/* -1 == found */
45110015Speter}
45210015Speter
45310015Speter/*
45410015Speter * Attach the device.  Initialize the card.
45510015Speter */
45612724Sphkstatic int
45710015Spetersiattach(id)
45810015Speter	struct isa_device *id;
45910015Speter{
46010015Speter	int unit = id->id_unit;
46110015Speter	struct si_softc *sc = &si_softc[unit];
46210015Speter	struct si_port *pp;
46310015Speter	volatile struct si_channel *ccbp;
46410015Speter	volatile struct si_reg *regp;
46510015Speter	volatile caddr_t maddr;
46610015Speter	struct si_module *modp;
46710015Speter	struct tty *tp;
46810015Speter	struct speedtab *spt;
46910015Speter	int nmodule, nport, x, y;
47012174Speter	int uart_type;
47110015Speter
47212496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", id->id_unit));
47310015Speter
47410015Speter	sc->sc_paddr = (caddr_t)vtophys(id->id_maddr);
47510015Speter	sc->sc_maddr = id->id_maddr;
47610015Speter	sc->sc_irq = id->id_irq;
47710015Speter
47810015Speter	sc->sc_ports = NULL;			/* mark as uninitialised */
47910015Speter
48010015Speter	maddr = sc->sc_maddr;
48110015Speter
48210015Speter	/*
48310015Speter	 * OK, now lets download the firmware and try and boot the CPU..
48410015Speter	 */
48510015Speter
48612496Speter	DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n",
48712496Speter		id->id_unit, si_dsize));
48810015Speter	bcopy(si_download, maddr, si_dsize);
48910015Speter
49010015Speter	switch (sc->sc_type) {
49110015Speter	case SIEISA:
49210015Speter#if NEISA > 0
49310015Speter		/* modify the Z280 firmware to tell it that it's on an EISA */
49410015Speter		*(maddr+0x42) = 1;
49510015Speter		outb(sc->sc_eisa_iobase+2, sc->sc_eisa_irqbits | 4);
49610015Speter		(void)inb(sc->sc_eisa_iobase+3); /* reset interrupt */
49710015Speter		break;
49810015Speter#endif	/* fall-through if not EISA */
49910015Speter	case SI2:
50012174Speter		/*
50112174Speter		 * must get around to converting the code for
50212174Speter		 * these one day, if FreeBSD ever supports it.
50312174Speter		 */
50410015Speter		return 0;
50510015Speter	case SIHOST:
50610015Speter		*(maddr+SIRESET_CL) = 0;
50710015Speter		*(maddr+SIINTCL_CL) = 0;
50810015Speter		break;
50910015Speter	case SIHOST2:
51010015Speter		*(maddr+SIPLRESET) = 0x10;
51110015Speter		switch (sc->sc_irq) {
51210015Speter		case IRQ11:
51310015Speter			*(maddr+SIPLIRQ11) = 0x10;
51410015Speter			break;
51510015Speter		case IRQ12:
51610015Speter			*(maddr+SIPLIRQ12) = 0x10;
51710015Speter			break;
51810015Speter		case IRQ15:
51910015Speter			*(maddr+SIPLIRQ15) = 0x10;
52010015Speter			break;
52110015Speter		}
52210015Speter		*(maddr+SIPLIRQCLR) = 0x10;
52310015Speter		break;
52410015Speter	}
52510015Speter
52610015Speter	DELAY(1000000);			/* wait around for a second */
52710015Speter
52810015Speter	regp = (struct si_reg *)maddr;
52910015Speter	y = 0;
53010015Speter					/* wait max of 5 sec for init OK */
53110015Speter	while (regp->initstat == 0 && y++ < 10) {
53210015Speter		DELAY(500000);
53310015Speter	}
53410015Speter	switch (regp->initstat) {
53510015Speter	case 0:
53610015Speter		printf("si%d: startup timeout - aborting\n", unit);
53712174Speter		sc->sc_type = SIEMPTY;
53810015Speter		return 0;
53910015Speter	case 1:
54012174Speter			/* set throttle to 125 intr per second */
54115639Speter		regp->int_count = INT_COUNT;
54210015Speter			/* rx intr max of 25 timer per second */
54315639Speter		regp->rx_int_count = RXINT_COUNT;
54410015Speter		regp->int_pending = 0;		/* no intr pending */
54510015Speter		regp->int_scounter = 0;	/* reset counter */
54610015Speter		break;
54710015Speter	case 0xff:
54810015Speter		/*
54910015Speter		 * No modules found, so give up on this one.
55010015Speter		 */
55110015Speter		printf("si%d: %s - no ports found\n", unit,
55210015Speter			si_type[sc->sc_type]);
55310015Speter		return 0;
55410015Speter	default:
55510015Speter		printf("si%d: Z280 version error - initstat %x\n",
55610015Speter			unit, regp->initstat);
55710015Speter		return 0;
55810015Speter	}
55910015Speter
56010015Speter	/*
56110015Speter	 * First time around the ports just count them in order
56210015Speter	 * to allocate some memory.
56310015Speter	 */
56410015Speter	nport = 0;
56510015Speter	modp = (struct si_module *)(maddr + 0x80);
56610015Speter	for (;;) {
56712174Speter		DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp));
56810015Speter		switch (modp->sm_type & (~MMASK)) {
56910015Speter		case M232:
57010015Speter		case M422:
57110015Speter			DPRINT((0, DBG_DOWNLOAD,
57212174Speter				"si%d: Found 232/422 module, %d ports\n",
57310015Speter				unit, (int)(modp->sm_type & MMASK)));
57410015Speter
57510015Speter			/* this is a firmware issue */
57610015Speter			if (si_Nports == SI_MAXPORTPERCARD) {
57710015Speter				printf("si%d: extra ports ignored\n", unit);
57810015Speter				continue;
57910015Speter			}
58010015Speter
58110015Speter			x = modp->sm_type & MMASK;
58210015Speter			nport += x;
58310015Speter			si_Nports += x;
58410015Speter			si_Nmodules++;
58510015Speter			break;
58610015Speter		default:
58710015Speter			printf("si%d: unknown module type %d\n",
58810015Speter				unit, modp->sm_type);
58910015Speter			break;
59010015Speter		}
59110015Speter		if (modp->sm_next == 0)
59210015Speter			break;
59310015Speter		modp = (struct si_module *)
59410015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
59510015Speter	}
59610015Speter	sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport,
59710015Speter		M_DEVBUF, M_NOWAIT);
59810015Speter	if (sc->sc_ports == 0) {
59910015Spetermem_fail:
60010015Speter		printf("si%d: fail to malloc memory for port structs\n",
60110015Speter			unit);
60210015Speter		return 0;
60310015Speter	}
60410015Speter	bzero(sc->sc_ports, sizeof(struct si_port) * nport);
60510015Speter	sc->sc_nport = nport;
60610015Speter
60710015Speter	/*
60810015Speter	 * allocate tty structures for ports
60910015Speter	 */
61010015Speter	tp = (struct tty *)malloc(sizeof(*tp) * nport, M_DEVBUF, M_NOWAIT);
61110015Speter	if (tp == 0)
61210015Speter		goto mem_fail;
61310015Speter	bzero(tp, sizeof(*tp) * nport);
61410962Speter	si_tty = tp;
61510015Speter
61610015Speter	/*
61710015Speter	 * Scan round the ports again, this time initialising.
61810015Speter	 */
61910015Speter	pp = sc->sc_ports;
62010015Speter	nmodule = 0;
62110015Speter	modp = (struct si_module *)(maddr + 0x80);
62212174Speter	uart_type = 0;
62310015Speter	for (;;) {
62410015Speter		switch (modp->sm_type & (~MMASK)) {
62510015Speter		case M232:
62610015Speter		case M422:
62710015Speter			nmodule++;
62810015Speter			nport = (modp->sm_type & MMASK);
62910015Speter			ccbp = (struct si_channel *)((char *)modp+0x100);
63012174Speter			if (uart_type == 0)
63112174Speter				uart_type = ccbp->type;
63210015Speter			for (x = 0; x < nport; x++, pp++, ccbp++) {
63310015Speter				pp->sp_ccb = ccbp;	/* save the address */
63410015Speter				pp->sp_tty = tp++;
63510015Speter				pp->sp_pend = IDLE_CLOSE;
63610015Speter				pp->sp_state = 0;	/* internal flag */
63710015Speter				pp->sp_dtr_wait = 3 * hz;
63810047Speter				pp->sp_iin.c_iflag = si_default_iflag;
63910047Speter				pp->sp_iin.c_oflag = si_default_oflag;
64010047Speter				pp->sp_iin.c_cflag = si_default_cflag;
64110047Speter				pp->sp_iin.c_lflag = si_default_lflag;
64210015Speter				termioschars(&pp->sp_iin);
64310015Speter				pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed =
64410047Speter					si_default_rate;
64510015Speter				pp->sp_iout = pp->sp_iin;
64610015Speter			}
64710015Speter			break;
64810015Speter		default:
64910015Speter			break;
65010015Speter		}
65110015Speter		if (modp->sm_next == 0) {
65212174Speter			printf("si%d: card: %s, ports: %d, modules: %d (type: %d)\n",
65310015Speter				unit,
65410015Speter				sc->sc_typename,
65510015Speter				sc->sc_nport,
65612174Speter				nmodule,
65712174Speter				uart_type);
65810015Speter			break;
65910015Speter		}
66010015Speter		modp = (struct si_module *)
66110015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
66210015Speter	}
66310015Speter	if (done_chartimes == 0) {
66410015Speter		for (spt = chartimes ; spt->sp_speed != -1; spt++) {
66510015Speter			if ((spt->sp_code /= hz) == 0)
66610015Speter				spt->sp_code = 1;
66710015Speter		}
66810015Speter		done_chartimes = 1;
66910015Speter	}
67012502Sjulian
67112675Sjulian#ifdef DEVFS
67212675Sjulian/*	path	name	devsw		minor	type   uid gid perm*/
67313169Speter	for ( x = 0; x < sc->sc_nport; x++ ) {
67413165Speter		y = x + 1;	/* For sync with the manuals that start at 1 */
67513630Sphk		sc->devfs_token[x].ttyd = devfs_add_devswf(
67613630Sphk			&si_cdevsw, x,
67713630Sphk			DV_CHR, 0, 0, 0600, "ttyA%02d", y);
67813630Sphk		sc->devfs_token[x].cuaa = devfs_add_devswf(
67913630Sphk			&si_cdevsw, x + 128,
68013630Sphk			DV_CHR, 0, 0, 0600, "cuaA%02d", y);
68113630Sphk		sc->devfs_token[x].ttyi = devfs_add_devswf(
68213630Sphk			&si_cdevsw, x + 0x10000,
68313630Sphk			DV_CHR, 0, 0, 0600, "ttyiA%02d", y);
68413630Sphk		sc->devfs_token[x].ttyl = devfs_add_devswf(
68513630Sphk			&si_cdevsw, x + 0x20000,
68613630Sphk			DV_CHR, 0, 0, 0600, "ttylA%02d", y);
68712675Sjulian	}
68814873Sscrappy	sc->control_token =
68914873Sscrappy		devfs_add_devswf(&si_cdevsw, 0x40000, DV_CHR, 0, 0, 0600,
69014873Sscrappy				 "si_control");
69112675Sjulian#endif
69210015Speter	return (1);
69310015Speter}
69410015Speter
69512675Sjulianstatic	int
69610015Spetersiopen(dev, flag, mode, p)
69710015Speter	dev_t dev;
69810015Speter	int flag, mode;
69910015Speter	struct proc *p;
70010015Speter{
70110015Speter	int oldspl, error;
70210015Speter	int card, port;
70310015Speter	register struct si_softc *sc;
70410015Speter	register struct tty *tp;
70510015Speter	volatile struct si_channel *ccbp;
70610015Speter	struct si_port *pp;
70710015Speter	int mynor = minor(dev);
70810015Speter
70910015Speter	/* quickly let in /dev/si_control */
71010015Speter	if (IS_CONTROLDEV(mynor)) {
71118515Speter		if ((error = suser(p->p_ucred, &p->p_acflag)))
71210015Speter			return(error);
71310015Speter		return(0);
71410015Speter	}
71510015Speter
71610015Speter	card = SI_CARD(mynor);
71710015Speter	if (card >= NSI)
71810015Speter		return (ENXIO);
71910015Speter	sc = &si_softc[card];
72010015Speter
72112174Speter	if (sc->sc_type == SIEMPTY) {
72212174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n",
72310015Speter			card, sc->sc_typename));
72410015Speter		return(ENXIO);
72510015Speter	}
72610015Speter
72710015Speter	port = SI_PORT(mynor);
72810015Speter	if (port >= sc->sc_nport) {
72912174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n",
73010015Speter			card, sc->sc_nport));
73110015Speter		return(ENXIO);
73210015Speter	}
73310015Speter
73410015Speter#ifdef	POLL
73510015Speter	/*
73610015Speter	 * We've now got a device, so start the poller.
73710015Speter	 */
73810015Speter	if (init_finished == 0) {
73915639Speter		timeout(si_poll, (caddr_t)0L, si_pollrate);
74010015Speter		init_finished = 1;
74110015Speter	}
74210015Speter#endif
74310015Speter
74410015Speter	/* initial/lock device */
74510015Speter	if (IS_STATE(mynor)) {
74610015Speter		return(0);
74710015Speter	}
74810015Speter
74910015Speter	pp = sc->sc_ports + port;
75010015Speter	tp = pp->sp_tty;			/* the "real" tty */
75110015Speter	ccbp = pp->sp_ccb;			/* Find control block */
75210015Speter	DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%x,%x,%x,%x)\n",
75310015Speter		dev, flag, mode, p));
75410015Speter
75510015Speter	oldspl = spltty();			/* Keep others out */
75610015Speter	error = 0;
75710015Speter
75810015Speteropen_top:
75910015Speter	while (pp->sp_state & SS_DTR_OFF) {
76010015Speter		error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sidtr", 0);
76110015Speter		if (error != 0)
76210015Speter			goto out;
76310015Speter	}
76410015Speter
76510015Speter	if (tp->t_state & TS_ISOPEN) {
76610015Speter		/*
76710015Speter		 * The device is open, so everything has been initialised.
76810015Speter		 * handle conflicts.
76910015Speter		 */
77010015Speter		if (IS_CALLOUT(mynor)) {
77110015Speter			if (!pp->sp_active_out) {
77210015Speter				error = EBUSY;
77310015Speter				goto out;
77410015Speter			}
77510015Speter		} else {
77610015Speter			if (pp->sp_active_out) {
77710015Speter				if (flag & O_NONBLOCK) {
77810015Speter					error = EBUSY;
77910015Speter					goto out;
78010015Speter				}
78110015Speter				error = tsleep(&pp->sp_active_out,
78210015Speter						TTIPRI|PCATCH, "sibi", 0);
78310015Speter				if (error != 0)
78410015Speter					goto out;
78510015Speter				goto open_top;
78610015Speter			}
78710015Speter		}
78810015Speter		if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
78910015Speter			DPRINT((pp, DBG_OPEN|DBG_FAIL,
79010015Speter				"already open and EXCLUSIVE set\n"));
79110015Speter			error = EBUSY;
79210015Speter			goto out;
79310015Speter		}
79410015Speter	} else {
79510015Speter		/*
79610015Speter		 * The device isn't open, so there are no conflicts.
79710015Speter		 * Initialize it. Avoid sleep... :-)
79810015Speter		 */
79910015Speter		DPRINT((pp, DBG_OPEN, "first open\n"));
80010015Speter		tp->t_oproc = si_start;
80110015Speter		tp->t_param = siparam;
80210015Speter		tp->t_dev = dev;
80310015Speter		tp->t_termios = mynor & SI_CALLOUT_MASK
80410015Speter				? pp->sp_iout : pp->sp_iin;
80510015Speter
80610015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
80710015Speter
80810015Speter		++pp->sp_wopeners;	/* in case of sleep in siparam */
80910015Speter
81010015Speter		error = siparam(tp, &tp->t_termios);
81110015Speter
81210015Speter		--pp->sp_wopeners;
81310015Speter		if (error != 0)
81410015Speter			goto out;
81510015Speter		/* XXX: we should goto_top if siparam slept */
81610015Speter
81710015Speter		ttsetwater(tp);
81810015Speter
81910015Speter		/* set initial DCD state */
82010015Speter		pp->sp_last_hi_ip = ccbp->hi_ip;
82110015Speter		if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) {
82210015Speter			(*linesw[tp->t_line].l_modem)(tp, 1);
82310015Speter		}
82410015Speter	}
82510015Speter
82610015Speter	/* whoops! we beat the close! */
82710015Speter	if (pp->sp_state & SS_CLOSING) {
82810015Speter		/* try and stop it from proceeding to bash the hardware */
82910015Speter		pp->sp_state &= ~SS_CLOSING;
83010015Speter	}
83110015Speter
83210015Speter	/*
83310015Speter	 * Wait for DCD if necessary
83410015Speter	 */
83510015Speter	if (!(tp->t_state & TS_CARR_ON)
83610015Speter	    && !IS_CALLOUT(mynor)
83710015Speter	    && !(tp->t_cflag & CLOCAL)
83810015Speter	    && !(flag & O_NONBLOCK)) {
83910015Speter		++pp->sp_wopeners;
84010015Speter		DPRINT((pp, DBG_OPEN, "sleeping for carrier\n"));
84110015Speter		error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0);
84210015Speter		--pp->sp_wopeners;
84310015Speter		if (error != 0)
84410015Speter			goto out;
84510015Speter		goto open_top;
84610015Speter	}
84710015Speter
84810015Speter	error = (*linesw[tp->t_line].l_open)(dev, tp);
84910015Speter	si_disc_optim(tp, &tp->t_termios, pp);
85010015Speter	if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor))
85110015Speter		pp->sp_active_out = TRUE;
85210015Speter
85310015Speter	pp->sp_state |= SS_OPEN;	/* made it! */
85410015Speter
85510015Speterout:
85610015Speter	splx(oldspl);
85710015Speter
85810015Speter	DPRINT((pp, DBG_OPEN, "leaving siopen\n"));
85910015Speter
86010015Speter	if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0)
86110015Speter		sihardclose(pp);
86210015Speter
86310015Speter	return(error);
86410015Speter}
86510015Speter
86612675Sjulianstatic	int
86710015Spetersiclose(dev, flag, mode, p)
86810015Speter	dev_t dev;
86910015Speter	int flag, mode;
87010015Speter	struct proc *p;
87110015Speter{
87210015Speter	register struct si_port *pp;
87310015Speter	register struct tty *tp;
87410015Speter	int oldspl;
87510015Speter	int error = 0;
87610015Speter	int mynor = minor(dev);
87710015Speter
87810015Speter	if (IS_SPECIAL(mynor))
87910015Speter		return(0);
88010015Speter
88110015Speter	oldspl = spltty();
88210015Speter
88310015Speter	pp = MINOR2PP(mynor);
88410015Speter	tp = pp->sp_tty;
88510015Speter
88610015Speter	DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%x,%x,%x,%x) sp_state:%x\n",
88710015Speter		dev, flag, mode, p, pp->sp_state));
88810015Speter
88910015Speter	/* did we sleep and loose a race? */
89010015Speter	if (pp->sp_state & SS_CLOSING) {
89110015Speter		/* error = ESOMETING? */
89210015Speter		goto out;
89310015Speter	}
89410015Speter
89510015Speter	/* begin race detection.. */
89610015Speter	pp->sp_state |= SS_CLOSING;
89710015Speter
89810015Speter	si_write_enable(pp, 0);		/* block writes for ttywait() */
89910015Speter
90010015Speter	/* THIS MAY SLEEP IN TTYWAIT!!! */
90110015Speter	(*linesw[tp->t_line].l_close)(tp, flag);
90210015Speter
90310015Speter	si_write_enable(pp, 1);
90410015Speter
90510015Speter	/* did we sleep and somebody started another open? */
90610015Speter	if (!(pp->sp_state & SS_CLOSING)) {
90710015Speter		/* error = ESOMETING? */
90810015Speter		goto out;
90910015Speter	}
91010015Speter	/* ok. we are now still on the right track.. nuke the hardware */
91110015Speter
91210015Speter	if (pp->sp_state & SS_LSTART) {
91329677Sgibbs		untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch);
91410015Speter		pp->sp_state &= ~SS_LSTART;
91510015Speter	}
91610015Speter
91710015Speter	sistop(tp, FREAD | FWRITE);
91810015Speter
91910015Speter	sihardclose(pp);
92010015Speter	ttyclose(tp);
92110015Speter	pp->sp_state &= ~SS_OPEN;
92210015Speter
92310015Speterout:
92410015Speter	DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n"));
92510015Speter	splx(oldspl);
92610015Speter	return(error);
92710015Speter}
92810015Speter
92910015Speterstatic void
93010015Spetersihardclose(pp)
93110015Speter	struct si_port *pp;
93210015Speter{
93310015Speter	int oldspl;
93410015Speter	struct tty *tp;
93510015Speter	volatile struct si_channel *ccbp;
93610015Speter
93710015Speter	oldspl = spltty();
93810015Speter
93910015Speter	tp = pp->sp_tty;
94010015Speter	ccbp = pp->sp_ccb;			/* Find control block */
94110015Speter	if (tp->t_cflag & HUPCL
94218515Speter	    || (!pp->sp_active_out
94318515Speter	        && !(ccbp->hi_ip & IP_DCD)
94418515Speter	        && !(pp->sp_iin.c_cflag && CLOCAL))
94510015Speter	    || !(tp->t_state & TS_ISOPEN)) {
94610015Speter
94710015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
94810015Speter		(void) si_command(pp, FCLOSE, SI_NOWAIT);
94910015Speter
95010015Speter		if (pp->sp_dtr_wait != 0) {
95110015Speter			timeout(sidtrwakeup, pp, pp->sp_dtr_wait);
95210015Speter			pp->sp_state |= SS_DTR_OFF;
95310015Speter		}
95410015Speter
95510015Speter	}
95610015Speter	pp->sp_active_out = FALSE;
95710015Speter	wakeup((caddr_t)&pp->sp_active_out);
95810015Speter	wakeup(TSA_CARR_ON(tp));
95910015Speter
96010015Speter	splx(oldspl);
96110015Speter}
96210015Speter
96310015Speter
96410015Speter/*
96510015Speter * called at splsoftclock()...
96610015Speter */
96710015Speterstatic void
96810015Spetersidtrwakeup(chan)
96910015Speter	void *chan;
97010015Speter{
97110015Speter	struct si_port *pp;
97210015Speter	int oldspl;
97310015Speter
97410015Speter	oldspl = spltty();
97510015Speter
97610015Speter	pp = (struct si_port *)chan;
97710015Speter	pp->sp_state &= ~SS_DTR_OFF;
97810015Speter	wakeup(&pp->sp_dtr_wait);
97910015Speter
98010015Speter	splx(oldspl);
98110015Speter}
98210015Speter
98310015Speter/*
98410015Speter * User level stuff - read and write
98510015Speter */
98612675Sjulianstatic	int
98710015Spetersiread(dev, uio, flag)
98810015Speter	register dev_t dev;
98910015Speter	struct uio *uio;
99010015Speter	int flag;
99110015Speter{
99210015Speter	register struct tty *tp;
99310015Speter	int mynor = minor(dev);
99410015Speter
99510015Speter	if (IS_SPECIAL(mynor)) {
99610015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_READ, "siread(CONTROLDEV!!)\n"));
99710015Speter		return(ENODEV);
99810015Speter	}
99910015Speter	tp = MINOR2TP(mynor);
100010015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_READ,
100110015Speter		"siread(%x,%x,%x)\n", dev, uio, flag));
100210015Speter	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
100310015Speter}
100410015Speter
100510015Speter
100612675Sjulianstatic	int
100710015Spetersiwrite(dev, uio, flag)
100810015Speter	dev_t dev;
100910015Speter	struct uio *uio;
101010015Speter	int flag;
101110015Speter{
101210015Speter	register struct si_port *pp;
101310015Speter	register struct tty *tp;
101410015Speter	int error = 0;
101510015Speter	int mynor = minor(dev);
101610015Speter	int oldspl;
101710015Speter
101810015Speter	if (IS_SPECIAL(mynor)) {
101910015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n"));
102010015Speter		return(ENODEV);
102110015Speter	}
102210015Speter	pp = MINOR2PP(mynor);
102310015Speter	tp = pp->sp_tty;
102410015Speter	DPRINT((pp, DBG_WRITE, "siwrite(%x,%x,%x)\n", dev, uio, flag));
102510015Speter
102610015Speter	oldspl = spltty();
102710015Speter	/*
102810015Speter	 * If writes are currently blocked, wait on the "real" tty
102910015Speter	 */
103010015Speter	while (pp->sp_state & SS_BLOCKWRITE) {
103110015Speter		pp->sp_state |= SS_WAITWRITE;
103210015Speter		DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n"));
103318515Speter		if ((error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH,
103418515Speter				     "siwrite", tp->t_timeout))) {
103517291Speter			if (error == EWOULDBLOCK)
103617290Speter				error = EIO;
103710015Speter			goto out;
103817290Speter		}
103910015Speter	}
104010015Speter
104110015Speter	error = (*linesw[tp->t_line].l_write)(tp, uio, flag);
104210015Speterout:
104310015Speter	splx(oldspl);
104410015Speter	return (error);
104510015Speter}
104610015Speter
104710015Speter
104812675Sjulianstatic	struct tty *
104910015Spetersidevtotty(dev_t dev)
105010015Speter{
105110015Speter	struct si_port *pp;
105210015Speter	int mynor = minor(dev);
105310015Speter	struct si_softc *sc = &si_softc[SI_CARD(mynor)];
105410015Speter
105510015Speter	if (IS_SPECIAL(mynor))
105610015Speter		return(NULL);
105710015Speter	if (SI_PORT(mynor) >= sc->sc_nport)
105810015Speter		return(NULL);
105910015Speter	pp = MINOR2PP(mynor);
106010015Speter	return (pp->sp_tty);
106110015Speter}
106210015Speter
106312675Sjulianstatic	int
106410015Spetersiioctl(dev, cmd, data, flag, p)
106510015Speter	dev_t dev;
106610015Speter	int cmd;
106710015Speter	caddr_t data;
106810015Speter	int flag;
106910015Speter	struct proc *p;
107010015Speter{
107110015Speter	struct si_port *pp;
107210015Speter	register struct tty *tp;
107310015Speter	int error;
107410015Speter	int mynor = minor(dev);
107510015Speter	int oldspl;
107610015Speter	int blocked = 0;
107710015Speter#if defined(COMPAT_43)
107810015Speter	int oldcmd;
107910015Speter	struct termios term;
108010015Speter#endif
108110015Speter
108210015Speter	if (IS_SI_IOCTL(cmd))
108310015Speter		return(si_Sioctl(dev, cmd, data, flag, p));
108410015Speter
108510015Speter	pp = MINOR2PP(mynor);
108610015Speter	tp = pp->sp_tty;
108710015Speter
108810015Speter	DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%x,%x,%x,%x)\n",
108910015Speter		dev, cmd, data, flag));
109010015Speter	if (IS_STATE(mynor)) {
109110015Speter		struct termios *ct;
109210015Speter
109310015Speter		switch (mynor & SI_STATE_MASK) {
109410015Speter		case SI_INIT_STATE_MASK:
109510015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
109610015Speter			break;
109710015Speter		case SI_LOCK_STATE_MASK:
109816839Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_lout : &pp->sp_lin;
109910015Speter			break;
110010015Speter		default:
110110015Speter			return (ENODEV);
110210015Speter		}
110310015Speter		switch (cmd) {
110410015Speter		case TIOCSETA:
110510015Speter			error = suser(p->p_ucred, &p->p_acflag);
110610015Speter			if (error != 0)
110710015Speter				return (error);
110810015Speter			*ct = *(struct termios *)data;
110910015Speter			return (0);
111010015Speter		case TIOCGETA:
111110015Speter			*(struct termios *)data = *ct;
111210015Speter			return (0);
111310015Speter		case TIOCGETD:
111410015Speter			*(int *)data = TTYDISC;
111510015Speter			return (0);
111610015Speter		case TIOCGWINSZ:
111710015Speter			bzero(data, sizeof(struct winsize));
111810015Speter			return (0);
111910015Speter		default:
112010015Speter			return (ENOTTY);
112110015Speter		}
112210015Speter	}
112310015Speter	/*
112410015Speter	 * Do the old-style ioctl compat routines...
112510015Speter	 */
112610015Speter#if defined(COMPAT_43)
112710015Speter	term = tp->t_termios;
112810015Speter	oldcmd = cmd;
112910015Speter	error = ttsetcompat(tp, &cmd, data, &term);
113010015Speter	if (error != 0)
113110015Speter		return (error);
113210015Speter	if (cmd != oldcmd)
113310015Speter		data = (caddr_t)&term;
113410015Speter#endif
113510015Speter	/*
113610015Speter	 * Do the initial / lock state business
113710015Speter	 */
113810015Speter	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
113910015Speter		int     cc;
114010015Speter		struct termios *dt = (struct termios *)data;
114110015Speter		struct termios *lt = mynor & SI_CALLOUT_MASK
114210015Speter				     ? &pp->sp_lout : &pp->sp_lin;
114310015Speter
114410015Speter		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
114510015Speter			| (dt->c_iflag & ~lt->c_iflag);
114610015Speter		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
114710015Speter			| (dt->c_oflag & ~lt->c_oflag);
114810015Speter		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
114910015Speter			| (dt->c_cflag & ~lt->c_cflag);
115010015Speter		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
115110015Speter			| (dt->c_lflag & ~lt->c_lflag);
115210015Speter		for (cc = 0; cc < NCCS; ++cc)
115310015Speter			if (lt->c_cc[cc] != 0)
115410015Speter				dt->c_cc[cc] = tp->t_cc[cc];
115510015Speter		if (lt->c_ispeed != 0)
115610015Speter			dt->c_ispeed = tp->t_ispeed;
115710015Speter		if (lt->c_ospeed != 0)
115810015Speter			dt->c_ospeed = tp->t_ospeed;
115910015Speter	}
116010015Speter
116110015Speter	/*
116210015Speter	 * Block user-level writes to give the ttywait()
116310015Speter	 * a chance to completely drain for commands
116410015Speter	 * that require the port to be in a quiescent state.
116510015Speter	 */
116610015Speter	switch (cmd) {
116710015Speter	case TIOCSETAW: case TIOCSETAF:
116817396Speter	case TIOCDRAIN:
116917396Speter#ifdef COMPAT_43
117017396Speter	case TIOCSETP:
117117396Speter#endif
117210015Speter		blocked++;	/* block writes for ttywait() and siparam() */
117310015Speter		si_write_enable(pp, 0);
117410015Speter	}
117510015Speter
117610015Speter	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
117710015Speter	if (error >= 0)
117810015Speter		goto out;
117910015Speter
118010015Speter	oldspl = spltty();
118110015Speter
118210015Speter	error = ttioctl(tp, cmd, data, flag);
118310015Speter	si_disc_optim(tp, &tp->t_termios, pp);
118410015Speter	if (error >= 0)
118510015Speter		goto outspl;
118610015Speter
118710015Speter	switch (cmd) {
118810015Speter	case TIOCSBRK:
118916575Speter		si_command(pp, SBREAK, SI_WAIT);
119010015Speter		break;
119110015Speter	case TIOCCBRK:
119216575Speter		si_command(pp, EBREAK, SI_WAIT);
119310015Speter		break;
119410015Speter	case TIOCSDTR:
119510015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
119610015Speter		break;
119710015Speter	case TIOCCDTR:
119810015Speter		(void) si_modem(pp, SET, 0);
119910015Speter		break;
120010015Speter	case TIOCMSET:
120110015Speter		(void) si_modem(pp, SET, *(int *)data);
120210015Speter		break;
120310015Speter	case TIOCMBIS:
120410015Speter		(void) si_modem(pp, BIS, *(int *)data);
120510015Speter		break;
120610015Speter	case TIOCMBIC:
120710015Speter		(void) si_modem(pp, BIC, *(int *)data);
120810015Speter		break;
120910015Speter	case TIOCMGET:
121010015Speter		*(int *)data = si_modem(pp, GET, 0);
121110015Speter		break;
121210015Speter	case TIOCMSDTRWAIT:
121310015Speter		/* must be root since the wait applies to following logins */
121410015Speter		error = suser(p->p_ucred, &p->p_acflag);
121510015Speter		if (error != 0) {
121610015Speter			goto outspl;
121710015Speter		}
121810015Speter		pp->sp_dtr_wait = *(int *)data * hz / 100;
121910015Speter		break;
122010015Speter	case TIOCMGDTRWAIT:
122110015Speter		*(int *)data = pp->sp_dtr_wait * 100 / hz;
122210015Speter		break;
122310015Speter
122410015Speter	default:
122510015Speter		error = ENOTTY;
122610015Speter	}
122710015Speter	error = 0;
122810015Speteroutspl:
122910015Speter	splx(oldspl);
123010015Speterout:
123110015Speter	DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error));
123210015Speter	if (blocked)
123310015Speter		si_write_enable(pp, 1);
123410015Speter	return(error);
123510015Speter}
123610015Speter
123710015Speter/*
123810015Speter * Handle the Specialix ioctls. All MUST be called via the CONTROL device
123910015Speter */
124010015Speterstatic int
124110015Spetersi_Sioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
124210015Speter{
124310015Speter	struct si_softc *xsc;
124410015Speter	register struct si_port *xpp;
124510015Speter	volatile struct si_reg *regp;
124610015Speter	struct si_tcsi *dp;
124710044Speter	struct si_pstat *sps;
124811872Sphk	int *ip, error = 0;
124910015Speter	int oldspl;
125010015Speter	int card, port;
125110015Speter	int mynor = minor(dev);
125210015Speter
125310015Speter	DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%x,%x,%x,%x)\n",
125410015Speter		dev, cmd, data, flag));
125510015Speter
125610044Speter#if 1
125710044Speter	DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT));
125810044Speter	DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB));
125910044Speter	DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY));
126010044Speter#endif
126110044Speter
126210015Speter	if (!IS_CONTROLDEV(mynor)) {
126310015Speter		DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n"));
126410015Speter		return(ENODEV);
126510015Speter	}
126610015Speter
126710015Speter	oldspl = spltty();	/* better safe than sorry */
126810015Speter
126910015Speter	ip = (int *)data;
127010015Speter
127118515Speter#define SUCHECK if ((error = suser(p->p_ucred, &p->p_acflag))) goto out
127210015Speter
127310015Speter	switch (cmd) {
127410015Speter	case TCSIPORTS:
127510015Speter		*ip = si_Nports;
127610015Speter		goto out;
127710015Speter	case TCSIMODULES:
127810015Speter		*ip = si_Nmodules;
127910015Speter		goto out;
128010015Speter	case TCSISDBG_ALL:
128110015Speter		SUCHECK;
128210015Speter		si_debug = *ip;
128310015Speter		goto out;
128410015Speter	case TCSIGDBG_ALL:
128510015Speter		*ip = si_debug;
128610015Speter		goto out;
128710015Speter	default:
128810015Speter		/*
128910015Speter		 * Check that a controller for this port exists
129010015Speter		 */
129110044Speter
129210044Speter		/* may also be a struct si_pstat, a superset of si_tcsi */
129310044Speter
129410015Speter		dp = (struct si_tcsi *)data;
129510044Speter		sps = (struct si_pstat *)data;
129610015Speter		card = dp->tc_card;
129710015Speter		xsc = &si_softc[card];	/* check.. */
129812174Speter		if (card < 0 || card >= NSI || xsc->sc_type == SIEMPTY) {
129910015Speter			error = ENOENT;
130010015Speter			goto out;
130110015Speter		}
130210015Speter		/*
130310015Speter		 * And check that a port exists
130410015Speter		 */
130510015Speter		port = dp->tc_port;
130610015Speter		if (port < 0 || port >= xsc->sc_nport) {
130710015Speter			error = ENOENT;
130810015Speter			goto out;
130910015Speter		}
131010015Speter		xpp = xsc->sc_ports + port;
131110015Speter		regp = (struct si_reg *)xsc->sc_maddr;
131210015Speter	}
131310015Speter
131410015Speter	switch (cmd) {
131510015Speter	case TCSIDEBUG:
131610015Speter#ifdef	SI_DEBUG
131710015Speter		SUCHECK;
131810015Speter		if (xpp->sp_debug)
131910015Speter			xpp->sp_debug = 0;
132010015Speter		else {
132110015Speter			xpp->sp_debug = DBG_ALL;
132210015Speter			DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n",
132310015Speter				(xpp->sp_debug&DBG_ALL)?"ON":"OFF"));
132410015Speter		}
132510015Speter		break;
132610015Speter#else
132710015Speter		error = ENODEV;
132810015Speter		goto out;
132910015Speter#endif
133010015Speter	case TCSISDBG_LEVEL:
133110015Speter	case TCSIGDBG_LEVEL:
133210015Speter#ifdef	SI_DEBUG
133310015Speter		if (cmd == TCSIGDBG_LEVEL) {
133410015Speter			dp->tc_dbglvl = xpp->sp_debug;
133510015Speter		} else {
133610015Speter			SUCHECK;
133710015Speter			xpp->sp_debug = dp->tc_dbglvl;
133810015Speter		}
133910015Speter		break;
134010015Speter#else
134110015Speter		error = ENODEV;
134210015Speter		goto out;
134310015Speter#endif
134410015Speter	case TCSIGRXIT:
134510015Speter		dp->tc_int = regp->rx_int_count;
134610015Speter		break;
134710015Speter	case TCSIRXIT:
134810015Speter		SUCHECK;
134910015Speter		regp->rx_int_count = dp->tc_int;
135010015Speter		break;
135110015Speter	case TCSIGIT:
135210015Speter		dp->tc_int = regp->int_count;
135310015Speter		break;
135410015Speter	case TCSIIT:
135510015Speter		SUCHECK;
135610015Speter		regp->int_count = dp->tc_int;
135710015Speter		break;
135810044Speter	case TCSISTATE:
135910044Speter		dp->tc_int = xpp->sp_ccb->hi_ip;
136010015Speter		break;
136110044Speter	/* these next three use a different structure */
136210044Speter	case TCSI_PORT:
136310015Speter		SUCHECK;
136416444Speter		bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport));
136510015Speter		break;
136610044Speter	case TCSI_CCB:
136710044Speter		SUCHECK;
136816444Speter		bcopy((char *)xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb));
136910015Speter		break;
137010044Speter	case TCSI_TTY:
137110044Speter		SUCHECK;
137216444Speter		bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty));
137310015Speter		break;
137410015Speter	default:
137510015Speter		error = EINVAL;
137610015Speter		goto out;
137710015Speter	}
137810015Speterout:
137910015Speter	splx(oldspl);
138010015Speter	return(error);		/* success */
138110015Speter}
138210015Speter
138310015Speter/*
138410015Speter *	siparam()	: Configure line params
138510015Speter *	called at spltty();
138610015Speter *	this may sleep, does not flush, nor wait for drain, nor block writes
138710015Speter *	caller must arrange this if it's important..
138810015Speter */
138912724Sphkstatic int
139010015Spetersiparam(tp, t)
139110015Speter	register struct tty *tp;
139210015Speter	register struct termios *t;
139310015Speter{
139410015Speter	register struct si_port *pp = TP2PP(tp);
139510015Speter	volatile struct si_channel *ccbp;
139610015Speter	int oldspl, cflag, iflag, oflag, lflag;
139710015Speter	int error = 0;		/* shutup gcc */
139810015Speter	int ispeed = 0;		/* shutup gcc */
139910015Speter	int ospeed = 0;		/* shutup gcc */
140010161Speter	BYTE val;
140110015Speter
140210015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t));
140310015Speter	cflag = t->c_cflag;
140410015Speter	iflag = t->c_iflag;
140510015Speter	oflag = t->c_oflag;
140610015Speter	lflag = t->c_lflag;
140710044Speter	DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n",
140810044Speter		oflag, cflag, iflag, lflag));
140910015Speter
141010015Speter
141110015Speter	/* if not hung up.. */
141210015Speter	if (t->c_ospeed != 0) {
141310015Speter		/* translate baud rate to firmware values */
141410015Speter		ospeed = ttspeedtab(t->c_ospeed, bdrates);
141510015Speter		ispeed = t->c_ispeed ?
141610015Speter			 ttspeedtab(t->c_ispeed, bdrates) : ospeed;
141710015Speter
141810015Speter		/* enforce legit baud rate */
141910015Speter		if (ospeed < 0 || ispeed < 0)
142010015Speter			return (EINVAL);
142110015Speter	}
142210015Speter
142310015Speter
142410015Speter	oldspl = spltty();
142510015Speter
142610015Speter	ccbp = pp->sp_ccb;
142710015Speter
142810161Speter	/* ========== set hi_break ========== */
142910161Speter	val = 0;
143010161Speter	if (iflag & IGNBRK)		/* Breaks */
143110161Speter		val |= BR_IGN;
143210161Speter	if (iflag & BRKINT)		/* Interrupt on break? */
143310161Speter		val |= BR_INT;
143410161Speter	if (iflag & PARMRK)		/* Parity mark? */
143510161Speter		val |= BR_PARMRK;
143610161Speter	if (iflag & IGNPAR)		/* Ignore chars with parity errors? */
143710161Speter		val |= BR_PARIGN;
143810161Speter	ccbp->hi_break = val;
143910161Speter
144010161Speter	/* ========== set hi_csr ========== */
144110015Speter	/* if not hung up.. */
144210015Speter	if (t->c_ospeed != 0) {
144310015Speter		/* Set I/O speeds */
144410161Speter		 val = (ispeed << 4) | ospeed;
144510015Speter	}
144610161Speter	ccbp->hi_csr = val;
144710015Speter
144810161Speter	/* ========== set hi_mr2 ========== */
144910161Speter	val = 0;
145010015Speter	if (cflag & CSTOPB)				/* Stop bits */
145110161Speter		val |= MR2_2_STOP;
145210015Speter	else
145310161Speter		val |= MR2_1_STOP;
145410161Speter	/*
145510161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
145610161Speter	 * a DCE, hence the reverse sense of RTS and CTS
145710161Speter	 */
145810161Speter	/* Output Flow - RTS must be raised before data can be sent */
145910161Speter	if (cflag & CCTS_OFLOW)
146010161Speter		val |= MR2_RTSCONT;
146110161Speter
146216575Speter	ccbp->hi_mr2 = val;
146310161Speter
146410161Speter	/* ========== set hi_mr1 ========== */
146510161Speter	val = 0;
146610015Speter	if (!(cflag & PARENB))				/* Parity */
146710161Speter		val |= MR1_NONE;
146810015Speter	else
146910161Speter		val |= MR1_WITH;
147010015Speter	if (cflag & PARODD)
147110161Speter		val |= MR1_ODD;
147210015Speter
147310015Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
147410161Speter		val |= MR1_8_BITS;
147510015Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
147610161Speter		val |= MR1_7_BITS;
147710015Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
147810161Speter		val |= MR1_6_BITS;
147910015Speter	} else {					/* Must be 5 */
148010161Speter		val |= MR1_5_BITS;
148110015Speter	}
148210161Speter	/*
148310161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
148410161Speter	 * a DCE, hence the reverse sense of RTS and CTS
148510161Speter	 */
148610161Speter	/* Input Flow - CTS is raised when port is ready to receive data */
148710161Speter	if (cflag & CRTS_IFLOW)
148810161Speter		val |= MR1_CTSCONT;
148910015Speter
149010161Speter	ccbp->hi_mr1 = val;
149110161Speter
149210161Speter	/* ========== set hi_mask ========== */
149310161Speter	val = 0xff;
149410161Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
149510161Speter		val &= 0xFF;
149610161Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
149710161Speter		val &= 0x7F;
149810161Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
149910161Speter		val &= 0x3F;
150010161Speter	} else {					/* Must be 5 */
150110161Speter		val &= 0x1F;
150210161Speter	}
150310015Speter	if (iflag & ISTRIP)
150410161Speter		val &= 0x7F;
150510015Speter
150610161Speter	ccbp->hi_mask = val;
150710161Speter
150810161Speter	/* ========== set hi_prtcl ========== */
150910161Speter	val = 0;
151010015Speter				/* Monitor DCD etc. if a modem */
151110015Speter	if (!(cflag & CLOCAL))
151210161Speter		val |= SP_DCEN;
151310161Speter	if (iflag & IXANY)
151410161Speter		val |= SP_TANY;
151510161Speter	if (iflag & IXON)
151610161Speter		val |= SP_TXEN;
151710161Speter	if (iflag & IXOFF)
151810161Speter		val |= SP_RXEN;
151910161Speter	if (iflag & INPCK)
152010161Speter		val |= SP_PAEN;
152110015Speter
152210161Speter	ccbp->hi_prtcl = val;
152310161Speter
152410161Speter
152510161Speter	/* ========== set hi_{rx|tx}{on|off} ========== */
152610161Speter	/* XXX: the card TOTALLY shields us from the flow control... */
152710015Speter	ccbp->hi_txon = t->c_cc[VSTART];
152810015Speter	ccbp->hi_txoff = t->c_cc[VSTOP];
152910015Speter
153010015Speter	ccbp->hi_rxon = t->c_cc[VSTART];
153110015Speter	ccbp->hi_rxoff = t->c_cc[VSTOP];
153210015Speter
153310161Speter	/* ========== send settings to the card ========== */
153410015Speter	/* potential sleep here */
153510015Speter	if (ccbp->hi_stat == IDLE_CLOSE)		/* Not yet open */
153610015Speter		si_command(pp, LOPEN, SI_WAIT);		/* open it */
153710015Speter	else
153810015Speter		si_command(pp, CONFIG, SI_WAIT);	/* change params */
153910015Speter
154010161Speter	/* ========== set DTR etc ========== */
154110015Speter	/* Hangup if ospeed == 0 */
154210015Speter	if (t->c_ospeed == 0) {
154310015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
154410015Speter	} else {
154510015Speter		/*
154610015Speter		 * If the previous speed was 0, may need to re-enable
154710015Speter	 	 * the modem signals
154810015Speter	 	 */
154910015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
155010015Speter	}
155110015Speter
155210044Speter	DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n",
155310044Speter		ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break));
155410015Speter
155510015Speter	splx(oldspl);
155610015Speter	return(error);
155710015Speter}
155810015Speter
155910015Speter/*
156010015Speter * Enable or Disable the writes to this channel...
156110015Speter * "state" ->  enabled = 1; disabled = 0;
156210015Speter */
156310015Speterstatic void
156410015Spetersi_write_enable(pp, state)
156510015Speter	register struct si_port *pp;
156610015Speter	int state;
156710015Speter{
156810015Speter	int oldspl;
156910015Speter
157010015Speter	oldspl = spltty();
157110015Speter
157210015Speter	if (state) {
157310015Speter		pp->sp_state &= ~SS_BLOCKWRITE;
157410015Speter		if (pp->sp_state & SS_WAITWRITE) {
157510015Speter			pp->sp_state &= ~SS_WAITWRITE;
157610015Speter			/* thunder away! */
157710015Speter			wakeup((caddr_t)pp);
157810015Speter		}
157910015Speter	} else {
158010015Speter		pp->sp_state |= SS_BLOCKWRITE;
158110015Speter	}
158210015Speter
158310015Speter	splx(oldspl);
158410015Speter}
158510015Speter
158610015Speter/*
158710015Speter * Set/Get state of modem control lines.
158810015Speter * Due to DCE-like behaviour of the adapter, some signals need translation:
158910015Speter *	TIOCM_DTR	DSR
159010015Speter *	TIOCM_RTS	CTS
159110015Speter */
159210015Speterstatic int
159310015Spetersi_modem(pp, cmd, bits)
159410015Speter	struct si_port *pp;
159510015Speter	enum si_mctl cmd;
159610015Speter	int bits;
159710015Speter{
159810015Speter	volatile struct si_channel *ccbp;
159910015Speter	int x;
160010015Speter
160110015Speter	DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits));
160210015Speter	ccbp = pp->sp_ccb;		/* Find channel address */
160310015Speter	switch (cmd) {
160410015Speter	case GET:
160510015Speter		x = ccbp->hi_ip;
160610015Speter		bits = TIOCM_LE;
160710015Speter		if (x & IP_DCD)		bits |= TIOCM_CAR;
160810015Speter		if (x & IP_DTR)		bits |= TIOCM_DTR;
160910015Speter		if (x & IP_RTS)		bits |= TIOCM_RTS;
161010015Speter		if (x & IP_RI)		bits |= TIOCM_RI;
161110015Speter		return(bits);
161210015Speter	case SET:
161310015Speter		ccbp->hi_op &= ~(OP_DSR|OP_CTS);
161410015Speter		/* fall through */
161510015Speter	case BIS:
161610015Speter		x = 0;
161710015Speter		if (bits & TIOCM_DTR)
161810015Speter			x |= OP_DSR;
161910015Speter		if (bits & TIOCM_RTS)
162010015Speter			x |= OP_CTS;
162110015Speter		ccbp->hi_op |= x;
162210015Speter		break;
162310015Speter	case BIC:
162410015Speter		if (bits & TIOCM_DTR)
162510015Speter			ccbp->hi_op &= ~OP_DSR;
162610015Speter		if (bits & TIOCM_RTS)
162710015Speter			ccbp->hi_op &= ~OP_CTS;
162810015Speter	}
162910015Speter	return 0;
163010015Speter}
163110015Speter
163210015Speter/*
163310015Speter * Handle change of modem state
163410015Speter */
163510015Speterstatic void
163610015Spetersi_modem_state(pp, tp, hi_ip)
163710015Speter	register struct si_port *pp;
163810015Speter	register struct tty *tp;
163910015Speter	register int hi_ip;
164010015Speter{
164110015Speter							/* if a modem dev */
164210015Speter	if (hi_ip & IP_DCD) {
164310015Speter		if ( !(pp->sp_last_hi_ip & IP_DCD)) {
164410015Speter			DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n",
164510015Speter				tp->t_line));
164610015Speter			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
164710015Speter		}
164810015Speter	} else {
164910015Speter		if (pp->sp_last_hi_ip & IP_DCD) {
165010015Speter			DPRINT((pp, DBG_INTR, "modem carr off\n"));
165110015Speter			if ((*linesw[tp->t_line].l_modem)(tp, 0))
165210015Speter				(void) si_modem(pp, SET, 0);
165310015Speter		}
165410015Speter	}
165510015Speter	pp->sp_last_hi_ip = hi_ip;
165610015Speter
165710015Speter}
165810015Speter
165910015Speter/*
166010015Speter * Poller to catch missed interrupts.
166112174Speter *
166212496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get
166312496Speter * better response.  We could really use a "periodic" version timeout(). :-)
166410015Speter */
166510015Speter#ifdef POLL
166610708Speterstatic void
166710015Spetersi_poll(void *nothing)
166810015Speter{
166910015Speter	register struct si_softc *sc;
167010015Speter	register int i;
167110015Speter	volatile struct si_reg *regp;
167212174Speter	register struct si_port *pp;
167312174Speter	int lost, oldspl, port;
167410015Speter
167510015Speter	DPRINT((0, DBG_POLL, "si_poll()\n"));
167611609Speter	oldspl = spltty();
167710015Speter	if (in_intr)
167810015Speter		goto out;
167910015Speter	lost = 0;
168010015Speter	for (i=0; i<NSI; i++) {
168110015Speter		sc = &si_softc[i];
168212174Speter		if (sc->sc_type == SIEMPTY)
168310015Speter			continue;
168410015Speter		regp = (struct si_reg *)sc->sc_maddr;
168510015Speter		/*
168610015Speter		 * See if there has been a pending interrupt for 2 seconds
168710015Speter		 * or so. The test <int_scounter >= 200) won't correspond
168810015Speter		 * to 2 seconds if int_count gets changed.
168910015Speter		 */
169010015Speter		if (regp->int_pending != 0) {
169110015Speter			if (regp->int_scounter >= 200 &&
169210015Speter			    regp->initstat == 1) {
169312174Speter				printf("si%d: lost intr\n", i);
169410015Speter				lost++;
169510015Speter			}
169610015Speter		} else {
169710015Speter			regp->int_scounter = 0;
169810015Speter		}
169910015Speter
170012174Speter		/*
170112174Speter		 * gripe about no input flow control..
170212174Speter		 */
170312174Speter		pp = sc->sc_ports;
170412174Speter		for (port = 0; port < sc->sc_nport; pp++, port++) {
170512174Speter			if (pp->sp_delta_overflows > 0) {
170612174Speter				printf("si%d: %d tty level buffer overflows\n",
170712174Speter					i, pp->sp_delta_overflows);
170812174Speter				pp->sp_delta_overflows = 0;
170912174Speter			}
171012174Speter		}
171110015Speter	}
171217547Speter	if (lost || si_realpoll)
171310015Speter		siintr(-1);	/* call intr with fake vector */
171411609Speterout:
171510015Speter	splx(oldspl);
171610015Speter
171715639Speter	timeout(si_poll, (caddr_t)0L, si_pollrate);
171810015Speter}
171910015Speter#endif	/* ifdef POLL */
172010015Speter
172110015Speter/*
172210015Speter * The interrupt handler polls ALL ports on ALL adapters each time
172310015Speter * it is called.
172410015Speter */
172510015Speter
172612496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE];	/* input staging area */
172710015Speter
172810708Spetervoid
172911609Spetersiintr(int unit)
173010015Speter{
173110015Speter	register struct si_softc *sc;
173210015Speter
173310015Speter	register struct si_port *pp;
173410015Speter	volatile struct si_channel *ccbp;
173510015Speter	register struct tty *tp;
173610015Speter	volatile caddr_t maddr;
173711872Sphk	BYTE op, ip;
173812174Speter	int x, card, port, n, i, isopen;
173910015Speter	volatile BYTE *z;
174010015Speter	BYTE c;
174110015Speter
174211609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "siintr(%d)\n", unit));
174311609Speter	if (in_intr) {
174411609Speter		if (unit < 0)	/* should never happen */
174510708Speter			return;
174612174Speter		printf("si%d: Warning interrupt handler re-entered\n",
174711609Speter			unit);
174810708Speter		return;
174910015Speter	}
175010015Speter	in_intr = 1;
175110015Speter
175210015Speter	/*
175310015Speter	 * When we get an int we poll all the channels and do ALL pending
175410015Speter	 * work, not just the first one we find. This allows all cards to
175510015Speter	 * share the same vector.
175610015Speter	 */
175710015Speter	for (card=0; card < NSI; card++) {
175810015Speter		sc = &si_softc[card];
175912174Speter		if (sc->sc_type == SIEMPTY)
176010015Speter			continue;
176112174Speter
176212174Speter		/*
176312174Speter		 * First, clear the interrupt
176412174Speter		 */
176510015Speter		switch(sc->sc_type) {
176610015Speter		case SIHOST :
176710015Speter			maddr = sc->sc_maddr;
176810015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
176910015Speter							/* flag nothing pending */
177010015Speter			*(maddr+SIINTCL) = 0x00;	/* Set IRQ clear */
177110015Speter			*(maddr+SIINTCL_CL) = 0x00;	/* Clear IRQ clear */
177210015Speter			break;
177310015Speter		case SIHOST2:
177410015Speter			maddr = sc->sc_maddr;
177510015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
177610015Speter			*(maddr+SIPLIRQCLR) = 0x00;
177710015Speter			*(maddr+SIPLIRQCLR) = 0x10;
177810015Speter			break;
177910015Speter		case SIEISA:
178010015Speter#if NEISA > 0
178110015Speter			maddr = sc->sc_maddr;
178210015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
178310015Speter			(void)inb(sc->sc_eisa_iobase+3);
178410015Speter			break;
178510015Speter#endif	/* fall through if not EISA kernel */
178610015Speter		case SIEMPTY:
178710015Speter		default:
178810015Speter			continue;
178910015Speter		}
179010015Speter		((volatile struct si_reg *)maddr)->int_scounter = 0;
179110015Speter
179212174Speter		/*
179312174Speter		 * check each port
179412174Speter		 */
179512174Speter		for (pp=sc->sc_ports,port=0; port < sc->sc_nport; pp++,port++) {
179610015Speter			ccbp = pp->sp_ccb;
179710015Speter			tp = pp->sp_tty;
179810015Speter
179912174Speter
180010015Speter			/*
180110015Speter			 * See if a command has completed ?
180210015Speter			 */
180310015Speter			if (ccbp->hi_stat != pp->sp_pend) {
180410015Speter				DPRINT((pp, DBG_INTR,
180510015Speter					"siintr hi_stat = 0x%x, pend = %d\n",
180610015Speter					ccbp->hi_stat, pp->sp_pend));
180710015Speter				switch(pp->sp_pend) {
180810015Speter				case LOPEN:
180910015Speter				case MPEND:
181010015Speter				case MOPEN:
181110015Speter				case CONFIG:
181216575Speter				case SBREAK:
181316575Speter				case EBREAK:
181410015Speter					pp->sp_pend = ccbp->hi_stat;
181510015Speter						/* sleeping in si_command */
181610015Speter					wakeup(&pp->sp_state);
181710015Speter					break;
181810015Speter				default:
181910015Speter					pp->sp_pend = ccbp->hi_stat;
182010015Speter				}
182110015Speter	 		}
182210015Speter
182310015Speter			/*
182410015Speter			 * Continue on if it's closed
182510015Speter			 */
182610015Speter			if (ccbp->hi_stat == IDLE_CLOSE) {
182710015Speter				continue;
182810015Speter			}
182910015Speter
183010015Speter			/*
183110015Speter			 * Do modem state change if not a local device
183210015Speter			 */
183310015Speter			si_modem_state(pp, tp, ccbp->hi_ip);
183410015Speter
183510015Speter			/*
183612174Speter			 * Check to see if there's we should 'receive'
183712174Speter			 * characters.
183812174Speter			 */
183912174Speter			if (tp->t_state & TS_CONNECTED &&
184012174Speter			    tp->t_state & TS_ISOPEN)
184112174Speter				isopen = 1;
184212174Speter			else
184312174Speter				isopen = 0;
184412174Speter
184512174Speter			/*
184616575Speter			 * Do input break processing
184710015Speter			 */
184810015Speter			if (ccbp->hi_state & ST_BREAK) {
184912174Speter				if (isopen) {
185012174Speter				    (*linesw[tp->t_line].l_rint)(TTY_BI, tp);
185110015Speter				}
185210015Speter				ccbp->hi_state &= ~ST_BREAK;   /* A Bit iffy this */
185310015Speter				DPRINT((pp, DBG_INTR, "si_intr break\n"));
185410015Speter			}
185510015Speter
185610015Speter			/*
185712174Speter			 * Do RX stuff - if not open then dump any characters.
185812174Speter			 * XXX: This is VERY messy and needs to be cleaned up.
185912174Speter			 *
186012174Speter			 * XXX: can we leave data in the host adapter buffer
186112174Speter			 * when the clists are full?  That may be dangerous
186212174Speter			 * if the user cannot get an interrupt signal through.
186310015Speter			 */
186410015Speter
186512174Speter	more_rx:	/* XXX Sorry. the nesting was driving me bats! :-( */
186612174Speter
186712174Speter			if (!isopen) {
186810015Speter				ccbp->hi_rxopos = ccbp->hi_rxipos;
186912174Speter				goto end_rx;
187012174Speter			}
187110015Speter
187212174Speter			/*
187315640Speter			 * If the tty input buffers are blocked, stop emptying
187415640Speter			 * the incoming buffers and let the auto flow control
187515640Speter			 * assert..
187615640Speter			 */
187715640Speter			if (tp->t_state & TS_TBLOCK) {
187815640Speter				goto end_rx;
187915640Speter			}
188015640Speter
188115640Speter			/*
188212174Speter			 * Process read characters if not skipped above
188312174Speter			 */
188415640Speter			op = ccbp->hi_rxopos;
188515640Speter			ip = ccbp->hi_rxipos;
188615640Speter			c = ip - op;
188712174Speter			if (c == 0) {
188812174Speter				goto end_rx;
188912174Speter			}
189010015Speter
189112174Speter			n = c & 0xff;
189215640Speter			if (n > 250)
189315640Speter				n = 250;
189412174Speter
189512174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
189610015Speter						n, op, ip));
189710015Speter
189812174Speter			/*
189912174Speter			 * Suck characters out of host card buffer into the
190012174Speter			 * "input staging buffer" - so that we dont leave the
190112174Speter			 * host card in limbo while we're possibly echoing
190212174Speter			 * characters and possibly flushing input inside the
190312174Speter			 * ldisc l_rint() routine.
190412174Speter			 */
190512496Speter			if (n <= SI_BUFFERSIZE - op) {
190610015Speter
190712174Speter				DPRINT((pp, DBG_INTR, "\tsingle copy\n"));
190812174Speter				z = ccbp->hi_rxbuf + op;
190912174Speter				bcopy((caddr_t)z, si_rxbuf, n);
191010015Speter
191112174Speter				op += n;
191212174Speter			} else {
191312496Speter				x = SI_BUFFERSIZE - op;
191410015Speter
191512174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x));
191612174Speter				z = ccbp->hi_rxbuf + op;
191712174Speter				bcopy((caddr_t)z, si_rxbuf, x);
191810015Speter
191912174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n-x));
192012174Speter				z = ccbp->hi_rxbuf;
192112174Speter				bcopy((caddr_t)z, si_rxbuf+x, n-x);
192210015Speter
192312174Speter				op += n;
192412174Speter			}
192510015Speter
192612174Speter			/* clear collected characters from buffer */
192712174Speter			ccbp->hi_rxopos = op;
192812174Speter
192912174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
193010015Speter						n, op, ip));
193110015Speter
193212174Speter			/*
193312174Speter			 * at this point...
193412174Speter			 * n = number of chars placed in si_rxbuf
193512174Speter			 */
193610015Speter
193712174Speter			/*
193812174Speter			 * Avoid the grotesquely inefficient lineswitch
193912174Speter			 * routine (ttyinput) in "raw" mode. It usually
194012174Speter			 * takes about 450 instructions (that's without
194112174Speter			 * canonical processing or echo!). slinput is
194212174Speter			 * reasonably fast (usually 40 instructions
194312174Speter			 * plus call overhead).
194412174Speter			 */
194512174Speter			if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
194610015Speter
194712174Speter				/* block if the driver supports it */
194812174Speter				if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER
194912174Speter				    && (tp->t_cflag & CRTS_IFLOW
195012174Speter					|| tp->t_iflag & IXOFF)
195112174Speter				    && !(tp->t_state & TS_TBLOCK))
195212174Speter					ttyblock(tp);
195310015Speter
195412174Speter				tk_nin += n;
195512174Speter				tk_rawcc += n;
195612174Speter				tp->t_rawcc += n;
195712174Speter
195812174Speter				pp->sp_delta_overflows +=
195912174Speter				    b_to_q((char *)si_rxbuf, n, &tp->t_rawq);
196012174Speter
196112174Speter				ttwakeup(tp);
196212174Speter				if (tp->t_state & TS_TTSTOP
196312174Speter				    && (tp->t_iflag & IXANY
196412174Speter					|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
196512174Speter					tp->t_state &= ~TS_TTSTOP;
196612174Speter					tp->t_lflag &= ~FLUSHO;
196712174Speter					si_start(tp);
196812174Speter				}
196912174Speter			} else {
197012174Speter				/*
197112174Speter				 * It'd be nice to not have to go through the
197212174Speter				 * function call overhead for each char here.
197312174Speter				 * It'd be nice to block input it, saving a
197412174Speter				 * loop here and the call/return overhead.
197512174Speter				 */
197612174Speter				for(x = 0; x < n; x++) {
197712174Speter					i = si_rxbuf[x];
197812174Speter					if ((*linesw[tp->t_line].l_rint)(i, tp)
197912174Speter					     == -1) {
198012174Speter						pp->sp_delta_overflows++;
198110015Speter					}
198212174Speter					/*
198312174Speter					 * doesn't seem to be much point doing
198412174Speter					 * this here.. this driver has no
198512174Speter					 * softtty processing! ??
198612174Speter					 */
198712174Speter					if (pp->sp_hotchar && i == pp->sp_hotchar) {
198812174Speter						setsofttty();
198912174Speter					}
199012174Speter				}
199112174Speter			}
199212174Speter			goto more_rx;	/* try for more until RXbuf is empty */
199310015Speter
199412174Speter	end_rx:		/* XXX: Again, sorry about the gotos.. :-) */
199510015Speter
199610015Speter			/*
199710015Speter			 * Do TX stuff
199810015Speter			 */
199910015Speter			(*linesw[tp->t_line].l_start)(tp);
200010015Speter
200110015Speter		} /* end of for (all ports on this controller) */
200210015Speter	} /* end of for (all controllers) */
200310015Speter
200411609Speter	in_intr = 0;
200511609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "end siintr(%d)\n", unit));
200610015Speter}
200710015Speter
200810015Speter/*
200910015Speter * Nudge the transmitter...
201012174Speter *
201112174Speter * XXX: I inherited some funny code here.  It implies the host card only
201212174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does
201312174Speter * not interrupt when it's actually hits empty.  In some cases, we have
201412174Speter * processes waiting for complete drain, and we need to simulate an interrupt
201512174Speter * about when we think the buffer is going to be empty (and retry if not).
201612174Speter * I really am not certain about this...  I *need* the hardware manuals.
201710015Speter */
201810015Speterstatic void
201910015Spetersi_start(tp)
202010015Speter	register struct tty *tp;
202110015Speter{
202210015Speter	struct si_port *pp;
202310015Speter	volatile struct si_channel *ccbp;
202410015Speter	register struct clist *qp;
202510015Speter	BYTE ipos;
202610015Speter	int nchar;
202710015Speter	int oldspl, count, n, amount, buffer_full;
202810015Speter	int do_exitproc;
202910015Speter
203010015Speter	oldspl = spltty();
203110015Speter
203210015Speter	qp = &tp->t_outq;
203310015Speter	pp = TP2PP(tp);
203410015Speter
203510015Speter	DPRINT((pp, DBG_ENTRY|DBG_START,
203610015Speter		"si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n",
203710015Speter		tp, tp->t_state, pp->sp_state, qp->c_cc));
203810015Speter
203910015Speter	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
204010015Speter		goto out;
204110015Speter
204210015Speter	do_exitproc = 0;
204310015Speter	buffer_full = 0;
204410015Speter	ccbp = pp->sp_ccb;
204510015Speter
204610015Speter	/*
204710015Speter	 * Handle the case where ttywait() is called on process exit
204810015Speter	 * this may be BSDI specific, I dont know...
204910015Speter	 */
205010015Speter	if (tp->t_session != NULL && tp->t_session->s_leader != NULL &&
205110015Speter	    (tp->t_session->s_leader->p_flag & P_WEXIT)) {
205210015Speter		do_exitproc++;
205310015Speter	}
205410015Speter
205510015Speter	count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
205610015Speter	DPRINT((pp, DBG_START, "count %d\n", (BYTE)count));
205710015Speter
205810015Speter	while ((nchar = qp->c_cc) > 0) {
205910015Speter		if ((BYTE)count >= 255) {
206010015Speter			buffer_full++;
206110015Speter			break;
206210015Speter		}
206310015Speter		amount = min(nchar, (255 - (BYTE)count));
206410015Speter		ipos = (unsigned int)ccbp->hi_txipos;
206510015Speter		/* will it fit in one lump? */
206612496Speter		if ((SI_BUFFERSIZE - ipos) >= amount) {
206710015Speter			n = q_to_b(&tp->t_outq,
206810015Speter				(char *)&ccbp->hi_txbuf[ipos], amount);
206910015Speter		} else {
207010015Speter			n = q_to_b(&tp->t_outq,
207110015Speter				(char *)&ccbp->hi_txbuf[ipos],
207212496Speter				SI_BUFFERSIZE-ipos);
207312496Speter			if (n == SI_BUFFERSIZE-ipos) {
207410015Speter				n += q_to_b(&tp->t_outq,
207510015Speter					(char *)&ccbp->hi_txbuf[0],
207612496Speter					amount - (SI_BUFFERSIZE-ipos));
207710015Speter			}
207810015Speter		}
207910015Speter		ccbp->hi_txipos += n;
208010015Speter		count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
208110015Speter	}
208210015Speter
208310015Speter	if (count != 0 && nchar == 0) {
208410015Speter		tp->t_state |= TS_BUSY;
208510015Speter	} else {
208610015Speter		tp->t_state &= ~TS_BUSY;
208710015Speter	}
208810015Speter
208910015Speter	/* wakeup time? */
209010015Speter	ttwwakeup(tp);
209110015Speter
209210015Speter	DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n",
209310015Speter		(BYTE)count, nchar, tp->t_state));
209410015Speter
209510015Speter	if ((tp->t_state & TS_BUSY) || do_exitproc)
209610015Speter	{
209710015Speter		int time;
209810015Speter
209910015Speter		if (do_exitproc != 0) {
210010015Speter			time = hz / 10;
210110015Speter		} else {
210210015Speter			time = ttspeedtab(tp->t_ospeed, chartimes);
210310015Speter
210410015Speter			if (time > 0) {
210510015Speter				if (time < nchar)
210610015Speter					time = nchar / time;
210710015Speter				else
210810015Speter					time = 2;
210910015Speter			} else {
211016024Speter				DPRINT((pp, DBG_START,
211116024Speter					"bad char time value! %d\n", time));
211216024Speter				time = hz/10;
211310015Speter			}
211410015Speter		}
211510015Speter
211610015Speter		if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) {
211729677Sgibbs			untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch);
211810015Speter		} else {
211910015Speter			pp->sp_state |= SS_LSTART;
212010015Speter		}
212110015Speter		DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time));
212229677Sgibbs		pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time);
212310015Speter	}
212410015Speter
212510015Speterout:
212610015Speter	splx(oldspl);
212710015Speter	DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n"));
212810015Speter}
212910015Speter
213010015Speter/*
213110015Speter * Note: called at splsoftclock from the timeout code
213210015Speter * This has to deal with two things...  cause wakeups while waiting for
213310015Speter * tty drains on last process exit, and call l_start at about the right
213410015Speter * time for protocols like ppp.
213510015Speter */
213610015Speterstatic void
213725047Sbdesi_lstart(void *arg)
213810015Speter{
213925047Sbde	register struct si_port *pp = arg;
214010015Speter	register struct tty *tp;
214110015Speter	int oldspl;
214210015Speter
214310015Speter	DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n",
214410015Speter		pp, pp->sp_state));
214510015Speter
214610015Speter	oldspl = spltty();
214710015Speter
214810015Speter	if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) {
214910015Speter		splx(oldspl);
215010015Speter		return;
215110015Speter	}
215210015Speter	pp->sp_state &= ~SS_LSTART;
215310015Speter	pp->sp_state |= SS_INLSTART;
215410015Speter
215510015Speter	tp = pp->sp_tty;
215610015Speter
215710015Speter	/* deal with the process exit case */
215810015Speter	ttwwakeup(tp);
215910015Speter
216012174Speter	/* nudge protocols - eg: ppp */
216110015Speter	(*linesw[tp->t_line].l_start)(tp);
216210015Speter
216310015Speter	pp->sp_state &= ~SS_INLSTART;
216410015Speter	splx(oldspl);
216510015Speter}
216610015Speter
216710015Speter/*
216810015Speter * Stop output on a line. called at spltty();
216910015Speter */
217010015Spetervoid
217110015Spetersistop(tp, rw)
217210015Speter	register struct tty *tp;
217310015Speter	int rw;
217410015Speter{
217510015Speter	volatile struct si_channel *ccbp;
217610015Speter	struct si_port *pp;
217710015Speter
217810015Speter	pp = TP2PP(tp);
217910015Speter	ccbp = pp->sp_ccb;
218010015Speter
218110015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "sistop(%x,%x)\n", tp, rw));
218210015Speter
218310015Speter	/* XXX: must check (rw & FWRITE | FREAD) etc flushing... */
218410015Speter	if (rw & FWRITE) {
218510015Speter		/* what level are we meant to be flushing anyway? */
218610015Speter		if (tp->t_state & TS_BUSY) {
218710015Speter			si_command(TP2PP(tp), WFLUSH, SI_NOWAIT);
218810015Speter			tp->t_state &= ~TS_BUSY;
218910015Speter			ttwwakeup(tp);	/* Bruce???? */
219010015Speter		}
219110015Speter	}
219212174Speter#if 1	/* XXX: this doesn't work right yet.. */
219312174Speter	/* XXX: this may have been failing because we used to call l_rint()
219412174Speter	 * while we were looping based on these two counters. Now, we collect
219512174Speter	 * the data and then loop stuffing it into l_rint(), making this
219612174Speter	 * useless.  Should we cause this to blow away the staging buffer?
219712174Speter	 */
219810015Speter	if (rw & FREAD) {
219910015Speter		ccbp->hi_rxopos = ccbp->hi_rxipos;
220010015Speter	}
220110015Speter#endif
220210015Speter}
220310015Speter
220410015Speter/*
220510015Speter * Issue a command to the Z280 host card CPU.
220610015Speter */
220710015Speter
220810015Speterstatic void
220910015Spetersi_command(pp, cmd, waitflag)
221010015Speter	struct si_port *pp;		/* port control block (local) */
221110015Speter	int cmd;
221210015Speter	int waitflag;
221310015Speter{
221410015Speter	int oldspl;
221510015Speter	volatile struct si_channel *ccbp = pp->sp_ccb;
221610015Speter	int x;
221710015Speter
221810015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n",
221910015Speter		pp, cmd, waitflag, ccbp->hi_stat));
222010015Speter
222110015Speter	oldspl = spltty();		/* Keep others out */
222210015Speter
222310015Speter	/* wait until it's finished what it was doing.. */
222416575Speter	/* XXX: sits in IDLE_BREAK until something disturbs it or break
222516575Speter	 * is turned off. */
222610015Speter	while((x = ccbp->hi_stat) != IDLE_OPEN &&
222710015Speter			x != IDLE_CLOSE &&
222816575Speter			x != IDLE_BREAK &&
222910015Speter			x != cmd) {
223010015Speter		if (in_intr) {			/* Prevent sleep in intr */
223110015Speter			DPRINT((pp, DBG_PARAM,
223210015Speter				"cmd intr collision - completing %d\trequested %d\n",
223310015Speter				x, cmd));
223410015Speter			splx(oldspl);
223510015Speter			return;
223610015Speter		} else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
223710015Speter				"sicmd1", 1)) {
223810015Speter			splx(oldspl);
223910015Speter			return;
224010015Speter		}
224110015Speter	}
224216575Speter	/* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */
224310015Speter
224410015Speter	/* if there was a pending command, cause a state-change wakeup */
224516575Speter	switch(pp->sp_pend) {
224616575Speter	case LOPEN:
224716575Speter	case MPEND:
224816575Speter	case MOPEN:
224916575Speter	case CONFIG:
225016575Speter	case SBREAK:
225116575Speter	case EBREAK:
225216575Speter		wakeup(&pp->sp_state);
225316575Speter		break;
225416575Speter	default:
225516575Speter		break;
225610015Speter	}
225710015Speter
225810015Speter	pp->sp_pend = cmd;		/* New command pending */
225910015Speter	ccbp->hi_stat = cmd;		/* Post it */
226010015Speter
226110015Speter	if (waitflag) {
226210015Speter		if (in_intr) {		/* If in interrupt handler */
226310015Speter			DPRINT((pp, DBG_PARAM,
226410015Speter				"attempt to sleep in si_intr - cmd req %d\n",
226510015Speter				cmd));
226610015Speter			splx(oldspl);
226710015Speter			return;
226816575Speter		} else while(ccbp->hi_stat != IDLE_OPEN &&
226916575Speter			     ccbp->hi_stat != IDLE_BREAK) {
227010015Speter			if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
227110015Speter			    "sicmd2", 0))
227210015Speter				break;
227310015Speter		}
227410015Speter	}
227510015Speter	splx(oldspl);
227610015Speter}
227710015Speter
227810015Speterstatic void
227910015Spetersi_disc_optim(tp, t, pp)
228010015Speter	struct tty	*tp;
228110015Speter	struct termios	*t;
228210015Speter	struct si_port	*pp;
228310015Speter{
228410015Speter	/*
228510015Speter	 * XXX can skip a lot more cases if Smarts.  Maybe
228610015Speter	 * (IGNCR | ISTRIP | IXON) in c_iflag.  But perhaps we
228710015Speter	 * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state.
228810015Speter	 */
228910015Speter	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
229010015Speter	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
229110015Speter	    && (!(t->c_iflag & PARMRK)
229210015Speter		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
229310015Speter	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
229410015Speter	    && linesw[tp->t_line].l_rint == ttyinput)
229510015Speter		tp->t_state |= TS_CAN_BYPASS_L_RINT;
229610015Speter	else
229710015Speter		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
229810161Speter
229910015Speter	/*
230010015Speter	 * Prepare to reduce input latency for packet
230110015Speter	 * discplines with a end of packet character.
230210015Speter	 */
230310015Speter	if (tp->t_line == SLIPDISC)
230410015Speter		pp->sp_hotchar = 0xc0;
230510015Speter	else if (tp->t_line == PPPDISC)
230610015Speter		pp->sp_hotchar = 0x7e;
230710015Speter	else
230810015Speter		pp->sp_hotchar = 0;
230910161Speter
231010161Speter	DPRINT((pp, DBG_OPTIM, "bypass: %s, hotchar: %x\n",
231110161Speter		(tp->t_state & TS_CAN_BYPASS_L_RINT) ? "on" : "off",
231210161Speter		pp->sp_hotchar));
231310015Speter}
231410015Speter
231510015Speter
231610015Speter#ifdef	SI_DEBUG
231713353Speter
231810015Speterstatic void
231913353Speter#ifdef __STDC__
232013353Spetersi_dprintf(struct si_port *pp, int flags, const char *fmt, ...)
232113353Speter#else
232213353Spetersi_dprintf(pp, flags, fmt, va_alist)
232310015Speter	struct si_port *pp;
232410015Speter	int flags;
232513353Speter	char *fmt;
232613353Speter#endif
232710015Speter{
232813353Speter	va_list ap;
232913630Sphk
233010015Speter	if ((pp == NULL && (si_debug&flags)) ||
233110015Speter	    (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) {
233210015Speter	    	if (pp != NULL)
233312496Speter	    		printf("%ci%d(%d): ", 's',
233412174Speter	    			(int)SI_CARD(pp->sp_tty->t_dev),
233512174Speter	    			(int)SI_PORT(pp->sp_tty->t_dev));
233613630Sphk		va_start(ap, fmt);
233713630Sphk		vprintf(fmt, ap);
233813353Speter		va_end(ap);
233910015Speter	}
234010015Speter}
234110015Speter
234210015Speterstatic char *
234310015Spetersi_mctl2str(cmd)
234410015Speter	enum si_mctl cmd;
234510015Speter{
234610015Speter	switch (cmd) {
234710015Speter	case GET:	return("GET");
234810015Speter	case SET:	return("SET");
234910015Speter	case BIS:	return("BIS");
235010015Speter	case BIC:	return("BIC");
235110015Speter	}
235210015Speter	return("BAD");
235310015Speter}
235412502Sjulian
235512624Speter#endif	/* DEBUG */
235612502Sjulian
235712624Speter
235812502Sjulian
235912502Sjulianstatic si_devsw_installed = 0;
236012502Sjulian
236112517Sjulianstatic void 	si_drvinit(void *unused)
236212502Sjulian{
236312517Sjulian	dev_t dev;
236412517Sjulian
236512502Sjulian	if( ! si_devsw_installed ) {
236612675Sjulian		dev = makedev(CDEV_MAJOR, 0);
236712675Sjulian		cdevsw_add(&dev,&si_cdevsw, NULL);
236812502Sjulian		si_devsw_installed = 1;
236912517Sjulian    	}
237012502Sjulian}
237112517Sjulian
237212517SjulianSYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,si_drvinit,NULL)
237312517Sjulian
2374