si.c revision 16403
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 *
3316403Speter *	$Id: si.c,v 1.43 1996/06/12 05:03:50 gpalmer 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>
4410015Speter#include <sys/ioctl.h>
4510015Speter#include <sys/tty.h>
4610015Speter#include <sys/ttydefaults.h>
4710015Speter#include <sys/proc.h>
4810015Speter#include <sys/conf.h>
4910015Speter#include <sys/file.h>
5010015Speter#include <sys/uio.h>
5110015Speter#include <sys/dkstat.h>
5210015Speter#include <sys/kernel.h>
5310015Speter#include <sys/syslog.h>
5410015Speter#include <sys/malloc.h>
5515683Speter#include <sys/sysctl.h>
5610015Speter#include <sys/devconf.h>
5712675Sjulian#ifdef DEVFS
5812675Sjulian#include <sys/devfsext.h>
5912675Sjulian#endif /*DEVFS*/
6010015Speter
6110015Speter#include <machine/clock.h>
6210015Speter
6312659Sbde#include <vm/vm.h>
6412662Sdg#include <vm/vm_param.h>
6512662Sdg#include <vm/pmap.h>
6612659Sbde
6710015Speter#include <i386/isa/icu.h>
6810015Speter#include <i386/isa/isa.h>
6910015Speter#include <i386/isa/isa_device.h>
7010015Speter
7110015Speter#include <i386/isa/sireg.h>
7210015Speter#include <machine/si.h>
7313353Speter#include <machine/stdarg.h>
7410015Speter
7510015Speter#include "si.h"
7610015Speter
7710015Speter/*
7810015Speter * This device driver is designed to interface the Specialix International
7912496Speter * range of serial multiplexor cards (SI/XIO) to BSDI/386 on an ISA bus machine.
8010015Speter *
8110015Speter * The controller is interfaced to the host via dual port ram
8210015Speter * and a (programmable - SIHOST2) interrupt at IRQ 11,12 or 15.
8310015Speter */
8410015Speter
8510015Speter#define	POLL		/* turn on poller to generate buffer empty interrupt */
8610047Speter#define SI_DEF_HWFLOW	/* turn on default CRTSCTS flow control */
8712496Speter#define SI_I_HIGH_WATER	(TTYHOG - 2 * SI_BUFFERSIZE)
8815639Speter#define INT_COUNT 25000	/* max of 125 ints per second */
8915639Speter#define RXINT_COUNT 1	/* one rxint per 10 milliseconds */
9010015Speter
9110015Speterenum si_mctl { GET, SET, BIS, BIC };
9210015Speter
9310015Speterstatic void si_command __P((struct si_port *, int, int));
9410015Speterstatic int si_modem __P((struct si_port *, enum si_mctl, int));
9510015Speterstatic void si_write_enable __P((struct si_port *, int));
9610015Speterstatic int si_Sioctl __P((dev_t, int, caddr_t, int, struct proc *));
9710015Speterstatic void si_start __P((struct tty *));
9810015Speterstatic void si_lstart __P((struct si_port *));
9910015Speterstatic void si_disc_optim __P((struct tty *tp, struct termios *t,
10010015Speter					struct si_port *pp));
10110015Speterstatic void sihardclose __P((struct si_port *pp));
10210015Speterstatic void sidtrwakeup __P((void *chan));
10310015Speter
10412724Sphkstatic int	siparam __P((struct tty *, struct termios *));
10510015Speter
10612724Sphkstatic	void	si_registerdev __P((struct isa_device *id));
10712724Sphkstatic	int	siprobe __P((struct isa_device *id));
10812724Sphkstatic	int	siattach __P((struct isa_device *id));
10910708Speterstatic	void	si_modem_state __P((struct si_port *pp, struct tty *tp, int hi_ip));
11010708Speter
11112675Sjulianstruct isa_driver sidriver =
11212675Sjulian	{ siprobe, siattach, "si" };
11312675Sjulian
11412675Sjulian
11512675Sjulianstatic	d_open_t	siopen;
11612675Sjulianstatic	d_close_t	siclose;
11712675Sjulianstatic	d_read_t	siread;
11812675Sjulianstatic	d_write_t	siwrite;
11912675Sjulianstatic	d_ioctl_t	siioctl;
12012675Sjulianstatic	d_stop_t	sistop;
12112731Sbdestatic	d_devtotty_t	sidevtotty;
12212675Sjulian
12312675Sjulian#define CDEV_MAJOR 68
12412678Sphkstatic struct cdevsw si_cdevsw =
12512675Sjulian	{ siopen,	siclose,	siread,		siwrite,	/*68*/
12612743Sbde	  siioctl,	sistop,		noreset,	sidevtotty,/* si */
12712742Sbde	  ttselect,	nommap,		NULL,	"si",	NULL,	-1 };
12812675Sjulian
12912675Sjulian
13012174Speter#ifdef SI_DEBUG		/* use: ``options "SI_DEBUG"'' in your config file */
13113353Speter
13213353Speterstatic	void	si_dprintf __P((struct si_port *pp, int flags, const char *fmt,
13313353Speter				...));
13410708Speterstatic	char	*si_mctl2str __P((enum si_mctl cmd));
13513353Speter
13610708Speter#define	DPRINT(x)	si_dprintf x
13713353Speter
13810708Speter#else
13910708Speter#define	DPRINT(x)	/* void */
14010708Speter#endif
14110708Speter
14210962Speterstatic int si_Nports;
14310962Speterstatic int si_Nmodules;
14410962Speterstatic int si_debug = 0;	/* data, not bss, so it's patchable */
14510015Speter
14610962Speterstatic struct tty *si_tty;
14710962Speter
14812174Speter/* where the firmware lives; defined in si_code.c */
14910015Speterextern int si_dsize;
15010015Speterextern unsigned char si_download[];
15110015Speter
15210044Speterstruct si_softc {
15310044Speter	int 		sc_type;	/* adapter type */
15410044Speter	char 		*sc_typename;	/* adapter type string */
15510044Speter
15610044Speter	struct si_port	*sc_ports;	/* port structures for this card */
15710044Speter
15810044Speter	caddr_t		sc_paddr;	/* physical addr of iomem */
15910044Speter	caddr_t		sc_maddr;	/* kvaddr of iomem */
16010044Speter	int		sc_nport;	/* # ports on this card */
16110044Speter	int		sc_irq;		/* copy of attach irq */
16210044Speter	int		sc_eisa_iobase;	/* EISA io port address */
16310044Speter	int		sc_eisa_irqbits;
16410044Speter	struct kern_devconf sc_kdc;
16512675Sjulian#ifdef	DEVFS
16612675Sjulian	struct {
16712675Sjulian		void	*ttyd;
16812826Speter		void	*cuaa;
16912675Sjulian		void	*ttyl;
17012675Sjulian		void	*ttyi;
17112675Sjulian	} devfs_token[32]; /* what is the max per card? */
17213165Speter	void	*control_token;
17312675Sjulian#endif
17410044Speter};
17512724Sphkstatic struct si_softc si_softc[NSI];		/* up to 4 elements */
17610044Speter
17712174Speter#ifndef B2000	/* not standard, but the hardware knows it. */
17810015Speter# define B2000 2000
17910015Speter#endif
18010015Speterstatic struct speedtab bdrates[] = {
18110015Speter	B75,	CLK75,		/* 0x0 */
18210015Speter	B110,	CLK110,		/* 0x1 */
18310015Speter	B150,	CLK150,		/* 0x3 */
18410015Speter	B300,	CLK300,		/* 0x4 */
18510015Speter	B600,	CLK600,		/* 0x5 */
18610015Speter	B1200,	CLK1200,	/* 0x6 */
18710015Speter	B2000,	CLK2000,	/* 0x7 */
18810015Speter	B2400,	CLK2400,	/* 0x8 */
18910015Speter	B4800,	CLK4800,	/* 0x9 */
19010015Speter	B9600,	CLK9600,	/* 0xb */
19110015Speter	B19200,	CLK19200,	/* 0xc */
19210015Speter	B38400, CLK38400,	/* 0x2 (out of order!) */
19310015Speter	B57600, CLK57600,	/* 0xd */
19410015Speter	B115200, CLK110,	/* 0x1 (dupe!, 110 baud on "si") */
19510015Speter	-1,	-1
19610015Speter};
19710015Speter
19810015Speter
19910015Speter/* populated with approx character/sec rates - translated at card
20010015Speter * initialisation time to chars per tick of the clock */
20110015Speterstatic int done_chartimes = 0;
20210015Speterstatic struct speedtab chartimes[] = {
20310015Speter	B75,	8,
20410015Speter	B110,	11,
20510015Speter	B150,	15,
20610015Speter	B300,	30,
20710015Speter	B600,	60,
20810015Speter	B1200,	120,
20910015Speter	B2000,	200,
21010015Speter	B2400,	240,
21110015Speter	B4800,	480,
21210015Speter	B9600,	960,
21310015Speter	B19200,	1920,
21410015Speter	B38400, 3840,
21510015Speter	B57600, 5760,
21610015Speter	B115200, 11520,
21710015Speter	-1,	-1
21810015Speter};
21910015Speterstatic volatile int in_intr = 0;	/* Inside interrupt handler? */
22010015Speter
22110047Speterstatic int si_default_rate =	TTYDEF_SPEED;
22210047Speterstatic int si_default_iflag =	0;
22310047Speterstatic int si_default_oflag =	0;
22410047Speterstatic int si_default_lflag =	0;
22510047Speter#ifdef SI_DEF_HWFLOW
22610047Speterstatic int si_default_cflag =	TTYDEF_CFLAG | CRTSCTS;
22710047Speter#else
22810047Speterstatic int si_default_cflag =	TTYDEF_CFLAG;
22910047Speter#endif
23010047Speter
23110015Speter#ifdef POLL
23215683Speterstatic int si_pollrate;			/* in addition to irq */
23315639Speter
23416403SpeterSYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, "");
23515639Speter
23610015Speterstatic int init_finished = 0;
23710015Speterstatic void si_poll __P((void *));
23810015Speter#endif
23910015Speter
24010015Speter/*
24110015Speter * Array of adapter types and the corresponding RAM size. The order of
24210015Speter * entries here MUST match the ordinal of the adapter type.
24310015Speter */
24410015Speterstatic char *si_type[] = {
24510015Speter	"EMPTY",
24610015Speter	"SIHOST",
24710015Speter	"SI2",				/* MCA */
24810015Speter	"SIHOST2",
24910015Speter	"SIEISA",
25010015Speter};
25110015Speter
25210015Speter
25310015Speterstatic struct kern_devconf si_kdc[NSI] = { {
25410015Speter	0, 0, 0,		/* filled in by dev_attach */
25510015Speter	"si", 0, { MDDT_ISA, 0, "tty" },
25610015Speter	isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
25710015Speter	&kdc_isa0,		/* parent */
25810015Speter	0,			/* parent data */
25910015Speter	DC_UNCONFIGURED,	/* state */
26010015Speter	"Specialix SI/XIO Host adapter",
26110015Speter	DC_CLS_SERIAL,		/* class */
26210015Speter} };
26310015Speter
26412724Sphkstatic void
26510015Spetersi_registerdev(id)
26610015Speter	struct isa_device *id;
26710015Speter{
26810015Speter	if (id->id_unit != 0) {
26916214Speter		bcopy(&si_kdc[0], &si_kdc[id->id_unit], sizeof(si_kdc[0]));
27010015Speter	}
27110015Speter	si_kdc[id->id_unit].kdc_unit = id->id_unit;
27210015Speter	si_kdc[id->id_unit].kdc_isa = id;
27312174Speter	si_kdc[id->id_unit].kdc_state = DC_UNCONFIGURED;
27410015Speter	dev_attach(&si_kdc[id->id_unit]);
27510015Speter}
27610015Speter
27710015Speter/* Look for a valid board at the given mem addr */
27812724Sphkstatic int
27910015Spetersiprobe(id)
28010015Speter	struct isa_device *id;
28110015Speter{
28210015Speter	struct si_softc *sc;
28310015Speter	int type;
28410015Speter	u_int i, ramsize;
28510015Speter	volatile BYTE was, *ux;
28610015Speter	volatile unsigned char *maddr;
28710015Speter	unsigned char *paddr;
28810015Speter
28910015Speter	si_registerdev(id);
29010015Speter
29115683Speter	si_pollrate = (hz / 10);	/* 10 per second */
29215683Speter
29310015Speter	maddr = id->id_maddr;		/* virtual address... */
29410015Speter	paddr = (caddr_t)vtophys(id->id_maddr);	/* physical address... */
29510015Speter
29612496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: probe at virtual=0x%x physical=0x%x\n",
29712496Speter		id->id_unit, id->id_maddr, paddr));
29810015Speter
29910015Speter	/*
30010015Speter	 * this is a lie, but it's easier than trying to handle caching
30110015Speter	 * and ram conflicts in the >1M and <16M region.
30210015Speter	 */
30310015Speter	if ((caddr_t)paddr < (caddr_t)IOM_BEGIN ||
30410015Speter	    (caddr_t)paddr >= (caddr_t)IOM_END) {
30512174Speter		printf("si%d: iomem (%lx) out of range\n",
30612174Speter			id->id_unit, (long)paddr);
30710015Speter		return(0);
30810015Speter	}
30910015Speter
31010015Speter	if (id->id_unit >= NSI) {
31110015Speter		/* THIS IS IMPOSSIBLE */
31210015Speter		return(0);
31310015Speter	}
31410015Speter
31510015Speter	if (((u_int)paddr & 0x7fff) != 0) {
31610015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
31710015Speter			"si%d: iomem (%x) not on 32k boundary\n",
31810015Speter			id->id_unit, paddr));
31910015Speter		return(0);
32010015Speter	}
32110015Speter
32210015Speter
32310015Speter	for (i=0; i < NSI; i++) {
32410015Speter		if ((sc = &si_softc[i]) == NULL)
32510015Speter			continue;
32610015Speter		if ((caddr_t)sc->sc_paddr == (caddr_t)paddr) {
32710015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
32810015Speter				"si%d: iomem (%x) already configured to si%d\n",
32910015Speter				id->id_unit, sc->sc_paddr, i));
33010015Speter			return(0);
33110015Speter		}
33210015Speter	}
33310015Speter
33410015Speter#if NEISA > 0
33510015Speter	if (id->id_iobase > 0x0fff) {	/* EISA card */
33610015Speter		int irq, port;
33710015Speter		unsigned long base;
33810015Speter		int eisa_irqs[] = { 0,IRQ1,IRQ2,IRQ3,IRQ4,IRQ5,IRQ6,IRQ7,
33910015Speter			IRQ8,IRQ9,IRQ10,IRQ11,IRQ12,IRQ13,IRQ14,IRQ15 };
34010015Speter
34110015Speter		port = id->id_iobase;
34210015Speter		base = (inb(port+1) << 24) | (inb(port) << 16);
34310015Speter		irq  = ((inb(port+2) >> 4) & 0xf);
34410015Speter
34510015Speter		id->id_irq = eisa_irqs[irq];
34610015Speter
34710015Speter		DPRINT((0, DBG_AUTOBOOT,
34812496Speter		    "si%d: EISA base %x, irq %x, id_irq %x, port %x\n",
34910015Speter		    id->id_unit, base, irq, id->id_irq, port));
35010015Speter
35110015Speter		if ((id->id_irq&(IRQ1|IRQ2|IRQ8|IRQ13)) != 0)
35210015Speter			goto bad_irq;
35310015Speter
35410015Speter		id->id_iobase &= 0xf000;
35510015Speter		id->id_iosize  = 0x0fff;
35610015Speter
35710015Speter		type = EISA;
35810015Speter		outb(p+2, (BYTE)irq << 4);
35910015Speter
36010015Speter		sc->sc_eisa_iobase = p;
36110015Speter		sc->sc_eisa_irqbits = irq << 4;
36210015Speter		ramsize = SIEISA_RAMSIZE;
36310015Speter		goto got_card;
36410015Speter	}
36510015Speter#endif
36610015Speter
36710015Speter	/* Is there anything out there? (0x17 is just an arbitrary number) */
36810015Speter	*maddr = 0x17;
36910015Speter	if (*maddr != 0x17) {
37010015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
37110015Speter			"si%d: 0x17 check fail at phys 0x%x\n",
37210015Speter			id->id_unit, paddr));
37310015Speterfail:
37410015Speter		return(0);
37510015Speter	}
37610015Speter	/*
37710015Speter	 * OK, now to see if whatever responded is really an SI card.
37810015Speter	 * Try for a MK II first (SIHOST2)
37910015Speter	 */
38010015Speter	for (i=SIPLSIG; i<SIPLSIG+8; i++)
38110015Speter		if ((*(maddr+i) & 7) != (~(BYTE)i & 7))
38210015Speter			goto try_mk1;
38310015Speter
38410015Speter	/* It must be an SIHOST2 */
38510015Speter	*(maddr + SIPLRESET) = 0;
38610015Speter	*(maddr + SIPLIRQCLR) = 0;
38710015Speter	*(maddr + SIPLIRQSET) = 0x10;
38810015Speter	type = SIHOST2;
38910015Speter	ramsize = SIHOST2_RAMSIZE;
39010015Speter	goto got_card;
39110015Speter
39210015Speter	/*
39310015Speter	 * Its not a MK II, so try for a MK I (SIHOST)
39410015Speter	 */
39510015Spetertry_mk1:
39610015Speter	*(maddr+SIRESET) = 0x0;		/* reset the card */
39710015Speter	*(maddr+SIINTCL) = 0x0;		/* clear int */
39810015Speter	*(maddr+SIRAM) = 0x17;
39910015Speter	if (*(maddr+SIRAM) != (BYTE)0x17)
40010015Speter		goto fail;
40110015Speter	*(maddr+0x7ff8) = 0x17;
40210015Speter	if (*(maddr+0x7ff8) != (BYTE)0x17) {
40310015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
40410015Speter			"si%d: 0x17 check fail at phys 0x%x = 0x%x\n",
40510015Speter			id->id_unit, paddr+0x77f8, *(maddr+0x77f8)));
40610015Speter		goto fail;
40710015Speter	}
40810015Speter
40910015Speter	/* It must be an SIHOST (maybe?) - there must be a better way XXXX */
41010015Speter	type = SIHOST;
41110015Speter	ramsize = SIHOST_RAMSIZE;
41210015Speter
41310015Spetergot_card:
41412496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: found type %d card, try memory test\n",
41512496Speter		id->id_unit, type));
41610015Speter	/* Try the acid test */
41710015Speter	ux = (BYTE *)(maddr + SIRAM);
41810015Speter	for (i=0; i<ramsize; i++, ux++)
41910015Speter		*ux = (BYTE)(i&0xff);
42010015Speter	ux = (BYTE *)(maddr + SIRAM);
42110015Speter	for (i=0; i<ramsize; i++, ux++) {
42210015Speter		if ((was = *ux) != (BYTE)(i&0xff)) {
42310015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
42412174Speter				"si%d: match fail at phys 0x%x, was %x should be %x\n",
42510015Speter				id->id_unit, paddr+i, was, i&0xff));
42610015Speter			goto fail;
42710015Speter		}
42810015Speter	}
42910015Speter
43010015Speter	/* clear out the RAM */
43110015Speter	ux = (BYTE *)(maddr + SIRAM);
43210015Speter	for (i=0; i<ramsize; i++)
43310015Speter		*ux++ = 0;
43410015Speter	ux = (BYTE *)(maddr + SIRAM);
43510015Speter	for (i=0; i<ramsize; i++) {
43610015Speter		if ((was = *ux++) != 0) {
43710015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
43812174Speter				"si%d: clear fail at phys 0x%x, was %x\n",
43910015Speter				id->id_unit, paddr+i, was));
44010015Speter			goto fail;
44110015Speter		}
44210015Speter	}
44310015Speter
44410015Speter	/*
44510015Speter	 * Success, we've found a valid board, now fill in
44610015Speter	 * the adapter structure.
44710015Speter	 */
44810015Speter	switch (type) {
44910015Speter	case SIHOST2:
45010015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
45110015Speterbad_irq:
45210015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
45310015Speter				"si%d: bad IRQ value - %d\n",
45410015Speter				id->id_unit, id->id_irq));
45510015Speter			return(0);
45610015Speter		}
45710015Speter		id->id_msize = SIHOST2_MEMSIZE;
45810015Speter		break;
45910015Speter	case SIHOST:
46010015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
46110015Speter			goto bad_irq;
46210015Speter		}
46310015Speter		id->id_msize = SIHOST_MEMSIZE;
46410015Speter		break;
46510015Speter	case SIEISA:
46610015Speter		id->id_msize = SIEISA_MEMSIZE;
46710015Speter		break;
46810015Speter	case SI2:		/* MCA */
46910015Speter	default:
47010015Speter		printf("si%d: %s not supported\n", id->id_unit, si_type[type]);
47110015Speter		return(0);
47210015Speter	}
47310015Speter	si_softc[id->id_unit].sc_type = type;
47410015Speter	si_softc[id->id_unit].sc_typename = si_type[type];
47510015Speter	return(-1);	/* -1 == found */
47610015Speter}
47710015Speter
47810015Speter/*
47910015Speter * Attach the device.  Initialize the card.
48010015Speter */
48112724Sphkstatic int
48210015Spetersiattach(id)
48310015Speter	struct isa_device *id;
48410015Speter{
48510015Speter	int unit = id->id_unit;
48610015Speter	struct si_softc *sc = &si_softc[unit];
48710015Speter	struct si_port *pp;
48810015Speter	volatile struct si_channel *ccbp;
48910015Speter	volatile struct si_reg *regp;
49010015Speter	volatile caddr_t maddr;
49110015Speter	struct si_module *modp;
49210015Speter	struct tty *tp;
49310015Speter	struct speedtab *spt;
49410015Speter	int nmodule, nport, x, y;
49512174Speter	int uart_type;
49610015Speter
49712496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", id->id_unit));
49810015Speter
49910015Speter	sc->sc_paddr = (caddr_t)vtophys(id->id_maddr);
50010015Speter	sc->sc_maddr = id->id_maddr;
50110015Speter	sc->sc_irq = id->id_irq;
50210015Speter
50310015Speter	sc->sc_ports = NULL;			/* mark as uninitialised */
50410015Speter
50510015Speter	maddr = sc->sc_maddr;
50610015Speter
50710015Speter	/*
50810015Speter	 * OK, now lets download the firmware and try and boot the CPU..
50910015Speter	 */
51010015Speter
51112496Speter	DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n",
51212496Speter		id->id_unit, si_dsize));
51310015Speter	bcopy(si_download, maddr, si_dsize);
51410015Speter
51510015Speter	switch (sc->sc_type) {
51610015Speter	case SIEISA:
51710015Speter#if NEISA > 0
51810015Speter		/* modify the Z280 firmware to tell it that it's on an EISA */
51910015Speter		*(maddr+0x42) = 1;
52010015Speter		outb(sc->sc_eisa_iobase+2, sc->sc_eisa_irqbits | 4);
52110015Speter		(void)inb(sc->sc_eisa_iobase+3); /* reset interrupt */
52210015Speter		break;
52310015Speter#endif	/* fall-through if not EISA */
52410015Speter	case SI2:
52512174Speter		/*
52612174Speter		 * must get around to converting the code for
52712174Speter		 * these one day, if FreeBSD ever supports it.
52812174Speter		 */
52910015Speter		return 0;
53010015Speter	case SIHOST:
53110015Speter		*(maddr+SIRESET_CL) = 0;
53210015Speter		*(maddr+SIINTCL_CL) = 0;
53310015Speter		break;
53410015Speter	case SIHOST2:
53510015Speter		*(maddr+SIPLRESET) = 0x10;
53610015Speter		switch (sc->sc_irq) {
53710015Speter		case IRQ11:
53810015Speter			*(maddr+SIPLIRQ11) = 0x10;
53910015Speter			break;
54010015Speter		case IRQ12:
54110015Speter			*(maddr+SIPLIRQ12) = 0x10;
54210015Speter			break;
54310015Speter		case IRQ15:
54410015Speter			*(maddr+SIPLIRQ15) = 0x10;
54510015Speter			break;
54610015Speter		}
54710015Speter		*(maddr+SIPLIRQCLR) = 0x10;
54810015Speter		break;
54910015Speter	}
55010015Speter
55110015Speter	DELAY(1000000);			/* wait around for a second */
55210015Speter
55310015Speter	regp = (struct si_reg *)maddr;
55410015Speter	y = 0;
55510015Speter					/* wait max of 5 sec for init OK */
55610015Speter	while (regp->initstat == 0 && y++ < 10) {
55710015Speter		DELAY(500000);
55810015Speter	}
55910015Speter	switch (regp->initstat) {
56010015Speter	case 0:
56110015Speter		printf("si%d: startup timeout - aborting\n", unit);
56212174Speter		sc->sc_type = SIEMPTY;
56310015Speter		return 0;
56410015Speter	case 1:
56512174Speter			/* set throttle to 125 intr per second */
56615639Speter		regp->int_count = INT_COUNT;
56710015Speter			/* rx intr max of 25 timer per second */
56815639Speter		regp->rx_int_count = RXINT_COUNT;
56910015Speter		regp->int_pending = 0;		/* no intr pending */
57010015Speter		regp->int_scounter = 0;	/* reset counter */
57110015Speter		break;
57210015Speter	case 0xff:
57310015Speter		/*
57410015Speter		 * No modules found, so give up on this one.
57510015Speter		 */
57610015Speter		printf("si%d: %s - no ports found\n", unit,
57710015Speter			si_type[sc->sc_type]);
57810015Speter		return 0;
57910015Speter	default:
58010015Speter		printf("si%d: Z280 version error - initstat %x\n",
58110015Speter			unit, regp->initstat);
58210015Speter		return 0;
58310015Speter	}
58410015Speter
58510015Speter	/*
58610015Speter	 * First time around the ports just count them in order
58710015Speter	 * to allocate some memory.
58810015Speter	 */
58910015Speter	nport = 0;
59010015Speter	modp = (struct si_module *)(maddr + 0x80);
59110015Speter	for (;;) {
59212174Speter		DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp));
59310015Speter		switch (modp->sm_type & (~MMASK)) {
59410015Speter		case M232:
59510015Speter		case M422:
59610015Speter			DPRINT((0, DBG_DOWNLOAD,
59712174Speter				"si%d: Found 232/422 module, %d ports\n",
59810015Speter				unit, (int)(modp->sm_type & MMASK)));
59910015Speter
60010015Speter			/* this is a firmware issue */
60110015Speter			if (si_Nports == SI_MAXPORTPERCARD) {
60210015Speter				printf("si%d: extra ports ignored\n", unit);
60310015Speter				continue;
60410015Speter			}
60510015Speter
60610015Speter			x = modp->sm_type & MMASK;
60710015Speter			nport += x;
60810015Speter			si_Nports += x;
60910015Speter			si_Nmodules++;
61010015Speter			break;
61110015Speter		default:
61210015Speter			printf("si%d: unknown module type %d\n",
61310015Speter				unit, modp->sm_type);
61410015Speter			break;
61510015Speter		}
61610015Speter		if (modp->sm_next == 0)
61710015Speter			break;
61810015Speter		modp = (struct si_module *)
61910015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
62010015Speter	}
62110015Speter	sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport,
62210015Speter		M_DEVBUF, M_NOWAIT);
62310015Speter	if (sc->sc_ports == 0) {
62410015Spetermem_fail:
62510015Speter		printf("si%d: fail to malloc memory for port structs\n",
62610015Speter			unit);
62710015Speter		return 0;
62810015Speter	}
62910015Speter	bzero(sc->sc_ports, sizeof(struct si_port) * nport);
63010015Speter	sc->sc_nport = nport;
63110015Speter
63210015Speter	/*
63310015Speter	 * allocate tty structures for ports
63410015Speter	 */
63510015Speter	tp = (struct tty *)malloc(sizeof(*tp) * nport, M_DEVBUF, M_NOWAIT);
63610015Speter	if (tp == 0)
63710015Speter		goto mem_fail;
63810015Speter	bzero(tp, sizeof(*tp) * nport);
63910962Speter	si_tty = tp;
64010015Speter
64110015Speter	/* mark the device state as attached */
64210015Speter	si_kdc[unit].kdc_state = DC_BUSY;
64310015Speter
64410015Speter	/*
64510015Speter	 * Scan round the ports again, this time initialising.
64610015Speter	 */
64710015Speter	pp = sc->sc_ports;
64810015Speter	nmodule = 0;
64910015Speter	modp = (struct si_module *)(maddr + 0x80);
65012174Speter	uart_type = 0;
65110015Speter	for (;;) {
65210015Speter		switch (modp->sm_type & (~MMASK)) {
65310015Speter		case M232:
65410015Speter		case M422:
65510015Speter			nmodule++;
65610015Speter			nport = (modp->sm_type & MMASK);
65710015Speter			ccbp = (struct si_channel *)((char *)modp+0x100);
65812174Speter			if (uart_type == 0)
65912174Speter				uart_type = ccbp->type;
66010015Speter			for (x = 0; x < nport; x++, pp++, ccbp++) {
66110015Speter				pp->sp_ccb = ccbp;	/* save the address */
66210015Speter				pp->sp_tty = tp++;
66310015Speter				pp->sp_pend = IDLE_CLOSE;
66410015Speter				pp->sp_state = 0;	/* internal flag */
66510015Speter				pp->sp_dtr_wait = 3 * hz;
66610047Speter				pp->sp_iin.c_iflag = si_default_iflag;
66710047Speter				pp->sp_iin.c_oflag = si_default_oflag;
66810047Speter				pp->sp_iin.c_cflag = si_default_cflag;
66910047Speter				pp->sp_iin.c_lflag = si_default_lflag;
67010015Speter				termioschars(&pp->sp_iin);
67110015Speter				pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed =
67210047Speter					si_default_rate;
67310015Speter				pp->sp_iout = pp->sp_iin;
67410015Speter			}
67510015Speter			break;
67610015Speter		default:
67710015Speter			break;
67810015Speter		}
67910015Speter		if (modp->sm_next == 0) {
68012174Speter			printf("si%d: card: %s, ports: %d, modules: %d (type: %d)\n",
68110015Speter				unit,
68210015Speter				sc->sc_typename,
68310015Speter				sc->sc_nport,
68412174Speter				nmodule,
68512174Speter				uart_type);
68610015Speter			break;
68710015Speter		}
68810015Speter		modp = (struct si_module *)
68910015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
69010015Speter	}
69110015Speter	if (done_chartimes == 0) {
69210015Speter		for (spt = chartimes ; spt->sp_speed != -1; spt++) {
69310015Speter			if ((spt->sp_code /= hz) == 0)
69410015Speter				spt->sp_code = 1;
69510015Speter		}
69610015Speter		done_chartimes = 1;
69710015Speter	}
69812502Sjulian
69912675Sjulian#ifdef DEVFS
70012675Sjulian/*	path	name	devsw		minor	type   uid gid perm*/
70113169Speter	for ( x = 0; x < sc->sc_nport; x++ ) {
70213165Speter		y = x + 1;	/* For sync with the manuals that start at 1 */
70313630Sphk		sc->devfs_token[x].ttyd = devfs_add_devswf(
70413630Sphk			&si_cdevsw, x,
70513630Sphk			DV_CHR, 0, 0, 0600, "ttyA%02d", y);
70613630Sphk		sc->devfs_token[x].cuaa = devfs_add_devswf(
70713630Sphk			&si_cdevsw, x + 128,
70813630Sphk			DV_CHR, 0, 0, 0600, "cuaA%02d", y);
70913630Sphk		sc->devfs_token[x].ttyi = devfs_add_devswf(
71013630Sphk			&si_cdevsw, x + 0x10000,
71113630Sphk			DV_CHR, 0, 0, 0600, "ttyiA%02d", y);
71213630Sphk		sc->devfs_token[x].ttyl = devfs_add_devswf(
71313630Sphk			&si_cdevsw, x + 0x20000,
71413630Sphk			DV_CHR, 0, 0, 0600, "ttylA%02d", y);
71512675Sjulian	}
71614873Sscrappy	sc->control_token =
71714873Sscrappy		devfs_add_devswf(&si_cdevsw, 0x40000, DV_CHR, 0, 0, 0600,
71814873Sscrappy				 "si_control");
71912675Sjulian#endif
72010015Speter	return (1);
72110015Speter}
72210015Speter
72312675Sjulianstatic	int
72410015Spetersiopen(dev, flag, mode, p)
72510015Speter	dev_t dev;
72610015Speter	int flag, mode;
72710015Speter	struct proc *p;
72810015Speter{
72910015Speter	int oldspl, error;
73010015Speter	int card, port;
73110015Speter	register struct si_softc *sc;
73210015Speter	register struct tty *tp;
73310015Speter	volatile struct si_channel *ccbp;
73410015Speter	struct si_port *pp;
73510015Speter	int mynor = minor(dev);
73610015Speter
73710015Speter	/* quickly let in /dev/si_control */
73810015Speter	if (IS_CONTROLDEV(mynor)) {
73910015Speter		if (error = suser(p->p_ucred, &p->p_acflag))
74010015Speter			return(error);
74110015Speter		return(0);
74210015Speter	}
74310015Speter
74410015Speter	card = SI_CARD(mynor);
74510015Speter	if (card >= NSI)
74610015Speter		return (ENXIO);
74710015Speter	sc = &si_softc[card];
74810015Speter
74912174Speter	if (sc->sc_type == SIEMPTY) {
75012174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n",
75110015Speter			card, sc->sc_typename));
75210015Speter		return(ENXIO);
75310015Speter	}
75410015Speter
75510015Speter	port = SI_PORT(mynor);
75610015Speter	if (port >= sc->sc_nport) {
75712174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n",
75810015Speter			card, sc->sc_nport));
75910015Speter		return(ENXIO);
76010015Speter	}
76110015Speter
76210015Speter#ifdef	POLL
76310015Speter	/*
76410015Speter	 * We've now got a device, so start the poller.
76510015Speter	 */
76610015Speter	if (init_finished == 0) {
76715639Speter		timeout(si_poll, (caddr_t)0L, si_pollrate);
76810015Speter		init_finished = 1;
76910015Speter	}
77010015Speter#endif
77110015Speter
77210015Speter	/* initial/lock device */
77310015Speter	if (IS_STATE(mynor)) {
77410015Speter		return(0);
77510015Speter	}
77610015Speter
77710015Speter	pp = sc->sc_ports + port;
77810015Speter	tp = pp->sp_tty;			/* the "real" tty */
77910015Speter	ccbp = pp->sp_ccb;			/* Find control block */
78010015Speter	DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%x,%x,%x,%x)\n",
78110015Speter		dev, flag, mode, p));
78210015Speter
78310015Speter	oldspl = spltty();			/* Keep others out */
78410015Speter	error = 0;
78510015Speter
78610015Speteropen_top:
78710015Speter	while (pp->sp_state & SS_DTR_OFF) {
78810015Speter		error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sidtr", 0);
78910015Speter		if (error != 0)
79010015Speter			goto out;
79110015Speter	}
79210015Speter
79310015Speter	if (tp->t_state & TS_ISOPEN) {
79410015Speter		/*
79510015Speter		 * The device is open, so everything has been initialised.
79610015Speter		 * handle conflicts.
79710015Speter		 */
79810015Speter		if (IS_CALLOUT(mynor)) {
79910015Speter			if (!pp->sp_active_out) {
80010015Speter				error = EBUSY;
80110015Speter				goto out;
80210015Speter			}
80310015Speter		} else {
80410015Speter			if (pp->sp_active_out) {
80510015Speter				if (flag & O_NONBLOCK) {
80610015Speter					error = EBUSY;
80710015Speter					goto out;
80810015Speter				}
80910015Speter				error = tsleep(&pp->sp_active_out,
81010015Speter						TTIPRI|PCATCH, "sibi", 0);
81110015Speter				if (error != 0)
81210015Speter					goto out;
81310015Speter				goto open_top;
81410015Speter			}
81510015Speter		}
81610015Speter		if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
81710015Speter			DPRINT((pp, DBG_OPEN|DBG_FAIL,
81810015Speter				"already open and EXCLUSIVE set\n"));
81910015Speter			error = EBUSY;
82010015Speter			goto out;
82110015Speter		}
82210015Speter	} else {
82310015Speter		/*
82410015Speter		 * The device isn't open, so there are no conflicts.
82510015Speter		 * Initialize it. Avoid sleep... :-)
82610015Speter		 */
82710015Speter		DPRINT((pp, DBG_OPEN, "first open\n"));
82810015Speter		tp->t_oproc = si_start;
82910015Speter		tp->t_param = siparam;
83010015Speter		tp->t_dev = dev;
83110015Speter		tp->t_termios = mynor & SI_CALLOUT_MASK
83210015Speter				? pp->sp_iout : pp->sp_iin;
83310015Speter
83410015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
83510015Speter
83610015Speter		++pp->sp_wopeners;	/* in case of sleep in siparam */
83710015Speter
83810015Speter		error = siparam(tp, &tp->t_termios);
83910015Speter
84010015Speter		--pp->sp_wopeners;
84110015Speter		if (error != 0)
84210015Speter			goto out;
84310015Speter		/* XXX: we should goto_top if siparam slept */
84410015Speter
84510015Speter		ttsetwater(tp);
84610015Speter
84710015Speter		/* set initial DCD state */
84810015Speter		pp->sp_last_hi_ip = ccbp->hi_ip;
84910015Speter		if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) {
85010015Speter			(*linesw[tp->t_line].l_modem)(tp, 1);
85110015Speter		}
85210015Speter	}
85310015Speter
85410015Speter	/* whoops! we beat the close! */
85510015Speter	if (pp->sp_state & SS_CLOSING) {
85610015Speter		/* try and stop it from proceeding to bash the hardware */
85710015Speter		pp->sp_state &= ~SS_CLOSING;
85810015Speter	}
85910015Speter
86010015Speter	/*
86110015Speter	 * Wait for DCD if necessary
86210015Speter	 */
86310015Speter	if (!(tp->t_state & TS_CARR_ON)
86410015Speter	    && !IS_CALLOUT(mynor)
86510015Speter	    && !(tp->t_cflag & CLOCAL)
86610015Speter	    && !(flag & O_NONBLOCK)) {
86710015Speter		++pp->sp_wopeners;
86810015Speter		DPRINT((pp, DBG_OPEN, "sleeping for carrier\n"));
86910015Speter		error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0);
87010015Speter		--pp->sp_wopeners;
87110015Speter		if (error != 0)
87210015Speter			goto out;
87310015Speter		goto open_top;
87410015Speter	}
87510015Speter
87610015Speter	error = (*linesw[tp->t_line].l_open)(dev, tp);
87710015Speter	si_disc_optim(tp, &tp->t_termios, pp);
87810015Speter	if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor))
87910015Speter		pp->sp_active_out = TRUE;
88010015Speter
88110015Speter	pp->sp_state |= SS_OPEN;	/* made it! */
88210015Speter
88310015Speterout:
88410015Speter	splx(oldspl);
88510015Speter
88610015Speter	DPRINT((pp, DBG_OPEN, "leaving siopen\n"));
88710015Speter
88810015Speter	if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0)
88910015Speter		sihardclose(pp);
89010015Speter
89110015Speter	return(error);
89210015Speter}
89310015Speter
89412675Sjulianstatic	int
89510015Spetersiclose(dev, flag, mode, p)
89610015Speter	dev_t dev;
89710015Speter	int flag, mode;
89810015Speter	struct proc *p;
89910015Speter{
90010015Speter	register struct si_port *pp;
90110015Speter	register struct tty *tp;
90210015Speter	int oldspl;
90310015Speter	int error = 0;
90410015Speter	int mynor = minor(dev);
90510015Speter
90610015Speter	if (IS_SPECIAL(mynor))
90710015Speter		return(0);
90810015Speter
90910015Speter	oldspl = spltty();
91010015Speter
91110015Speter	pp = MINOR2PP(mynor);
91210015Speter	tp = pp->sp_tty;
91310015Speter
91410015Speter	DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%x,%x,%x,%x) sp_state:%x\n",
91510015Speter		dev, flag, mode, p, pp->sp_state));
91610015Speter
91710015Speter	/* did we sleep and loose a race? */
91810015Speter	if (pp->sp_state & SS_CLOSING) {
91910015Speter		/* error = ESOMETING? */
92010015Speter		goto out;
92110015Speter	}
92210015Speter
92310015Speter	/* begin race detection.. */
92410015Speter	pp->sp_state |= SS_CLOSING;
92510015Speter
92610015Speter	si_write_enable(pp, 0);		/* block writes for ttywait() */
92710015Speter
92810015Speter	/* THIS MAY SLEEP IN TTYWAIT!!! */
92910015Speter	(*linesw[tp->t_line].l_close)(tp, flag);
93010015Speter
93110015Speter	si_write_enable(pp, 1);
93210015Speter
93310015Speter	/* did we sleep and somebody started another open? */
93410015Speter	if (!(pp->sp_state & SS_CLOSING)) {
93510015Speter		/* error = ESOMETING? */
93610015Speter		goto out;
93710015Speter	}
93810015Speter	/* ok. we are now still on the right track.. nuke the hardware */
93910015Speter
94010015Speter	if (pp->sp_state & SS_LSTART) {
94110015Speter		untimeout((timeout_func_t)si_lstart, (caddr_t)pp);
94210015Speter		pp->sp_state &= ~SS_LSTART;
94310015Speter	}
94410015Speter
94510015Speter	sistop(tp, FREAD | FWRITE);
94610015Speter
94710015Speter	sihardclose(pp);
94810015Speter	ttyclose(tp);
94910015Speter	pp->sp_state &= ~SS_OPEN;
95010015Speter
95110015Speterout:
95210015Speter	DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n"));
95310015Speter	splx(oldspl);
95410015Speter	return(error);
95510015Speter}
95610015Speter
95710015Speterstatic void
95810015Spetersihardclose(pp)
95910015Speter	struct si_port *pp;
96010015Speter{
96110015Speter	int oldspl;
96210015Speter	struct tty *tp;
96310015Speter	volatile struct si_channel *ccbp;
96410015Speter
96510015Speter	oldspl = spltty();
96610015Speter
96710015Speter	tp = pp->sp_tty;
96810015Speter	ccbp = pp->sp_ccb;			/* Find control block */
96910015Speter	if (tp->t_cflag & HUPCL
97010015Speter	    || !pp->sp_active_out
97110015Speter	       && !(ccbp->hi_ip & IP_DCD)
97210015Speter	       && !(pp->sp_iin.c_cflag && CLOCAL)
97310015Speter	    || !(tp->t_state & TS_ISOPEN)) {
97410015Speter
97510015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
97610015Speter		(void) si_command(pp, FCLOSE, SI_NOWAIT);
97710015Speter
97810015Speter		if (pp->sp_dtr_wait != 0) {
97910015Speter			timeout(sidtrwakeup, pp, pp->sp_dtr_wait);
98010015Speter			pp->sp_state |= SS_DTR_OFF;
98110015Speter		}
98210015Speter
98310015Speter	}
98410015Speter	pp->sp_active_out = FALSE;
98510015Speter	wakeup((caddr_t)&pp->sp_active_out);
98610015Speter	wakeup(TSA_CARR_ON(tp));
98710015Speter
98810015Speter	splx(oldspl);
98910015Speter}
99010015Speter
99110015Speter
99210015Speter/*
99310015Speter * called at splsoftclock()...
99410015Speter */
99510015Speterstatic void
99610015Spetersidtrwakeup(chan)
99710015Speter	void *chan;
99810015Speter{
99910015Speter	struct si_port *pp;
100010015Speter	int oldspl;
100110015Speter
100210015Speter	oldspl = spltty();
100310015Speter
100410015Speter	pp = (struct si_port *)chan;
100510015Speter	pp->sp_state &= ~SS_DTR_OFF;
100610015Speter	wakeup(&pp->sp_dtr_wait);
100710015Speter
100810015Speter	splx(oldspl);
100910015Speter}
101010015Speter
101110015Speter/*
101210015Speter * User level stuff - read and write
101310015Speter */
101412675Sjulianstatic	int
101510015Spetersiread(dev, uio, flag)
101610015Speter	register dev_t dev;
101710015Speter	struct uio *uio;
101810015Speter	int flag;
101910015Speter{
102010015Speter	register struct tty *tp;
102110015Speter	int mynor = minor(dev);
102210015Speter
102310015Speter	if (IS_SPECIAL(mynor)) {
102410015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_READ, "siread(CONTROLDEV!!)\n"));
102510015Speter		return(ENODEV);
102610015Speter	}
102710015Speter	tp = MINOR2TP(mynor);
102810015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_READ,
102910015Speter		"siread(%x,%x,%x)\n", dev, uio, flag));
103010015Speter	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
103110015Speter}
103210015Speter
103310015Speter
103412675Sjulianstatic	int
103510015Spetersiwrite(dev, uio, flag)
103610015Speter	dev_t dev;
103710015Speter	struct uio *uio;
103810015Speter	int flag;
103910015Speter{
104010015Speter	register struct si_port *pp;
104110015Speter	register struct tty *tp;
104210015Speter	int error = 0;
104310015Speter	int mynor = minor(dev);
104410015Speter	int oldspl;
104510015Speter
104610015Speter	if (IS_SPECIAL(mynor)) {
104710015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n"));
104810015Speter		return(ENODEV);
104910015Speter	}
105010015Speter	pp = MINOR2PP(mynor);
105110015Speter	tp = pp->sp_tty;
105210015Speter	DPRINT((pp, DBG_WRITE, "siwrite(%x,%x,%x)\n", dev, uio, flag));
105310015Speter
105410015Speter	oldspl = spltty();
105510015Speter	/*
105610015Speter	 * If writes are currently blocked, wait on the "real" tty
105710015Speter	 */
105810015Speter	while (pp->sp_state & SS_BLOCKWRITE) {
105910015Speter		pp->sp_state |= SS_WAITWRITE;
106010015Speter		DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n"));
106110015Speter		if (error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH,
106210015Speter				     "siwrite", 0))
106310015Speter			goto out;
106410015Speter	}
106510015Speter
106610015Speter	error = (*linesw[tp->t_line].l_write)(tp, uio, flag);
106710015Speterout:
106810015Speter	splx(oldspl);
106910015Speter	return (error);
107010015Speter}
107110015Speter
107210015Speter
107312675Sjulianstatic	struct tty *
107410015Spetersidevtotty(dev_t dev)
107510015Speter{
107610015Speter	struct si_port *pp;
107710015Speter	int mynor = minor(dev);
107810015Speter	struct si_softc *sc = &si_softc[SI_CARD(mynor)];
107910015Speter
108010015Speter	if (IS_SPECIAL(mynor))
108110015Speter		return(NULL);
108210015Speter	if (SI_PORT(mynor) >= sc->sc_nport)
108310015Speter		return(NULL);
108410015Speter	pp = MINOR2PP(mynor);
108510015Speter	return (pp->sp_tty);
108610015Speter}
108710015Speter
108812675Sjulianstatic	int
108910015Spetersiioctl(dev, cmd, data, flag, p)
109010015Speter	dev_t dev;
109110015Speter	int cmd;
109210015Speter	caddr_t data;
109310015Speter	int flag;
109410015Speter	struct proc *p;
109510015Speter{
109610015Speter	struct si_port *pp;
109710015Speter	register struct tty *tp;
109810015Speter	int error;
109910015Speter	int mynor = minor(dev);
110010015Speter	int oldspl;
110110015Speter	int blocked = 0;
110210015Speter#if defined(COMPAT_43)
110310015Speter	int oldcmd;
110410015Speter	struct termios term;
110510015Speter#endif
110610015Speter
110710015Speter	if (IS_SI_IOCTL(cmd))
110810015Speter		return(si_Sioctl(dev, cmd, data, flag, p));
110910015Speter
111010015Speter	pp = MINOR2PP(mynor);
111110015Speter	tp = pp->sp_tty;
111210015Speter
111310015Speter	DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%x,%x,%x,%x)\n",
111410015Speter		dev, cmd, data, flag));
111510015Speter	if (IS_STATE(mynor)) {
111610015Speter		struct termios *ct;
111710015Speter
111810015Speter		switch (mynor & SI_STATE_MASK) {
111910015Speter		case SI_INIT_STATE_MASK:
112010015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
112110015Speter			break;
112210015Speter		case SI_LOCK_STATE_MASK:
112310015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
112410015Speter			break;
112510015Speter		default:
112610015Speter			return (ENODEV);
112710015Speter		}
112810015Speter		switch (cmd) {
112910015Speter		case TIOCSETA:
113010015Speter			error = suser(p->p_ucred, &p->p_acflag);
113110015Speter			if (error != 0)
113210015Speter				return (error);
113310015Speter			*ct = *(struct termios *)data;
113410015Speter			return (0);
113510015Speter		case TIOCGETA:
113610015Speter			*(struct termios *)data = *ct;
113710015Speter			return (0);
113810015Speter		case TIOCGETD:
113910015Speter			*(int *)data = TTYDISC;
114010015Speter			return (0);
114110015Speter		case TIOCGWINSZ:
114210015Speter			bzero(data, sizeof(struct winsize));
114310015Speter			return (0);
114410015Speter		default:
114510015Speter			return (ENOTTY);
114610015Speter		}
114710015Speter	}
114810015Speter	/*
114910015Speter	 * Do the old-style ioctl compat routines...
115010015Speter	 */
115110015Speter#if defined(COMPAT_43)
115210015Speter	term = tp->t_termios;
115310015Speter	oldcmd = cmd;
115410015Speter	error = ttsetcompat(tp, &cmd, data, &term);
115510015Speter	if (error != 0)
115610015Speter		return (error);
115710015Speter	if (cmd != oldcmd)
115810015Speter		data = (caddr_t)&term;
115910015Speter#endif
116010015Speter	/*
116110015Speter	 * Do the initial / lock state business
116210015Speter	 */
116310015Speter	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
116410015Speter		int     cc;
116510015Speter		struct termios *dt = (struct termios *)data;
116610015Speter		struct termios *lt = mynor & SI_CALLOUT_MASK
116710015Speter				     ? &pp->sp_lout : &pp->sp_lin;
116810015Speter
116910015Speter		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
117010015Speter			| (dt->c_iflag & ~lt->c_iflag);
117110015Speter		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
117210015Speter			| (dt->c_oflag & ~lt->c_oflag);
117310015Speter		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
117410015Speter			| (dt->c_cflag & ~lt->c_cflag);
117510015Speter		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
117610015Speter			| (dt->c_lflag & ~lt->c_lflag);
117710015Speter		for (cc = 0; cc < NCCS; ++cc)
117810015Speter			if (lt->c_cc[cc] != 0)
117910015Speter				dt->c_cc[cc] = tp->t_cc[cc];
118010015Speter		if (lt->c_ispeed != 0)
118110015Speter			dt->c_ispeed = tp->t_ispeed;
118210015Speter		if (lt->c_ospeed != 0)
118310015Speter			dt->c_ospeed = tp->t_ospeed;
118410015Speter	}
118510015Speter
118610015Speter	/*
118710015Speter	 * Block user-level writes to give the ttywait()
118810015Speter	 * a chance to completely drain for commands
118910015Speter	 * that require the port to be in a quiescent state.
119010015Speter	 */
119110015Speter	switch (cmd) {
119210015Speter	case TIOCSETAW: case TIOCSETAF:
119310015Speter	case TIOCDRAIN: case TIOCSETP:
119410015Speter		blocked++;	/* block writes for ttywait() and siparam() */
119510015Speter		si_write_enable(pp, 0);
119610015Speter	}
119710015Speter
119810015Speter	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
119910015Speter	if (error >= 0)
120010015Speter		goto out;
120110015Speter
120210015Speter	oldspl = spltty();
120310015Speter
120410015Speter	error = ttioctl(tp, cmd, data, flag);
120510015Speter	si_disc_optim(tp, &tp->t_termios, pp);
120610015Speter	if (error >= 0)
120710015Speter		goto outspl;
120810015Speter
120910015Speter	switch (cmd) {
121010015Speter	case TIOCSBRK:
121110015Speter		si_command(pp, SBREAK, SI_NOWAIT);
121210015Speter		break;
121310015Speter	case TIOCCBRK:
121410015Speter		si_command(pp, EBREAK, SI_NOWAIT);
121510015Speter		break;
121610015Speter	case TIOCSDTR:
121710015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
121810015Speter		break;
121910015Speter	case TIOCCDTR:
122010015Speter		(void) si_modem(pp, SET, 0);
122110015Speter		break;
122210015Speter	case TIOCMSET:
122310015Speter		(void) si_modem(pp, SET, *(int *)data);
122410015Speter		break;
122510015Speter	case TIOCMBIS:
122610015Speter		(void) si_modem(pp, BIS, *(int *)data);
122710015Speter		break;
122810015Speter	case TIOCMBIC:
122910015Speter		(void) si_modem(pp, BIC, *(int *)data);
123010015Speter		break;
123110015Speter	case TIOCMGET:
123210015Speter		*(int *)data = si_modem(pp, GET, 0);
123310015Speter		break;
123410015Speter	case TIOCMSDTRWAIT:
123510015Speter		/* must be root since the wait applies to following logins */
123610015Speter		error = suser(p->p_ucred, &p->p_acflag);
123710015Speter		if (error != 0) {
123810015Speter			goto outspl;
123910015Speter		}
124010015Speter		pp->sp_dtr_wait = *(int *)data * hz / 100;
124110015Speter		break;
124210015Speter	case TIOCMGDTRWAIT:
124310015Speter		*(int *)data = pp->sp_dtr_wait * 100 / hz;
124410015Speter		break;
124510015Speter
124610015Speter	default:
124710015Speter		error = ENOTTY;
124810015Speter	}
124910015Speter	error = 0;
125010015Speteroutspl:
125110015Speter	splx(oldspl);
125210015Speterout:
125310015Speter	DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error));
125410015Speter	if (blocked)
125510015Speter		si_write_enable(pp, 1);
125610015Speter	return(error);
125710015Speter}
125810015Speter
125910015Speter/*
126010015Speter * Handle the Specialix ioctls. All MUST be called via the CONTROL device
126110015Speter */
126210015Speterstatic int
126310015Spetersi_Sioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
126410015Speter{
126510015Speter	struct si_softc *xsc;
126610015Speter	register struct si_port *xpp;
126710015Speter	volatile struct si_reg *regp;
126810015Speter	struct si_tcsi *dp;
126910044Speter	struct si_pstat *sps;
127011872Sphk	int *ip, error = 0;
127110015Speter	int oldspl;
127210015Speter	int card, port;
127310015Speter	int mynor = minor(dev);
127410015Speter
127510015Speter	DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%x,%x,%x,%x)\n",
127610015Speter		dev, cmd, data, flag));
127710015Speter
127810044Speter#if 1
127910044Speter	DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT));
128010044Speter	DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB));
128110044Speter	DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY));
128210044Speter#endif
128310044Speter
128410015Speter	if (!IS_CONTROLDEV(mynor)) {
128510015Speter		DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n"));
128610015Speter		return(ENODEV);
128710015Speter	}
128810015Speter
128910015Speter	oldspl = spltty();	/* better safe than sorry */
129010015Speter
129110015Speter	ip = (int *)data;
129210015Speter
129310015Speter#define SUCHECK if (error = suser(p->p_ucred, &p->p_acflag)) goto out
129410015Speter
129510015Speter	switch (cmd) {
129610015Speter	case TCSIPORTS:
129710015Speter		*ip = si_Nports;
129810015Speter		goto out;
129910015Speter	case TCSIMODULES:
130010015Speter		*ip = si_Nmodules;
130110015Speter		goto out;
130210015Speter	case TCSISDBG_ALL:
130310015Speter		SUCHECK;
130410015Speter		si_debug = *ip;
130510015Speter		goto out;
130610015Speter	case TCSIGDBG_ALL:
130710015Speter		*ip = si_debug;
130810015Speter		goto out;
130910015Speter	default:
131010015Speter		/*
131110015Speter		 * Check that a controller for this port exists
131210015Speter		 */
131310044Speter
131410044Speter		/* may also be a struct si_pstat, a superset of si_tcsi */
131510044Speter
131610015Speter		dp = (struct si_tcsi *)data;
131710044Speter		sps = (struct si_pstat *)data;
131810015Speter		card = dp->tc_card;
131910015Speter		xsc = &si_softc[card];	/* check.. */
132012174Speter		if (card < 0 || card >= NSI || xsc->sc_type == SIEMPTY) {
132110015Speter			error = ENOENT;
132210015Speter			goto out;
132310015Speter		}
132410015Speter		/*
132510015Speter		 * And check that a port exists
132610015Speter		 */
132710015Speter		port = dp->tc_port;
132810015Speter		if (port < 0 || port >= xsc->sc_nport) {
132910015Speter			error = ENOENT;
133010015Speter			goto out;
133110015Speter		}
133210015Speter		xpp = xsc->sc_ports + port;
133310015Speter		regp = (struct si_reg *)xsc->sc_maddr;
133410015Speter	}
133510015Speter
133610015Speter	switch (cmd) {
133710015Speter	case TCSIDEBUG:
133810015Speter#ifdef	SI_DEBUG
133910015Speter		SUCHECK;
134010015Speter		if (xpp->sp_debug)
134110015Speter			xpp->sp_debug = 0;
134210015Speter		else {
134310015Speter			xpp->sp_debug = DBG_ALL;
134410015Speter			DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n",
134510015Speter				(xpp->sp_debug&DBG_ALL)?"ON":"OFF"));
134610015Speter		}
134710015Speter		break;
134810015Speter#else
134910015Speter		error = ENODEV;
135010015Speter		goto out;
135110015Speter#endif
135210015Speter	case TCSISDBG_LEVEL:
135310015Speter	case TCSIGDBG_LEVEL:
135410015Speter#ifdef	SI_DEBUG
135510015Speter		if (cmd == TCSIGDBG_LEVEL) {
135610015Speter			dp->tc_dbglvl = xpp->sp_debug;
135710015Speter		} else {
135810015Speter			SUCHECK;
135910015Speter			xpp->sp_debug = dp->tc_dbglvl;
136010015Speter		}
136110015Speter		break;
136210015Speter#else
136310015Speter		error = ENODEV;
136410015Speter		goto out;
136510015Speter#endif
136610015Speter	case TCSIGRXIT:
136710015Speter		dp->tc_int = regp->rx_int_count;
136810015Speter		break;
136910015Speter	case TCSIRXIT:
137010015Speter		SUCHECK;
137110015Speter		regp->rx_int_count = dp->tc_int;
137210015Speter		break;
137310015Speter	case TCSIGIT:
137410015Speter		dp->tc_int = regp->int_count;
137510015Speter		break;
137610015Speter	case TCSIIT:
137710015Speter		SUCHECK;
137810015Speter		regp->int_count = dp->tc_int;
137910015Speter		break;
138010044Speter	case TCSISTATE:
138110044Speter		dp->tc_int = xpp->sp_ccb->hi_ip;
138210015Speter		break;
138310044Speter	/* these next three use a different structure */
138410044Speter	case TCSI_PORT:
138510015Speter		SUCHECK;
138610044Speter		sps->tc_siport = *xpp;
138710015Speter		break;
138810044Speter	case TCSI_CCB:
138910044Speter		SUCHECK;
139010044Speter		sps->tc_ccb = *xpp->sp_ccb;
139110015Speter		break;
139210044Speter	case TCSI_TTY:
139310044Speter		SUCHECK;
139410044Speter		sps->tc_tty = *xpp->sp_tty;
139510015Speter		break;
139610015Speter	default:
139710015Speter		error = EINVAL;
139810015Speter		goto out;
139910015Speter	}
140010015Speterout:
140110015Speter	splx(oldspl);
140210015Speter	return(error);		/* success */
140310015Speter}
140410015Speter
140510015Speter/*
140610015Speter *	siparam()	: Configure line params
140710015Speter *	called at spltty();
140810015Speter *	this may sleep, does not flush, nor wait for drain, nor block writes
140910015Speter *	caller must arrange this if it's important..
141010015Speter */
141112724Sphkstatic int
141210015Spetersiparam(tp, t)
141310015Speter	register struct tty *tp;
141410015Speter	register struct termios *t;
141510015Speter{
141610015Speter	register struct si_port *pp = TP2PP(tp);
141710015Speter	volatile struct si_channel *ccbp;
141810015Speter	int oldspl, cflag, iflag, oflag, lflag;
141910015Speter	int error = 0;		/* shutup gcc */
142010015Speter	int ispeed = 0;		/* shutup gcc */
142110015Speter	int ospeed = 0;		/* shutup gcc */
142210161Speter	BYTE val;
142310015Speter
142410015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t));
142510015Speter	cflag = t->c_cflag;
142610015Speter	iflag = t->c_iflag;
142710015Speter	oflag = t->c_oflag;
142810015Speter	lflag = t->c_lflag;
142910044Speter	DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n",
143010044Speter		oflag, cflag, iflag, lflag));
143110015Speter
143210015Speter
143310015Speter	/* if not hung up.. */
143410015Speter	if (t->c_ospeed != 0) {
143510015Speter		/* translate baud rate to firmware values */
143610015Speter		ospeed = ttspeedtab(t->c_ospeed, bdrates);
143710015Speter		ispeed = t->c_ispeed ?
143810015Speter			 ttspeedtab(t->c_ispeed, bdrates) : ospeed;
143910015Speter
144010015Speter		/* enforce legit baud rate */
144110015Speter		if (ospeed < 0 || ispeed < 0)
144210015Speter			return (EINVAL);
144310015Speter	}
144410015Speter
144510015Speter
144610015Speter	oldspl = spltty();
144710015Speter
144810015Speter	ccbp = pp->sp_ccb;
144910015Speter
145010161Speter	/* ========== set hi_break ========== */
145110161Speter	val = 0;
145210161Speter	if (iflag & IGNBRK)		/* Breaks */
145310161Speter		val |= BR_IGN;
145410161Speter	if (iflag & BRKINT)		/* Interrupt on break? */
145510161Speter		val |= BR_INT;
145610161Speter	if (iflag & PARMRK)		/* Parity mark? */
145710161Speter		val |= BR_PARMRK;
145810161Speter	if (iflag & IGNPAR)		/* Ignore chars with parity errors? */
145910161Speter		val |= BR_PARIGN;
146010161Speter	ccbp->hi_break = val;
146110161Speter
146210161Speter	/* ========== set hi_csr ========== */
146310015Speter	/* if not hung up.. */
146410015Speter	if (t->c_ospeed != 0) {
146510015Speter		/* Set I/O speeds */
146610161Speter		 val = (ispeed << 4) | ospeed;
146710015Speter	}
146810161Speter	ccbp->hi_csr = val;
146910015Speter
147010161Speter	/* ========== set hi_mr2 ========== */
147110161Speter	val = 0;
147210015Speter	if (cflag & CSTOPB)				/* Stop bits */
147310161Speter		val |= MR2_2_STOP;
147410015Speter	else
147510161Speter		val |= MR2_1_STOP;
147610161Speter	/*
147710161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
147810161Speter	 * a DCE, hence the reverse sense of RTS and CTS
147910161Speter	 */
148010161Speter	/* Output Flow - RTS must be raised before data can be sent */
148110161Speter	if (cflag & CCTS_OFLOW)
148210161Speter		val |= MR2_RTSCONT;
148310161Speter
148410161Speter	ccbp->hi_mr1 = val;
148510161Speter
148610161Speter	/* ========== set hi_mr1 ========== */
148710161Speter	val = 0;
148810015Speter	if (!(cflag & PARENB))				/* Parity */
148910161Speter		val |= MR1_NONE;
149010015Speter	else
149110161Speter		val |= MR1_WITH;
149210015Speter	if (cflag & PARODD)
149310161Speter		val |= MR1_ODD;
149410015Speter
149510015Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
149610161Speter		val |= MR1_8_BITS;
149710015Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
149810161Speter		val |= MR1_7_BITS;
149910015Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
150010161Speter		val |= MR1_6_BITS;
150110015Speter	} else {					/* Must be 5 */
150210161Speter		val |= MR1_5_BITS;
150310015Speter	}
150410161Speter	/*
150510161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
150610161Speter	 * a DCE, hence the reverse sense of RTS and CTS
150710161Speter	 */
150810161Speter	/* Input Flow - CTS is raised when port is ready to receive data */
150910161Speter	if (cflag & CRTS_IFLOW)
151010161Speter		val |= MR1_CTSCONT;
151110015Speter
151210161Speter	ccbp->hi_mr1 = val;
151310161Speter
151410161Speter	/* ========== set hi_mask ========== */
151510161Speter	val = 0xff;
151610161Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
151710161Speter		val &= 0xFF;
151810161Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
151910161Speter		val &= 0x7F;
152010161Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
152110161Speter		val &= 0x3F;
152210161Speter	} else {					/* Must be 5 */
152310161Speter		val &= 0x1F;
152410161Speter	}
152510015Speter	if (iflag & ISTRIP)
152610161Speter		val &= 0x7F;
152710015Speter
152810161Speter	ccbp->hi_mask = val;
152910161Speter
153010161Speter	/* ========== set hi_prtcl ========== */
153110161Speter	val = 0;
153210015Speter				/* Monitor DCD etc. if a modem */
153310015Speter	if (!(cflag & CLOCAL))
153410161Speter		val |= SP_DCEN;
153510161Speter	if (iflag & IXANY)
153610161Speter		val |= SP_TANY;
153710161Speter	if (iflag & IXON)
153810161Speter		val |= SP_TXEN;
153910161Speter	if (iflag & IXOFF)
154010161Speter		val |= SP_RXEN;
154110161Speter	if (iflag & INPCK)
154210161Speter		val |= SP_PAEN;
154310015Speter
154410161Speter	ccbp->hi_prtcl = val;
154510161Speter
154610161Speter
154710161Speter	/* ========== set hi_{rx|tx}{on|off} ========== */
154810161Speter	/* XXX: the card TOTALLY shields us from the flow control... */
154910015Speter	ccbp->hi_txon = t->c_cc[VSTART];
155010015Speter	ccbp->hi_txoff = t->c_cc[VSTOP];
155110015Speter
155210015Speter	ccbp->hi_rxon = t->c_cc[VSTART];
155310015Speter	ccbp->hi_rxoff = t->c_cc[VSTOP];
155410015Speter
155510161Speter	/* ========== send settings to the card ========== */
155610015Speter	/* potential sleep here */
155710015Speter	if (ccbp->hi_stat == IDLE_CLOSE)		/* Not yet open */
155810015Speter		si_command(pp, LOPEN, SI_WAIT);		/* open it */
155910015Speter	else
156010015Speter		si_command(pp, CONFIG, SI_WAIT);	/* change params */
156110015Speter
156210161Speter	/* ========== set DTR etc ========== */
156310015Speter	/* Hangup if ospeed == 0 */
156410015Speter	if (t->c_ospeed == 0) {
156510015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
156610015Speter	} else {
156710015Speter		/*
156810015Speter		 * If the previous speed was 0, may need to re-enable
156910015Speter	 	 * the modem signals
157010015Speter	 	 */
157110015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
157210015Speter	}
157310015Speter
157410044Speter	DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n",
157510044Speter		ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break));
157610015Speter
157710015Speter	splx(oldspl);
157810015Speter	return(error);
157910015Speter}
158010015Speter
158110015Speter/*
158210015Speter * Enable or Disable the writes to this channel...
158310015Speter * "state" ->  enabled = 1; disabled = 0;
158410015Speter */
158510015Speterstatic void
158610015Spetersi_write_enable(pp, state)
158710015Speter	register struct si_port *pp;
158810015Speter	int state;
158910015Speter{
159010015Speter	int oldspl;
159110015Speter
159210015Speter	oldspl = spltty();
159310015Speter
159410015Speter	if (state) {
159510015Speter		pp->sp_state &= ~SS_BLOCKWRITE;
159610015Speter		if (pp->sp_state & SS_WAITWRITE) {
159710015Speter			pp->sp_state &= ~SS_WAITWRITE;
159810015Speter			/* thunder away! */
159910015Speter			wakeup((caddr_t)pp);
160010015Speter		}
160110015Speter	} else {
160210015Speter		pp->sp_state |= SS_BLOCKWRITE;
160310015Speter	}
160410015Speter
160510015Speter	splx(oldspl);
160610015Speter}
160710015Speter
160810015Speter/*
160910015Speter * Set/Get state of modem control lines.
161010015Speter * Due to DCE-like behaviour of the adapter, some signals need translation:
161110015Speter *	TIOCM_DTR	DSR
161210015Speter *	TIOCM_RTS	CTS
161310015Speter */
161410015Speterstatic int
161510015Spetersi_modem(pp, cmd, bits)
161610015Speter	struct si_port *pp;
161710015Speter	enum si_mctl cmd;
161810015Speter	int bits;
161910015Speter{
162010015Speter	volatile struct si_channel *ccbp;
162110015Speter	int x;
162210015Speter
162310015Speter	DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits));
162410015Speter	ccbp = pp->sp_ccb;		/* Find channel address */
162510015Speter	switch (cmd) {
162610015Speter	case GET:
162710015Speter		x = ccbp->hi_ip;
162810015Speter		bits = TIOCM_LE;
162910015Speter		if (x & IP_DCD)		bits |= TIOCM_CAR;
163010015Speter		if (x & IP_DTR)		bits |= TIOCM_DTR;
163110015Speter		if (x & IP_RTS)		bits |= TIOCM_RTS;
163210015Speter		if (x & IP_RI)		bits |= TIOCM_RI;
163310015Speter		return(bits);
163410015Speter	case SET:
163510015Speter		ccbp->hi_op &= ~(OP_DSR|OP_CTS);
163610015Speter		/* fall through */
163710015Speter	case BIS:
163810015Speter		x = 0;
163910015Speter		if (bits & TIOCM_DTR)
164010015Speter			x |= OP_DSR;
164110015Speter		if (bits & TIOCM_RTS)
164210015Speter			x |= OP_CTS;
164310015Speter		ccbp->hi_op |= x;
164410015Speter		break;
164510015Speter	case BIC:
164610015Speter		if (bits & TIOCM_DTR)
164710015Speter			ccbp->hi_op &= ~OP_DSR;
164810015Speter		if (bits & TIOCM_RTS)
164910015Speter			ccbp->hi_op &= ~OP_CTS;
165010015Speter	}
165110015Speter	return 0;
165210015Speter}
165310015Speter
165410015Speter/*
165510015Speter * Handle change of modem state
165610015Speter */
165710015Speterstatic void
165810015Spetersi_modem_state(pp, tp, hi_ip)
165910015Speter	register struct si_port *pp;
166010015Speter	register struct tty *tp;
166110015Speter	register int hi_ip;
166210015Speter{
166310015Speter							/* if a modem dev */
166410015Speter	if (hi_ip & IP_DCD) {
166510015Speter		if ( !(pp->sp_last_hi_ip & IP_DCD)) {
166610015Speter			DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n",
166710015Speter				tp->t_line));
166810015Speter			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
166910015Speter		}
167010015Speter	} else {
167110015Speter		if (pp->sp_last_hi_ip & IP_DCD) {
167210015Speter			DPRINT((pp, DBG_INTR, "modem carr off\n"));
167310015Speter			if ((*linesw[tp->t_line].l_modem)(tp, 0))
167410015Speter				(void) si_modem(pp, SET, 0);
167510015Speter		}
167610015Speter	}
167710015Speter	pp->sp_last_hi_ip = hi_ip;
167810015Speter
167910015Speter}
168010015Speter
168110015Speter/*
168210015Speter * Poller to catch missed interrupts.
168312174Speter *
168412496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get
168512496Speter * better response.  We could really use a "periodic" version timeout(). :-)
168610015Speter */
168710015Speter#ifdef POLL
168810708Speterstatic void
168910015Spetersi_poll(void *nothing)
169010015Speter{
169110015Speter	register struct si_softc *sc;
169210015Speter	register int i;
169310015Speter	volatile struct si_reg *regp;
169412174Speter	register struct si_port *pp;
169512174Speter	int lost, oldspl, port;
169610015Speter
169710015Speter	DPRINT((0, DBG_POLL, "si_poll()\n"));
169811609Speter	oldspl = spltty();
169910015Speter	if (in_intr)
170010015Speter		goto out;
170110015Speter	lost = 0;
170210015Speter	for (i=0; i<NSI; i++) {
170310015Speter		sc = &si_softc[i];
170412174Speter		if (sc->sc_type == SIEMPTY)
170510015Speter			continue;
170610015Speter		regp = (struct si_reg *)sc->sc_maddr;
170710015Speter		/*
170810015Speter		 * See if there has been a pending interrupt for 2 seconds
170910015Speter		 * or so. The test <int_scounter >= 200) won't correspond
171010015Speter		 * to 2 seconds if int_count gets changed.
171110015Speter		 */
171210015Speter		if (regp->int_pending != 0) {
171310015Speter			if (regp->int_scounter >= 200 &&
171410015Speter			    regp->initstat == 1) {
171512174Speter				printf("si%d: lost intr\n", i);
171610015Speter				lost++;
171710015Speter			}
171810015Speter		} else {
171910015Speter			regp->int_scounter = 0;
172010015Speter		}
172110015Speter
172212174Speter		/*
172312174Speter		 * gripe about no input flow control..
172412174Speter		 */
172512174Speter		pp = sc->sc_ports;
172612174Speter		for (port = 0; port < sc->sc_nport; pp++, port++) {
172712174Speter			if (pp->sp_delta_overflows > 0) {
172812174Speter				printf("si%d: %d tty level buffer overflows\n",
172912174Speter					i, pp->sp_delta_overflows);
173012174Speter				pp->sp_delta_overflows = 0;
173112174Speter			}
173212174Speter		}
173310015Speter	}
173410015Speter	if (lost)
173510015Speter		siintr(-1);	/* call intr with fake vector */
173611609Speterout:
173710015Speter	splx(oldspl);
173810015Speter
173915639Speter	timeout(si_poll, (caddr_t)0L, si_pollrate);
174010015Speter}
174110015Speter#endif	/* ifdef POLL */
174210015Speter
174310015Speter/*
174410015Speter * The interrupt handler polls ALL ports on ALL adapters each time
174510015Speter * it is called.
174610015Speter */
174710015Speter
174812496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE];	/* input staging area */
174910015Speter
175010708Spetervoid
175111609Spetersiintr(int unit)
175210015Speter{
175310015Speter	register struct si_softc *sc;
175410015Speter
175510015Speter	register struct si_port *pp;
175610015Speter	volatile struct si_channel *ccbp;
175710015Speter	register struct tty *tp;
175810015Speter	volatile caddr_t maddr;
175911872Sphk	BYTE op, ip;
176012174Speter	int x, card, port, n, i, isopen;
176110015Speter	volatile BYTE *z;
176210015Speter	BYTE c;
176310015Speter
176411609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "siintr(%d)\n", unit));
176511609Speter	if (in_intr) {
176611609Speter		if (unit < 0)	/* should never happen */
176710708Speter			return;
176812174Speter		printf("si%d: Warning interrupt handler re-entered\n",
176911609Speter			unit);
177010708Speter		return;
177110015Speter	}
177210015Speter	in_intr = 1;
177310015Speter
177410015Speter	/*
177510015Speter	 * When we get an int we poll all the channels and do ALL pending
177610015Speter	 * work, not just the first one we find. This allows all cards to
177710015Speter	 * share the same vector.
177810015Speter	 */
177910015Speter	for (card=0; card < NSI; card++) {
178010015Speter		sc = &si_softc[card];
178112174Speter		if (sc->sc_type == SIEMPTY)
178210015Speter			continue;
178312174Speter
178412174Speter		/*
178512174Speter		 * First, clear the interrupt
178612174Speter		 */
178710015Speter		switch(sc->sc_type) {
178810015Speter		case SIHOST :
178910015Speter			maddr = sc->sc_maddr;
179010015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
179110015Speter							/* flag nothing pending */
179210015Speter			*(maddr+SIINTCL) = 0x00;	/* Set IRQ clear */
179310015Speter			*(maddr+SIINTCL_CL) = 0x00;	/* Clear IRQ clear */
179410015Speter			break;
179510015Speter		case SIHOST2:
179610015Speter			maddr = sc->sc_maddr;
179710015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
179810015Speter			*(maddr+SIPLIRQCLR) = 0x00;
179910015Speter			*(maddr+SIPLIRQCLR) = 0x10;
180010015Speter			break;
180110015Speter		case SIEISA:
180210015Speter#if NEISA > 0
180310015Speter			maddr = sc->sc_maddr;
180410015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
180510015Speter			(void)inb(sc->sc_eisa_iobase+3);
180610015Speter			break;
180710015Speter#endif	/* fall through if not EISA kernel */
180810015Speter		case SIEMPTY:
180910015Speter		default:
181010015Speter			continue;
181110015Speter		}
181210015Speter		((volatile struct si_reg *)maddr)->int_scounter = 0;
181310015Speter
181412174Speter		/*
181512174Speter		 * check each port
181612174Speter		 */
181712174Speter		for (pp=sc->sc_ports,port=0; port < sc->sc_nport; pp++,port++) {
181810015Speter			ccbp = pp->sp_ccb;
181910015Speter			tp = pp->sp_tty;
182010015Speter
182112174Speter
182210015Speter			/*
182310015Speter			 * See if a command has completed ?
182410015Speter			 */
182510015Speter			if (ccbp->hi_stat != pp->sp_pend) {
182610015Speter				DPRINT((pp, DBG_INTR,
182710015Speter					"siintr hi_stat = 0x%x, pend = %d\n",
182810015Speter					ccbp->hi_stat, pp->sp_pend));
182910015Speter				switch(pp->sp_pend) {
183010015Speter				case LOPEN:
183110015Speter				case MPEND:
183210015Speter				case MOPEN:
183310015Speter				case CONFIG:
183410015Speter					pp->sp_pend = ccbp->hi_stat;
183510015Speter						/* sleeping in si_command */
183610015Speter					wakeup(&pp->sp_state);
183710015Speter					break;
183810015Speter				default:
183910015Speter					pp->sp_pend = ccbp->hi_stat;
184010015Speter				}
184110015Speter	 		}
184210015Speter
184310015Speter			/*
184410015Speter			 * Continue on if it's closed
184510015Speter			 */
184610015Speter			if (ccbp->hi_stat == IDLE_CLOSE) {
184710015Speter				continue;
184810015Speter			}
184910015Speter
185010015Speter			/*
185110015Speter			 * Do modem state change if not a local device
185210015Speter			 */
185310015Speter			si_modem_state(pp, tp, ccbp->hi_ip);
185410015Speter
185510015Speter			/*
185612174Speter			 * Check to see if there's we should 'receive'
185712174Speter			 * characters.
185812174Speter			 */
185912174Speter			if (tp->t_state & TS_CONNECTED &&
186012174Speter			    tp->t_state & TS_ISOPEN)
186112174Speter				isopen = 1;
186212174Speter			else
186312174Speter				isopen = 0;
186412174Speter
186512174Speter			/*
186610015Speter			 * Do break processing
186710015Speter			 */
186810015Speter			if (ccbp->hi_state & ST_BREAK) {
186912174Speter				if (isopen) {
187012174Speter				    (*linesw[tp->t_line].l_rint)(TTY_BI, tp);
187110015Speter				}
187210015Speter				ccbp->hi_state &= ~ST_BREAK;   /* A Bit iffy this */
187310015Speter				DPRINT((pp, DBG_INTR, "si_intr break\n"));
187410015Speter			}
187510015Speter
187610015Speter			/*
187712174Speter			 * Do RX stuff - if not open then dump any characters.
187812174Speter			 * XXX: This is VERY messy and needs to be cleaned up.
187912174Speter			 *
188012174Speter			 * XXX: can we leave data in the host adapter buffer
188112174Speter			 * when the clists are full?  That may be dangerous
188212174Speter			 * if the user cannot get an interrupt signal through.
188310015Speter			 */
188410015Speter
188512174Speter	more_rx:	/* XXX Sorry. the nesting was driving me bats! :-( */
188612174Speter
188712174Speter			if (!isopen) {
188810015Speter				ccbp->hi_rxopos = ccbp->hi_rxipos;
188912174Speter				goto end_rx;
189012174Speter			}
189110015Speter
189212174Speter			/*
189315640Speter			 * If the tty input buffers are blocked, stop emptying
189415640Speter			 * the incoming buffers and let the auto flow control
189515640Speter			 * assert..
189615640Speter			 */
189715640Speter			if (tp->t_state & TS_TBLOCK) {
189815640Speter				goto end_rx;
189915640Speter			}
190015640Speter
190115640Speter			/*
190212174Speter			 * Process read characters if not skipped above
190312174Speter			 */
190415640Speter			op = ccbp->hi_rxopos;
190515640Speter			ip = ccbp->hi_rxipos;
190615640Speter			c = ip - op;
190712174Speter			if (c == 0) {
190812174Speter				goto end_rx;
190912174Speter			}
191010015Speter
191112174Speter			n = c & 0xff;
191215640Speter			if (n > 250)
191315640Speter				n = 250;
191412174Speter
191512174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
191610015Speter						n, op, ip));
191710015Speter
191812174Speter			/*
191912174Speter			 * Suck characters out of host card buffer into the
192012174Speter			 * "input staging buffer" - so that we dont leave the
192112174Speter			 * host card in limbo while we're possibly echoing
192212174Speter			 * characters and possibly flushing input inside the
192312174Speter			 * ldisc l_rint() routine.
192412174Speter			 */
192512496Speter			if (n <= SI_BUFFERSIZE - op) {
192610015Speter
192712174Speter				DPRINT((pp, DBG_INTR, "\tsingle copy\n"));
192812174Speter				z = ccbp->hi_rxbuf + op;
192912174Speter				bcopy((caddr_t)z, si_rxbuf, n);
193010015Speter
193112174Speter				op += n;
193212174Speter			} else {
193312496Speter				x = SI_BUFFERSIZE - op;
193410015Speter
193512174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x));
193612174Speter				z = ccbp->hi_rxbuf + op;
193712174Speter				bcopy((caddr_t)z, si_rxbuf, x);
193810015Speter
193912174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n-x));
194012174Speter				z = ccbp->hi_rxbuf;
194112174Speter				bcopy((caddr_t)z, si_rxbuf+x, n-x);
194210015Speter
194312174Speter				op += n;
194412174Speter			}
194510015Speter
194612174Speter			/* clear collected characters from buffer */
194712174Speter			ccbp->hi_rxopos = op;
194812174Speter
194912174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
195010015Speter						n, op, ip));
195110015Speter
195212174Speter			/*
195312174Speter			 * at this point...
195412174Speter			 * n = number of chars placed in si_rxbuf
195512174Speter			 */
195610015Speter
195712174Speter			/*
195812174Speter			 * Avoid the grotesquely inefficient lineswitch
195912174Speter			 * routine (ttyinput) in "raw" mode. It usually
196012174Speter			 * takes about 450 instructions (that's without
196112174Speter			 * canonical processing or echo!). slinput is
196212174Speter			 * reasonably fast (usually 40 instructions
196312174Speter			 * plus call overhead).
196412174Speter			 */
196512174Speter			if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
196610015Speter
196712174Speter				/* block if the driver supports it */
196812174Speter				if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER
196912174Speter				    && (tp->t_cflag & CRTS_IFLOW
197012174Speter					|| tp->t_iflag & IXOFF)
197112174Speter				    && !(tp->t_state & TS_TBLOCK))
197212174Speter					ttyblock(tp);
197310015Speter
197412174Speter				tk_nin += n;
197512174Speter				tk_rawcc += n;
197612174Speter				tp->t_rawcc += n;
197712174Speter
197812174Speter				pp->sp_delta_overflows +=
197912174Speter				    b_to_q((char *)si_rxbuf, n, &tp->t_rawq);
198012174Speter
198112174Speter				ttwakeup(tp);
198212174Speter				if (tp->t_state & TS_TTSTOP
198312174Speter				    && (tp->t_iflag & IXANY
198412174Speter					|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
198512174Speter					tp->t_state &= ~TS_TTSTOP;
198612174Speter					tp->t_lflag &= ~FLUSHO;
198712174Speter					si_start(tp);
198812174Speter				}
198912174Speter			} else {
199012174Speter				/*
199112174Speter				 * It'd be nice to not have to go through the
199212174Speter				 * function call overhead for each char here.
199312174Speter				 * It'd be nice to block input it, saving a
199412174Speter				 * loop here and the call/return overhead.
199512174Speter				 */
199612174Speter				for(x = 0; x < n; x++) {
199712174Speter					i = si_rxbuf[x];
199812174Speter					if ((*linesw[tp->t_line].l_rint)(i, tp)
199912174Speter					     == -1) {
200012174Speter						pp->sp_delta_overflows++;
200110015Speter					}
200212174Speter					/*
200312174Speter					 * doesn't seem to be much point doing
200412174Speter					 * this here.. this driver has no
200512174Speter					 * softtty processing! ??
200612174Speter					 */
200712174Speter					if (pp->sp_hotchar && i == pp->sp_hotchar) {
200812174Speter						setsofttty();
200912174Speter					}
201012174Speter				}
201112174Speter			}
201212174Speter			goto more_rx;	/* try for more until RXbuf is empty */
201310015Speter
201412174Speter	end_rx:		/* XXX: Again, sorry about the gotos.. :-) */
201510015Speter
201610015Speter			/*
201710015Speter			 * Do TX stuff
201810015Speter			 */
201910015Speter			(*linesw[tp->t_line].l_start)(tp);
202010015Speter
202110015Speter		} /* end of for (all ports on this controller) */
202210015Speter	} /* end of for (all controllers) */
202310015Speter
202411609Speter	in_intr = 0;
202511609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "end siintr(%d)\n", unit));
202610015Speter}
202710015Speter
202810015Speter/*
202910015Speter * Nudge the transmitter...
203012174Speter *
203112174Speter * XXX: I inherited some funny code here.  It implies the host card only
203212174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does
203312174Speter * not interrupt when it's actually hits empty.  In some cases, we have
203412174Speter * processes waiting for complete drain, and we need to simulate an interrupt
203512174Speter * about when we think the buffer is going to be empty (and retry if not).
203612174Speter * I really am not certain about this...  I *need* the hardware manuals.
203710015Speter */
203810015Speterstatic void
203910015Spetersi_start(tp)
204010015Speter	register struct tty *tp;
204110015Speter{
204210015Speter	struct si_port *pp;
204310015Speter	volatile struct si_channel *ccbp;
204410015Speter	register struct clist *qp;
204510015Speter	register char *dptr;
204610015Speter	BYTE ipos;
204710015Speter	int nchar;
204810015Speter	int oldspl, count, n, amount, buffer_full;
204910015Speter	int do_exitproc;
205010015Speter
205110015Speter	oldspl = spltty();
205210015Speter
205310015Speter	qp = &tp->t_outq;
205410015Speter	pp = TP2PP(tp);
205510015Speter
205610015Speter	DPRINT((pp, DBG_ENTRY|DBG_START,
205710015Speter		"si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n",
205810015Speter		tp, tp->t_state, pp->sp_state, qp->c_cc));
205910015Speter
206010015Speter	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
206110015Speter		goto out;
206210015Speter
206310015Speter	do_exitproc = 0;
206410015Speter	buffer_full = 0;
206510015Speter	ccbp = pp->sp_ccb;
206610015Speter
206710015Speter	/*
206810015Speter	 * Handle the case where ttywait() is called on process exit
206910015Speter	 * this may be BSDI specific, I dont know...
207010015Speter	 */
207110015Speter	if (tp->t_session != NULL && tp->t_session->s_leader != NULL &&
207210015Speter	    (tp->t_session->s_leader->p_flag & P_WEXIT)) {
207310015Speter		do_exitproc++;
207410015Speter	}
207510015Speter
207610015Speter	count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
207710015Speter	DPRINT((pp, DBG_START, "count %d\n", (BYTE)count));
207810015Speter
207910015Speter	dptr = (char *)ccbp->hi_txbuf;	/* data buffer */
208010015Speter
208110015Speter	while ((nchar = qp->c_cc) > 0) {
208210015Speter		if ((BYTE)count >= 255) {
208310015Speter			buffer_full++;
208410015Speter			break;
208510015Speter		}
208610015Speter		amount = min(nchar, (255 - (BYTE)count));
208710015Speter		ipos = (unsigned int)ccbp->hi_txipos;
208810015Speter		/* will it fit in one lump? */
208912496Speter		if ((SI_BUFFERSIZE - ipos) >= amount) {
209010015Speter			n = q_to_b(&tp->t_outq,
209110015Speter				(char *)&ccbp->hi_txbuf[ipos], amount);
209210015Speter		} else {
209310015Speter			n = q_to_b(&tp->t_outq,
209410015Speter				(char *)&ccbp->hi_txbuf[ipos],
209512496Speter				SI_BUFFERSIZE-ipos);
209612496Speter			if (n == SI_BUFFERSIZE-ipos) {
209710015Speter				n += q_to_b(&tp->t_outq,
209810015Speter					(char *)&ccbp->hi_txbuf[0],
209912496Speter					amount - (SI_BUFFERSIZE-ipos));
210010015Speter			}
210110015Speter		}
210210015Speter		ccbp->hi_txipos += n;
210310015Speter		count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
210410015Speter	}
210510015Speter
210610015Speter	if (count != 0 && nchar == 0) {
210710015Speter		tp->t_state |= TS_BUSY;
210810015Speter	} else {
210910015Speter		tp->t_state &= ~TS_BUSY;
211010015Speter	}
211110015Speter
211210015Speter	/* wakeup time? */
211310015Speter	ttwwakeup(tp);
211410015Speter
211510015Speter	DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n",
211610015Speter		(BYTE)count, nchar, tp->t_state));
211710015Speter
211810015Speter	if ((tp->t_state & TS_BUSY) || do_exitproc)
211910015Speter	{
212010015Speter		int time;
212110015Speter
212210015Speter		if (do_exitproc != 0) {
212310015Speter			time = hz / 10;
212410015Speter		} else {
212510015Speter			time = ttspeedtab(tp->t_ospeed, chartimes);
212610015Speter
212710015Speter			if (time > 0) {
212810015Speter				if (time < nchar)
212910015Speter					time = nchar / time;
213010015Speter				else
213110015Speter					time = 2;
213210015Speter			} else {
213316024Speter				DPRINT((pp, DBG_START,
213416024Speter					"bad char time value! %d\n", time));
213516024Speter				time = hz/10;
213610015Speter			}
213710015Speter		}
213810015Speter
213910015Speter		if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) {
214010015Speter			untimeout((timeout_func_t)si_lstart, (caddr_t)pp);
214110015Speter		} else {
214210015Speter			pp->sp_state |= SS_LSTART;
214310015Speter		}
214410015Speter		DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time));
214510015Speter		timeout((timeout_func_t)si_lstart, (caddr_t)pp, time);
214610015Speter	}
214710015Speter
214810015Speterout:
214910015Speter	splx(oldspl);
215010015Speter	DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n"));
215110015Speter}
215210015Speter
215310015Speter/*
215410015Speter * Note: called at splsoftclock from the timeout code
215510015Speter * This has to deal with two things...  cause wakeups while waiting for
215610015Speter * tty drains on last process exit, and call l_start at about the right
215710015Speter * time for protocols like ppp.
215810015Speter */
215910015Speterstatic void
216010015Spetersi_lstart(pp)
216110015Speter	register struct si_port *pp;
216210015Speter{
216310015Speter	register struct tty *tp;
216410015Speter	int oldspl;
216510015Speter
216610015Speter	DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n",
216710015Speter		pp, pp->sp_state));
216810015Speter
216910015Speter	oldspl = spltty();
217010015Speter
217110015Speter	if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) {
217210015Speter		splx(oldspl);
217310015Speter		return;
217410015Speter	}
217510015Speter	pp->sp_state &= ~SS_LSTART;
217610015Speter	pp->sp_state |= SS_INLSTART;
217710015Speter
217810015Speter	tp = pp->sp_tty;
217910015Speter
218010015Speter	/* deal with the process exit case */
218110015Speter	ttwwakeup(tp);
218210015Speter
218312174Speter	/* nudge protocols - eg: ppp */
218410015Speter	(*linesw[tp->t_line].l_start)(tp);
218510015Speter
218610015Speter	pp->sp_state &= ~SS_INLSTART;
218710015Speter	splx(oldspl);
218810015Speter}
218910015Speter
219010015Speter/*
219110015Speter * Stop output on a line. called at spltty();
219210015Speter */
219310015Spetervoid
219410015Spetersistop(tp, rw)
219510015Speter	register struct tty *tp;
219610015Speter	int rw;
219710015Speter{
219810015Speter	volatile struct si_channel *ccbp;
219910015Speter	struct si_port *pp;
220010015Speter
220110015Speter	pp = TP2PP(tp);
220210015Speter	ccbp = pp->sp_ccb;
220310015Speter
220410015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "sistop(%x,%x)\n", tp, rw));
220510015Speter
220610015Speter	/* XXX: must check (rw & FWRITE | FREAD) etc flushing... */
220710015Speter	if (rw & FWRITE) {
220810015Speter		/* what level are we meant to be flushing anyway? */
220910015Speter		if (tp->t_state & TS_BUSY) {
221010015Speter			si_command(TP2PP(tp), WFLUSH, SI_NOWAIT);
221110015Speter			tp->t_state &= ~TS_BUSY;
221210015Speter			ttwwakeup(tp);	/* Bruce???? */
221310015Speter		}
221410015Speter	}
221512174Speter#if 1	/* XXX: this doesn't work right yet.. */
221612174Speter	/* XXX: this may have been failing because we used to call l_rint()
221712174Speter	 * while we were looping based on these two counters. Now, we collect
221812174Speter	 * the data and then loop stuffing it into l_rint(), making this
221912174Speter	 * useless.  Should we cause this to blow away the staging buffer?
222012174Speter	 */
222110015Speter	if (rw & FREAD) {
222210015Speter		ccbp->hi_rxopos = ccbp->hi_rxipos;
222310015Speter	}
222410015Speter#endif
222510015Speter}
222610015Speter
222710015Speter/*
222810015Speter * Issue a command to the Z280 host card CPU.
222910015Speter */
223010015Speter
223110015Speterstatic void
223210015Spetersi_command(pp, cmd, waitflag)
223310015Speter	struct si_port *pp;		/* port control block (local) */
223410015Speter	int cmd;
223510015Speter	int waitflag;
223610015Speter{
223710015Speter	int oldspl;
223810015Speter	volatile struct si_channel *ccbp = pp->sp_ccb;
223910015Speter	int x;
224010015Speter
224110015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n",
224210015Speter		pp, cmd, waitflag, ccbp->hi_stat));
224310015Speter
224410015Speter	oldspl = spltty();		/* Keep others out */
224510015Speter
224610015Speter	/* wait until it's finished what it was doing.. */
224710015Speter	while((x = ccbp->hi_stat) != IDLE_OPEN &&
224810015Speter			x != IDLE_CLOSE &&
224910015Speter			x != cmd) {
225010015Speter		if (in_intr) {			/* Prevent sleep in intr */
225110015Speter			DPRINT((pp, DBG_PARAM,
225210015Speter				"cmd intr collision - completing %d\trequested %d\n",
225310015Speter				x, cmd));
225410015Speter			splx(oldspl);
225510015Speter			return;
225610015Speter		} else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
225710015Speter				"sicmd1", 1)) {
225810015Speter			splx(oldspl);
225910015Speter			return;
226010015Speter		}
226110015Speter	}
226210015Speter	/* it should now be in IDLE_OPEN, IDLE_CLOSE, or "cmd" */
226310015Speter
226410015Speter	/* if there was a pending command, cause a state-change wakeup */
226510015Speter	if (pp->sp_pend != IDLE_OPEN) {
226610015Speter		switch(pp->sp_pend) {
226710015Speter		case LOPEN:
226810015Speter		case MPEND:
226910015Speter		case MOPEN:
227010015Speter		case CONFIG:
227110015Speter			wakeup(&pp->sp_state);
227210015Speter			break;
227310015Speter		default:
227410015Speter			break;
227510015Speter		}
227610015Speter	}
227710015Speter
227810015Speter	pp->sp_pend = cmd;		/* New command pending */
227910015Speter	ccbp->hi_stat = cmd;		/* Post it */
228010015Speter
228110015Speter	if (waitflag) {
228210015Speter		if (in_intr) {		/* If in interrupt handler */
228310015Speter			DPRINT((pp, DBG_PARAM,
228410015Speter				"attempt to sleep in si_intr - cmd req %d\n",
228510015Speter				cmd));
228610015Speter			splx(oldspl);
228710015Speter			return;
228810015Speter		} else while(ccbp->hi_stat != IDLE_OPEN) {
228910015Speter			if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
229010015Speter			    "sicmd2", 0))
229110015Speter				break;
229210015Speter		}
229310015Speter	}
229410015Speter	splx(oldspl);
229510015Speter}
229610015Speter
229710015Speterstatic void
229810015Spetersi_disc_optim(tp, t, pp)
229910015Speter	struct tty	*tp;
230010015Speter	struct termios	*t;
230110015Speter	struct si_port	*pp;
230210015Speter{
230310015Speter	/*
230410015Speter	 * XXX can skip a lot more cases if Smarts.  Maybe
230510015Speter	 * (IGNCR | ISTRIP | IXON) in c_iflag.  But perhaps we
230610015Speter	 * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state.
230710015Speter	 */
230810015Speter	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
230910015Speter	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
231010015Speter	    && (!(t->c_iflag & PARMRK)
231110015Speter		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
231210015Speter	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
231310015Speter	    && linesw[tp->t_line].l_rint == ttyinput)
231410015Speter		tp->t_state |= TS_CAN_BYPASS_L_RINT;
231510015Speter	else
231610015Speter		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
231710161Speter
231810015Speter	/*
231910015Speter	 * Prepare to reduce input latency for packet
232010015Speter	 * discplines with a end of packet character.
232110015Speter	 */
232210015Speter	if (tp->t_line == SLIPDISC)
232310015Speter		pp->sp_hotchar = 0xc0;
232410015Speter	else if (tp->t_line == PPPDISC)
232510015Speter		pp->sp_hotchar = 0x7e;
232610015Speter	else
232710015Speter		pp->sp_hotchar = 0;
232810161Speter
232910161Speter	DPRINT((pp, DBG_OPTIM, "bypass: %s, hotchar: %x\n",
233010161Speter		(tp->t_state & TS_CAN_BYPASS_L_RINT) ? "on" : "off",
233110161Speter		pp->sp_hotchar));
233210015Speter}
233310015Speter
233410015Speter
233510015Speter#ifdef	SI_DEBUG
233613353Speter
233710015Speterstatic void
233813353Speter#ifdef __STDC__
233913353Spetersi_dprintf(struct si_port *pp, int flags, const char *fmt, ...)
234013353Speter#else
234113353Spetersi_dprintf(pp, flags, fmt, va_alist)
234210015Speter	struct si_port *pp;
234310015Speter	int flags;
234413353Speter	char *fmt;
234513353Speter#endif
234610015Speter{
234713353Speter	va_list ap;
234813630Sphk
234910015Speter	if ((pp == NULL && (si_debug&flags)) ||
235010015Speter	    (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) {
235110015Speter	    	if (pp != NULL)
235212496Speter	    		printf("%ci%d(%d): ", 's',
235312174Speter	    			(int)SI_CARD(pp->sp_tty->t_dev),
235412174Speter	    			(int)SI_PORT(pp->sp_tty->t_dev));
235513630Sphk		va_start(ap, fmt);
235613630Sphk		vprintf(fmt, ap);
235713353Speter		va_end(ap);
235810015Speter	}
235910015Speter}
236010015Speter
236110015Speterstatic char *
236210015Spetersi_mctl2str(cmd)
236310015Speter	enum si_mctl cmd;
236410015Speter{
236510015Speter	switch (cmd) {
236610015Speter	case GET:	return("GET");
236710015Speter	case SET:	return("SET");
236810015Speter	case BIS:	return("BIS");
236910015Speter	case BIC:	return("BIC");
237010015Speter	}
237110015Speter	return("BAD");
237210015Speter}
237312502Sjulian
237412624Speter#endif	/* DEBUG */
237512502Sjulian
237612624Speter
237712502Sjulian
237812502Sjulianstatic si_devsw_installed = 0;
237912502Sjulian
238012517Sjulianstatic void 	si_drvinit(void *unused)
238112502Sjulian{
238212517Sjulian	dev_t dev;
238312517Sjulian
238412502Sjulian	if( ! si_devsw_installed ) {
238512675Sjulian		dev = makedev(CDEV_MAJOR, 0);
238612675Sjulian		cdevsw_add(&dev,&si_cdevsw, NULL);
238712502Sjulian		si_devsw_installed = 1;
238812517Sjulian    	}
238912502Sjulian}
239012517Sjulian
239112517SjulianSYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,si_drvinit,NULL)
239212517Sjulian
2393