si.c revision 12724
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 *
3312724Sphk *	$Id: si.c,v 1.24 1995/12/08 23:20:41 phk Exp $
3410015Speter */
3510015Speter
3610015Speter#ifndef lint
3710015Speterstatic char si_copyright1[] =  "@(#) (C) Specialix International, 1990,1992",
3810015Speter            si_copyright2[] =  "@(#) (C) Andy Rutter 1993",
3910015Speter            si_copyright3[] =  "@(#) (C) Peter Wemm 1995";
4010015Speter#endif	/* not lint */
4110015Speter
4210015Speter#include <sys/param.h>
4310015Speter#include <sys/systm.h>
4410015Speter#include <sys/ioctl.h>
4510015Speter#include <sys/tty.h>
4610015Speter#include <sys/ttydefaults.h>
4710015Speter#include <sys/proc.h>
4810015Speter#include <sys/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>
5510015Speter#include <sys/devconf.h>
5612675Sjulian#ifdef DEVFS
5712675Sjulian#include <sys/devfsext.h>
5812675Sjulian#endif /*DEVFS*/
5910015Speter
6010015Speter#include <machine/clock.h>
6110015Speter
6212659Sbde#include <vm/vm.h>
6312662Sdg#include <vm/vm_param.h>
6412662Sdg#include <vm/pmap.h>
6512659Sbde
6610015Speter#include <i386/isa/icu.h>
6710015Speter#include <i386/isa/isa.h>
6810015Speter#include <i386/isa/isa_device.h>
6910015Speter
7010015Speter#include <i386/isa/sireg.h>
7110015Speter#include <machine/si.h>
7210015Speter
7310015Speter#include "si.h"
7410015Speter
7510015Speter/*
7610015Speter * This device driver is designed to interface the Specialix International
7712496Speter * range of serial multiplexor cards (SI/XIO) to BSDI/386 on an ISA bus machine.
7810015Speter *
7910015Speter * The controller is interfaced to the host via dual port ram
8010015Speter * and a (programmable - SIHOST2) interrupt at IRQ 11,12 or 15.
8110015Speter */
8210015Speter
8310015Speter#define	POLL		/* turn on poller to generate buffer empty interrupt */
8412174Speter#undef	FASTPOLL	/* turn on 100Hz poller, (XXX: NOTYET!) */
8510047Speter#define SI_DEF_HWFLOW	/* turn on default CRTSCTS flow control */
8612496Speter#define SI_I_HIGH_WATER	(TTYHOG - 2 * SI_BUFFERSIZE)
8710015Speter
8810015Speterenum si_mctl { GET, SET, BIS, BIC };
8910015Speter
9012675Sjulianstatic	const char devchar[] = "ABCDEFGHIJK";
9112675Sjulianstatic	const char portchar[] = "0123456789abcdefghijklmnopqrstuvwxyz";
9212502Sjulian
9312502Sjulian
9410015Speterstatic void si_command __P((struct si_port *, int, int));
9510015Speterstatic int si_modem __P((struct si_port *, enum si_mctl, int));
9610015Speterstatic void si_write_enable __P((struct si_port *, int));
9710015Speterstatic int si_Sioctl __P((dev_t, int, caddr_t, int, struct proc *));
9810015Speterstatic void si_start __P((struct tty *));
9910015Speterstatic void si_lstart __P((struct si_port *));
10010015Speterstatic void si_disc_optim __P((struct tty *tp, struct termios *t,
10110015Speter					struct si_port *pp));
10210015Speterstatic void sihardclose __P((struct si_port *pp));
10310015Speterstatic void sidtrwakeup __P((void *chan));
10410015Speter
10512724Sphkstatic int	siparam __P((struct tty *, struct termios *));
10610015Speter
10712724Sphkstatic	void	si_registerdev __P((struct isa_device *id));
10812724Sphkstatic	int	siprobe __P((struct isa_device *id));
10912724Sphkstatic	int	siattach __P((struct isa_device *id));
11010708Speterstatic	void	si_modem_state __P((struct si_port *pp, struct tty *tp, int hi_ip));
11110708Speter
11212675Sjulianstruct isa_driver sidriver =
11312675Sjulian	{ siprobe, siattach, "si" };
11412675Sjulian
11512675Sjulian
11612675Sjulianstatic	d_open_t	siopen;
11712675Sjulianstatic	d_close_t	siclose;
11812675Sjulianstatic	d_read_t	siread;
11912675Sjulianstatic	d_write_t	siwrite;
12012675Sjulianstatic	d_ioctl_t	siioctl;
12112675Sjulianstatic	d_stop_t	sistop;
12212675Sjulianstatic	d_ttycv_t	sidevtotty;
12312675Sjulian
12412675Sjulian#define CDEV_MAJOR 68
12512678Sphkstatic struct cdevsw si_cdevsw =
12612675Sjulian	{ siopen,	siclose,	siread,		siwrite,	/*68*/
12712675Sjulian	  siioctl,	sistop,		nxreset,	sidevtotty,/* si */
12812675Sjulian	  ttselect,	nxmmap,		NULL,	"si",	NULL,	-1 };
12912675Sjulian
13012675Sjulian
13112174Speter#ifdef SI_DEBUG		/* use: ``options "SI_DEBUG"'' in your config file */
13212174Speter/* XXX: should be varargs, I know.. but where's vprintf()? */
13312174Speterstatic	void	si_dprintf __P((/* struct si_port *pp, int flags, char *str, int a1, int a2, int a3, int a4, int a5, int a6 */));
13410708Speterstatic	char	*si_mctl2str __P((enum si_mctl cmd));
13510708Speter#define	DPRINT(x)	si_dprintf x
13610708Speter#else
13710708Speter#define	DPRINT(x)	/* void */
13810708Speter#endif
13910708Speter
14010962Speterstatic int si_Nports;
14110962Speterstatic int si_Nmodules;
14210962Speterstatic int si_debug = 0;	/* data, not bss, so it's patchable */
14310015Speter
14410962Speterstatic struct tty *si_tty;
14510962Speter
14612174Speter/* where the firmware lives; defined in si_code.c */
14710015Speterextern int si_dsize;
14810015Speterextern unsigned char si_download[];
14910015Speter
15010044Speterstruct si_softc {
15110044Speter	int 		sc_type;	/* adapter type */
15210044Speter	char 		*sc_typename;	/* adapter type string */
15310044Speter
15410044Speter	struct si_port	*sc_ports;	/* port structures for this card */
15510044Speter
15610044Speter	caddr_t		sc_paddr;	/* physical addr of iomem */
15710044Speter	caddr_t		sc_maddr;	/* kvaddr of iomem */
15810044Speter	int		sc_nport;	/* # ports on this card */
15910044Speter	int		sc_irq;		/* copy of attach irq */
16010044Speter	int		sc_eisa_iobase;	/* EISA io port address */
16110044Speter	int		sc_eisa_irqbits;
16210044Speter	struct kern_devconf sc_kdc;
16312675Sjulian#ifdef	DEVFS
16412675Sjulian	struct {
16512675Sjulian		void	*ttyd;
16612675Sjulian		void	*ttyl;
16712675Sjulian		void	*ttyi;
16812675Sjulian		void	*cuaa;
16912675Sjulian		void	*cual;
17012675Sjulian		void	*cuai;
17112675Sjulian	} devfs_token[32]; /* what is the max per card? */
17212675Sjulian#endif
17310044Speter};
17412724Sphkstatic struct si_softc si_softc[NSI];		/* up to 4 elements */
17510044Speter
17612174Speter#ifndef B2000	/* not standard, but the hardware knows it. */
17710015Speter# define B2000 2000
17810015Speter#endif
17910015Speterstatic struct speedtab bdrates[] = {
18010015Speter	B75,	CLK75,		/* 0x0 */
18110015Speter	B110,	CLK110,		/* 0x1 */
18210015Speter	B150,	CLK150,		/* 0x3 */
18310015Speter	B300,	CLK300,		/* 0x4 */
18410015Speter	B600,	CLK600,		/* 0x5 */
18510015Speter	B1200,	CLK1200,	/* 0x6 */
18610015Speter	B2000,	CLK2000,	/* 0x7 */
18710015Speter	B2400,	CLK2400,	/* 0x8 */
18810015Speter	B4800,	CLK4800,	/* 0x9 */
18910015Speter	B9600,	CLK9600,	/* 0xb */
19010015Speter	B19200,	CLK19200,	/* 0xc */
19110015Speter	B38400, CLK38400,	/* 0x2 (out of order!) */
19210015Speter	B57600, CLK57600,	/* 0xd */
19310015Speter	B115200, CLK110,	/* 0x1 (dupe!, 110 baud on "si") */
19410015Speter	-1,	-1
19510015Speter};
19610015Speter
19710015Speter
19810015Speter/* populated with approx character/sec rates - translated at card
19910015Speter * initialisation time to chars per tick of the clock */
20010015Speterstatic int done_chartimes = 0;
20110015Speterstatic struct speedtab chartimes[] = {
20210015Speter	B75,	8,
20310015Speter	B110,	11,
20410015Speter	B150,	15,
20510015Speter	B300,	30,
20610015Speter	B600,	60,
20710015Speter	B1200,	120,
20810015Speter	B2000,	200,
20910015Speter	B2400,	240,
21010015Speter	B4800,	480,
21110015Speter	B9600,	960,
21210015Speter	B19200,	1920,
21310015Speter	B38400, 3840,
21410015Speter	B57600, 5760,
21510015Speter	B115200, 11520,
21610015Speter	-1,	-1
21710015Speter};
21810015Speterstatic volatile int in_intr = 0;	/* Inside interrupt handler? */
21910015Speter
22010047Speterstatic int si_default_rate =	TTYDEF_SPEED;
22110047Speterstatic int si_default_iflag =	0;
22210047Speterstatic int si_default_oflag =	0;
22310047Speterstatic int si_default_lflag =	0;
22410047Speter#ifdef SI_DEF_HWFLOW
22510047Speterstatic int si_default_cflag =	TTYDEF_CFLAG | CRTSCTS;
22610047Speter#else
22710047Speterstatic int si_default_cflag =	TTYDEF_CFLAG;
22810047Speter#endif
22910047Speter
23010015Speter#ifdef POLL
23110015Speter#define	POLL_INTERVAL	(hz/2)
23210015Speterstatic int init_finished = 0;
23312174Speterstatic int fastpoll = 0;
23410015Speterstatic void si_poll __P((void *));
23510015Speter#endif
23610015Speter
23710015Speter/*
23810015Speter * Array of adapter types and the corresponding RAM size. The order of
23910015Speter * entries here MUST match the ordinal of the adapter type.
24010015Speter */
24110015Speterstatic char *si_type[] = {
24210015Speter	"EMPTY",
24310015Speter	"SIHOST",
24410015Speter	"SI2",				/* MCA */
24510015Speter	"SIHOST2",
24610015Speter	"SIEISA",
24710015Speter};
24810015Speter
24910015Speter
25010015Speterstatic struct kern_devconf si_kdc[NSI] = { {
25110015Speter	0, 0, 0,		/* filled in by dev_attach */
25210015Speter	"si", 0, { MDDT_ISA, 0, "tty" },
25310015Speter	isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
25410015Speter	&kdc_isa0,		/* parent */
25510015Speter	0,			/* parent data */
25610015Speter	DC_UNCONFIGURED,	/* state */
25710015Speter	"Specialix SI/XIO Host adapter",
25810015Speter	DC_CLS_SERIAL,		/* class */
25910015Speter} };
26010015Speter
26112724Sphkstatic void
26210015Spetersi_registerdev(id)
26310015Speter	struct isa_device *id;
26410015Speter{
26510015Speter	if (id->id_unit != 0) {
26610015Speter		si_kdc[id->id_unit] = si_kdc[0];	/* struct copy */
26710015Speter	}
26810015Speter	si_kdc[id->id_unit].kdc_unit = id->id_unit;
26910015Speter	si_kdc[id->id_unit].kdc_isa = id;
27012174Speter	si_kdc[id->id_unit].kdc_state = DC_UNCONFIGURED;
27110015Speter	dev_attach(&si_kdc[id->id_unit]);
27210015Speter}
27310015Speter
27410015Speter/* Look for a valid board at the given mem addr */
27512724Sphkstatic int
27610015Spetersiprobe(id)
27710015Speter	struct isa_device *id;
27810015Speter{
27910015Speter	struct si_softc *sc;
28010015Speter	int type;
28110015Speter	u_int i, ramsize;
28210015Speter	volatile BYTE was, *ux;
28310015Speter	volatile unsigned char *maddr;
28410015Speter	unsigned char *paddr;
28510015Speter
28610015Speter	si_registerdev(id);
28710015Speter
28810015Speter	maddr = id->id_maddr;		/* virtual address... */
28910015Speter	paddr = (caddr_t)vtophys(id->id_maddr);	/* physical address... */
29010015Speter
29112496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: probe at virtual=0x%x physical=0x%x\n",
29212496Speter		id->id_unit, id->id_maddr, paddr));
29310015Speter
29410015Speter	/*
29510015Speter	 * this is a lie, but it's easier than trying to handle caching
29610015Speter	 * and ram conflicts in the >1M and <16M region.
29710015Speter	 */
29810015Speter	if ((caddr_t)paddr < (caddr_t)IOM_BEGIN ||
29910015Speter	    (caddr_t)paddr >= (caddr_t)IOM_END) {
30012174Speter		printf("si%d: iomem (%lx) out of range\n",
30112174Speter			id->id_unit, (long)paddr);
30210015Speter		return(0);
30310015Speter	}
30410015Speter
30510015Speter	if (id->id_unit >= NSI) {
30610015Speter		/* THIS IS IMPOSSIBLE */
30710015Speter		return(0);
30810015Speter	}
30910015Speter
31010015Speter	if (((u_int)paddr & 0x7fff) != 0) {
31110015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
31210015Speter			"si%d: iomem (%x) not on 32k boundary\n",
31310015Speter			id->id_unit, paddr));
31410015Speter		return(0);
31510015Speter	}
31610015Speter
31710015Speter
31810015Speter	for (i=0; i < NSI; i++) {
31910015Speter		if ((sc = &si_softc[i]) == NULL)
32010015Speter			continue;
32110015Speter		if ((caddr_t)sc->sc_paddr == (caddr_t)paddr) {
32210015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
32310015Speter				"si%d: iomem (%x) already configured to si%d\n",
32410015Speter				id->id_unit, sc->sc_paddr, i));
32510015Speter			return(0);
32610015Speter		}
32710015Speter	}
32810015Speter
32910015Speter#if NEISA > 0
33010015Speter	if (id->id_iobase > 0x0fff) {	/* EISA card */
33110015Speter		int irq, port;
33210015Speter		unsigned long base;
33310015Speter		int eisa_irqs[] = { 0,IRQ1,IRQ2,IRQ3,IRQ4,IRQ5,IRQ6,IRQ7,
33410015Speter			IRQ8,IRQ9,IRQ10,IRQ11,IRQ12,IRQ13,IRQ14,IRQ15 };
33510015Speter
33610015Speter		port = id->id_iobase;
33710015Speter		base = (inb(port+1) << 24) | (inb(port) << 16);
33810015Speter		irq  = ((inb(port+2) >> 4) & 0xf);
33910015Speter
34010015Speter		id->id_irq = eisa_irqs[irq];
34110015Speter
34210015Speter		DPRINT((0, DBG_AUTOBOOT,
34312496Speter		    "si%d: EISA base %x, irq %x, id_irq %x, port %x\n",
34410015Speter		    id->id_unit, base, irq, id->id_irq, port));
34510015Speter
34610015Speter		if ((id->id_irq&(IRQ1|IRQ2|IRQ8|IRQ13)) != 0)
34710015Speter			goto bad_irq;
34810015Speter
34910015Speter		id->id_iobase &= 0xf000;
35010015Speter		id->id_iosize  = 0x0fff;
35110015Speter
35210015Speter		type = EISA;
35310015Speter		outb(p+2, (BYTE)irq << 4);
35410015Speter
35510015Speter		sc->sc_eisa_iobase = p;
35610015Speter		sc->sc_eisa_irqbits = irq << 4;
35710015Speter		ramsize = SIEISA_RAMSIZE;
35810015Speter		goto got_card;
35910015Speter	}
36010015Speter#endif
36110015Speter
36210015Speter	/* Is there anything out there? (0x17 is just an arbitrary number) */
36310015Speter	*maddr = 0x17;
36410015Speter	if (*maddr != 0x17) {
36510015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
36610015Speter			"si%d: 0x17 check fail at phys 0x%x\n",
36710015Speter			id->id_unit, paddr));
36810015Speterfail:
36910015Speter		return(0);
37010015Speter	}
37110015Speter	/*
37210015Speter	 * OK, now to see if whatever responded is really an SI card.
37310015Speter	 * Try for a MK II first (SIHOST2)
37410015Speter	 */
37510015Speter	for (i=SIPLSIG; i<SIPLSIG+8; i++)
37610015Speter		if ((*(maddr+i) & 7) != (~(BYTE)i & 7))
37710015Speter			goto try_mk1;
37810015Speter
37910015Speter	/* It must be an SIHOST2 */
38010015Speter	*(maddr + SIPLRESET) = 0;
38110015Speter	*(maddr + SIPLIRQCLR) = 0;
38210015Speter	*(maddr + SIPLIRQSET) = 0x10;
38310015Speter	type = SIHOST2;
38410015Speter	ramsize = SIHOST2_RAMSIZE;
38510015Speter	goto got_card;
38610015Speter
38710015Speter	/*
38810015Speter	 * Its not a MK II, so try for a MK I (SIHOST)
38910015Speter	 */
39010015Spetertry_mk1:
39110015Speter	*(maddr+SIRESET) = 0x0;		/* reset the card */
39210015Speter	*(maddr+SIINTCL) = 0x0;		/* clear int */
39310015Speter	*(maddr+SIRAM) = 0x17;
39410015Speter	if (*(maddr+SIRAM) != (BYTE)0x17)
39510015Speter		goto fail;
39610015Speter	*(maddr+0x7ff8) = 0x17;
39710015Speter	if (*(maddr+0x7ff8) != (BYTE)0x17) {
39810015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
39910015Speter			"si%d: 0x17 check fail at phys 0x%x = 0x%x\n",
40010015Speter			id->id_unit, paddr+0x77f8, *(maddr+0x77f8)));
40110015Speter		goto fail;
40210015Speter	}
40310015Speter
40410015Speter	/* It must be an SIHOST (maybe?) - there must be a better way XXXX */
40510015Speter	type = SIHOST;
40610015Speter	ramsize = SIHOST_RAMSIZE;
40710015Speter
40810015Spetergot_card:
40912496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: found type %d card, try memory test\n",
41012496Speter		id->id_unit, type));
41110015Speter	/* Try the acid test */
41210015Speter	ux = (BYTE *)(maddr + SIRAM);
41310015Speter	for (i=0; i<ramsize; i++, ux++)
41410015Speter		*ux = (BYTE)(i&0xff);
41510015Speter	ux = (BYTE *)(maddr + SIRAM);
41610015Speter	for (i=0; i<ramsize; i++, ux++) {
41710015Speter		if ((was = *ux) != (BYTE)(i&0xff)) {
41810015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
41912174Speter				"si%d: match fail at phys 0x%x, was %x should be %x\n",
42010015Speter				id->id_unit, paddr+i, was, i&0xff));
42110015Speter			goto fail;
42210015Speter		}
42310015Speter	}
42410015Speter
42510015Speter	/* clear out the RAM */
42610015Speter	ux = (BYTE *)(maddr + SIRAM);
42710015Speter	for (i=0; i<ramsize; i++)
42810015Speter		*ux++ = 0;
42910015Speter	ux = (BYTE *)(maddr + SIRAM);
43010015Speter	for (i=0; i<ramsize; i++) {
43110015Speter		if ((was = *ux++) != 0) {
43210015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
43312174Speter				"si%d: clear fail at phys 0x%x, was %x\n",
43410015Speter				id->id_unit, paddr+i, was));
43510015Speter			goto fail;
43610015Speter		}
43710015Speter	}
43810015Speter
43910015Speter	/*
44010015Speter	 * Success, we've found a valid board, now fill in
44110015Speter	 * the adapter structure.
44210015Speter	 */
44310015Speter	switch (type) {
44410015Speter	case SIHOST2:
44510015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
44610015Speterbad_irq:
44710015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
44810015Speter				"si%d: bad IRQ value - %d\n",
44910015Speter				id->id_unit, id->id_irq));
45010015Speter			return(0);
45110015Speter		}
45210015Speter		id->id_msize = SIHOST2_MEMSIZE;
45310015Speter		break;
45410015Speter	case SIHOST:
45510015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
45610015Speter			goto bad_irq;
45710015Speter		}
45810015Speter		id->id_msize = SIHOST_MEMSIZE;
45910015Speter		break;
46010015Speter	case SIEISA:
46110015Speter		id->id_msize = SIEISA_MEMSIZE;
46210015Speter		break;
46310015Speter	case SI2:		/* MCA */
46410015Speter	default:
46510015Speter		printf("si%d: %s not supported\n", id->id_unit, si_type[type]);
46610015Speter		return(0);
46710015Speter	}
46810015Speter	si_softc[id->id_unit].sc_type = type;
46910015Speter	si_softc[id->id_unit].sc_typename = si_type[type];
47010015Speter	return(-1);	/* -1 == found */
47110015Speter}
47210015Speter
47310015Speter/*
47410015Speter * Attach the device.  Initialize the card.
47510015Speter */
47612724Sphkstatic int
47710015Spetersiattach(id)
47810015Speter	struct isa_device *id;
47910015Speter{
48010015Speter	int unit = id->id_unit;
48110015Speter	struct si_softc *sc = &si_softc[unit];
48210015Speter	struct si_port *pp;
48310015Speter	volatile struct si_channel *ccbp;
48410015Speter	volatile struct si_reg *regp;
48510015Speter	volatile caddr_t maddr;
48610015Speter	struct si_module *modp;
48710015Speter	struct tty *tp;
48810015Speter	struct speedtab *spt;
48910015Speter	int nmodule, nport, x, y;
49012174Speter	int uart_type;
49112675Sjulian	char	name[32];
49210015Speter
49312496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", id->id_unit));
49410015Speter
49510015Speter	sc->sc_paddr = (caddr_t)vtophys(id->id_maddr);
49610015Speter	sc->sc_maddr = id->id_maddr;
49710015Speter	sc->sc_irq = id->id_irq;
49810015Speter
49910015Speter	sc->sc_ports = NULL;			/* mark as uninitialised */
50010015Speter
50110015Speter	maddr = sc->sc_maddr;
50210015Speter
50310015Speter	/*
50410015Speter	 * OK, now lets download the firmware and try and boot the CPU..
50510015Speter	 */
50610015Speter
50712496Speter	DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n",
50812496Speter		id->id_unit, si_dsize));
50910015Speter	bcopy(si_download, maddr, si_dsize);
51010015Speter
51110015Speter	switch (sc->sc_type) {
51210015Speter	case SIEISA:
51310015Speter#if NEISA > 0
51410015Speter		/* modify the Z280 firmware to tell it that it's on an EISA */
51510015Speter		*(maddr+0x42) = 1;
51610015Speter		outb(sc->sc_eisa_iobase+2, sc->sc_eisa_irqbits | 4);
51710015Speter		(void)inb(sc->sc_eisa_iobase+3); /* reset interrupt */
51810015Speter		break;
51910015Speter#endif	/* fall-through if not EISA */
52010015Speter	case SI2:
52112174Speter		/*
52212174Speter		 * must get around to converting the code for
52312174Speter		 * these one day, if FreeBSD ever supports it.
52412174Speter		 */
52510015Speter		return 0;
52610015Speter	case SIHOST:
52710015Speter		*(maddr+SIRESET_CL) = 0;
52810015Speter		*(maddr+SIINTCL_CL) = 0;
52910015Speter		break;
53010015Speter	case SIHOST2:
53110015Speter		*(maddr+SIPLRESET) = 0x10;
53210015Speter		switch (sc->sc_irq) {
53310015Speter		case IRQ11:
53410015Speter			*(maddr+SIPLIRQ11) = 0x10;
53510015Speter			break;
53610015Speter		case IRQ12:
53710015Speter			*(maddr+SIPLIRQ12) = 0x10;
53810015Speter			break;
53910015Speter		case IRQ15:
54010015Speter			*(maddr+SIPLIRQ15) = 0x10;
54110015Speter			break;
54210015Speter		}
54310015Speter		*(maddr+SIPLIRQCLR) = 0x10;
54410015Speter		break;
54510015Speter	}
54610015Speter
54710015Speter	DELAY(1000000);			/* wait around for a second */
54810015Speter
54910015Speter	regp = (struct si_reg *)maddr;
55010015Speter	y = 0;
55110015Speter					/* wait max of 5 sec for init OK */
55210015Speter	while (regp->initstat == 0 && y++ < 10) {
55310015Speter		DELAY(500000);
55410015Speter	}
55510015Speter	switch (regp->initstat) {
55610015Speter	case 0:
55710015Speter		printf("si%d: startup timeout - aborting\n", unit);
55812174Speter		sc->sc_type = SIEMPTY;
55910015Speter		return 0;
56010015Speter	case 1:
56112174Speter			/* set throttle to 125 intr per second */
56210015Speter		regp->int_count = 25000;
56310015Speter			/* rx intr max of 25 timer per second */
56410015Speter		regp->rx_int_count = 4;
56510015Speter		regp->int_pending = 0;		/* no intr pending */
56610015Speter		regp->int_scounter = 0;	/* reset counter */
56710015Speter		break;
56810015Speter	case 0xff:
56910015Speter		/*
57010015Speter		 * No modules found, so give up on this one.
57110015Speter		 */
57210015Speter		printf("si%d: %s - no ports found\n", unit,
57310015Speter			si_type[sc->sc_type]);
57410015Speter		return 0;
57510015Speter	default:
57610015Speter		printf("si%d: Z280 version error - initstat %x\n",
57710015Speter			unit, regp->initstat);
57810015Speter		return 0;
57910015Speter	}
58010015Speter
58110015Speter	/*
58210015Speter	 * First time around the ports just count them in order
58310015Speter	 * to allocate some memory.
58410015Speter	 */
58510015Speter	nport = 0;
58610015Speter	modp = (struct si_module *)(maddr + 0x80);
58710015Speter	for (;;) {
58812174Speter		DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp));
58910015Speter		switch (modp->sm_type & (~MMASK)) {
59010015Speter		case M232:
59110015Speter		case M422:
59210015Speter			DPRINT((0, DBG_DOWNLOAD,
59312174Speter				"si%d: Found 232/422 module, %d ports\n",
59410015Speter				unit, (int)(modp->sm_type & MMASK)));
59510015Speter
59610015Speter			/* this is a firmware issue */
59710015Speter			if (si_Nports == SI_MAXPORTPERCARD) {
59810015Speter				printf("si%d: extra ports ignored\n", unit);
59910015Speter				continue;
60010015Speter			}
60110015Speter
60210015Speter			x = modp->sm_type & MMASK;
60310015Speter			nport += x;
60410015Speter			si_Nports += x;
60510015Speter			si_Nmodules++;
60610015Speter			break;
60710015Speter		default:
60810015Speter			printf("si%d: unknown module type %d\n",
60910015Speter				unit, modp->sm_type);
61010015Speter			break;
61110015Speter		}
61210015Speter		if (modp->sm_next == 0)
61310015Speter			break;
61410015Speter		modp = (struct si_module *)
61510015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
61610015Speter	}
61710015Speter	sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport,
61810015Speter		M_DEVBUF, M_NOWAIT);
61910015Speter	if (sc->sc_ports == 0) {
62010015Spetermem_fail:
62110015Speter		printf("si%d: fail to malloc memory for port structs\n",
62210015Speter			unit);
62310015Speter		return 0;
62410015Speter	}
62510015Speter	bzero(sc->sc_ports, sizeof(struct si_port) * nport);
62610015Speter	sc->sc_nport = nport;
62710015Speter
62810015Speter	/*
62910015Speter	 * allocate tty structures for ports
63010015Speter	 */
63110015Speter	tp = (struct tty *)malloc(sizeof(*tp) * nport, M_DEVBUF, M_NOWAIT);
63210015Speter	if (tp == 0)
63310015Speter		goto mem_fail;
63410015Speter	bzero(tp, sizeof(*tp) * nport);
63510962Speter	si_tty = tp;
63610015Speter
63710015Speter	/* mark the device state as attached */
63810015Speter	si_kdc[unit].kdc_state = DC_BUSY;
63910015Speter
64010015Speter	/*
64110015Speter	 * Scan round the ports again, this time initialising.
64210015Speter	 */
64310015Speter	pp = sc->sc_ports;
64410015Speter	nmodule = 0;
64510015Speter	modp = (struct si_module *)(maddr + 0x80);
64612174Speter	uart_type = 0;
64710015Speter	for (;;) {
64810015Speter		switch (modp->sm_type & (~MMASK)) {
64910015Speter		case M232:
65010015Speter		case M422:
65110015Speter			nmodule++;
65210015Speter			nport = (modp->sm_type & MMASK);
65310015Speter			ccbp = (struct si_channel *)((char *)modp+0x100);
65412174Speter			if (uart_type == 0)
65512174Speter				uart_type = ccbp->type;
65610015Speter			for (x = 0; x < nport; x++, pp++, ccbp++) {
65710015Speter				pp->sp_ccb = ccbp;	/* save the address */
65810015Speter				pp->sp_tty = tp++;
65910015Speter				pp->sp_pend = IDLE_CLOSE;
66010015Speter				pp->sp_state = 0;	/* internal flag */
66110015Speter				pp->sp_dtr_wait = 3 * hz;
66210047Speter				pp->sp_iin.c_iflag = si_default_iflag;
66310047Speter				pp->sp_iin.c_oflag = si_default_oflag;
66410047Speter				pp->sp_iin.c_cflag = si_default_cflag;
66510047Speter				pp->sp_iin.c_lflag = si_default_lflag;
66610015Speter				termioschars(&pp->sp_iin);
66710015Speter				pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed =
66810047Speter					si_default_rate;
66910015Speter				pp->sp_iout = pp->sp_iin;
67010015Speter			}
67110015Speter			break;
67210015Speter		default:
67310015Speter			break;
67410015Speter		}
67510015Speter		if (modp->sm_next == 0) {
67612174Speter			printf("si%d: card: %s, ports: %d, modules: %d (type: %d)\n",
67710015Speter				unit,
67810015Speter				sc->sc_typename,
67910015Speter				sc->sc_nport,
68012174Speter				nmodule,
68112174Speter				uart_type);
68210015Speter			break;
68310015Speter		}
68410015Speter		modp = (struct si_module *)
68510015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
68610015Speter	}
68710015Speter	if (done_chartimes == 0) {
68810015Speter		for (spt = chartimes ; spt->sp_speed != -1; spt++) {
68910015Speter			if ((spt->sp_code /= hz) == 0)
69010015Speter				spt->sp_code = 1;
69110015Speter		}
69210015Speter		done_chartimes = 1;
69310015Speter	}
69412502Sjulian
69512675Sjulian#ifdef DEVFS
69612675Sjulian/*	path	name	devsw		minor	type   uid gid perm*/
69712675Sjulian	for ( x = 0; x < nport; x++ ) {
69812675Sjulian		sprintf(name,"tty%c%c",devchar[unit],portchar[x + 1]);
69912675Sjulian		sc->devfs_token[x].ttyd = devfs_add_devsw(
70012675Sjulian			"/", name, &si_cdevsw, unit,
70112675Sjulian			DV_CHR, 0, 0, 0600);
70212675Sjulian		sprintf(name,"ttyi%c%c",devchar[unit],portchar[x + 1]);
70312675Sjulian		sc->devfs_token[x].ttyi = devfs_add_devsw(
70412675Sjulian			"/", name, &si_cdevsw, unit + 32,
70512675Sjulian			DV_CHR, 0, 0, 0600);
70612675Sjulian		sprintf(name,"ttyl%c%c",devchar[unit],portchar[x + 1]);
70712675Sjulian		sc->devfs_token[x].ttyl = devfs_add_devsw(
70812675Sjulian			"/", name, &si_cdevsw, unit + 64,
70912675Sjulian			DV_CHR, 0, 0, 0600);
71012675Sjulian		sprintf(name,"cua%c%c",devchar[unit],portchar[x + 1]);
71112675Sjulian		sc->devfs_token[x].cuaa = devfs_add_devsw(
71212675Sjulian			"/", name, &si_cdevsw, unit + 128,
71312675Sjulian			DV_CHR, 0, 0, 0600);
71412675Sjulian		sprintf(name,"cuai%c%c",devchar[unit],portchar[x + 1]);
71512675Sjulian		sc->devfs_token[x].cuai = devfs_add_devsw(
71612675Sjulian			"/", name, &si_cdevsw, unit + 160,
71712675Sjulian			DV_CHR, 0, 0, 0600);
71812675Sjulian		sprintf(name,"cual%c%c",devchar[unit],portchar[x + 1]);
71912675Sjulian		sc->devfs_token[x].cual = devfs_add_devsw(
72012675Sjulian			"/", name, &si_cdevsw, unit + 192,
72112675Sjulian			DV_CHR, 0, 0, 0600);
72212675Sjulian	}
72312675Sjulian#endif
72410015Speter	return (1);
72510015Speter}
72610015Speter
72712675Sjulianstatic	int
72810015Spetersiopen(dev, flag, mode, p)
72910015Speter	dev_t dev;
73010015Speter	int flag, mode;
73110015Speter	struct proc *p;
73210015Speter{
73310015Speter	int oldspl, error;
73410015Speter	int card, port;
73510015Speter	register struct si_softc *sc;
73610015Speter	register struct tty *tp;
73710015Speter	volatile struct si_channel *ccbp;
73810015Speter	struct si_port *pp;
73910015Speter	int mynor = minor(dev);
74010015Speter
74110015Speter	/* quickly let in /dev/si_control */
74210015Speter	if (IS_CONTROLDEV(mynor)) {
74310015Speter		if (error = suser(p->p_ucred, &p->p_acflag))
74410015Speter			return(error);
74510015Speter		return(0);
74610015Speter	}
74710015Speter
74810015Speter	card = SI_CARD(mynor);
74910015Speter	if (card >= NSI)
75010015Speter		return (ENXIO);
75110015Speter	sc = &si_softc[card];
75210015Speter
75312174Speter	if (sc->sc_type == SIEMPTY) {
75412174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n",
75510015Speter			card, sc->sc_typename));
75610015Speter		return(ENXIO);
75710015Speter	}
75810015Speter
75910015Speter	port = SI_PORT(mynor);
76010015Speter	if (port >= sc->sc_nport) {
76112174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n",
76210015Speter			card, sc->sc_nport));
76310015Speter		return(ENXIO);
76410015Speter	}
76510015Speter
76610015Speter#ifdef	POLL
76710015Speter	/*
76810015Speter	 * We've now got a device, so start the poller.
76910015Speter	 */
77010015Speter	if (init_finished == 0) {
77110015Speter		timeout(si_poll, (caddr_t)0L, POLL_INTERVAL);
77210015Speter		init_finished = 1;
77310015Speter	}
77410015Speter#endif
77510015Speter
77610015Speter	/* initial/lock device */
77710015Speter	if (IS_STATE(mynor)) {
77810015Speter		return(0);
77910015Speter	}
78010015Speter
78110015Speter	pp = sc->sc_ports + port;
78210015Speter	tp = pp->sp_tty;			/* the "real" tty */
78310015Speter	ccbp = pp->sp_ccb;			/* Find control block */
78410015Speter	DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%x,%x,%x,%x)\n",
78510015Speter		dev, flag, mode, p));
78610015Speter
78710015Speter	oldspl = spltty();			/* Keep others out */
78810015Speter	error = 0;
78910015Speter
79010015Speteropen_top:
79110015Speter	while (pp->sp_state & SS_DTR_OFF) {
79210015Speter		error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sidtr", 0);
79310015Speter		if (error != 0)
79410015Speter			goto out;
79510015Speter	}
79610015Speter
79710015Speter	if (tp->t_state & TS_ISOPEN) {
79810015Speter		/*
79910015Speter		 * The device is open, so everything has been initialised.
80010015Speter		 * handle conflicts.
80110015Speter		 */
80210015Speter		if (IS_CALLOUT(mynor)) {
80310015Speter			if (!pp->sp_active_out) {
80410015Speter				error = EBUSY;
80510015Speter				goto out;
80610015Speter			}
80710015Speter		} else {
80810015Speter			if (pp->sp_active_out) {
80910015Speter				if (flag & O_NONBLOCK) {
81010015Speter					error = EBUSY;
81110015Speter					goto out;
81210015Speter				}
81310015Speter				error = tsleep(&pp->sp_active_out,
81410015Speter						TTIPRI|PCATCH, "sibi", 0);
81510015Speter				if (error != 0)
81610015Speter					goto out;
81710015Speter				goto open_top;
81810015Speter			}
81910015Speter		}
82010015Speter		if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
82110015Speter			DPRINT((pp, DBG_OPEN|DBG_FAIL,
82210015Speter				"already open and EXCLUSIVE set\n"));
82310015Speter			error = EBUSY;
82410015Speter			goto out;
82510015Speter		}
82610015Speter	} else {
82710015Speter		/*
82810015Speter		 * The device isn't open, so there are no conflicts.
82910015Speter		 * Initialize it. Avoid sleep... :-)
83010015Speter		 */
83110015Speter		DPRINT((pp, DBG_OPEN, "first open\n"));
83210015Speter		tp->t_oproc = si_start;
83310015Speter		tp->t_param = siparam;
83410015Speter		tp->t_dev = dev;
83510015Speter		tp->t_termios = mynor & SI_CALLOUT_MASK
83610015Speter				? pp->sp_iout : pp->sp_iin;
83710015Speter
83810015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
83910015Speter
84010015Speter		++pp->sp_wopeners;	/* in case of sleep in siparam */
84110015Speter
84210015Speter		error = siparam(tp, &tp->t_termios);
84310015Speter
84410015Speter		--pp->sp_wopeners;
84510015Speter		if (error != 0)
84610015Speter			goto out;
84710015Speter		/* XXX: we should goto_top if siparam slept */
84810015Speter
84910015Speter		ttsetwater(tp);
85010015Speter
85110015Speter		/* set initial DCD state */
85210015Speter		pp->sp_last_hi_ip = ccbp->hi_ip;
85310015Speter		if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) {
85410015Speter			(*linesw[tp->t_line].l_modem)(tp, 1);
85510015Speter		}
85610015Speter	}
85710015Speter
85810015Speter	/* whoops! we beat the close! */
85910015Speter	if (pp->sp_state & SS_CLOSING) {
86010015Speter		/* try and stop it from proceeding to bash the hardware */
86110015Speter		pp->sp_state &= ~SS_CLOSING;
86210015Speter	}
86310015Speter
86410015Speter	/*
86510015Speter	 * Wait for DCD if necessary
86610015Speter	 */
86710015Speter	if (!(tp->t_state & TS_CARR_ON)
86810015Speter	    && !IS_CALLOUT(mynor)
86910015Speter	    && !(tp->t_cflag & CLOCAL)
87010015Speter	    && !(flag & O_NONBLOCK)) {
87110015Speter		++pp->sp_wopeners;
87210015Speter		DPRINT((pp, DBG_OPEN, "sleeping for carrier\n"));
87310015Speter		error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0);
87410015Speter		--pp->sp_wopeners;
87510015Speter		if (error != 0)
87610015Speter			goto out;
87710015Speter		goto open_top;
87810015Speter	}
87910015Speter
88010015Speter	error = (*linesw[tp->t_line].l_open)(dev, tp);
88110015Speter	si_disc_optim(tp, &tp->t_termios, pp);
88210015Speter	if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor))
88310015Speter		pp->sp_active_out = TRUE;
88410015Speter
88510015Speter	pp->sp_state |= SS_OPEN;	/* made it! */
88610015Speter
88710015Speterout:
88810015Speter	splx(oldspl);
88910015Speter
89010015Speter	DPRINT((pp, DBG_OPEN, "leaving siopen\n"));
89110015Speter
89210015Speter	if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0)
89310015Speter		sihardclose(pp);
89410015Speter
89510015Speter	return(error);
89610015Speter}
89710015Speter
89812675Sjulianstatic	int
89910015Spetersiclose(dev, flag, mode, p)
90010015Speter	dev_t dev;
90110015Speter	int flag, mode;
90210015Speter	struct proc *p;
90310015Speter{
90410015Speter	register struct si_port *pp;
90510015Speter	register struct tty *tp;
90610015Speter	int oldspl;
90710015Speter	int error = 0;
90810015Speter	int mynor = minor(dev);
90910015Speter
91010015Speter	if (IS_SPECIAL(mynor))
91110015Speter		return(0);
91210015Speter
91310015Speter	oldspl = spltty();
91410015Speter
91510015Speter	pp = MINOR2PP(mynor);
91610015Speter	tp = pp->sp_tty;
91710015Speter
91810015Speter	DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%x,%x,%x,%x) sp_state:%x\n",
91910015Speter		dev, flag, mode, p, pp->sp_state));
92010015Speter
92110015Speter	/* did we sleep and loose a race? */
92210015Speter	if (pp->sp_state & SS_CLOSING) {
92310015Speter		/* error = ESOMETING? */
92410015Speter		goto out;
92510015Speter	}
92610015Speter
92710015Speter	/* begin race detection.. */
92810015Speter	pp->sp_state |= SS_CLOSING;
92910015Speter
93010015Speter	si_write_enable(pp, 0);		/* block writes for ttywait() */
93110015Speter
93210015Speter	/* THIS MAY SLEEP IN TTYWAIT!!! */
93310015Speter	(*linesw[tp->t_line].l_close)(tp, flag);
93410015Speter
93510015Speter	si_write_enable(pp, 1);
93610015Speter
93710015Speter	/* did we sleep and somebody started another open? */
93810015Speter	if (!(pp->sp_state & SS_CLOSING)) {
93910015Speter		/* error = ESOMETING? */
94010015Speter		goto out;
94110015Speter	}
94210015Speter	/* ok. we are now still on the right track.. nuke the hardware */
94310015Speter
94410015Speter	if (pp->sp_state & SS_LSTART) {
94510015Speter		untimeout((timeout_func_t)si_lstart, (caddr_t)pp);
94610015Speter		pp->sp_state &= ~SS_LSTART;
94710015Speter	}
94810015Speter
94910015Speter	sistop(tp, FREAD | FWRITE);
95010015Speter
95110015Speter	sihardclose(pp);
95210015Speter	ttyclose(tp);
95310015Speter	pp->sp_state &= ~SS_OPEN;
95410015Speter
95510015Speterout:
95610015Speter	DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n"));
95710015Speter	splx(oldspl);
95810015Speter	return(error);
95910015Speter}
96010015Speter
96110015Speterstatic void
96210015Spetersihardclose(pp)
96310015Speter	struct si_port *pp;
96410015Speter{
96510015Speter	int oldspl;
96610015Speter	struct tty *tp;
96710015Speter	volatile struct si_channel *ccbp;
96810015Speter
96910015Speter	oldspl = spltty();
97010015Speter
97110015Speter	tp = pp->sp_tty;
97210015Speter	ccbp = pp->sp_ccb;			/* Find control block */
97310015Speter	if (tp->t_cflag & HUPCL
97410015Speter	    || !pp->sp_active_out
97510015Speter	       && !(ccbp->hi_ip & IP_DCD)
97610015Speter	       && !(pp->sp_iin.c_cflag && CLOCAL)
97710015Speter	    || !(tp->t_state & TS_ISOPEN)) {
97810015Speter
97910015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
98010015Speter		(void) si_command(pp, FCLOSE, SI_NOWAIT);
98110015Speter
98210015Speter		if (pp->sp_dtr_wait != 0) {
98310015Speter			timeout(sidtrwakeup, pp, pp->sp_dtr_wait);
98410015Speter			pp->sp_state |= SS_DTR_OFF;
98510015Speter		}
98610015Speter
98710015Speter	}
98810015Speter	pp->sp_active_out = FALSE;
98910015Speter	wakeup((caddr_t)&pp->sp_active_out);
99010015Speter	wakeup(TSA_CARR_ON(tp));
99110015Speter
99210015Speter	splx(oldspl);
99310015Speter}
99410015Speter
99510015Speter
99610015Speter/*
99710015Speter * called at splsoftclock()...
99810015Speter */
99910015Speterstatic void
100010015Spetersidtrwakeup(chan)
100110015Speter	void *chan;
100210015Speter{
100310015Speter	struct si_port *pp;
100410015Speter	int oldspl;
100510015Speter
100610015Speter	oldspl = spltty();
100710015Speter
100810015Speter	pp = (struct si_port *)chan;
100910015Speter	pp->sp_state &= ~SS_DTR_OFF;
101010015Speter	wakeup(&pp->sp_dtr_wait);
101110015Speter
101210015Speter	splx(oldspl);
101310015Speter}
101410015Speter
101510015Speter/*
101610015Speter * User level stuff - read and write
101710015Speter */
101812675Sjulianstatic	int
101910015Spetersiread(dev, uio, flag)
102010015Speter	register dev_t dev;
102110015Speter	struct uio *uio;
102210015Speter	int flag;
102310015Speter{
102410015Speter	register struct tty *tp;
102510015Speter	int mynor = minor(dev);
102610015Speter
102710015Speter	if (IS_SPECIAL(mynor)) {
102810015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_READ, "siread(CONTROLDEV!!)\n"));
102910015Speter		return(ENODEV);
103010015Speter	}
103110015Speter	tp = MINOR2TP(mynor);
103210015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_READ,
103310015Speter		"siread(%x,%x,%x)\n", dev, uio, flag));
103410015Speter	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
103510015Speter}
103610015Speter
103710015Speter
103812675Sjulianstatic	int
103910015Spetersiwrite(dev, uio, flag)
104010015Speter	dev_t dev;
104110015Speter	struct uio *uio;
104210015Speter	int flag;
104310015Speter{
104410015Speter	register struct si_port *pp;
104510015Speter	register struct tty *tp;
104610015Speter	int error = 0;
104710015Speter	int mynor = minor(dev);
104810015Speter	int oldspl;
104910015Speter
105010015Speter	if (IS_SPECIAL(mynor)) {
105110015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n"));
105210015Speter		return(ENODEV);
105310015Speter	}
105410015Speter	pp = MINOR2PP(mynor);
105510015Speter	tp = pp->sp_tty;
105610015Speter	DPRINT((pp, DBG_WRITE, "siwrite(%x,%x,%x)\n", dev, uio, flag));
105710015Speter
105810015Speter	oldspl = spltty();
105910015Speter	/*
106010015Speter	 * If writes are currently blocked, wait on the "real" tty
106110015Speter	 */
106210015Speter	while (pp->sp_state & SS_BLOCKWRITE) {
106310015Speter		pp->sp_state |= SS_WAITWRITE;
106410015Speter		DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n"));
106510015Speter		if (error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH,
106610015Speter				     "siwrite", 0))
106710015Speter			goto out;
106810015Speter	}
106910015Speter
107010015Speter	error = (*linesw[tp->t_line].l_write)(tp, uio, flag);
107110015Speterout:
107210015Speter	splx(oldspl);
107310015Speter	return (error);
107410015Speter}
107510015Speter
107610015Speter
107712675Sjulianstatic	struct tty *
107810015Spetersidevtotty(dev_t dev)
107910015Speter{
108010015Speter	struct si_port *pp;
108110015Speter	int mynor = minor(dev);
108210015Speter	struct si_softc *sc = &si_softc[SI_CARD(mynor)];
108310015Speter
108410015Speter	if (IS_SPECIAL(mynor))
108510015Speter		return(NULL);
108610015Speter	if (SI_PORT(mynor) >= sc->sc_nport)
108710015Speter		return(NULL);
108810015Speter	pp = MINOR2PP(mynor);
108910015Speter	return (pp->sp_tty);
109010015Speter}
109110015Speter
109212675Sjulianstatic	int
109310015Spetersiioctl(dev, cmd, data, flag, p)
109410015Speter	dev_t dev;
109510015Speter	int cmd;
109610015Speter	caddr_t data;
109710015Speter	int flag;
109810015Speter	struct proc *p;
109910015Speter{
110010015Speter	struct si_port *pp;
110110015Speter	register struct tty *tp;
110210015Speter	int error;
110310015Speter	int mynor = minor(dev);
110410015Speter	int oldspl;
110510015Speter	int blocked = 0;
110610015Speter#if defined(COMPAT_43)
110710015Speter	int oldcmd;
110810015Speter	struct termios term;
110910015Speter#endif
111010015Speter
111110015Speter	if (IS_SI_IOCTL(cmd))
111210015Speter		return(si_Sioctl(dev, cmd, data, flag, p));
111310015Speter
111410015Speter	pp = MINOR2PP(mynor);
111510015Speter	tp = pp->sp_tty;
111610015Speter
111710015Speter	DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%x,%x,%x,%x)\n",
111810015Speter		dev, cmd, data, flag));
111910015Speter	if (IS_STATE(mynor)) {
112010015Speter		struct termios *ct;
112110015Speter
112210015Speter		switch (mynor & SI_STATE_MASK) {
112310015Speter		case SI_INIT_STATE_MASK:
112410015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
112510015Speter			break;
112610015Speter		case SI_LOCK_STATE_MASK:
112710015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
112810015Speter			break;
112910015Speter		default:
113010015Speter			return (ENODEV);
113110015Speter		}
113210015Speter		switch (cmd) {
113310015Speter		case TIOCSETA:
113410015Speter			error = suser(p->p_ucred, &p->p_acflag);
113510015Speter			if (error != 0)
113610015Speter				return (error);
113710015Speter			*ct = *(struct termios *)data;
113810015Speter			return (0);
113910015Speter		case TIOCGETA:
114010015Speter			*(struct termios *)data = *ct;
114110015Speter			return (0);
114210015Speter		case TIOCGETD:
114310015Speter			*(int *)data = TTYDISC;
114410015Speter			return (0);
114510015Speter		case TIOCGWINSZ:
114610015Speter			bzero(data, sizeof(struct winsize));
114710015Speter			return (0);
114810015Speter		default:
114910015Speter			return (ENOTTY);
115010015Speter		}
115110015Speter	}
115210015Speter	/*
115310015Speter	 * Do the old-style ioctl compat routines...
115410015Speter	 */
115510015Speter#if defined(COMPAT_43)
115610015Speter	term = tp->t_termios;
115710015Speter	oldcmd = cmd;
115810015Speter	error = ttsetcompat(tp, &cmd, data, &term);
115910015Speter	if (error != 0)
116010015Speter		return (error);
116110015Speter	if (cmd != oldcmd)
116210015Speter		data = (caddr_t)&term;
116310015Speter#endif
116410015Speter	/*
116510015Speter	 * Do the initial / lock state business
116610015Speter	 */
116710015Speter	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
116810015Speter		int     cc;
116910015Speter		struct termios *dt = (struct termios *)data;
117010015Speter		struct termios *lt = mynor & SI_CALLOUT_MASK
117110015Speter				     ? &pp->sp_lout : &pp->sp_lin;
117210015Speter
117310015Speter		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
117410015Speter			| (dt->c_iflag & ~lt->c_iflag);
117510015Speter		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
117610015Speter			| (dt->c_oflag & ~lt->c_oflag);
117710015Speter		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
117810015Speter			| (dt->c_cflag & ~lt->c_cflag);
117910015Speter		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
118010015Speter			| (dt->c_lflag & ~lt->c_lflag);
118110015Speter		for (cc = 0; cc < NCCS; ++cc)
118210015Speter			if (lt->c_cc[cc] != 0)
118310015Speter				dt->c_cc[cc] = tp->t_cc[cc];
118410015Speter		if (lt->c_ispeed != 0)
118510015Speter			dt->c_ispeed = tp->t_ispeed;
118610015Speter		if (lt->c_ospeed != 0)
118710015Speter			dt->c_ospeed = tp->t_ospeed;
118810015Speter	}
118910015Speter
119010015Speter	/*
119110015Speter	 * Block user-level writes to give the ttywait()
119210015Speter	 * a chance to completely drain for commands
119310015Speter	 * that require the port to be in a quiescent state.
119410015Speter	 */
119510015Speter	switch (cmd) {
119610015Speter	case TIOCSETAW: case TIOCSETAF:
119710015Speter	case TIOCDRAIN: case TIOCSETP:
119810015Speter		blocked++;	/* block writes for ttywait() and siparam() */
119910015Speter		si_write_enable(pp, 0);
120010015Speter	}
120110015Speter
120210015Speter	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
120310015Speter	if (error >= 0)
120410015Speter		goto out;
120510015Speter
120610015Speter	oldspl = spltty();
120710015Speter
120810015Speter	error = ttioctl(tp, cmd, data, flag);
120910015Speter	si_disc_optim(tp, &tp->t_termios, pp);
121010015Speter	if (error >= 0)
121110015Speter		goto outspl;
121210015Speter
121310015Speter	switch (cmd) {
121410015Speter	case TIOCSBRK:
121510015Speter		si_command(pp, SBREAK, SI_NOWAIT);
121610015Speter		break;
121710015Speter	case TIOCCBRK:
121810015Speter		si_command(pp, EBREAK, SI_NOWAIT);
121910015Speter		break;
122010015Speter	case TIOCSDTR:
122110015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
122210015Speter		break;
122310015Speter	case TIOCCDTR:
122410015Speter		(void) si_modem(pp, SET, 0);
122510015Speter		break;
122610015Speter	case TIOCMSET:
122710015Speter		(void) si_modem(pp, SET, *(int *)data);
122810015Speter		break;
122910015Speter	case TIOCMBIS:
123010015Speter		(void) si_modem(pp, BIS, *(int *)data);
123110015Speter		break;
123210015Speter	case TIOCMBIC:
123310015Speter		(void) si_modem(pp, BIC, *(int *)data);
123410015Speter		break;
123510015Speter	case TIOCMGET:
123610015Speter		*(int *)data = si_modem(pp, GET, 0);
123710015Speter		break;
123810015Speter	case TIOCMSDTRWAIT:
123910015Speter		/* must be root since the wait applies to following logins */
124010015Speter		error = suser(p->p_ucred, &p->p_acflag);
124110015Speter		if (error != 0) {
124210015Speter			goto outspl;
124310015Speter		}
124410015Speter		pp->sp_dtr_wait = *(int *)data * hz / 100;
124510015Speter		break;
124610015Speter	case TIOCMGDTRWAIT:
124710015Speter		*(int *)data = pp->sp_dtr_wait * 100 / hz;
124810015Speter		break;
124910015Speter
125010015Speter	default:
125110015Speter		error = ENOTTY;
125210015Speter	}
125310015Speter	error = 0;
125410015Speteroutspl:
125510015Speter	splx(oldspl);
125610015Speterout:
125710015Speter	DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error));
125810015Speter	if (blocked)
125910015Speter		si_write_enable(pp, 1);
126010015Speter	return(error);
126110015Speter}
126210015Speter
126310015Speter/*
126410015Speter * Handle the Specialix ioctls. All MUST be called via the CONTROL device
126510015Speter */
126610015Speterstatic int
126710015Spetersi_Sioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
126810015Speter{
126910015Speter	struct si_softc *xsc;
127010015Speter	register struct si_port *xpp;
127110015Speter	volatile struct si_reg *regp;
127210015Speter	struct si_tcsi *dp;
127310044Speter	struct si_pstat *sps;
127411872Sphk	int *ip, error = 0;
127510015Speter	int oldspl;
127610015Speter	int card, port;
127710015Speter	int mynor = minor(dev);
127810015Speter
127910015Speter	DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%x,%x,%x,%x)\n",
128010015Speter		dev, cmd, data, flag));
128110015Speter
128210044Speter#if 1
128310044Speter	DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT));
128410044Speter	DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB));
128510044Speter	DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY));
128610044Speter#endif
128710044Speter
128810015Speter	if (!IS_CONTROLDEV(mynor)) {
128910015Speter		DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n"));
129010015Speter		return(ENODEV);
129110015Speter	}
129210015Speter
129310015Speter	oldspl = spltty();	/* better safe than sorry */
129410015Speter
129510015Speter	ip = (int *)data;
129610015Speter
129710015Speter#define SUCHECK if (error = suser(p->p_ucred, &p->p_acflag)) goto out
129810015Speter
129910015Speter	switch (cmd) {
130010015Speter	case TCSIPORTS:
130110015Speter		*ip = si_Nports;
130210015Speter		goto out;
130310015Speter	case TCSIMODULES:
130410015Speter		*ip = si_Nmodules;
130510015Speter		goto out;
130610015Speter	case TCSISDBG_ALL:
130710015Speter		SUCHECK;
130810015Speter		si_debug = *ip;
130910015Speter		goto out;
131010015Speter	case TCSIGDBG_ALL:
131110015Speter		*ip = si_debug;
131210015Speter		goto out;
131310015Speter	default:
131410015Speter		/*
131510015Speter		 * Check that a controller for this port exists
131610015Speter		 */
131710044Speter
131810044Speter		/* may also be a struct si_pstat, a superset of si_tcsi */
131910044Speter
132010015Speter		dp = (struct si_tcsi *)data;
132110044Speter		sps = (struct si_pstat *)data;
132210015Speter		card = dp->tc_card;
132310015Speter		xsc = &si_softc[card];	/* check.. */
132412174Speter		if (card < 0 || card >= NSI || xsc->sc_type == SIEMPTY) {
132510015Speter			error = ENOENT;
132610015Speter			goto out;
132710015Speter		}
132810015Speter		/*
132910015Speter		 * And check that a port exists
133010015Speter		 */
133110015Speter		port = dp->tc_port;
133210015Speter		if (port < 0 || port >= xsc->sc_nport) {
133310015Speter			error = ENOENT;
133410015Speter			goto out;
133510015Speter		}
133610015Speter		xpp = xsc->sc_ports + port;
133710015Speter		regp = (struct si_reg *)xsc->sc_maddr;
133810015Speter	}
133910015Speter
134010015Speter	switch (cmd) {
134110015Speter	case TCSIDEBUG:
134210015Speter#ifdef	SI_DEBUG
134310015Speter		SUCHECK;
134410015Speter		if (xpp->sp_debug)
134510015Speter			xpp->sp_debug = 0;
134610015Speter		else {
134710015Speter			xpp->sp_debug = DBG_ALL;
134810015Speter			DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n",
134910015Speter				(xpp->sp_debug&DBG_ALL)?"ON":"OFF"));
135010015Speter		}
135110015Speter		break;
135210015Speter#else
135310015Speter		error = ENODEV;
135410015Speter		goto out;
135510015Speter#endif
135610015Speter	case TCSISDBG_LEVEL:
135710015Speter	case TCSIGDBG_LEVEL:
135810015Speter#ifdef	SI_DEBUG
135910015Speter		if (cmd == TCSIGDBG_LEVEL) {
136010015Speter			dp->tc_dbglvl = xpp->sp_debug;
136110015Speter		} else {
136210015Speter			SUCHECK;
136310015Speter			xpp->sp_debug = dp->tc_dbglvl;
136410015Speter		}
136510015Speter		break;
136610015Speter#else
136710015Speter		error = ENODEV;
136810015Speter		goto out;
136910015Speter#endif
137010015Speter	case TCSIGRXIT:
137110015Speter		dp->tc_int = regp->rx_int_count;
137210015Speter		break;
137310015Speter	case TCSIRXIT:
137410015Speter		SUCHECK;
137510015Speter		regp->rx_int_count = dp->tc_int;
137610015Speter		break;
137710015Speter	case TCSIGIT:
137810015Speter		dp->tc_int = regp->int_count;
137910015Speter		break;
138010015Speter	case TCSIIT:
138110015Speter		SUCHECK;
138210015Speter		regp->int_count = dp->tc_int;
138310015Speter		break;
138410044Speter	case TCSISTATE:
138510044Speter		dp->tc_int = xpp->sp_ccb->hi_ip;
138610015Speter		break;
138710044Speter	/* these next three use a different structure */
138810044Speter	case TCSI_PORT:
138910015Speter		SUCHECK;
139010044Speter		sps->tc_siport = *xpp;
139110015Speter		break;
139210044Speter	case TCSI_CCB:
139310044Speter		SUCHECK;
139410044Speter		sps->tc_ccb = *xpp->sp_ccb;
139510015Speter		break;
139610044Speter	case TCSI_TTY:
139710044Speter		SUCHECK;
139810044Speter		sps->tc_tty = *xpp->sp_tty;
139910015Speter		break;
140010015Speter	default:
140110015Speter		error = EINVAL;
140210015Speter		goto out;
140310015Speter	}
140410015Speterout:
140510015Speter	splx(oldspl);
140610015Speter	return(error);		/* success */
140710015Speter}
140810015Speter
140910015Speter/*
141010015Speter *	siparam()	: Configure line params
141110015Speter *	called at spltty();
141210015Speter *	this may sleep, does not flush, nor wait for drain, nor block writes
141310015Speter *	caller must arrange this if it's important..
141410015Speter */
141512724Sphkstatic int
141610015Spetersiparam(tp, t)
141710015Speter	register struct tty *tp;
141810015Speter	register struct termios *t;
141910015Speter{
142010015Speter	register struct si_port *pp = TP2PP(tp);
142110015Speter	volatile struct si_channel *ccbp;
142210015Speter	int oldspl, cflag, iflag, oflag, lflag;
142310015Speter	int error = 0;		/* shutup gcc */
142410015Speter	int ispeed = 0;		/* shutup gcc */
142510015Speter	int ospeed = 0;		/* shutup gcc */
142610161Speter	BYTE val;
142710015Speter
142810015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t));
142910015Speter	cflag = t->c_cflag;
143010015Speter	iflag = t->c_iflag;
143110015Speter	oflag = t->c_oflag;
143210015Speter	lflag = t->c_lflag;
143310044Speter	DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n",
143410044Speter		oflag, cflag, iflag, lflag));
143510015Speter
143610015Speter
143710015Speter	/* if not hung up.. */
143810015Speter	if (t->c_ospeed != 0) {
143910015Speter		/* translate baud rate to firmware values */
144010015Speter		ospeed = ttspeedtab(t->c_ospeed, bdrates);
144110015Speter		ispeed = t->c_ispeed ?
144210015Speter			 ttspeedtab(t->c_ispeed, bdrates) : ospeed;
144310015Speter
144410015Speter		/* enforce legit baud rate */
144510015Speter		if (ospeed < 0 || ispeed < 0)
144610015Speter			return (EINVAL);
144710015Speter	}
144810015Speter
144910015Speter
145010015Speter	oldspl = spltty();
145110015Speter
145210015Speter	ccbp = pp->sp_ccb;
145310015Speter
145410161Speter	/* ========== set hi_break ========== */
145510161Speter	val = 0;
145610161Speter	if (iflag & IGNBRK)		/* Breaks */
145710161Speter		val |= BR_IGN;
145810161Speter	if (iflag & BRKINT)		/* Interrupt on break? */
145910161Speter		val |= BR_INT;
146010161Speter	if (iflag & PARMRK)		/* Parity mark? */
146110161Speter		val |= BR_PARMRK;
146210161Speter	if (iflag & IGNPAR)		/* Ignore chars with parity errors? */
146310161Speter		val |= BR_PARIGN;
146410161Speter	ccbp->hi_break = val;
146510161Speter
146610161Speter	/* ========== set hi_csr ========== */
146710015Speter	/* if not hung up.. */
146810015Speter	if (t->c_ospeed != 0) {
146910015Speter		/* Set I/O speeds */
147010161Speter		 val = (ispeed << 4) | ospeed;
147110015Speter	}
147210161Speter	ccbp->hi_csr = val;
147310015Speter
147410161Speter	/* ========== set hi_mr2 ========== */
147510161Speter	val = 0;
147610015Speter	if (cflag & CSTOPB)				/* Stop bits */
147710161Speter		val |= MR2_2_STOP;
147810015Speter	else
147910161Speter		val |= MR2_1_STOP;
148010161Speter	/*
148110161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
148210161Speter	 * a DCE, hence the reverse sense of RTS and CTS
148310161Speter	 */
148410161Speter	/* Output Flow - RTS must be raised before data can be sent */
148510161Speter	if (cflag & CCTS_OFLOW)
148610161Speter		val |= MR2_RTSCONT;
148710161Speter
148810161Speter	ccbp->hi_mr1 = val;
148910161Speter
149010161Speter	/* ========== set hi_mr1 ========== */
149110161Speter	val = 0;
149210015Speter	if (!(cflag & PARENB))				/* Parity */
149310161Speter		val |= MR1_NONE;
149410015Speter	else
149510161Speter		val |= MR1_WITH;
149610015Speter	if (cflag & PARODD)
149710161Speter		val |= MR1_ODD;
149810015Speter
149910015Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
150010161Speter		val |= MR1_8_BITS;
150110015Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
150210161Speter		val |= MR1_7_BITS;
150310015Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
150410161Speter		val |= MR1_6_BITS;
150510015Speter	} else {					/* Must be 5 */
150610161Speter		val |= MR1_5_BITS;
150710015Speter	}
150810161Speter	/*
150910161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
151010161Speter	 * a DCE, hence the reverse sense of RTS and CTS
151110161Speter	 */
151210161Speter	/* Input Flow - CTS is raised when port is ready to receive data */
151310161Speter	if (cflag & CRTS_IFLOW)
151410161Speter		val |= MR1_CTSCONT;
151510015Speter
151610161Speter	ccbp->hi_mr1 = val;
151710161Speter
151810161Speter	/* ========== set hi_mask ========== */
151910161Speter	val = 0xff;
152010161Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
152110161Speter		val &= 0xFF;
152210161Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
152310161Speter		val &= 0x7F;
152410161Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
152510161Speter		val &= 0x3F;
152610161Speter	} else {					/* Must be 5 */
152710161Speter		val &= 0x1F;
152810161Speter	}
152910015Speter	if (iflag & ISTRIP)
153010161Speter		val &= 0x7F;
153110015Speter
153210161Speter	ccbp->hi_mask = val;
153310161Speter
153410161Speter	/* ========== set hi_prtcl ========== */
153510161Speter	val = 0;
153610015Speter				/* Monitor DCD etc. if a modem */
153710015Speter	if (!(cflag & CLOCAL))
153810161Speter		val |= SP_DCEN;
153910161Speter	if (iflag & IXANY)
154010161Speter		val |= SP_TANY;
154110161Speter	if (iflag & IXON)
154210161Speter		val |= SP_TXEN;
154310161Speter	if (iflag & IXOFF)
154410161Speter		val |= SP_RXEN;
154510161Speter	if (iflag & INPCK)
154610161Speter		val |= SP_PAEN;
154710015Speter
154810161Speter	ccbp->hi_prtcl = val;
154910161Speter
155010161Speter
155110161Speter	/* ========== set hi_{rx|tx}{on|off} ========== */
155210161Speter	/* XXX: the card TOTALLY shields us from the flow control... */
155310015Speter	ccbp->hi_txon = t->c_cc[VSTART];
155410015Speter	ccbp->hi_txoff = t->c_cc[VSTOP];
155510015Speter
155610015Speter	ccbp->hi_rxon = t->c_cc[VSTART];
155710015Speter	ccbp->hi_rxoff = t->c_cc[VSTOP];
155810015Speter
155910161Speter	/* ========== send settings to the card ========== */
156010015Speter	/* potential sleep here */
156110015Speter	if (ccbp->hi_stat == IDLE_CLOSE)		/* Not yet open */
156210015Speter		si_command(pp, LOPEN, SI_WAIT);		/* open it */
156310015Speter	else
156410015Speter		si_command(pp, CONFIG, SI_WAIT);	/* change params */
156510015Speter
156610161Speter	/* ========== set DTR etc ========== */
156710015Speter	/* Hangup if ospeed == 0 */
156810015Speter	if (t->c_ospeed == 0) {
156910015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
157010015Speter	} else {
157110015Speter		/*
157210015Speter		 * If the previous speed was 0, may need to re-enable
157310015Speter	 	 * the modem signals
157410015Speter	 	 */
157510015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
157610015Speter	}
157710015Speter
157810044Speter	DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n",
157910044Speter		ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break));
158010015Speter
158110015Speter	splx(oldspl);
158210015Speter	return(error);
158310015Speter}
158410015Speter
158510015Speter/*
158610015Speter * Enable or Disable the writes to this channel...
158710015Speter * "state" ->  enabled = 1; disabled = 0;
158810015Speter */
158910015Speterstatic void
159010015Spetersi_write_enable(pp, state)
159110015Speter	register struct si_port *pp;
159210015Speter	int state;
159310015Speter{
159410015Speter	int oldspl;
159510015Speter
159610015Speter	oldspl = spltty();
159710015Speter
159810015Speter	if (state) {
159910015Speter		pp->sp_state &= ~SS_BLOCKWRITE;
160010015Speter		if (pp->sp_state & SS_WAITWRITE) {
160110015Speter			pp->sp_state &= ~SS_WAITWRITE;
160210015Speter			/* thunder away! */
160310015Speter			wakeup((caddr_t)pp);
160410015Speter		}
160510015Speter	} else {
160610015Speter		pp->sp_state |= SS_BLOCKWRITE;
160710015Speter	}
160810015Speter
160910015Speter	splx(oldspl);
161010015Speter}
161110015Speter
161210015Speter/*
161310015Speter * Set/Get state of modem control lines.
161410015Speter * Due to DCE-like behaviour of the adapter, some signals need translation:
161510015Speter *	TIOCM_DTR	DSR
161610015Speter *	TIOCM_RTS	CTS
161710015Speter */
161810015Speterstatic int
161910015Spetersi_modem(pp, cmd, bits)
162010015Speter	struct si_port *pp;
162110015Speter	enum si_mctl cmd;
162210015Speter	int bits;
162310015Speter{
162410015Speter	volatile struct si_channel *ccbp;
162510015Speter	int x;
162610015Speter
162710015Speter	DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits));
162810015Speter	ccbp = pp->sp_ccb;		/* Find channel address */
162910015Speter	switch (cmd) {
163010015Speter	case GET:
163110015Speter		x = ccbp->hi_ip;
163210015Speter		bits = TIOCM_LE;
163310015Speter		if (x & IP_DCD)		bits |= TIOCM_CAR;
163410015Speter		if (x & IP_DTR)		bits |= TIOCM_DTR;
163510015Speter		if (x & IP_RTS)		bits |= TIOCM_RTS;
163610015Speter		if (x & IP_RI)		bits |= TIOCM_RI;
163710015Speter		return(bits);
163810015Speter	case SET:
163910015Speter		ccbp->hi_op &= ~(OP_DSR|OP_CTS);
164010015Speter		/* fall through */
164110015Speter	case BIS:
164210015Speter		x = 0;
164310015Speter		if (bits & TIOCM_DTR)
164410015Speter			x |= OP_DSR;
164510015Speter		if (bits & TIOCM_RTS)
164610015Speter			x |= OP_CTS;
164710015Speter		ccbp->hi_op |= x;
164810015Speter		break;
164910015Speter	case BIC:
165010015Speter		if (bits & TIOCM_DTR)
165110015Speter			ccbp->hi_op &= ~OP_DSR;
165210015Speter		if (bits & TIOCM_RTS)
165310015Speter			ccbp->hi_op &= ~OP_CTS;
165410015Speter	}
165510015Speter	return 0;
165610015Speter}
165710015Speter
165810015Speter/*
165910015Speter * Handle change of modem state
166010015Speter */
166110015Speterstatic void
166210015Spetersi_modem_state(pp, tp, hi_ip)
166310015Speter	register struct si_port *pp;
166410015Speter	register struct tty *tp;
166510015Speter	register int hi_ip;
166610015Speter{
166710015Speter							/* if a modem dev */
166810015Speter	if (hi_ip & IP_DCD) {
166910015Speter		if ( !(pp->sp_last_hi_ip & IP_DCD)) {
167010015Speter			DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n",
167110015Speter				tp->t_line));
167210015Speter			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
167310015Speter		}
167410015Speter	} else {
167510015Speter		if (pp->sp_last_hi_ip & IP_DCD) {
167610015Speter			DPRINT((pp, DBG_INTR, "modem carr off\n"));
167710015Speter			if ((*linesw[tp->t_line].l_modem)(tp, 0))
167810015Speter				(void) si_modem(pp, SET, 0);
167910015Speter		}
168010015Speter	}
168110015Speter	pp->sp_last_hi_ip = hi_ip;
168210015Speter
168310015Speter}
168410015Speter
168510015Speter/*
168610015Speter * Poller to catch missed interrupts.
168712174Speter *
168812496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get
168912496Speter * better response.  We could really use a "periodic" version timeout(). :-)
169010015Speter */
169110015Speter#ifdef POLL
169210708Speterstatic void
169310015Spetersi_poll(void *nothing)
169410015Speter{
169510015Speter	register struct si_softc *sc;
169610015Speter	register int i;
169710015Speter	volatile struct si_reg *regp;
169812174Speter	register struct si_port *pp;
169912174Speter	int lost, oldspl, port;
170010015Speter
170110015Speter	DPRINT((0, DBG_POLL, "si_poll()\n"));
170211609Speter	oldspl = spltty();
170310015Speter	if (in_intr)
170410015Speter		goto out;
170510015Speter	lost = 0;
170610015Speter	for (i=0; i<NSI; i++) {
170710015Speter		sc = &si_softc[i];
170812174Speter		if (sc->sc_type == SIEMPTY)
170910015Speter			continue;
171010015Speter		regp = (struct si_reg *)sc->sc_maddr;
171110015Speter		/*
171210015Speter		 * See if there has been a pending interrupt for 2 seconds
171310015Speter		 * or so. The test <int_scounter >= 200) won't correspond
171410015Speter		 * to 2 seconds if int_count gets changed.
171510015Speter		 */
171610015Speter		if (regp->int_pending != 0) {
171710015Speter			if (regp->int_scounter >= 200 &&
171810015Speter			    regp->initstat == 1) {
171912174Speter				printf("si%d: lost intr\n", i);
172010015Speter				lost++;
172110015Speter			}
172210015Speter		} else {
172310015Speter			regp->int_scounter = 0;
172410015Speter		}
172510015Speter
172612174Speter		/*
172712174Speter		 * gripe about no input flow control..
172812174Speter		 */
172912174Speter		pp = sc->sc_ports;
173012174Speter		for (port = 0; port < sc->sc_nport; pp++, port++) {
173112174Speter			if (pp->sp_delta_overflows > 0) {
173212174Speter				printf("si%d: %d tty level buffer overflows\n",
173312174Speter					i, pp->sp_delta_overflows);
173412174Speter				pp->sp_delta_overflows = 0;
173512174Speter			}
173612174Speter		}
173710015Speter	}
173810015Speter	if (lost)
173910015Speter		siintr(-1);	/* call intr with fake vector */
174011609Speterout:
174110015Speter	splx(oldspl);
174210015Speter
174310015Speter	timeout(si_poll, (caddr_t)0L, POLL_INTERVAL);
174410015Speter}
174510015Speter#endif	/* ifdef POLL */
174610015Speter
174710015Speter/*
174810015Speter * The interrupt handler polls ALL ports on ALL adapters each time
174910015Speter * it is called.
175010015Speter */
175110015Speter
175212496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE];	/* input staging area */
175310015Speter
175410708Spetervoid
175511609Spetersiintr(int unit)
175610015Speter{
175710015Speter	register struct si_softc *sc;
175810015Speter
175910015Speter	register struct si_port *pp;
176010015Speter	volatile struct si_channel *ccbp;
176110015Speter	register struct tty *tp;
176210015Speter	volatile caddr_t maddr;
176311872Sphk	BYTE op, ip;
176412174Speter	int x, card, port, n, i, isopen;
176510015Speter	volatile BYTE *z;
176610015Speter	BYTE c;
176710015Speter
176811609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "siintr(%d)\n", unit));
176911609Speter	if (in_intr) {
177011609Speter		if (unit < 0)	/* should never happen */
177110708Speter			return;
177212174Speter		printf("si%d: Warning interrupt handler re-entered\n",
177311609Speter			unit);
177410708Speter		return;
177510015Speter	}
177610015Speter	in_intr = 1;
177710015Speter
177810015Speter	/*
177910015Speter	 * When we get an int we poll all the channels and do ALL pending
178010015Speter	 * work, not just the first one we find. This allows all cards to
178110015Speter	 * share the same vector.
178210015Speter	 */
178310015Speter	for (card=0; card < NSI; card++) {
178410015Speter		sc = &si_softc[card];
178512174Speter		if (sc->sc_type == SIEMPTY)
178610015Speter			continue;
178712174Speter
178812174Speter		/*
178912174Speter		 * First, clear the interrupt
179012174Speter		 */
179110015Speter		switch(sc->sc_type) {
179210015Speter		case SIHOST :
179310015Speter			maddr = sc->sc_maddr;
179410015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
179510015Speter							/* flag nothing pending */
179610015Speter			*(maddr+SIINTCL) = 0x00;	/* Set IRQ clear */
179710015Speter			*(maddr+SIINTCL_CL) = 0x00;	/* Clear IRQ clear */
179810015Speter			break;
179910015Speter		case SIHOST2:
180010015Speter			maddr = sc->sc_maddr;
180110015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
180210015Speter			*(maddr+SIPLIRQCLR) = 0x00;
180310015Speter			*(maddr+SIPLIRQCLR) = 0x10;
180410015Speter			break;
180510015Speter		case SIEISA:
180610015Speter#if NEISA > 0
180710015Speter			maddr = sc->sc_maddr;
180810015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
180910015Speter			(void)inb(sc->sc_eisa_iobase+3);
181010015Speter			break;
181110015Speter#endif	/* fall through if not EISA kernel */
181210015Speter		case SIEMPTY:
181310015Speter		default:
181410015Speter			continue;
181510015Speter		}
181610015Speter		((volatile struct si_reg *)maddr)->int_scounter = 0;
181710015Speter
181812174Speter		/*
181912174Speter		 * check each port
182012174Speter		 */
182112174Speter		for (pp=sc->sc_ports,port=0; port < sc->sc_nport; pp++,port++) {
182210015Speter			ccbp = pp->sp_ccb;
182310015Speter			tp = pp->sp_tty;
182410015Speter
182512174Speter
182610015Speter			/*
182710015Speter			 * See if a command has completed ?
182810015Speter			 */
182910015Speter			if (ccbp->hi_stat != pp->sp_pend) {
183010015Speter				DPRINT((pp, DBG_INTR,
183110015Speter					"siintr hi_stat = 0x%x, pend = %d\n",
183210015Speter					ccbp->hi_stat, pp->sp_pend));
183310015Speter				switch(pp->sp_pend) {
183410015Speter				case LOPEN:
183510015Speter				case MPEND:
183610015Speter				case MOPEN:
183710015Speter				case CONFIG:
183810015Speter					pp->sp_pend = ccbp->hi_stat;
183910015Speter						/* sleeping in si_command */
184010015Speter					wakeup(&pp->sp_state);
184110015Speter					break;
184210015Speter				default:
184310015Speter					pp->sp_pend = ccbp->hi_stat;
184410015Speter				}
184510015Speter	 		}
184610015Speter
184710015Speter			/*
184810015Speter			 * Continue on if it's closed
184910015Speter			 */
185010015Speter			if (ccbp->hi_stat == IDLE_CLOSE) {
185110015Speter				continue;
185210015Speter			}
185310015Speter
185410015Speter			/*
185510015Speter			 * Do modem state change if not a local device
185610015Speter			 */
185710015Speter			si_modem_state(pp, tp, ccbp->hi_ip);
185810015Speter
185910015Speter			/*
186012174Speter			 * Check to see if there's we should 'receive'
186112174Speter			 * characters.
186212174Speter			 */
186312174Speter			if (tp->t_state & TS_CONNECTED &&
186412174Speter			    tp->t_state & TS_ISOPEN)
186512174Speter				isopen = 1;
186612174Speter			else
186712174Speter				isopen = 0;
186812174Speter
186912174Speter			/*
187010015Speter			 * Do break processing
187110015Speter			 */
187210015Speter			if (ccbp->hi_state & ST_BREAK) {
187312174Speter				if (isopen) {
187412174Speter				    (*linesw[tp->t_line].l_rint)(TTY_BI, tp);
187510015Speter				}
187610015Speter				ccbp->hi_state &= ~ST_BREAK;   /* A Bit iffy this */
187710015Speter				DPRINT((pp, DBG_INTR, "si_intr break\n"));
187810015Speter			}
187910015Speter
188010015Speter			/*
188112174Speter			 * Do RX stuff - if not open then dump any characters.
188212174Speter			 * XXX: This is VERY messy and needs to be cleaned up.
188312174Speter			 *
188412174Speter			 * XXX: can we leave data in the host adapter buffer
188512174Speter			 * when the clists are full?  That may be dangerous
188612174Speter			 * if the user cannot get an interrupt signal through.
188710015Speter			 */
188810015Speter
188912174Speter	more_rx:	/* XXX Sorry. the nesting was driving me bats! :-( */
189012174Speter
189112174Speter			if (!isopen) {
189210015Speter				ccbp->hi_rxopos = ccbp->hi_rxipos;
189312174Speter				goto end_rx;
189412174Speter			}
189510015Speter
189612174Speter			/*
189712174Speter			 * Process read characters if not skipped above
189812174Speter			 */
189912174Speter			c = ccbp->hi_rxipos - ccbp->hi_rxopos;
190012174Speter			if (c == 0) {
190112174Speter				goto end_rx;
190212174Speter			}
190310015Speter
190412174Speter			op = ccbp->hi_rxopos;
190512174Speter			ip = ccbp->hi_rxipos;
190612174Speter			n = c & 0xff;
190712174Speter
190812174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
190910015Speter						n, op, ip));
191010015Speter
191112174Speter			/*
191212174Speter			 * Suck characters out of host card buffer into the
191312174Speter			 * "input staging buffer" - so that we dont leave the
191412174Speter			 * host card in limbo while we're possibly echoing
191512174Speter			 * characters and possibly flushing input inside the
191612174Speter			 * ldisc l_rint() routine.
191712174Speter			 */
191812496Speter			if (n <= SI_BUFFERSIZE - op) {
191910015Speter
192012174Speter				DPRINT((pp, DBG_INTR, "\tsingle copy\n"));
192112174Speter				z = ccbp->hi_rxbuf + op;
192212174Speter				bcopy((caddr_t)z, si_rxbuf, n);
192310015Speter
192412174Speter				op += n;
192512174Speter			} else {
192612496Speter				x = SI_BUFFERSIZE - op;
192710015Speter
192812174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x));
192912174Speter				z = ccbp->hi_rxbuf + op;
193012174Speter				bcopy((caddr_t)z, si_rxbuf, x);
193110015Speter
193212174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n-x));
193312174Speter				z = ccbp->hi_rxbuf;
193412174Speter				bcopy((caddr_t)z, si_rxbuf+x, n-x);
193510015Speter
193612174Speter				op += n;
193712174Speter			}
193810015Speter
193912174Speter			/* clear collected characters from buffer */
194012174Speter			ccbp->hi_rxopos = op;
194112174Speter
194212174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
194310015Speter						n, op, ip));
194410015Speter
194512174Speter			/*
194612174Speter			 * at this point...
194712174Speter			 * n = number of chars placed in si_rxbuf
194812174Speter			 */
194910015Speter
195012174Speter			/*
195112174Speter			 * Avoid the grotesquely inefficient lineswitch
195212174Speter			 * routine (ttyinput) in "raw" mode. It usually
195312174Speter			 * takes about 450 instructions (that's without
195412174Speter			 * canonical processing or echo!). slinput is
195512174Speter			 * reasonably fast (usually 40 instructions
195612174Speter			 * plus call overhead).
195712174Speter			 */
195812174Speter			if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
195910015Speter
196012174Speter				/* block if the driver supports it */
196112174Speter				if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER
196212174Speter				    && (tp->t_cflag & CRTS_IFLOW
196312174Speter					|| tp->t_iflag & IXOFF)
196412174Speter				    && !(tp->t_state & TS_TBLOCK))
196512174Speter					ttyblock(tp);
196610015Speter
196712174Speter				tk_nin += n;
196812174Speter				tk_rawcc += n;
196912174Speter				tp->t_rawcc += n;
197012174Speter
197112174Speter				pp->sp_delta_overflows +=
197212174Speter				    b_to_q((char *)si_rxbuf, n, &tp->t_rawq);
197312174Speter
197412174Speter				ttwakeup(tp);
197512174Speter				if (tp->t_state & TS_TTSTOP
197612174Speter				    && (tp->t_iflag & IXANY
197712174Speter					|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
197812174Speter					tp->t_state &= ~TS_TTSTOP;
197912174Speter					tp->t_lflag &= ~FLUSHO;
198012174Speter					si_start(tp);
198112174Speter				}
198212174Speter			} else {
198312174Speter				/*
198412174Speter				 * It'd be nice to not have to go through the
198512174Speter				 * function call overhead for each char here.
198612174Speter				 * It'd be nice to block input it, saving a
198712174Speter				 * loop here and the call/return overhead.
198812174Speter				 */
198912174Speter				for(x = 0; x < n; x++) {
199012174Speter					i = si_rxbuf[x];
199112174Speter					if ((*linesw[tp->t_line].l_rint)(i, tp)
199212174Speter					     == -1) {
199312174Speter						pp->sp_delta_overflows++;
199410015Speter					}
199512174Speter					/*
199612174Speter					 * doesn't seem to be much point doing
199712174Speter					 * this here.. this driver has no
199812174Speter					 * softtty processing! ??
199912174Speter					 */
200012174Speter					if (pp->sp_hotchar && i == pp->sp_hotchar) {
200112174Speter						setsofttty();
200212174Speter					}
200312174Speter				}
200412174Speter			}
200512174Speter			goto more_rx;	/* try for more until RXbuf is empty */
200610015Speter
200712174Speter	end_rx:		/* XXX: Again, sorry about the gotos.. :-) */
200810015Speter
200910015Speter			/*
201010015Speter			 * Do TX stuff
201110015Speter			 */
201210015Speter			(*linesw[tp->t_line].l_start)(tp);
201310015Speter
201410015Speter		} /* end of for (all ports on this controller) */
201510015Speter	} /* end of for (all controllers) */
201610015Speter
201711609Speter	in_intr = 0;
201811609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "end siintr(%d)\n", unit));
201910015Speter}
202010015Speter
202110015Speter/*
202210015Speter * Nudge the transmitter...
202312174Speter *
202412174Speter * XXX: I inherited some funny code here.  It implies the host card only
202512174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does
202612174Speter * not interrupt when it's actually hits empty.  In some cases, we have
202712174Speter * processes waiting for complete drain, and we need to simulate an interrupt
202812174Speter * about when we think the buffer is going to be empty (and retry if not).
202912174Speter * I really am not certain about this...  I *need* the hardware manuals.
203010015Speter */
203110015Speterstatic void
203210015Spetersi_start(tp)
203310015Speter	register struct tty *tp;
203410015Speter{
203510015Speter	struct si_port *pp;
203610015Speter	volatile struct si_channel *ccbp;
203710015Speter	register struct clist *qp;
203810015Speter	register char *dptr;
203910015Speter	BYTE ipos;
204010015Speter	int nchar;
204110015Speter	int oldspl, count, n, amount, buffer_full;
204210015Speter	int do_exitproc;
204310015Speter
204410015Speter	oldspl = spltty();
204510015Speter
204610015Speter	qp = &tp->t_outq;
204710015Speter	pp = TP2PP(tp);
204810015Speter
204910015Speter	DPRINT((pp, DBG_ENTRY|DBG_START,
205010015Speter		"si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n",
205110015Speter		tp, tp->t_state, pp->sp_state, qp->c_cc));
205210015Speter
205310015Speter	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
205410015Speter		goto out;
205510015Speter
205610015Speter	do_exitproc = 0;
205710015Speter	buffer_full = 0;
205810015Speter	ccbp = pp->sp_ccb;
205910015Speter
206010015Speter	/*
206110015Speter	 * Handle the case where ttywait() is called on process exit
206210015Speter	 * this may be BSDI specific, I dont know...
206310015Speter	 */
206410015Speter	if (tp->t_session != NULL && tp->t_session->s_leader != NULL &&
206510015Speter	    (tp->t_session->s_leader->p_flag & P_WEXIT)) {
206610015Speter		do_exitproc++;
206710015Speter	}
206810015Speter
206910015Speter	count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
207010015Speter	DPRINT((pp, DBG_START, "count %d\n", (BYTE)count));
207110015Speter
207210015Speter	dptr = (char *)ccbp->hi_txbuf;	/* data buffer */
207310015Speter
207410015Speter	while ((nchar = qp->c_cc) > 0) {
207510015Speter		if ((BYTE)count >= 255) {
207610015Speter			buffer_full++;
207710015Speter			break;
207810015Speter		}
207910015Speter		amount = min(nchar, (255 - (BYTE)count));
208010015Speter		ipos = (unsigned int)ccbp->hi_txipos;
208110015Speter		/* will it fit in one lump? */
208212496Speter		if ((SI_BUFFERSIZE - ipos) >= amount) {
208310015Speter			n = q_to_b(&tp->t_outq,
208410015Speter				(char *)&ccbp->hi_txbuf[ipos], amount);
208510015Speter		} else {
208610015Speter			n = q_to_b(&tp->t_outq,
208710015Speter				(char *)&ccbp->hi_txbuf[ipos],
208812496Speter				SI_BUFFERSIZE-ipos);
208912496Speter			if (n == SI_BUFFERSIZE-ipos) {
209010015Speter				n += q_to_b(&tp->t_outq,
209110015Speter					(char *)&ccbp->hi_txbuf[0],
209212496Speter					amount - (SI_BUFFERSIZE-ipos));
209310015Speter			}
209410015Speter		}
209510015Speter		ccbp->hi_txipos += n;
209610015Speter		count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
209710015Speter	}
209810015Speter
209910015Speter	if (count != 0 && nchar == 0) {
210010015Speter		tp->t_state |= TS_BUSY;
210110015Speter	} else {
210210015Speter		tp->t_state &= ~TS_BUSY;
210310015Speter	}
210410015Speter
210510015Speter	/* wakeup time? */
210610015Speter	ttwwakeup(tp);
210710015Speter
210810015Speter	DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n",
210910015Speter		(BYTE)count, nchar, tp->t_state));
211010015Speter
211110015Speter	if ((tp->t_state & TS_BUSY) || do_exitproc)
211210015Speter	{
211310015Speter		int time;
211410015Speter
211510015Speter		if (do_exitproc != 0) {
211610015Speter			time = hz / 10;
211710015Speter		} else {
211810015Speter			time = ttspeedtab(tp->t_ospeed, chartimes);
211910015Speter
212010015Speter			if (time > 0) {
212110015Speter				if (time < nchar)
212210015Speter					time = nchar / time;
212310015Speter				else
212410015Speter					time = 2;
212510015Speter			} else {
212612174Speter				printf("si%d: bad char time value!!\n",
212712174Speter					(int)SI_CARD(tp->t_dev));
212810015Speter				goto out;
212910015Speter			}
213010015Speter		}
213110015Speter
213210015Speter		if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) {
213310015Speter			untimeout((timeout_func_t)si_lstart, (caddr_t)pp);
213410015Speter		} else {
213510015Speter			pp->sp_state |= SS_LSTART;
213610015Speter		}
213710015Speter		DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time));
213810015Speter		timeout((timeout_func_t)si_lstart, (caddr_t)pp, time);
213910015Speter	}
214010015Speter
214110015Speterout:
214210015Speter	splx(oldspl);
214310015Speter	DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n"));
214410015Speter}
214510015Speter
214610015Speter/*
214710015Speter * Note: called at splsoftclock from the timeout code
214810015Speter * This has to deal with two things...  cause wakeups while waiting for
214910015Speter * tty drains on last process exit, and call l_start at about the right
215010015Speter * time for protocols like ppp.
215110015Speter */
215210015Speterstatic void
215310015Spetersi_lstart(pp)
215410015Speter	register struct si_port *pp;
215510015Speter{
215610015Speter	register struct tty *tp;
215710015Speter	int oldspl;
215810015Speter
215910015Speter	DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n",
216010015Speter		pp, pp->sp_state));
216110015Speter
216210015Speter	oldspl = spltty();
216310015Speter
216410015Speter	if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) {
216510015Speter		splx(oldspl);
216610015Speter		return;
216710015Speter	}
216810015Speter	pp->sp_state &= ~SS_LSTART;
216910015Speter	pp->sp_state |= SS_INLSTART;
217010015Speter
217110015Speter	tp = pp->sp_tty;
217210015Speter
217310015Speter	/* deal with the process exit case */
217410015Speter	ttwwakeup(tp);
217510015Speter
217612174Speter	/* nudge protocols - eg: ppp */
217710015Speter	(*linesw[tp->t_line].l_start)(tp);
217810015Speter
217910015Speter	pp->sp_state &= ~SS_INLSTART;
218010015Speter	splx(oldspl);
218110015Speter}
218210015Speter
218310015Speter/*
218410015Speter * Stop output on a line. called at spltty();
218510015Speter */
218610015Spetervoid
218710015Spetersistop(tp, rw)
218810015Speter	register struct tty *tp;
218910015Speter	int rw;
219010015Speter{
219110015Speter	volatile struct si_channel *ccbp;
219210015Speter	struct si_port *pp;
219310015Speter
219410015Speter	pp = TP2PP(tp);
219510015Speter	ccbp = pp->sp_ccb;
219610015Speter
219710015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "sistop(%x,%x)\n", tp, rw));
219810015Speter
219910015Speter	/* XXX: must check (rw & FWRITE | FREAD) etc flushing... */
220010015Speter	if (rw & FWRITE) {
220110015Speter		/* what level are we meant to be flushing anyway? */
220210015Speter		if (tp->t_state & TS_BUSY) {
220310015Speter			si_command(TP2PP(tp), WFLUSH, SI_NOWAIT);
220410015Speter			tp->t_state &= ~TS_BUSY;
220510015Speter			ttwwakeup(tp);	/* Bruce???? */
220610015Speter		}
220710015Speter	}
220812174Speter#if 1	/* XXX: this doesn't work right yet.. */
220912174Speter	/* XXX: this may have been failing because we used to call l_rint()
221012174Speter	 * while we were looping based on these two counters. Now, we collect
221112174Speter	 * the data and then loop stuffing it into l_rint(), making this
221212174Speter	 * useless.  Should we cause this to blow away the staging buffer?
221312174Speter	 */
221410015Speter	if (rw & FREAD) {
221510015Speter		ccbp->hi_rxopos = ccbp->hi_rxipos;
221610015Speter	}
221710015Speter#endif
221810015Speter}
221910015Speter
222010015Speter/*
222110015Speter * Issue a command to the Z280 host card CPU.
222210015Speter */
222310015Speter
222410015Speterstatic void
222510015Spetersi_command(pp, cmd, waitflag)
222610015Speter	struct si_port *pp;		/* port control block (local) */
222710015Speter	int cmd;
222810015Speter	int waitflag;
222910015Speter{
223010015Speter	int oldspl;
223110015Speter	volatile struct si_channel *ccbp = pp->sp_ccb;
223210015Speter	int x;
223310015Speter
223410015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n",
223510015Speter		pp, cmd, waitflag, ccbp->hi_stat));
223610015Speter
223710015Speter	oldspl = spltty();		/* Keep others out */
223810015Speter
223910015Speter	/* wait until it's finished what it was doing.. */
224010015Speter	while((x = ccbp->hi_stat) != IDLE_OPEN &&
224110015Speter			x != IDLE_CLOSE &&
224210015Speter			x != cmd) {
224310015Speter		if (in_intr) {			/* Prevent sleep in intr */
224410015Speter			DPRINT((pp, DBG_PARAM,
224510015Speter				"cmd intr collision - completing %d\trequested %d\n",
224610015Speter				x, cmd));
224710015Speter			splx(oldspl);
224810015Speter			return;
224910015Speter		} else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
225010015Speter				"sicmd1", 1)) {
225110015Speter			splx(oldspl);
225210015Speter			return;
225310015Speter		}
225410015Speter	}
225510015Speter	/* it should now be in IDLE_OPEN, IDLE_CLOSE, or "cmd" */
225610015Speter
225710015Speter	/* if there was a pending command, cause a state-change wakeup */
225810015Speter	if (pp->sp_pend != IDLE_OPEN) {
225910015Speter		switch(pp->sp_pend) {
226010015Speter		case LOPEN:
226110015Speter		case MPEND:
226210015Speter		case MOPEN:
226310015Speter		case CONFIG:
226410015Speter			wakeup(&pp->sp_state);
226510015Speter			break;
226610015Speter		default:
226710015Speter			break;
226810015Speter		}
226910015Speter	}
227010015Speter
227110015Speter	pp->sp_pend = cmd;		/* New command pending */
227210015Speter	ccbp->hi_stat = cmd;		/* Post it */
227310015Speter
227410015Speter	if (waitflag) {
227510015Speter		if (in_intr) {		/* If in interrupt handler */
227610015Speter			DPRINT((pp, DBG_PARAM,
227710015Speter				"attempt to sleep in si_intr - cmd req %d\n",
227810015Speter				cmd));
227910015Speter			splx(oldspl);
228010015Speter			return;
228110015Speter		} else while(ccbp->hi_stat != IDLE_OPEN) {
228210015Speter			if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
228310015Speter			    "sicmd2", 0))
228410015Speter				break;
228510015Speter		}
228610015Speter	}
228710015Speter	splx(oldspl);
228810015Speter}
228910015Speter
229010015Speterstatic void
229110015Spetersi_disc_optim(tp, t, pp)
229210015Speter	struct tty	*tp;
229310015Speter	struct termios	*t;
229410015Speter	struct si_port	*pp;
229510015Speter{
229610015Speter	/*
229710015Speter	 * XXX can skip a lot more cases if Smarts.  Maybe
229810015Speter	 * (IGNCR | ISTRIP | IXON) in c_iflag.  But perhaps we
229910015Speter	 * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state.
230010015Speter	 */
230110015Speter	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
230210015Speter	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
230310015Speter	    && (!(t->c_iflag & PARMRK)
230410015Speter		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
230510015Speter	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
230610015Speter	    && linesw[tp->t_line].l_rint == ttyinput)
230710015Speter		tp->t_state |= TS_CAN_BYPASS_L_RINT;
230810015Speter	else
230910015Speter		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
231010161Speter
231110015Speter	/*
231210015Speter	 * Prepare to reduce input latency for packet
231310015Speter	 * discplines with a end of packet character.
231410015Speter	 */
231510015Speter	if (tp->t_line == SLIPDISC)
231610015Speter		pp->sp_hotchar = 0xc0;
231710015Speter	else if (tp->t_line == PPPDISC)
231810015Speter		pp->sp_hotchar = 0x7e;
231910015Speter	else
232010015Speter		pp->sp_hotchar = 0;
232110161Speter
232210161Speter	DPRINT((pp, DBG_OPTIM, "bypass: %s, hotchar: %x\n",
232310161Speter		(tp->t_state & TS_CAN_BYPASS_L_RINT) ? "on" : "off",
232410161Speter		pp->sp_hotchar));
232510015Speter}
232610015Speter
232710015Speter
232810015Speter#ifdef	SI_DEBUG
232910015Speterstatic void
233010015Spetersi_dprintf(pp, flags, str, a1, a2, a3, a4, a5, a6)
233110015Speter	struct si_port *pp;
233210015Speter	int flags;
233310015Speter	char *str;
233410015Speter	int a1, a2, a3, a4, a5, a6;
233510015Speter{
233610015Speter	if ((pp == NULL && (si_debug&flags)) ||
233710015Speter	    (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) {
233810015Speter	    	if (pp != NULL)
233912496Speter	    		printf("%ci%d(%d): ", 's',
234012174Speter	    			(int)SI_CARD(pp->sp_tty->t_dev),
234112174Speter	    			(int)SI_PORT(pp->sp_tty->t_dev));
234210015Speter		printf(str, a1, a2, a3, a4, a5, a6);
234310015Speter	}
234410015Speter}
234510015Speter
234610015Speterstatic char *
234710015Spetersi_mctl2str(cmd)
234810015Speter	enum si_mctl cmd;
234910015Speter{
235010015Speter	switch (cmd) {
235110015Speter	case GET:	return("GET");
235210015Speter	case SET:	return("SET");
235310015Speter	case BIS:	return("BIS");
235410015Speter	case BIC:	return("BIC");
235510015Speter	}
235610015Speter	return("BAD");
235710015Speter}
235812502Sjulian
235912624Speter#endif	/* DEBUG */
236012502Sjulian
236112624Speter
236212502Sjulian
236312502Sjulianstatic si_devsw_installed = 0;
236412502Sjulian
236512517Sjulianstatic void 	si_drvinit(void *unused)
236612502Sjulian{
236712517Sjulian	dev_t dev;
236812517Sjulian
236912502Sjulian	if( ! si_devsw_installed ) {
237012675Sjulian		dev = makedev(CDEV_MAJOR, 0);
237112675Sjulian		cdevsw_add(&dev,&si_cdevsw, NULL);
237212502Sjulian		si_devsw_installed = 1;
237312517Sjulian    	}
237412502Sjulian}
237512517Sjulian
237612517SjulianSYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,si_drvinit,NULL)
237712517Sjulian
2378