si.c revision 17290
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 *
3317290Speter *	$Id: si.c,v 1.47 1996/06/30 04:56:05 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>
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,
106217290Speter				     "siwrite", tp->t_timeout)) {
106317290Speter			if (error == ETIMEDOUT)
106417290Speter				error = EIO;
106510015Speter			goto out;
106617290Speter		}
106710015Speter	}
106810015Speter
106910015Speter	error = (*linesw[tp->t_line].l_write)(tp, uio, flag);
107010015Speterout:
107110015Speter	splx(oldspl);
107210015Speter	return (error);
107310015Speter}
107410015Speter
107510015Speter
107612675Sjulianstatic	struct tty *
107710015Spetersidevtotty(dev_t dev)
107810015Speter{
107910015Speter	struct si_port *pp;
108010015Speter	int mynor = minor(dev);
108110015Speter	struct si_softc *sc = &si_softc[SI_CARD(mynor)];
108210015Speter
108310015Speter	if (IS_SPECIAL(mynor))
108410015Speter		return(NULL);
108510015Speter	if (SI_PORT(mynor) >= sc->sc_nport)
108610015Speter		return(NULL);
108710015Speter	pp = MINOR2PP(mynor);
108810015Speter	return (pp->sp_tty);
108910015Speter}
109010015Speter
109112675Sjulianstatic	int
109210015Spetersiioctl(dev, cmd, data, flag, p)
109310015Speter	dev_t dev;
109410015Speter	int cmd;
109510015Speter	caddr_t data;
109610015Speter	int flag;
109710015Speter	struct proc *p;
109810015Speter{
109910015Speter	struct si_port *pp;
110010015Speter	register struct tty *tp;
110110015Speter	int error;
110210015Speter	int mynor = minor(dev);
110310015Speter	int oldspl;
110410015Speter	int blocked = 0;
110510015Speter#if defined(COMPAT_43)
110610015Speter	int oldcmd;
110710015Speter	struct termios term;
110810015Speter#endif
110910015Speter
111010015Speter	if (IS_SI_IOCTL(cmd))
111110015Speter		return(si_Sioctl(dev, cmd, data, flag, p));
111210015Speter
111310015Speter	pp = MINOR2PP(mynor);
111410015Speter	tp = pp->sp_tty;
111510015Speter
111610015Speter	DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%x,%x,%x,%x)\n",
111710015Speter		dev, cmd, data, flag));
111810015Speter	if (IS_STATE(mynor)) {
111910015Speter		struct termios *ct;
112010015Speter
112110015Speter		switch (mynor & SI_STATE_MASK) {
112210015Speter		case SI_INIT_STATE_MASK:
112310015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
112410015Speter			break;
112510015Speter		case SI_LOCK_STATE_MASK:
112616839Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_lout : &pp->sp_lin;
112710015Speter			break;
112810015Speter		default:
112910015Speter			return (ENODEV);
113010015Speter		}
113110015Speter		switch (cmd) {
113210015Speter		case TIOCSETA:
113310015Speter			error = suser(p->p_ucred, &p->p_acflag);
113410015Speter			if (error != 0)
113510015Speter				return (error);
113610015Speter			*ct = *(struct termios *)data;
113710015Speter			return (0);
113810015Speter		case TIOCGETA:
113910015Speter			*(struct termios *)data = *ct;
114010015Speter			return (0);
114110015Speter		case TIOCGETD:
114210015Speter			*(int *)data = TTYDISC;
114310015Speter			return (0);
114410015Speter		case TIOCGWINSZ:
114510015Speter			bzero(data, sizeof(struct winsize));
114610015Speter			return (0);
114710015Speter		default:
114810015Speter			return (ENOTTY);
114910015Speter		}
115010015Speter	}
115110015Speter	/*
115210015Speter	 * Do the old-style ioctl compat routines...
115310015Speter	 */
115410015Speter#if defined(COMPAT_43)
115510015Speter	term = tp->t_termios;
115610015Speter	oldcmd = cmd;
115710015Speter	error = ttsetcompat(tp, &cmd, data, &term);
115810015Speter	if (error != 0)
115910015Speter		return (error);
116010015Speter	if (cmd != oldcmd)
116110015Speter		data = (caddr_t)&term;
116210015Speter#endif
116310015Speter	/*
116410015Speter	 * Do the initial / lock state business
116510015Speter	 */
116610015Speter	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
116710015Speter		int     cc;
116810015Speter		struct termios *dt = (struct termios *)data;
116910015Speter		struct termios *lt = mynor & SI_CALLOUT_MASK
117010015Speter				     ? &pp->sp_lout : &pp->sp_lin;
117110015Speter
117210015Speter		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
117310015Speter			| (dt->c_iflag & ~lt->c_iflag);
117410015Speter		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
117510015Speter			| (dt->c_oflag & ~lt->c_oflag);
117610015Speter		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
117710015Speter			| (dt->c_cflag & ~lt->c_cflag);
117810015Speter		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
117910015Speter			| (dt->c_lflag & ~lt->c_lflag);
118010015Speter		for (cc = 0; cc < NCCS; ++cc)
118110015Speter			if (lt->c_cc[cc] != 0)
118210015Speter				dt->c_cc[cc] = tp->t_cc[cc];
118310015Speter		if (lt->c_ispeed != 0)
118410015Speter			dt->c_ispeed = tp->t_ispeed;
118510015Speter		if (lt->c_ospeed != 0)
118610015Speter			dt->c_ospeed = tp->t_ospeed;
118710015Speter	}
118810015Speter
118910015Speter	/*
119010015Speter	 * Block user-level writes to give the ttywait()
119110015Speter	 * a chance to completely drain for commands
119210015Speter	 * that require the port to be in a quiescent state.
119310015Speter	 */
119410015Speter	switch (cmd) {
119510015Speter	case TIOCSETAW: case TIOCSETAF:
119610015Speter	case TIOCDRAIN: case TIOCSETP:
119710015Speter		blocked++;	/* block writes for ttywait() and siparam() */
119810015Speter		si_write_enable(pp, 0);
119910015Speter	}
120010015Speter
120110015Speter	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
120210015Speter	if (error >= 0)
120310015Speter		goto out;
120410015Speter
120510015Speter	oldspl = spltty();
120610015Speter
120710015Speter	error = ttioctl(tp, cmd, data, flag);
120810015Speter	si_disc_optim(tp, &tp->t_termios, pp);
120910015Speter	if (error >= 0)
121010015Speter		goto outspl;
121110015Speter
121210015Speter	switch (cmd) {
121310015Speter	case TIOCSBRK:
121416575Speter		si_command(pp, SBREAK, SI_WAIT);
121510015Speter		break;
121610015Speter	case TIOCCBRK:
121716575Speter		si_command(pp, EBREAK, SI_WAIT);
121810015Speter		break;
121910015Speter	case TIOCSDTR:
122010015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
122110015Speter		break;
122210015Speter	case TIOCCDTR:
122310015Speter		(void) si_modem(pp, SET, 0);
122410015Speter		break;
122510015Speter	case TIOCMSET:
122610015Speter		(void) si_modem(pp, SET, *(int *)data);
122710015Speter		break;
122810015Speter	case TIOCMBIS:
122910015Speter		(void) si_modem(pp, BIS, *(int *)data);
123010015Speter		break;
123110015Speter	case TIOCMBIC:
123210015Speter		(void) si_modem(pp, BIC, *(int *)data);
123310015Speter		break;
123410015Speter	case TIOCMGET:
123510015Speter		*(int *)data = si_modem(pp, GET, 0);
123610015Speter		break;
123710015Speter	case TIOCMSDTRWAIT:
123810015Speter		/* must be root since the wait applies to following logins */
123910015Speter		error = suser(p->p_ucred, &p->p_acflag);
124010015Speter		if (error != 0) {
124110015Speter			goto outspl;
124210015Speter		}
124310015Speter		pp->sp_dtr_wait = *(int *)data * hz / 100;
124410015Speter		break;
124510015Speter	case TIOCMGDTRWAIT:
124610015Speter		*(int *)data = pp->sp_dtr_wait * 100 / hz;
124710015Speter		break;
124810015Speter
124910015Speter	default:
125010015Speter		error = ENOTTY;
125110015Speter	}
125210015Speter	error = 0;
125310015Speteroutspl:
125410015Speter	splx(oldspl);
125510015Speterout:
125610015Speter	DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error));
125710015Speter	if (blocked)
125810015Speter		si_write_enable(pp, 1);
125910015Speter	return(error);
126010015Speter}
126110015Speter
126210015Speter/*
126310015Speter * Handle the Specialix ioctls. All MUST be called via the CONTROL device
126410015Speter */
126510015Speterstatic int
126610015Spetersi_Sioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
126710015Speter{
126810015Speter	struct si_softc *xsc;
126910015Speter	register struct si_port *xpp;
127010015Speter	volatile struct si_reg *regp;
127110015Speter	struct si_tcsi *dp;
127210044Speter	struct si_pstat *sps;
127311872Sphk	int *ip, error = 0;
127410015Speter	int oldspl;
127510015Speter	int card, port;
127610015Speter	int mynor = minor(dev);
127710015Speter
127810015Speter	DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%x,%x,%x,%x)\n",
127910015Speter		dev, cmd, data, flag));
128010015Speter
128110044Speter#if 1
128210044Speter	DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT));
128310044Speter	DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB));
128410044Speter	DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY));
128510044Speter#endif
128610044Speter
128710015Speter	if (!IS_CONTROLDEV(mynor)) {
128810015Speter		DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n"));
128910015Speter		return(ENODEV);
129010015Speter	}
129110015Speter
129210015Speter	oldspl = spltty();	/* better safe than sorry */
129310015Speter
129410015Speter	ip = (int *)data;
129510015Speter
129610015Speter#define SUCHECK if (error = suser(p->p_ucred, &p->p_acflag)) goto out
129710015Speter
129810015Speter	switch (cmd) {
129910015Speter	case TCSIPORTS:
130010015Speter		*ip = si_Nports;
130110015Speter		goto out;
130210015Speter	case TCSIMODULES:
130310015Speter		*ip = si_Nmodules;
130410015Speter		goto out;
130510015Speter	case TCSISDBG_ALL:
130610015Speter		SUCHECK;
130710015Speter		si_debug = *ip;
130810015Speter		goto out;
130910015Speter	case TCSIGDBG_ALL:
131010015Speter		*ip = si_debug;
131110015Speter		goto out;
131210015Speter	default:
131310015Speter		/*
131410015Speter		 * Check that a controller for this port exists
131510015Speter		 */
131610044Speter
131710044Speter		/* may also be a struct si_pstat, a superset of si_tcsi */
131810044Speter
131910015Speter		dp = (struct si_tcsi *)data;
132010044Speter		sps = (struct si_pstat *)data;
132110015Speter		card = dp->tc_card;
132210015Speter		xsc = &si_softc[card];	/* check.. */
132312174Speter		if (card < 0 || card >= NSI || xsc->sc_type == SIEMPTY) {
132410015Speter			error = ENOENT;
132510015Speter			goto out;
132610015Speter		}
132710015Speter		/*
132810015Speter		 * And check that a port exists
132910015Speter		 */
133010015Speter		port = dp->tc_port;
133110015Speter		if (port < 0 || port >= xsc->sc_nport) {
133210015Speter			error = ENOENT;
133310015Speter			goto out;
133410015Speter		}
133510015Speter		xpp = xsc->sc_ports + port;
133610015Speter		regp = (struct si_reg *)xsc->sc_maddr;
133710015Speter	}
133810015Speter
133910015Speter	switch (cmd) {
134010015Speter	case TCSIDEBUG:
134110015Speter#ifdef	SI_DEBUG
134210015Speter		SUCHECK;
134310015Speter		if (xpp->sp_debug)
134410015Speter			xpp->sp_debug = 0;
134510015Speter		else {
134610015Speter			xpp->sp_debug = DBG_ALL;
134710015Speter			DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n",
134810015Speter				(xpp->sp_debug&DBG_ALL)?"ON":"OFF"));
134910015Speter		}
135010015Speter		break;
135110015Speter#else
135210015Speter		error = ENODEV;
135310015Speter		goto out;
135410015Speter#endif
135510015Speter	case TCSISDBG_LEVEL:
135610015Speter	case TCSIGDBG_LEVEL:
135710015Speter#ifdef	SI_DEBUG
135810015Speter		if (cmd == TCSIGDBG_LEVEL) {
135910015Speter			dp->tc_dbglvl = xpp->sp_debug;
136010015Speter		} else {
136110015Speter			SUCHECK;
136210015Speter			xpp->sp_debug = dp->tc_dbglvl;
136310015Speter		}
136410015Speter		break;
136510015Speter#else
136610015Speter		error = ENODEV;
136710015Speter		goto out;
136810015Speter#endif
136910015Speter	case TCSIGRXIT:
137010015Speter		dp->tc_int = regp->rx_int_count;
137110015Speter		break;
137210015Speter	case TCSIRXIT:
137310015Speter		SUCHECK;
137410015Speter		regp->rx_int_count = dp->tc_int;
137510015Speter		break;
137610015Speter	case TCSIGIT:
137710015Speter		dp->tc_int = regp->int_count;
137810015Speter		break;
137910015Speter	case TCSIIT:
138010015Speter		SUCHECK;
138110015Speter		regp->int_count = dp->tc_int;
138210015Speter		break;
138310044Speter	case TCSISTATE:
138410044Speter		dp->tc_int = xpp->sp_ccb->hi_ip;
138510015Speter		break;
138610044Speter	/* these next three use a different structure */
138710044Speter	case TCSI_PORT:
138810015Speter		SUCHECK;
138916444Speter		bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport));
139010015Speter		break;
139110044Speter	case TCSI_CCB:
139210044Speter		SUCHECK;
139316444Speter		bcopy((char *)xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb));
139410015Speter		break;
139510044Speter	case TCSI_TTY:
139610044Speter		SUCHECK;
139716444Speter		bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty));
139810015Speter		break;
139910015Speter	default:
140010015Speter		error = EINVAL;
140110015Speter		goto out;
140210015Speter	}
140310015Speterout:
140410015Speter	splx(oldspl);
140510015Speter	return(error);		/* success */
140610015Speter}
140710015Speter
140810015Speter/*
140910015Speter *	siparam()	: Configure line params
141010015Speter *	called at spltty();
141110015Speter *	this may sleep, does not flush, nor wait for drain, nor block writes
141210015Speter *	caller must arrange this if it's important..
141310015Speter */
141412724Sphkstatic int
141510015Spetersiparam(tp, t)
141610015Speter	register struct tty *tp;
141710015Speter	register struct termios *t;
141810015Speter{
141910015Speter	register struct si_port *pp = TP2PP(tp);
142010015Speter	volatile struct si_channel *ccbp;
142110015Speter	int oldspl, cflag, iflag, oflag, lflag;
142210015Speter	int error = 0;		/* shutup gcc */
142310015Speter	int ispeed = 0;		/* shutup gcc */
142410015Speter	int ospeed = 0;		/* shutup gcc */
142510161Speter	BYTE val;
142610015Speter
142710015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t));
142810015Speter	cflag = t->c_cflag;
142910015Speter	iflag = t->c_iflag;
143010015Speter	oflag = t->c_oflag;
143110015Speter	lflag = t->c_lflag;
143210044Speter	DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n",
143310044Speter		oflag, cflag, iflag, lflag));
143410015Speter
143510015Speter
143610015Speter	/* if not hung up.. */
143710015Speter	if (t->c_ospeed != 0) {
143810015Speter		/* translate baud rate to firmware values */
143910015Speter		ospeed = ttspeedtab(t->c_ospeed, bdrates);
144010015Speter		ispeed = t->c_ispeed ?
144110015Speter			 ttspeedtab(t->c_ispeed, bdrates) : ospeed;
144210015Speter
144310015Speter		/* enforce legit baud rate */
144410015Speter		if (ospeed < 0 || ispeed < 0)
144510015Speter			return (EINVAL);
144610015Speter	}
144710015Speter
144810015Speter
144910015Speter	oldspl = spltty();
145010015Speter
145110015Speter	ccbp = pp->sp_ccb;
145210015Speter
145310161Speter	/* ========== set hi_break ========== */
145410161Speter	val = 0;
145510161Speter	if (iflag & IGNBRK)		/* Breaks */
145610161Speter		val |= BR_IGN;
145710161Speter	if (iflag & BRKINT)		/* Interrupt on break? */
145810161Speter		val |= BR_INT;
145910161Speter	if (iflag & PARMRK)		/* Parity mark? */
146010161Speter		val |= BR_PARMRK;
146110161Speter	if (iflag & IGNPAR)		/* Ignore chars with parity errors? */
146210161Speter		val |= BR_PARIGN;
146310161Speter	ccbp->hi_break = val;
146410161Speter
146510161Speter	/* ========== set hi_csr ========== */
146610015Speter	/* if not hung up.. */
146710015Speter	if (t->c_ospeed != 0) {
146810015Speter		/* Set I/O speeds */
146910161Speter		 val = (ispeed << 4) | ospeed;
147010015Speter	}
147110161Speter	ccbp->hi_csr = val;
147210015Speter
147310161Speter	/* ========== set hi_mr2 ========== */
147410161Speter	val = 0;
147510015Speter	if (cflag & CSTOPB)				/* Stop bits */
147610161Speter		val |= MR2_2_STOP;
147710015Speter	else
147810161Speter		val |= MR2_1_STOP;
147910161Speter	/*
148010161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
148110161Speter	 * a DCE, hence the reverse sense of RTS and CTS
148210161Speter	 */
148310161Speter	/* Output Flow - RTS must be raised before data can be sent */
148410161Speter	if (cflag & CCTS_OFLOW)
148510161Speter		val |= MR2_RTSCONT;
148610161Speter
148716575Speter	ccbp->hi_mr2 = val;
148810161Speter
148910161Speter	/* ========== set hi_mr1 ========== */
149010161Speter	val = 0;
149110015Speter	if (!(cflag & PARENB))				/* Parity */
149210161Speter		val |= MR1_NONE;
149310015Speter	else
149410161Speter		val |= MR1_WITH;
149510015Speter	if (cflag & PARODD)
149610161Speter		val |= MR1_ODD;
149710015Speter
149810015Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
149910161Speter		val |= MR1_8_BITS;
150010015Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
150110161Speter		val |= MR1_7_BITS;
150210015Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
150310161Speter		val |= MR1_6_BITS;
150410015Speter	} else {					/* Must be 5 */
150510161Speter		val |= MR1_5_BITS;
150610015Speter	}
150710161Speter	/*
150810161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
150910161Speter	 * a DCE, hence the reverse sense of RTS and CTS
151010161Speter	 */
151110161Speter	/* Input Flow - CTS is raised when port is ready to receive data */
151210161Speter	if (cflag & CRTS_IFLOW)
151310161Speter		val |= MR1_CTSCONT;
151410015Speter
151510161Speter	ccbp->hi_mr1 = val;
151610161Speter
151710161Speter	/* ========== set hi_mask ========== */
151810161Speter	val = 0xff;
151910161Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
152010161Speter		val &= 0xFF;
152110161Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
152210161Speter		val &= 0x7F;
152310161Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
152410161Speter		val &= 0x3F;
152510161Speter	} else {					/* Must be 5 */
152610161Speter		val &= 0x1F;
152710161Speter	}
152810015Speter	if (iflag & ISTRIP)
152910161Speter		val &= 0x7F;
153010015Speter
153110161Speter	ccbp->hi_mask = val;
153210161Speter
153310161Speter	/* ========== set hi_prtcl ========== */
153410161Speter	val = 0;
153510015Speter				/* Monitor DCD etc. if a modem */
153610015Speter	if (!(cflag & CLOCAL))
153710161Speter		val |= SP_DCEN;
153810161Speter	if (iflag & IXANY)
153910161Speter		val |= SP_TANY;
154010161Speter	if (iflag & IXON)
154110161Speter		val |= SP_TXEN;
154210161Speter	if (iflag & IXOFF)
154310161Speter		val |= SP_RXEN;
154410161Speter	if (iflag & INPCK)
154510161Speter		val |= SP_PAEN;
154610015Speter
154710161Speter	ccbp->hi_prtcl = val;
154810161Speter
154910161Speter
155010161Speter	/* ========== set hi_{rx|tx}{on|off} ========== */
155110161Speter	/* XXX: the card TOTALLY shields us from the flow control... */
155210015Speter	ccbp->hi_txon = t->c_cc[VSTART];
155310015Speter	ccbp->hi_txoff = t->c_cc[VSTOP];
155410015Speter
155510015Speter	ccbp->hi_rxon = t->c_cc[VSTART];
155610015Speter	ccbp->hi_rxoff = t->c_cc[VSTOP];
155710015Speter
155810161Speter	/* ========== send settings to the card ========== */
155910015Speter	/* potential sleep here */
156010015Speter	if (ccbp->hi_stat == IDLE_CLOSE)		/* Not yet open */
156110015Speter		si_command(pp, LOPEN, SI_WAIT);		/* open it */
156210015Speter	else
156310015Speter		si_command(pp, CONFIG, SI_WAIT);	/* change params */
156410015Speter
156510161Speter	/* ========== set DTR etc ========== */
156610015Speter	/* Hangup if ospeed == 0 */
156710015Speter	if (t->c_ospeed == 0) {
156810015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
156910015Speter	} else {
157010015Speter		/*
157110015Speter		 * If the previous speed was 0, may need to re-enable
157210015Speter	 	 * the modem signals
157310015Speter	 	 */
157410015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
157510015Speter	}
157610015Speter
157710044Speter	DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n",
157810044Speter		ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break));
157910015Speter
158010015Speter	splx(oldspl);
158110015Speter	return(error);
158210015Speter}
158310015Speter
158410015Speter/*
158510015Speter * Enable or Disable the writes to this channel...
158610015Speter * "state" ->  enabled = 1; disabled = 0;
158710015Speter */
158810015Speterstatic void
158910015Spetersi_write_enable(pp, state)
159010015Speter	register struct si_port *pp;
159110015Speter	int state;
159210015Speter{
159310015Speter	int oldspl;
159410015Speter
159510015Speter	oldspl = spltty();
159610015Speter
159710015Speter	if (state) {
159810015Speter		pp->sp_state &= ~SS_BLOCKWRITE;
159910015Speter		if (pp->sp_state & SS_WAITWRITE) {
160010015Speter			pp->sp_state &= ~SS_WAITWRITE;
160110015Speter			/* thunder away! */
160210015Speter			wakeup((caddr_t)pp);
160310015Speter		}
160410015Speter	} else {
160510015Speter		pp->sp_state |= SS_BLOCKWRITE;
160610015Speter	}
160710015Speter
160810015Speter	splx(oldspl);
160910015Speter}
161010015Speter
161110015Speter/*
161210015Speter * Set/Get state of modem control lines.
161310015Speter * Due to DCE-like behaviour of the adapter, some signals need translation:
161410015Speter *	TIOCM_DTR	DSR
161510015Speter *	TIOCM_RTS	CTS
161610015Speter */
161710015Speterstatic int
161810015Spetersi_modem(pp, cmd, bits)
161910015Speter	struct si_port *pp;
162010015Speter	enum si_mctl cmd;
162110015Speter	int bits;
162210015Speter{
162310015Speter	volatile struct si_channel *ccbp;
162410015Speter	int x;
162510015Speter
162610015Speter	DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits));
162710015Speter	ccbp = pp->sp_ccb;		/* Find channel address */
162810015Speter	switch (cmd) {
162910015Speter	case GET:
163010015Speter		x = ccbp->hi_ip;
163110015Speter		bits = TIOCM_LE;
163210015Speter		if (x & IP_DCD)		bits |= TIOCM_CAR;
163310015Speter		if (x & IP_DTR)		bits |= TIOCM_DTR;
163410015Speter		if (x & IP_RTS)		bits |= TIOCM_RTS;
163510015Speter		if (x & IP_RI)		bits |= TIOCM_RI;
163610015Speter		return(bits);
163710015Speter	case SET:
163810015Speter		ccbp->hi_op &= ~(OP_DSR|OP_CTS);
163910015Speter		/* fall through */
164010015Speter	case BIS:
164110015Speter		x = 0;
164210015Speter		if (bits & TIOCM_DTR)
164310015Speter			x |= OP_DSR;
164410015Speter		if (bits & TIOCM_RTS)
164510015Speter			x |= OP_CTS;
164610015Speter		ccbp->hi_op |= x;
164710015Speter		break;
164810015Speter	case BIC:
164910015Speter		if (bits & TIOCM_DTR)
165010015Speter			ccbp->hi_op &= ~OP_DSR;
165110015Speter		if (bits & TIOCM_RTS)
165210015Speter			ccbp->hi_op &= ~OP_CTS;
165310015Speter	}
165410015Speter	return 0;
165510015Speter}
165610015Speter
165710015Speter/*
165810015Speter * Handle change of modem state
165910015Speter */
166010015Speterstatic void
166110015Spetersi_modem_state(pp, tp, hi_ip)
166210015Speter	register struct si_port *pp;
166310015Speter	register struct tty *tp;
166410015Speter	register int hi_ip;
166510015Speter{
166610015Speter							/* if a modem dev */
166710015Speter	if (hi_ip & IP_DCD) {
166810015Speter		if ( !(pp->sp_last_hi_ip & IP_DCD)) {
166910015Speter			DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n",
167010015Speter				tp->t_line));
167110015Speter			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
167210015Speter		}
167310015Speter	} else {
167410015Speter		if (pp->sp_last_hi_ip & IP_DCD) {
167510015Speter			DPRINT((pp, DBG_INTR, "modem carr off\n"));
167610015Speter			if ((*linesw[tp->t_line].l_modem)(tp, 0))
167710015Speter				(void) si_modem(pp, SET, 0);
167810015Speter		}
167910015Speter	}
168010015Speter	pp->sp_last_hi_ip = hi_ip;
168110015Speter
168210015Speter}
168310015Speter
168410015Speter/*
168510015Speter * Poller to catch missed interrupts.
168612174Speter *
168712496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get
168812496Speter * better response.  We could really use a "periodic" version timeout(). :-)
168910015Speter */
169010015Speter#ifdef POLL
169110708Speterstatic void
169210015Spetersi_poll(void *nothing)
169310015Speter{
169410015Speter	register struct si_softc *sc;
169510015Speter	register int i;
169610015Speter	volatile struct si_reg *regp;
169712174Speter	register struct si_port *pp;
169812174Speter	int lost, oldspl, port;
169910015Speter
170010015Speter	DPRINT((0, DBG_POLL, "si_poll()\n"));
170111609Speter	oldspl = spltty();
170210015Speter	if (in_intr)
170310015Speter		goto out;
170410015Speter	lost = 0;
170510015Speter	for (i=0; i<NSI; i++) {
170610015Speter		sc = &si_softc[i];
170712174Speter		if (sc->sc_type == SIEMPTY)
170810015Speter			continue;
170910015Speter		regp = (struct si_reg *)sc->sc_maddr;
171010015Speter		/*
171110015Speter		 * See if there has been a pending interrupt for 2 seconds
171210015Speter		 * or so. The test <int_scounter >= 200) won't correspond
171310015Speter		 * to 2 seconds if int_count gets changed.
171410015Speter		 */
171510015Speter		if (regp->int_pending != 0) {
171610015Speter			if (regp->int_scounter >= 200 &&
171710015Speter			    regp->initstat == 1) {
171812174Speter				printf("si%d: lost intr\n", i);
171910015Speter				lost++;
172010015Speter			}
172110015Speter		} else {
172210015Speter			regp->int_scounter = 0;
172310015Speter		}
172410015Speter
172512174Speter		/*
172612174Speter		 * gripe about no input flow control..
172712174Speter		 */
172812174Speter		pp = sc->sc_ports;
172912174Speter		for (port = 0; port < sc->sc_nport; pp++, port++) {
173012174Speter			if (pp->sp_delta_overflows > 0) {
173112174Speter				printf("si%d: %d tty level buffer overflows\n",
173212174Speter					i, pp->sp_delta_overflows);
173312174Speter				pp->sp_delta_overflows = 0;
173412174Speter			}
173512174Speter		}
173610015Speter	}
173710015Speter	if (lost)
173810015Speter		siintr(-1);	/* call intr with fake vector */
173911609Speterout:
174010015Speter	splx(oldspl);
174110015Speter
174215639Speter	timeout(si_poll, (caddr_t)0L, si_pollrate);
174310015Speter}
174410015Speter#endif	/* ifdef POLL */
174510015Speter
174610015Speter/*
174710015Speter * The interrupt handler polls ALL ports on ALL adapters each time
174810015Speter * it is called.
174910015Speter */
175010015Speter
175112496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE];	/* input staging area */
175210015Speter
175310708Spetervoid
175411609Spetersiintr(int unit)
175510015Speter{
175610015Speter	register struct si_softc *sc;
175710015Speter
175810015Speter	register struct si_port *pp;
175910015Speter	volatile struct si_channel *ccbp;
176010015Speter	register struct tty *tp;
176110015Speter	volatile caddr_t maddr;
176211872Sphk	BYTE op, ip;
176312174Speter	int x, card, port, n, i, isopen;
176410015Speter	volatile BYTE *z;
176510015Speter	BYTE c;
176610015Speter
176711609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "siintr(%d)\n", unit));
176811609Speter	if (in_intr) {
176911609Speter		if (unit < 0)	/* should never happen */
177010708Speter			return;
177112174Speter		printf("si%d: Warning interrupt handler re-entered\n",
177211609Speter			unit);
177310708Speter		return;
177410015Speter	}
177510015Speter	in_intr = 1;
177610015Speter
177710015Speter	/*
177810015Speter	 * When we get an int we poll all the channels and do ALL pending
177910015Speter	 * work, not just the first one we find. This allows all cards to
178010015Speter	 * share the same vector.
178110015Speter	 */
178210015Speter	for (card=0; card < NSI; card++) {
178310015Speter		sc = &si_softc[card];
178412174Speter		if (sc->sc_type == SIEMPTY)
178510015Speter			continue;
178612174Speter
178712174Speter		/*
178812174Speter		 * First, clear the interrupt
178912174Speter		 */
179010015Speter		switch(sc->sc_type) {
179110015Speter		case SIHOST :
179210015Speter			maddr = sc->sc_maddr;
179310015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
179410015Speter							/* flag nothing pending */
179510015Speter			*(maddr+SIINTCL) = 0x00;	/* Set IRQ clear */
179610015Speter			*(maddr+SIINTCL_CL) = 0x00;	/* Clear IRQ clear */
179710015Speter			break;
179810015Speter		case SIHOST2:
179910015Speter			maddr = sc->sc_maddr;
180010015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
180110015Speter			*(maddr+SIPLIRQCLR) = 0x00;
180210015Speter			*(maddr+SIPLIRQCLR) = 0x10;
180310015Speter			break;
180410015Speter		case SIEISA:
180510015Speter#if NEISA > 0
180610015Speter			maddr = sc->sc_maddr;
180710015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
180810015Speter			(void)inb(sc->sc_eisa_iobase+3);
180910015Speter			break;
181010015Speter#endif	/* fall through if not EISA kernel */
181110015Speter		case SIEMPTY:
181210015Speter		default:
181310015Speter			continue;
181410015Speter		}
181510015Speter		((volatile struct si_reg *)maddr)->int_scounter = 0;
181610015Speter
181712174Speter		/*
181812174Speter		 * check each port
181912174Speter		 */
182012174Speter		for (pp=sc->sc_ports,port=0; port < sc->sc_nport; pp++,port++) {
182110015Speter			ccbp = pp->sp_ccb;
182210015Speter			tp = pp->sp_tty;
182310015Speter
182412174Speter
182510015Speter			/*
182610015Speter			 * See if a command has completed ?
182710015Speter			 */
182810015Speter			if (ccbp->hi_stat != pp->sp_pend) {
182910015Speter				DPRINT((pp, DBG_INTR,
183010015Speter					"siintr hi_stat = 0x%x, pend = %d\n",
183110015Speter					ccbp->hi_stat, pp->sp_pend));
183210015Speter				switch(pp->sp_pend) {
183310015Speter				case LOPEN:
183410015Speter				case MPEND:
183510015Speter				case MOPEN:
183610015Speter				case CONFIG:
183716575Speter				case SBREAK:
183816575Speter				case EBREAK:
183910015Speter					pp->sp_pend = ccbp->hi_stat;
184010015Speter						/* sleeping in si_command */
184110015Speter					wakeup(&pp->sp_state);
184210015Speter					break;
184310015Speter				default:
184410015Speter					pp->sp_pend = ccbp->hi_stat;
184510015Speter				}
184610015Speter	 		}
184710015Speter
184810015Speter			/*
184910015Speter			 * Continue on if it's closed
185010015Speter			 */
185110015Speter			if (ccbp->hi_stat == IDLE_CLOSE) {
185210015Speter				continue;
185310015Speter			}
185410015Speter
185510015Speter			/*
185610015Speter			 * Do modem state change if not a local device
185710015Speter			 */
185810015Speter			si_modem_state(pp, tp, ccbp->hi_ip);
185910015Speter
186010015Speter			/*
186112174Speter			 * Check to see if there's we should 'receive'
186212174Speter			 * characters.
186312174Speter			 */
186412174Speter			if (tp->t_state & TS_CONNECTED &&
186512174Speter			    tp->t_state & TS_ISOPEN)
186612174Speter				isopen = 1;
186712174Speter			else
186812174Speter				isopen = 0;
186912174Speter
187012174Speter			/*
187116575Speter			 * Do input break processing
187210015Speter			 */
187310015Speter			if (ccbp->hi_state & ST_BREAK) {
187412174Speter				if (isopen) {
187512174Speter				    (*linesw[tp->t_line].l_rint)(TTY_BI, tp);
187610015Speter				}
187710015Speter				ccbp->hi_state &= ~ST_BREAK;   /* A Bit iffy this */
187810015Speter				DPRINT((pp, DBG_INTR, "si_intr break\n"));
187910015Speter			}
188010015Speter
188110015Speter			/*
188212174Speter			 * Do RX stuff - if not open then dump any characters.
188312174Speter			 * XXX: This is VERY messy and needs to be cleaned up.
188412174Speter			 *
188512174Speter			 * XXX: can we leave data in the host adapter buffer
188612174Speter			 * when the clists are full?  That may be dangerous
188712174Speter			 * if the user cannot get an interrupt signal through.
188810015Speter			 */
188910015Speter
189012174Speter	more_rx:	/* XXX Sorry. the nesting was driving me bats! :-( */
189112174Speter
189212174Speter			if (!isopen) {
189310015Speter				ccbp->hi_rxopos = ccbp->hi_rxipos;
189412174Speter				goto end_rx;
189512174Speter			}
189610015Speter
189712174Speter			/*
189815640Speter			 * If the tty input buffers are blocked, stop emptying
189915640Speter			 * the incoming buffers and let the auto flow control
190015640Speter			 * assert..
190115640Speter			 */
190215640Speter			if (tp->t_state & TS_TBLOCK) {
190315640Speter				goto end_rx;
190415640Speter			}
190515640Speter
190615640Speter			/*
190712174Speter			 * Process read characters if not skipped above
190812174Speter			 */
190915640Speter			op = ccbp->hi_rxopos;
191015640Speter			ip = ccbp->hi_rxipos;
191115640Speter			c = ip - op;
191212174Speter			if (c == 0) {
191312174Speter				goto end_rx;
191412174Speter			}
191510015Speter
191612174Speter			n = c & 0xff;
191715640Speter			if (n > 250)
191815640Speter				n = 250;
191912174Speter
192012174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
192110015Speter						n, op, ip));
192210015Speter
192312174Speter			/*
192412174Speter			 * Suck characters out of host card buffer into the
192512174Speter			 * "input staging buffer" - so that we dont leave the
192612174Speter			 * host card in limbo while we're possibly echoing
192712174Speter			 * characters and possibly flushing input inside the
192812174Speter			 * ldisc l_rint() routine.
192912174Speter			 */
193012496Speter			if (n <= SI_BUFFERSIZE - op) {
193110015Speter
193212174Speter				DPRINT((pp, DBG_INTR, "\tsingle copy\n"));
193312174Speter				z = ccbp->hi_rxbuf + op;
193412174Speter				bcopy((caddr_t)z, si_rxbuf, n);
193510015Speter
193612174Speter				op += n;
193712174Speter			} else {
193812496Speter				x = SI_BUFFERSIZE - op;
193910015Speter
194012174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x));
194112174Speter				z = ccbp->hi_rxbuf + op;
194212174Speter				bcopy((caddr_t)z, si_rxbuf, x);
194310015Speter
194412174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n-x));
194512174Speter				z = ccbp->hi_rxbuf;
194612174Speter				bcopy((caddr_t)z, si_rxbuf+x, n-x);
194710015Speter
194812174Speter				op += n;
194912174Speter			}
195010015Speter
195112174Speter			/* clear collected characters from buffer */
195212174Speter			ccbp->hi_rxopos = op;
195312174Speter
195412174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
195510015Speter						n, op, ip));
195610015Speter
195712174Speter			/*
195812174Speter			 * at this point...
195912174Speter			 * n = number of chars placed in si_rxbuf
196012174Speter			 */
196110015Speter
196212174Speter			/*
196312174Speter			 * Avoid the grotesquely inefficient lineswitch
196412174Speter			 * routine (ttyinput) in "raw" mode. It usually
196512174Speter			 * takes about 450 instructions (that's without
196612174Speter			 * canonical processing or echo!). slinput is
196712174Speter			 * reasonably fast (usually 40 instructions
196812174Speter			 * plus call overhead).
196912174Speter			 */
197012174Speter			if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
197110015Speter
197212174Speter				/* block if the driver supports it */
197312174Speter				if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER
197412174Speter				    && (tp->t_cflag & CRTS_IFLOW
197512174Speter					|| tp->t_iflag & IXOFF)
197612174Speter				    && !(tp->t_state & TS_TBLOCK))
197712174Speter					ttyblock(tp);
197810015Speter
197912174Speter				tk_nin += n;
198012174Speter				tk_rawcc += n;
198112174Speter				tp->t_rawcc += n;
198212174Speter
198312174Speter				pp->sp_delta_overflows +=
198412174Speter				    b_to_q((char *)si_rxbuf, n, &tp->t_rawq);
198512174Speter
198612174Speter				ttwakeup(tp);
198712174Speter				if (tp->t_state & TS_TTSTOP
198812174Speter				    && (tp->t_iflag & IXANY
198912174Speter					|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
199012174Speter					tp->t_state &= ~TS_TTSTOP;
199112174Speter					tp->t_lflag &= ~FLUSHO;
199212174Speter					si_start(tp);
199312174Speter				}
199412174Speter			} else {
199512174Speter				/*
199612174Speter				 * It'd be nice to not have to go through the
199712174Speter				 * function call overhead for each char here.
199812174Speter				 * It'd be nice to block input it, saving a
199912174Speter				 * loop here and the call/return overhead.
200012174Speter				 */
200112174Speter				for(x = 0; x < n; x++) {
200212174Speter					i = si_rxbuf[x];
200312174Speter					if ((*linesw[tp->t_line].l_rint)(i, tp)
200412174Speter					     == -1) {
200512174Speter						pp->sp_delta_overflows++;
200610015Speter					}
200712174Speter					/*
200812174Speter					 * doesn't seem to be much point doing
200912174Speter					 * this here.. this driver has no
201012174Speter					 * softtty processing! ??
201112174Speter					 */
201212174Speter					if (pp->sp_hotchar && i == pp->sp_hotchar) {
201312174Speter						setsofttty();
201412174Speter					}
201512174Speter				}
201612174Speter			}
201712174Speter			goto more_rx;	/* try for more until RXbuf is empty */
201810015Speter
201912174Speter	end_rx:		/* XXX: Again, sorry about the gotos.. :-) */
202010015Speter
202110015Speter			/*
202210015Speter			 * Do TX stuff
202310015Speter			 */
202410015Speter			(*linesw[tp->t_line].l_start)(tp);
202510015Speter
202610015Speter		} /* end of for (all ports on this controller) */
202710015Speter	} /* end of for (all controllers) */
202810015Speter
202911609Speter	in_intr = 0;
203011609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "end siintr(%d)\n", unit));
203110015Speter}
203210015Speter
203310015Speter/*
203410015Speter * Nudge the transmitter...
203512174Speter *
203612174Speter * XXX: I inherited some funny code here.  It implies the host card only
203712174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does
203812174Speter * not interrupt when it's actually hits empty.  In some cases, we have
203912174Speter * processes waiting for complete drain, and we need to simulate an interrupt
204012174Speter * about when we think the buffer is going to be empty (and retry if not).
204112174Speter * I really am not certain about this...  I *need* the hardware manuals.
204210015Speter */
204310015Speterstatic void
204410015Spetersi_start(tp)
204510015Speter	register struct tty *tp;
204610015Speter{
204710015Speter	struct si_port *pp;
204810015Speter	volatile struct si_channel *ccbp;
204910015Speter	register struct clist *qp;
205010015Speter	register char *dptr;
205110015Speter	BYTE ipos;
205210015Speter	int nchar;
205310015Speter	int oldspl, count, n, amount, buffer_full;
205410015Speter	int do_exitproc;
205510015Speter
205610015Speter	oldspl = spltty();
205710015Speter
205810015Speter	qp = &tp->t_outq;
205910015Speter	pp = TP2PP(tp);
206010015Speter
206110015Speter	DPRINT((pp, DBG_ENTRY|DBG_START,
206210015Speter		"si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n",
206310015Speter		tp, tp->t_state, pp->sp_state, qp->c_cc));
206410015Speter
206510015Speter	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
206610015Speter		goto out;
206710015Speter
206810015Speter	do_exitproc = 0;
206910015Speter	buffer_full = 0;
207010015Speter	ccbp = pp->sp_ccb;
207110015Speter
207210015Speter	/*
207310015Speter	 * Handle the case where ttywait() is called on process exit
207410015Speter	 * this may be BSDI specific, I dont know...
207510015Speter	 */
207610015Speter	if (tp->t_session != NULL && tp->t_session->s_leader != NULL &&
207710015Speter	    (tp->t_session->s_leader->p_flag & P_WEXIT)) {
207810015Speter		do_exitproc++;
207910015Speter	}
208010015Speter
208110015Speter	count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
208210015Speter	DPRINT((pp, DBG_START, "count %d\n", (BYTE)count));
208310015Speter
208410015Speter	dptr = (char *)ccbp->hi_txbuf;	/* data buffer */
208510015Speter
208610015Speter	while ((nchar = qp->c_cc) > 0) {
208710015Speter		if ((BYTE)count >= 255) {
208810015Speter			buffer_full++;
208910015Speter			break;
209010015Speter		}
209110015Speter		amount = min(nchar, (255 - (BYTE)count));
209210015Speter		ipos = (unsigned int)ccbp->hi_txipos;
209310015Speter		/* will it fit in one lump? */
209412496Speter		if ((SI_BUFFERSIZE - ipos) >= amount) {
209510015Speter			n = q_to_b(&tp->t_outq,
209610015Speter				(char *)&ccbp->hi_txbuf[ipos], amount);
209710015Speter		} else {
209810015Speter			n = q_to_b(&tp->t_outq,
209910015Speter				(char *)&ccbp->hi_txbuf[ipos],
210012496Speter				SI_BUFFERSIZE-ipos);
210112496Speter			if (n == SI_BUFFERSIZE-ipos) {
210210015Speter				n += q_to_b(&tp->t_outq,
210310015Speter					(char *)&ccbp->hi_txbuf[0],
210412496Speter					amount - (SI_BUFFERSIZE-ipos));
210510015Speter			}
210610015Speter		}
210710015Speter		ccbp->hi_txipos += n;
210810015Speter		count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
210910015Speter	}
211010015Speter
211110015Speter	if (count != 0 && nchar == 0) {
211210015Speter		tp->t_state |= TS_BUSY;
211310015Speter	} else {
211410015Speter		tp->t_state &= ~TS_BUSY;
211510015Speter	}
211610015Speter
211710015Speter	/* wakeup time? */
211810015Speter	ttwwakeup(tp);
211910015Speter
212010015Speter	DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n",
212110015Speter		(BYTE)count, nchar, tp->t_state));
212210015Speter
212310015Speter	if ((tp->t_state & TS_BUSY) || do_exitproc)
212410015Speter	{
212510015Speter		int time;
212610015Speter
212710015Speter		if (do_exitproc != 0) {
212810015Speter			time = hz / 10;
212910015Speter		} else {
213010015Speter			time = ttspeedtab(tp->t_ospeed, chartimes);
213110015Speter
213210015Speter			if (time > 0) {
213310015Speter				if (time < nchar)
213410015Speter					time = nchar / time;
213510015Speter				else
213610015Speter					time = 2;
213710015Speter			} else {
213816024Speter				DPRINT((pp, DBG_START,
213916024Speter					"bad char time value! %d\n", time));
214016024Speter				time = hz/10;
214110015Speter			}
214210015Speter		}
214310015Speter
214410015Speter		if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) {
214510015Speter			untimeout((timeout_func_t)si_lstart, (caddr_t)pp);
214610015Speter		} else {
214710015Speter			pp->sp_state |= SS_LSTART;
214810015Speter		}
214910015Speter		DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time));
215010015Speter		timeout((timeout_func_t)si_lstart, (caddr_t)pp, time);
215110015Speter	}
215210015Speter
215310015Speterout:
215410015Speter	splx(oldspl);
215510015Speter	DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n"));
215610015Speter}
215710015Speter
215810015Speter/*
215910015Speter * Note: called at splsoftclock from the timeout code
216010015Speter * This has to deal with two things...  cause wakeups while waiting for
216110015Speter * tty drains on last process exit, and call l_start at about the right
216210015Speter * time for protocols like ppp.
216310015Speter */
216410015Speterstatic void
216510015Spetersi_lstart(pp)
216610015Speter	register struct si_port *pp;
216710015Speter{
216810015Speter	register struct tty *tp;
216910015Speter	int oldspl;
217010015Speter
217110015Speter	DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n",
217210015Speter		pp, pp->sp_state));
217310015Speter
217410015Speter	oldspl = spltty();
217510015Speter
217610015Speter	if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) {
217710015Speter		splx(oldspl);
217810015Speter		return;
217910015Speter	}
218010015Speter	pp->sp_state &= ~SS_LSTART;
218110015Speter	pp->sp_state |= SS_INLSTART;
218210015Speter
218310015Speter	tp = pp->sp_tty;
218410015Speter
218510015Speter	/* deal with the process exit case */
218610015Speter	ttwwakeup(tp);
218710015Speter
218812174Speter	/* nudge protocols - eg: ppp */
218910015Speter	(*linesw[tp->t_line].l_start)(tp);
219010015Speter
219110015Speter	pp->sp_state &= ~SS_INLSTART;
219210015Speter	splx(oldspl);
219310015Speter}
219410015Speter
219510015Speter/*
219610015Speter * Stop output on a line. called at spltty();
219710015Speter */
219810015Spetervoid
219910015Spetersistop(tp, rw)
220010015Speter	register struct tty *tp;
220110015Speter	int rw;
220210015Speter{
220310015Speter	volatile struct si_channel *ccbp;
220410015Speter	struct si_port *pp;
220510015Speter
220610015Speter	pp = TP2PP(tp);
220710015Speter	ccbp = pp->sp_ccb;
220810015Speter
220910015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "sistop(%x,%x)\n", tp, rw));
221010015Speter
221110015Speter	/* XXX: must check (rw & FWRITE | FREAD) etc flushing... */
221210015Speter	if (rw & FWRITE) {
221310015Speter		/* what level are we meant to be flushing anyway? */
221410015Speter		if (tp->t_state & TS_BUSY) {
221510015Speter			si_command(TP2PP(tp), WFLUSH, SI_NOWAIT);
221610015Speter			tp->t_state &= ~TS_BUSY;
221710015Speter			ttwwakeup(tp);	/* Bruce???? */
221810015Speter		}
221910015Speter	}
222012174Speter#if 1	/* XXX: this doesn't work right yet.. */
222112174Speter	/* XXX: this may have been failing because we used to call l_rint()
222212174Speter	 * while we were looping based on these two counters. Now, we collect
222312174Speter	 * the data and then loop stuffing it into l_rint(), making this
222412174Speter	 * useless.  Should we cause this to blow away the staging buffer?
222512174Speter	 */
222610015Speter	if (rw & FREAD) {
222710015Speter		ccbp->hi_rxopos = ccbp->hi_rxipos;
222810015Speter	}
222910015Speter#endif
223010015Speter}
223110015Speter
223210015Speter/*
223310015Speter * Issue a command to the Z280 host card CPU.
223410015Speter */
223510015Speter
223610015Speterstatic void
223710015Spetersi_command(pp, cmd, waitflag)
223810015Speter	struct si_port *pp;		/* port control block (local) */
223910015Speter	int cmd;
224010015Speter	int waitflag;
224110015Speter{
224210015Speter	int oldspl;
224310015Speter	volatile struct si_channel *ccbp = pp->sp_ccb;
224410015Speter	int x;
224510015Speter
224610015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n",
224710015Speter		pp, cmd, waitflag, ccbp->hi_stat));
224810015Speter
224910015Speter	oldspl = spltty();		/* Keep others out */
225010015Speter
225110015Speter	/* wait until it's finished what it was doing.. */
225216575Speter	/* XXX: sits in IDLE_BREAK until something disturbs it or break
225316575Speter	 * is turned off. */
225410015Speter	while((x = ccbp->hi_stat) != IDLE_OPEN &&
225510015Speter			x != IDLE_CLOSE &&
225616575Speter			x != IDLE_BREAK &&
225710015Speter			x != cmd) {
225810015Speter		if (in_intr) {			/* Prevent sleep in intr */
225910015Speter			DPRINT((pp, DBG_PARAM,
226010015Speter				"cmd intr collision - completing %d\trequested %d\n",
226110015Speter				x, cmd));
226210015Speter			splx(oldspl);
226310015Speter			return;
226410015Speter		} else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
226510015Speter				"sicmd1", 1)) {
226610015Speter			splx(oldspl);
226710015Speter			return;
226810015Speter		}
226910015Speter	}
227016575Speter	/* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */
227110015Speter
227210015Speter	/* if there was a pending command, cause a state-change wakeup */
227316575Speter	switch(pp->sp_pend) {
227416575Speter	case LOPEN:
227516575Speter	case MPEND:
227616575Speter	case MOPEN:
227716575Speter	case CONFIG:
227816575Speter	case SBREAK:
227916575Speter	case EBREAK:
228016575Speter		wakeup(&pp->sp_state);
228116575Speter		break;
228216575Speter	default:
228316575Speter		break;
228410015Speter	}
228510015Speter
228610015Speter	pp->sp_pend = cmd;		/* New command pending */
228710015Speter	ccbp->hi_stat = cmd;		/* Post it */
228810015Speter
228910015Speter	if (waitflag) {
229010015Speter		if (in_intr) {		/* If in interrupt handler */
229110015Speter			DPRINT((pp, DBG_PARAM,
229210015Speter				"attempt to sleep in si_intr - cmd req %d\n",
229310015Speter				cmd));
229410015Speter			splx(oldspl);
229510015Speter			return;
229616575Speter		} else while(ccbp->hi_stat != IDLE_OPEN &&
229716575Speter			     ccbp->hi_stat != IDLE_BREAK) {
229810015Speter			if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
229910015Speter			    "sicmd2", 0))
230010015Speter				break;
230110015Speter		}
230210015Speter	}
230310015Speter	splx(oldspl);
230410015Speter}
230510015Speter
230610015Speterstatic void
230710015Spetersi_disc_optim(tp, t, pp)
230810015Speter	struct tty	*tp;
230910015Speter	struct termios	*t;
231010015Speter	struct si_port	*pp;
231110015Speter{
231210015Speter	/*
231310015Speter	 * XXX can skip a lot more cases if Smarts.  Maybe
231410015Speter	 * (IGNCR | ISTRIP | IXON) in c_iflag.  But perhaps we
231510015Speter	 * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state.
231610015Speter	 */
231710015Speter	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
231810015Speter	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
231910015Speter	    && (!(t->c_iflag & PARMRK)
232010015Speter		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
232110015Speter	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
232210015Speter	    && linesw[tp->t_line].l_rint == ttyinput)
232310015Speter		tp->t_state |= TS_CAN_BYPASS_L_RINT;
232410015Speter	else
232510015Speter		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
232610161Speter
232710015Speter	/*
232810015Speter	 * Prepare to reduce input latency for packet
232910015Speter	 * discplines with a end of packet character.
233010015Speter	 */
233110015Speter	if (tp->t_line == SLIPDISC)
233210015Speter		pp->sp_hotchar = 0xc0;
233310015Speter	else if (tp->t_line == PPPDISC)
233410015Speter		pp->sp_hotchar = 0x7e;
233510015Speter	else
233610015Speter		pp->sp_hotchar = 0;
233710161Speter
233810161Speter	DPRINT((pp, DBG_OPTIM, "bypass: %s, hotchar: %x\n",
233910161Speter		(tp->t_state & TS_CAN_BYPASS_L_RINT) ? "on" : "off",
234010161Speter		pp->sp_hotchar));
234110015Speter}
234210015Speter
234310015Speter
234410015Speter#ifdef	SI_DEBUG
234513353Speter
234610015Speterstatic void
234713353Speter#ifdef __STDC__
234813353Spetersi_dprintf(struct si_port *pp, int flags, const char *fmt, ...)
234913353Speter#else
235013353Spetersi_dprintf(pp, flags, fmt, va_alist)
235110015Speter	struct si_port *pp;
235210015Speter	int flags;
235313353Speter	char *fmt;
235413353Speter#endif
235510015Speter{
235613353Speter	va_list ap;
235713630Sphk
235810015Speter	if ((pp == NULL && (si_debug&flags)) ||
235910015Speter	    (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) {
236010015Speter	    	if (pp != NULL)
236112496Speter	    		printf("%ci%d(%d): ", 's',
236212174Speter	    			(int)SI_CARD(pp->sp_tty->t_dev),
236312174Speter	    			(int)SI_PORT(pp->sp_tty->t_dev));
236413630Sphk		va_start(ap, fmt);
236513630Sphk		vprintf(fmt, ap);
236613353Speter		va_end(ap);
236710015Speter	}
236810015Speter}
236910015Speter
237010015Speterstatic char *
237110015Spetersi_mctl2str(cmd)
237210015Speter	enum si_mctl cmd;
237310015Speter{
237410015Speter	switch (cmd) {
237510015Speter	case GET:	return("GET");
237610015Speter	case SET:	return("SET");
237710015Speter	case BIS:	return("BIS");
237810015Speter	case BIC:	return("BIC");
237910015Speter	}
238010015Speter	return("BAD");
238110015Speter}
238212502Sjulian
238312624Speter#endif	/* DEBUG */
238412502Sjulian
238512624Speter
238612502Sjulian
238712502Sjulianstatic si_devsw_installed = 0;
238812502Sjulian
238912517Sjulianstatic void 	si_drvinit(void *unused)
239012502Sjulian{
239112517Sjulian	dev_t dev;
239212517Sjulian
239312502Sjulian	if( ! si_devsw_installed ) {
239412675Sjulian		dev = makedev(CDEV_MAJOR, 0);
239512675Sjulian		cdevsw_add(&dev,&si_cdevsw, NULL);
239612502Sjulian		si_devsw_installed = 1;
239712517Sjulian    	}
239812502Sjulian}
239912517Sjulian
240012517SjulianSYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,si_drvinit,NULL)
240112517Sjulian
2402