si.c revision 13353
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 *
3313169Speter *	$Id: si.c,v 1.32 1996/01/02 09:20:29 peter 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>
7213353Speter#include <machine/stdarg.h>
7310015Speter
7410015Speter#include "si.h"
7510015Speter
7610015Speter/*
7710015Speter * This device driver is designed to interface the Specialix International
7812496Speter * range of serial multiplexor cards (SI/XIO) to BSDI/386 on an ISA bus machine.
7910015Speter *
8010015Speter * The controller is interfaced to the host via dual port ram
8110015Speter * and a (programmable - SIHOST2) interrupt at IRQ 11,12 or 15.
8210015Speter */
8310015Speter
8410015Speter#define	POLL		/* turn on poller to generate buffer empty interrupt */
8512174Speter#undef	FASTPOLL	/* turn on 100Hz poller, (XXX: NOTYET!) */
8610047Speter#define SI_DEF_HWFLOW	/* turn on default CRTSCTS flow control */
8712496Speter#define SI_I_HIGH_WATER	(TTYHOG - 2 * SI_BUFFERSIZE)
8810015Speter
8910015Speterenum si_mctl { GET, SET, BIS, BIC };
9010015Speter
9112675Sjulianstatic	const char devchar[] = "ABCDEFGHIJK";
9212675Sjulianstatic	const char portchar[] = "0123456789abcdefghijklmnopqrstuvwxyz";
9312502Sjulian
9412502Sjulian
9510015Speterstatic void si_command __P((struct si_port *, int, int));
9610015Speterstatic int si_modem __P((struct si_port *, enum si_mctl, int));
9710015Speterstatic void si_write_enable __P((struct si_port *, int));
9810015Speterstatic int si_Sioctl __P((dev_t, int, caddr_t, int, struct proc *));
9910015Speterstatic void si_start __P((struct tty *));
10010015Speterstatic void si_lstart __P((struct si_port *));
10110015Speterstatic void si_disc_optim __P((struct tty *tp, struct termios *t,
10210015Speter					struct si_port *pp));
10310015Speterstatic void sihardclose __P((struct si_port *pp));
10410015Speterstatic void sidtrwakeup __P((void *chan));
10510015Speter
10612724Sphkstatic int	siparam __P((struct tty *, struct termios *));
10710015Speter
10812724Sphkstatic	void	si_registerdev __P((struct isa_device *id));
10912724Sphkstatic	int	siprobe __P((struct isa_device *id));
11012724Sphkstatic	int	siattach __P((struct isa_device *id));
11110708Speterstatic	void	si_modem_state __P((struct si_port *pp, struct tty *tp, int hi_ip));
11210708Speter
11312675Sjulianstruct isa_driver sidriver =
11412675Sjulian	{ siprobe, siattach, "si" };
11512675Sjulian
11612675Sjulian
11712675Sjulianstatic	d_open_t	siopen;
11812675Sjulianstatic	d_close_t	siclose;
11912675Sjulianstatic	d_read_t	siread;
12012675Sjulianstatic	d_write_t	siwrite;
12112675Sjulianstatic	d_ioctl_t	siioctl;
12212675Sjulianstatic	d_stop_t	sistop;
12312731Sbdestatic	d_devtotty_t	sidevtotty;
12412675Sjulian
12512675Sjulian#define CDEV_MAJOR 68
12612678Sphkstatic struct cdevsw si_cdevsw =
12712675Sjulian	{ siopen,	siclose,	siread,		siwrite,	/*68*/
12812743Sbde	  siioctl,	sistop,		noreset,	sidevtotty,/* si */
12912742Sbde	  ttselect,	nommap,		NULL,	"si",	NULL,	-1 };
13012675Sjulian
13112675Sjulian
13212174Speter#ifdef SI_DEBUG		/* use: ``options "SI_DEBUG"'' in your config file */
13313353Speter
13413353Speterstatic	void	si_dprintf __P((struct si_port *pp, int flags, const char *fmt,
13513353Speter				...));
13610708Speterstatic	char	*si_mctl2str __P((enum si_mctl cmd));
13713353Speter
13810708Speter#define	DPRINT(x)	si_dprintf x
13913353Speter
14010708Speter#else
14110708Speter#define	DPRINT(x)	/* void */
14210708Speter#endif
14310708Speter
14410962Speterstatic int si_Nports;
14510962Speterstatic int si_Nmodules;
14610962Speterstatic int si_debug = 0;	/* data, not bss, so it's patchable */
14710015Speter
14810962Speterstatic struct tty *si_tty;
14910962Speter
15012174Speter/* where the firmware lives; defined in si_code.c */
15110015Speterextern int si_dsize;
15210015Speterextern unsigned char si_download[];
15310015Speter
15412888Speter#ifdef DEVFS
15512888Speterstatic char chardev[] = "0123456789abcdef";
15612888Speter#endif
15712888Speter
15810044Speterstruct si_softc {
15910044Speter	int 		sc_type;	/* adapter type */
16010044Speter	char 		*sc_typename;	/* adapter type string */
16110044Speter
16210044Speter	struct si_port	*sc_ports;	/* port structures for this card */
16310044Speter
16410044Speter	caddr_t		sc_paddr;	/* physical addr of iomem */
16510044Speter	caddr_t		sc_maddr;	/* kvaddr of iomem */
16610044Speter	int		sc_nport;	/* # ports on this card */
16710044Speter	int		sc_irq;		/* copy of attach irq */
16810044Speter	int		sc_eisa_iobase;	/* EISA io port address */
16910044Speter	int		sc_eisa_irqbits;
17010044Speter	struct kern_devconf sc_kdc;
17112675Sjulian#ifdef	DEVFS
17212675Sjulian	struct {
17312675Sjulian		void	*ttyd;
17412826Speter		void	*cuaa;
17512675Sjulian		void	*ttyl;
17612675Sjulian		void	*ttyi;
17712675Sjulian	} devfs_token[32]; /* what is the max per card? */
17813165Speter	void	*control_token;
17912675Sjulian#endif
18010044Speter};
18112724Sphkstatic struct si_softc si_softc[NSI];		/* up to 4 elements */
18210044Speter
18312174Speter#ifndef B2000	/* not standard, but the hardware knows it. */
18410015Speter# define B2000 2000
18510015Speter#endif
18610015Speterstatic struct speedtab bdrates[] = {
18710015Speter	B75,	CLK75,		/* 0x0 */
18810015Speter	B110,	CLK110,		/* 0x1 */
18910015Speter	B150,	CLK150,		/* 0x3 */
19010015Speter	B300,	CLK300,		/* 0x4 */
19110015Speter	B600,	CLK600,		/* 0x5 */
19210015Speter	B1200,	CLK1200,	/* 0x6 */
19310015Speter	B2000,	CLK2000,	/* 0x7 */
19410015Speter	B2400,	CLK2400,	/* 0x8 */
19510015Speter	B4800,	CLK4800,	/* 0x9 */
19610015Speter	B9600,	CLK9600,	/* 0xb */
19710015Speter	B19200,	CLK19200,	/* 0xc */
19810015Speter	B38400, CLK38400,	/* 0x2 (out of order!) */
19910015Speter	B57600, CLK57600,	/* 0xd */
20010015Speter	B115200, CLK110,	/* 0x1 (dupe!, 110 baud on "si") */
20110015Speter	-1,	-1
20210015Speter};
20310015Speter
20410015Speter
20510015Speter/* populated with approx character/sec rates - translated at card
20610015Speter * initialisation time to chars per tick of the clock */
20710015Speterstatic int done_chartimes = 0;
20810015Speterstatic struct speedtab chartimes[] = {
20910015Speter	B75,	8,
21010015Speter	B110,	11,
21110015Speter	B150,	15,
21210015Speter	B300,	30,
21310015Speter	B600,	60,
21410015Speter	B1200,	120,
21510015Speter	B2000,	200,
21610015Speter	B2400,	240,
21710015Speter	B4800,	480,
21810015Speter	B9600,	960,
21910015Speter	B19200,	1920,
22010015Speter	B38400, 3840,
22110015Speter	B57600, 5760,
22210015Speter	B115200, 11520,
22310015Speter	-1,	-1
22410015Speter};
22510015Speterstatic volatile int in_intr = 0;	/* Inside interrupt handler? */
22610015Speter
22710047Speterstatic int si_default_rate =	TTYDEF_SPEED;
22810047Speterstatic int si_default_iflag =	0;
22910047Speterstatic int si_default_oflag =	0;
23010047Speterstatic int si_default_lflag =	0;
23110047Speter#ifdef SI_DEF_HWFLOW
23210047Speterstatic int si_default_cflag =	TTYDEF_CFLAG | CRTSCTS;
23310047Speter#else
23410047Speterstatic int si_default_cflag =	TTYDEF_CFLAG;
23510047Speter#endif
23610047Speter
23710015Speter#ifdef POLL
23810015Speter#define	POLL_INTERVAL	(hz/2)
23910015Speterstatic int init_finished = 0;
24012174Speterstatic int fastpoll = 0;
24110015Speterstatic void si_poll __P((void *));
24210015Speter#endif
24310015Speter
24410015Speter/*
24510015Speter * Array of adapter types and the corresponding RAM size. The order of
24610015Speter * entries here MUST match the ordinal of the adapter type.
24710015Speter */
24810015Speterstatic char *si_type[] = {
24910015Speter	"EMPTY",
25010015Speter	"SIHOST",
25110015Speter	"SI2",				/* MCA */
25210015Speter	"SIHOST2",
25310015Speter	"SIEISA",
25410015Speter};
25510015Speter
25610015Speter
25710015Speterstatic struct kern_devconf si_kdc[NSI] = { {
25810015Speter	0, 0, 0,		/* filled in by dev_attach */
25910015Speter	"si", 0, { MDDT_ISA, 0, "tty" },
26010015Speter	isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
26110015Speter	&kdc_isa0,		/* parent */
26210015Speter	0,			/* parent data */
26310015Speter	DC_UNCONFIGURED,	/* state */
26410015Speter	"Specialix SI/XIO Host adapter",
26510015Speter	DC_CLS_SERIAL,		/* class */
26610015Speter} };
26710015Speter
26812724Sphkstatic void
26910015Spetersi_registerdev(id)
27010015Speter	struct isa_device *id;
27110015Speter{
27210015Speter	if (id->id_unit != 0) {
27310015Speter		si_kdc[id->id_unit] = si_kdc[0];	/* struct copy */
27410015Speter	}
27510015Speter	si_kdc[id->id_unit].kdc_unit = id->id_unit;
27610015Speter	si_kdc[id->id_unit].kdc_isa = id;
27712174Speter	si_kdc[id->id_unit].kdc_state = DC_UNCONFIGURED;
27810015Speter	dev_attach(&si_kdc[id->id_unit]);
27910015Speter}
28010015Speter
28110015Speter/* Look for a valid board at the given mem addr */
28212724Sphkstatic int
28310015Spetersiprobe(id)
28410015Speter	struct isa_device *id;
28510015Speter{
28610015Speter	struct si_softc *sc;
28710015Speter	int type;
28810015Speter	u_int i, ramsize;
28910015Speter	volatile BYTE was, *ux;
29010015Speter	volatile unsigned char *maddr;
29110015Speter	unsigned char *paddr;
29210015Speter
29310015Speter	si_registerdev(id);
29410015Speter
29510015Speter	maddr = id->id_maddr;		/* virtual address... */
29610015Speter	paddr = (caddr_t)vtophys(id->id_maddr);	/* physical address... */
29710015Speter
29812496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: probe at virtual=0x%x physical=0x%x\n",
29912496Speter		id->id_unit, id->id_maddr, paddr));
30010015Speter
30110015Speter	/*
30210015Speter	 * this is a lie, but it's easier than trying to handle caching
30310015Speter	 * and ram conflicts in the >1M and <16M region.
30410015Speter	 */
30510015Speter	if ((caddr_t)paddr < (caddr_t)IOM_BEGIN ||
30610015Speter	    (caddr_t)paddr >= (caddr_t)IOM_END) {
30712174Speter		printf("si%d: iomem (%lx) out of range\n",
30812174Speter			id->id_unit, (long)paddr);
30910015Speter		return(0);
31010015Speter	}
31110015Speter
31210015Speter	if (id->id_unit >= NSI) {
31310015Speter		/* THIS IS IMPOSSIBLE */
31410015Speter		return(0);
31510015Speter	}
31610015Speter
31710015Speter	if (((u_int)paddr & 0x7fff) != 0) {
31810015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
31910015Speter			"si%d: iomem (%x) not on 32k boundary\n",
32010015Speter			id->id_unit, paddr));
32110015Speter		return(0);
32210015Speter	}
32310015Speter
32410015Speter
32510015Speter	for (i=0; i < NSI; i++) {
32610015Speter		if ((sc = &si_softc[i]) == NULL)
32710015Speter			continue;
32810015Speter		if ((caddr_t)sc->sc_paddr == (caddr_t)paddr) {
32910015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
33010015Speter				"si%d: iomem (%x) already configured to si%d\n",
33110015Speter				id->id_unit, sc->sc_paddr, i));
33210015Speter			return(0);
33310015Speter		}
33410015Speter	}
33510015Speter
33610015Speter#if NEISA > 0
33710015Speter	if (id->id_iobase > 0x0fff) {	/* EISA card */
33810015Speter		int irq, port;
33910015Speter		unsigned long base;
34010015Speter		int eisa_irqs[] = { 0,IRQ1,IRQ2,IRQ3,IRQ4,IRQ5,IRQ6,IRQ7,
34110015Speter			IRQ8,IRQ9,IRQ10,IRQ11,IRQ12,IRQ13,IRQ14,IRQ15 };
34210015Speter
34310015Speter		port = id->id_iobase;
34410015Speter		base = (inb(port+1) << 24) | (inb(port) << 16);
34510015Speter		irq  = ((inb(port+2) >> 4) & 0xf);
34610015Speter
34710015Speter		id->id_irq = eisa_irqs[irq];
34810015Speter
34910015Speter		DPRINT((0, DBG_AUTOBOOT,
35012496Speter		    "si%d: EISA base %x, irq %x, id_irq %x, port %x\n",
35110015Speter		    id->id_unit, base, irq, id->id_irq, port));
35210015Speter
35310015Speter		if ((id->id_irq&(IRQ1|IRQ2|IRQ8|IRQ13)) != 0)
35410015Speter			goto bad_irq;
35510015Speter
35610015Speter		id->id_iobase &= 0xf000;
35710015Speter		id->id_iosize  = 0x0fff;
35810015Speter
35910015Speter		type = EISA;
36010015Speter		outb(p+2, (BYTE)irq << 4);
36110015Speter
36210015Speter		sc->sc_eisa_iobase = p;
36310015Speter		sc->sc_eisa_irqbits = irq << 4;
36410015Speter		ramsize = SIEISA_RAMSIZE;
36510015Speter		goto got_card;
36610015Speter	}
36710015Speter#endif
36810015Speter
36910015Speter	/* Is there anything out there? (0x17 is just an arbitrary number) */
37010015Speter	*maddr = 0x17;
37110015Speter	if (*maddr != 0x17) {
37210015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
37310015Speter			"si%d: 0x17 check fail at phys 0x%x\n",
37410015Speter			id->id_unit, paddr));
37510015Speterfail:
37610015Speter		return(0);
37710015Speter	}
37810015Speter	/*
37910015Speter	 * OK, now to see if whatever responded is really an SI card.
38010015Speter	 * Try for a MK II first (SIHOST2)
38110015Speter	 */
38210015Speter	for (i=SIPLSIG; i<SIPLSIG+8; i++)
38310015Speter		if ((*(maddr+i) & 7) != (~(BYTE)i & 7))
38410015Speter			goto try_mk1;
38510015Speter
38610015Speter	/* It must be an SIHOST2 */
38710015Speter	*(maddr + SIPLRESET) = 0;
38810015Speter	*(maddr + SIPLIRQCLR) = 0;
38910015Speter	*(maddr + SIPLIRQSET) = 0x10;
39010015Speter	type = SIHOST2;
39110015Speter	ramsize = SIHOST2_RAMSIZE;
39210015Speter	goto got_card;
39310015Speter
39410015Speter	/*
39510015Speter	 * Its not a MK II, so try for a MK I (SIHOST)
39610015Speter	 */
39710015Spetertry_mk1:
39810015Speter	*(maddr+SIRESET) = 0x0;		/* reset the card */
39910015Speter	*(maddr+SIINTCL) = 0x0;		/* clear int */
40010015Speter	*(maddr+SIRAM) = 0x17;
40110015Speter	if (*(maddr+SIRAM) != (BYTE)0x17)
40210015Speter		goto fail;
40310015Speter	*(maddr+0x7ff8) = 0x17;
40410015Speter	if (*(maddr+0x7ff8) != (BYTE)0x17) {
40510015Speter		DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
40610015Speter			"si%d: 0x17 check fail at phys 0x%x = 0x%x\n",
40710015Speter			id->id_unit, paddr+0x77f8, *(maddr+0x77f8)));
40810015Speter		goto fail;
40910015Speter	}
41010015Speter
41110015Speter	/* It must be an SIHOST (maybe?) - there must be a better way XXXX */
41210015Speter	type = SIHOST;
41310015Speter	ramsize = SIHOST_RAMSIZE;
41410015Speter
41510015Spetergot_card:
41612496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: found type %d card, try memory test\n",
41712496Speter		id->id_unit, type));
41810015Speter	/* Try the acid test */
41910015Speter	ux = (BYTE *)(maddr + SIRAM);
42010015Speter	for (i=0; i<ramsize; i++, ux++)
42110015Speter		*ux = (BYTE)(i&0xff);
42210015Speter	ux = (BYTE *)(maddr + SIRAM);
42310015Speter	for (i=0; i<ramsize; i++, ux++) {
42410015Speter		if ((was = *ux) != (BYTE)(i&0xff)) {
42510015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
42612174Speter				"si%d: match fail at phys 0x%x, was %x should be %x\n",
42710015Speter				id->id_unit, paddr+i, was, i&0xff));
42810015Speter			goto fail;
42910015Speter		}
43010015Speter	}
43110015Speter
43210015Speter	/* clear out the RAM */
43310015Speter	ux = (BYTE *)(maddr + SIRAM);
43410015Speter	for (i=0; i<ramsize; i++)
43510015Speter		*ux++ = 0;
43610015Speter	ux = (BYTE *)(maddr + SIRAM);
43710015Speter	for (i=0; i<ramsize; i++) {
43810015Speter		if ((was = *ux++) != 0) {
43910015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
44012174Speter				"si%d: clear fail at phys 0x%x, was %x\n",
44110015Speter				id->id_unit, paddr+i, was));
44210015Speter			goto fail;
44310015Speter		}
44410015Speter	}
44510015Speter
44610015Speter	/*
44710015Speter	 * Success, we've found a valid board, now fill in
44810015Speter	 * the adapter structure.
44910015Speter	 */
45010015Speter	switch (type) {
45110015Speter	case SIHOST2:
45210015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
45310015Speterbad_irq:
45410015Speter			DPRINT((0, DBG_AUTOBOOT|DBG_FAIL,
45510015Speter				"si%d: bad IRQ value - %d\n",
45610015Speter				id->id_unit, id->id_irq));
45710015Speter			return(0);
45810015Speter		}
45910015Speter		id->id_msize = SIHOST2_MEMSIZE;
46010015Speter		break;
46110015Speter	case SIHOST:
46210015Speter		if ((id->id_irq&(IRQ11|IRQ12|IRQ15)) == 0) {
46310015Speter			goto bad_irq;
46410015Speter		}
46510015Speter		id->id_msize = SIHOST_MEMSIZE;
46610015Speter		break;
46710015Speter	case SIEISA:
46810015Speter		id->id_msize = SIEISA_MEMSIZE;
46910015Speter		break;
47010015Speter	case SI2:		/* MCA */
47110015Speter	default:
47210015Speter		printf("si%d: %s not supported\n", id->id_unit, si_type[type]);
47310015Speter		return(0);
47410015Speter	}
47510015Speter	si_softc[id->id_unit].sc_type = type;
47610015Speter	si_softc[id->id_unit].sc_typename = si_type[type];
47710015Speter	return(-1);	/* -1 == found */
47810015Speter}
47910015Speter
48010015Speter/*
48110015Speter * Attach the device.  Initialize the card.
48210015Speter */
48312724Sphkstatic int
48410015Spetersiattach(id)
48510015Speter	struct isa_device *id;
48610015Speter{
48710015Speter	int unit = id->id_unit;
48810015Speter	struct si_softc *sc = &si_softc[unit];
48910015Speter	struct si_port *pp;
49010015Speter	volatile struct si_channel *ccbp;
49110015Speter	volatile struct si_reg *regp;
49210015Speter	volatile caddr_t maddr;
49310015Speter	struct si_module *modp;
49410015Speter	struct tty *tp;
49510015Speter	struct speedtab *spt;
49610015Speter	int nmodule, nport, x, y;
49712174Speter	int uart_type;
49812888Speter#ifdef DEVFS
49912675Sjulian	char	name[32];
50012888Speter#endif
50110015Speter
50212496Speter	DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", id->id_unit));
50310015Speter
50410015Speter	sc->sc_paddr = (caddr_t)vtophys(id->id_maddr);
50510015Speter	sc->sc_maddr = id->id_maddr;
50610015Speter	sc->sc_irq = id->id_irq;
50710015Speter
50810015Speter	sc->sc_ports = NULL;			/* mark as uninitialised */
50910015Speter
51010015Speter	maddr = sc->sc_maddr;
51110015Speter
51210015Speter	/*
51310015Speter	 * OK, now lets download the firmware and try and boot the CPU..
51410015Speter	 */
51510015Speter
51612496Speter	DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n",
51712496Speter		id->id_unit, si_dsize));
51810015Speter	bcopy(si_download, maddr, si_dsize);
51910015Speter
52010015Speter	switch (sc->sc_type) {
52110015Speter	case SIEISA:
52210015Speter#if NEISA > 0
52310015Speter		/* modify the Z280 firmware to tell it that it's on an EISA */
52410015Speter		*(maddr+0x42) = 1;
52510015Speter		outb(sc->sc_eisa_iobase+2, sc->sc_eisa_irqbits | 4);
52610015Speter		(void)inb(sc->sc_eisa_iobase+3); /* reset interrupt */
52710015Speter		break;
52810015Speter#endif	/* fall-through if not EISA */
52910015Speter	case SI2:
53012174Speter		/*
53112174Speter		 * must get around to converting the code for
53212174Speter		 * these one day, if FreeBSD ever supports it.
53312174Speter		 */
53410015Speter		return 0;
53510015Speter	case SIHOST:
53610015Speter		*(maddr+SIRESET_CL) = 0;
53710015Speter		*(maddr+SIINTCL_CL) = 0;
53810015Speter		break;
53910015Speter	case SIHOST2:
54010015Speter		*(maddr+SIPLRESET) = 0x10;
54110015Speter		switch (sc->sc_irq) {
54210015Speter		case IRQ11:
54310015Speter			*(maddr+SIPLIRQ11) = 0x10;
54410015Speter			break;
54510015Speter		case IRQ12:
54610015Speter			*(maddr+SIPLIRQ12) = 0x10;
54710015Speter			break;
54810015Speter		case IRQ15:
54910015Speter			*(maddr+SIPLIRQ15) = 0x10;
55010015Speter			break;
55110015Speter		}
55210015Speter		*(maddr+SIPLIRQCLR) = 0x10;
55310015Speter		break;
55410015Speter	}
55510015Speter
55610015Speter	DELAY(1000000);			/* wait around for a second */
55710015Speter
55810015Speter	regp = (struct si_reg *)maddr;
55910015Speter	y = 0;
56010015Speter					/* wait max of 5 sec for init OK */
56110015Speter	while (regp->initstat == 0 && y++ < 10) {
56210015Speter		DELAY(500000);
56310015Speter	}
56410015Speter	switch (regp->initstat) {
56510015Speter	case 0:
56610015Speter		printf("si%d: startup timeout - aborting\n", unit);
56712174Speter		sc->sc_type = SIEMPTY;
56810015Speter		return 0;
56910015Speter	case 1:
57012174Speter			/* set throttle to 125 intr per second */
57110015Speter		regp->int_count = 25000;
57210015Speter			/* rx intr max of 25 timer per second */
57310015Speter		regp->rx_int_count = 4;
57410015Speter		regp->int_pending = 0;		/* no intr pending */
57510015Speter		regp->int_scounter = 0;	/* reset counter */
57610015Speter		break;
57710015Speter	case 0xff:
57810015Speter		/*
57910015Speter		 * No modules found, so give up on this one.
58010015Speter		 */
58110015Speter		printf("si%d: %s - no ports found\n", unit,
58210015Speter			si_type[sc->sc_type]);
58310015Speter		return 0;
58410015Speter	default:
58510015Speter		printf("si%d: Z280 version error - initstat %x\n",
58610015Speter			unit, regp->initstat);
58710015Speter		return 0;
58810015Speter	}
58910015Speter
59010015Speter	/*
59110015Speter	 * First time around the ports just count them in order
59210015Speter	 * to allocate some memory.
59310015Speter	 */
59410015Speter	nport = 0;
59510015Speter	modp = (struct si_module *)(maddr + 0x80);
59610015Speter	for (;;) {
59712174Speter		DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp));
59810015Speter		switch (modp->sm_type & (~MMASK)) {
59910015Speter		case M232:
60010015Speter		case M422:
60110015Speter			DPRINT((0, DBG_DOWNLOAD,
60212174Speter				"si%d: Found 232/422 module, %d ports\n",
60310015Speter				unit, (int)(modp->sm_type & MMASK)));
60410015Speter
60510015Speter			/* this is a firmware issue */
60610015Speter			if (si_Nports == SI_MAXPORTPERCARD) {
60710015Speter				printf("si%d: extra ports ignored\n", unit);
60810015Speter				continue;
60910015Speter			}
61010015Speter
61110015Speter			x = modp->sm_type & MMASK;
61210015Speter			nport += x;
61310015Speter			si_Nports += x;
61410015Speter			si_Nmodules++;
61510015Speter			break;
61610015Speter		default:
61710015Speter			printf("si%d: unknown module type %d\n",
61810015Speter				unit, modp->sm_type);
61910015Speter			break;
62010015Speter		}
62110015Speter		if (modp->sm_next == 0)
62210015Speter			break;
62310015Speter		modp = (struct si_module *)
62410015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
62510015Speter	}
62610015Speter	sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport,
62710015Speter		M_DEVBUF, M_NOWAIT);
62810015Speter	if (sc->sc_ports == 0) {
62910015Spetermem_fail:
63010015Speter		printf("si%d: fail to malloc memory for port structs\n",
63110015Speter			unit);
63210015Speter		return 0;
63310015Speter	}
63410015Speter	bzero(sc->sc_ports, sizeof(struct si_port) * nport);
63510015Speter	sc->sc_nport = nport;
63610015Speter
63710015Speter	/*
63810015Speter	 * allocate tty structures for ports
63910015Speter	 */
64010015Speter	tp = (struct tty *)malloc(sizeof(*tp) * nport, M_DEVBUF, M_NOWAIT);
64110015Speter	if (tp == 0)
64210015Speter		goto mem_fail;
64310015Speter	bzero(tp, sizeof(*tp) * nport);
64410962Speter	si_tty = tp;
64510015Speter
64610015Speter	/* mark the device state as attached */
64710015Speter	si_kdc[unit].kdc_state = DC_BUSY;
64810015Speter
64910015Speter	/*
65010015Speter	 * Scan round the ports again, this time initialising.
65110015Speter	 */
65210015Speter	pp = sc->sc_ports;
65310015Speter	nmodule = 0;
65410015Speter	modp = (struct si_module *)(maddr + 0x80);
65512174Speter	uart_type = 0;
65610015Speter	for (;;) {
65710015Speter		switch (modp->sm_type & (~MMASK)) {
65810015Speter		case M232:
65910015Speter		case M422:
66010015Speter			nmodule++;
66110015Speter			nport = (modp->sm_type & MMASK);
66210015Speter			ccbp = (struct si_channel *)((char *)modp+0x100);
66312174Speter			if (uart_type == 0)
66412174Speter				uart_type = ccbp->type;
66510015Speter			for (x = 0; x < nport; x++, pp++, ccbp++) {
66610015Speter				pp->sp_ccb = ccbp;	/* save the address */
66710015Speter				pp->sp_tty = tp++;
66810015Speter				pp->sp_pend = IDLE_CLOSE;
66910015Speter				pp->sp_state = 0;	/* internal flag */
67010015Speter				pp->sp_dtr_wait = 3 * hz;
67110047Speter				pp->sp_iin.c_iflag = si_default_iflag;
67210047Speter				pp->sp_iin.c_oflag = si_default_oflag;
67310047Speter				pp->sp_iin.c_cflag = si_default_cflag;
67410047Speter				pp->sp_iin.c_lflag = si_default_lflag;
67510015Speter				termioschars(&pp->sp_iin);
67610015Speter				pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed =
67710047Speter					si_default_rate;
67810015Speter				pp->sp_iout = pp->sp_iin;
67910015Speter			}
68010015Speter			break;
68110015Speter		default:
68210015Speter			break;
68310015Speter		}
68410015Speter		if (modp->sm_next == 0) {
68512174Speter			printf("si%d: card: %s, ports: %d, modules: %d (type: %d)\n",
68610015Speter				unit,
68710015Speter				sc->sc_typename,
68810015Speter				sc->sc_nport,
68912174Speter				nmodule,
69012174Speter				uart_type);
69110015Speter			break;
69210015Speter		}
69310015Speter		modp = (struct si_module *)
69410015Speter			(maddr + (unsigned)(modp->sm_next & 0x7fff));
69510015Speter	}
69610015Speter	if (done_chartimes == 0) {
69710015Speter		for (spt = chartimes ; spt->sp_speed != -1; spt++) {
69810015Speter			if ((spt->sp_code /= hz) == 0)
69910015Speter				spt->sp_code = 1;
70010015Speter		}
70110015Speter		done_chartimes = 1;
70210015Speter	}
70312502Sjulian
70412675Sjulian#ifdef DEVFS
70512675Sjulian/*	path	name	devsw		minor	type   uid gid perm*/
70613169Speter	for ( x = 0; x < sc->sc_nport; x++ ) {
70713165Speter		y = x + 1;	/* For sync with the manuals that start at 1 */
70813165Speter		sprintf(name,"ttyA%c%c", chardev[y / 10], chardev[y % 10]);
70912675Sjulian		sc->devfs_token[x].ttyd = devfs_add_devsw(
71012888Speter			"/", name, &si_cdevsw, x,
71112675Sjulian			DV_CHR, 0, 0, 0600);
71213165Speter		sprintf(name,"cuaA%c%c", chardev[y / 10], chardev[y % 10]);
71312675Sjulian		sc->devfs_token[x].cuaa = devfs_add_devsw(
71412888Speter			"/", name, &si_cdevsw, x + 128,
71512675Sjulian			DV_CHR, 0, 0, 0600);
71613165Speter		sprintf(name,"ttyiA%c%c", chardev[y / 10], chardev[y % 10]);
71712826Speter		sc->devfs_token[x].ttyi = devfs_add_devsw(
71812888Speter			"/", name, &si_cdevsw, x + 0x10000,
71912675Sjulian			DV_CHR, 0, 0, 0600);
72013165Speter		sprintf(name,"ttylA%c%c", chardev[y / 10], chardev[y % 10]);
72112826Speter		sc->devfs_token[x].ttyl = devfs_add_devsw(
72212888Speter			"/", name, &si_cdevsw, x + 0x20000,
72312675Sjulian			DV_CHR, 0, 0, 0600);
72412675Sjulian	}
72513165Speter	sc->control_token = devfs_add_devsw("/", "si_control",
72613165Speter						&si_cdevsw, 0x40000,
72713165Speter						DV_CHR, 0, 0, 0600);
72812675Sjulian#endif
72910015Speter	return (1);
73010015Speter}
73110015Speter
73212675Sjulianstatic	int
73310015Spetersiopen(dev, flag, mode, p)
73410015Speter	dev_t dev;
73510015Speter	int flag, mode;
73610015Speter	struct proc *p;
73710015Speter{
73810015Speter	int oldspl, error;
73910015Speter	int card, port;
74010015Speter	register struct si_softc *sc;
74110015Speter	register struct tty *tp;
74210015Speter	volatile struct si_channel *ccbp;
74310015Speter	struct si_port *pp;
74410015Speter	int mynor = minor(dev);
74510015Speter
74610015Speter	/* quickly let in /dev/si_control */
74710015Speter	if (IS_CONTROLDEV(mynor)) {
74810015Speter		if (error = suser(p->p_ucred, &p->p_acflag))
74910015Speter			return(error);
75010015Speter		return(0);
75110015Speter	}
75210015Speter
75310015Speter	card = SI_CARD(mynor);
75410015Speter	if (card >= NSI)
75510015Speter		return (ENXIO);
75610015Speter	sc = &si_softc[card];
75710015Speter
75812174Speter	if (sc->sc_type == SIEMPTY) {
75912174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n",
76010015Speter			card, sc->sc_typename));
76110015Speter		return(ENXIO);
76210015Speter	}
76310015Speter
76410015Speter	port = SI_PORT(mynor);
76510015Speter	if (port >= sc->sc_nport) {
76612174Speter		DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n",
76710015Speter			card, sc->sc_nport));
76810015Speter		return(ENXIO);
76910015Speter	}
77010015Speter
77110015Speter#ifdef	POLL
77210015Speter	/*
77310015Speter	 * We've now got a device, so start the poller.
77410015Speter	 */
77510015Speter	if (init_finished == 0) {
77610015Speter		timeout(si_poll, (caddr_t)0L, POLL_INTERVAL);
77710015Speter		init_finished = 1;
77810015Speter	}
77910015Speter#endif
78010015Speter
78110015Speter	/* initial/lock device */
78210015Speter	if (IS_STATE(mynor)) {
78310015Speter		return(0);
78410015Speter	}
78510015Speter
78610015Speter	pp = sc->sc_ports + port;
78710015Speter	tp = pp->sp_tty;			/* the "real" tty */
78810015Speter	ccbp = pp->sp_ccb;			/* Find control block */
78910015Speter	DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%x,%x,%x,%x)\n",
79010015Speter		dev, flag, mode, p));
79110015Speter
79210015Speter	oldspl = spltty();			/* Keep others out */
79310015Speter	error = 0;
79410015Speter
79510015Speteropen_top:
79610015Speter	while (pp->sp_state & SS_DTR_OFF) {
79710015Speter		error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sidtr", 0);
79810015Speter		if (error != 0)
79910015Speter			goto out;
80010015Speter	}
80110015Speter
80210015Speter	if (tp->t_state & TS_ISOPEN) {
80310015Speter		/*
80410015Speter		 * The device is open, so everything has been initialised.
80510015Speter		 * handle conflicts.
80610015Speter		 */
80710015Speter		if (IS_CALLOUT(mynor)) {
80810015Speter			if (!pp->sp_active_out) {
80910015Speter				error = EBUSY;
81010015Speter				goto out;
81110015Speter			}
81210015Speter		} else {
81310015Speter			if (pp->sp_active_out) {
81410015Speter				if (flag & O_NONBLOCK) {
81510015Speter					error = EBUSY;
81610015Speter					goto out;
81710015Speter				}
81810015Speter				error = tsleep(&pp->sp_active_out,
81910015Speter						TTIPRI|PCATCH, "sibi", 0);
82010015Speter				if (error != 0)
82110015Speter					goto out;
82210015Speter				goto open_top;
82310015Speter			}
82410015Speter		}
82510015Speter		if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
82610015Speter			DPRINT((pp, DBG_OPEN|DBG_FAIL,
82710015Speter				"already open and EXCLUSIVE set\n"));
82810015Speter			error = EBUSY;
82910015Speter			goto out;
83010015Speter		}
83110015Speter	} else {
83210015Speter		/*
83310015Speter		 * The device isn't open, so there are no conflicts.
83410015Speter		 * Initialize it. Avoid sleep... :-)
83510015Speter		 */
83610015Speter		DPRINT((pp, DBG_OPEN, "first open\n"));
83710015Speter		tp->t_oproc = si_start;
83810015Speter		tp->t_param = siparam;
83910015Speter		tp->t_dev = dev;
84010015Speter		tp->t_termios = mynor & SI_CALLOUT_MASK
84110015Speter				? pp->sp_iout : pp->sp_iin;
84210015Speter
84310015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
84410015Speter
84510015Speter		++pp->sp_wopeners;	/* in case of sleep in siparam */
84610015Speter
84710015Speter		error = siparam(tp, &tp->t_termios);
84810015Speter
84910015Speter		--pp->sp_wopeners;
85010015Speter		if (error != 0)
85110015Speter			goto out;
85210015Speter		/* XXX: we should goto_top if siparam slept */
85310015Speter
85410015Speter		ttsetwater(tp);
85510015Speter
85610015Speter		/* set initial DCD state */
85710015Speter		pp->sp_last_hi_ip = ccbp->hi_ip;
85810015Speter		if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) {
85910015Speter			(*linesw[tp->t_line].l_modem)(tp, 1);
86010015Speter		}
86110015Speter	}
86210015Speter
86310015Speter	/* whoops! we beat the close! */
86410015Speter	if (pp->sp_state & SS_CLOSING) {
86510015Speter		/* try and stop it from proceeding to bash the hardware */
86610015Speter		pp->sp_state &= ~SS_CLOSING;
86710015Speter	}
86810015Speter
86910015Speter	/*
87010015Speter	 * Wait for DCD if necessary
87110015Speter	 */
87210015Speter	if (!(tp->t_state & TS_CARR_ON)
87310015Speter	    && !IS_CALLOUT(mynor)
87410015Speter	    && !(tp->t_cflag & CLOCAL)
87510015Speter	    && !(flag & O_NONBLOCK)) {
87610015Speter		++pp->sp_wopeners;
87710015Speter		DPRINT((pp, DBG_OPEN, "sleeping for carrier\n"));
87810015Speter		error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0);
87910015Speter		--pp->sp_wopeners;
88010015Speter		if (error != 0)
88110015Speter			goto out;
88210015Speter		goto open_top;
88310015Speter	}
88410015Speter
88510015Speter	error = (*linesw[tp->t_line].l_open)(dev, tp);
88610015Speter	si_disc_optim(tp, &tp->t_termios, pp);
88710015Speter	if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor))
88810015Speter		pp->sp_active_out = TRUE;
88910015Speter
89010015Speter	pp->sp_state |= SS_OPEN;	/* made it! */
89110015Speter
89210015Speterout:
89310015Speter	splx(oldspl);
89410015Speter
89510015Speter	DPRINT((pp, DBG_OPEN, "leaving siopen\n"));
89610015Speter
89710015Speter	if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0)
89810015Speter		sihardclose(pp);
89910015Speter
90010015Speter	return(error);
90110015Speter}
90210015Speter
90312675Sjulianstatic	int
90410015Spetersiclose(dev, flag, mode, p)
90510015Speter	dev_t dev;
90610015Speter	int flag, mode;
90710015Speter	struct proc *p;
90810015Speter{
90910015Speter	register struct si_port *pp;
91010015Speter	register struct tty *tp;
91110015Speter	int oldspl;
91210015Speter	int error = 0;
91310015Speter	int mynor = minor(dev);
91410015Speter
91510015Speter	if (IS_SPECIAL(mynor))
91610015Speter		return(0);
91710015Speter
91810015Speter	oldspl = spltty();
91910015Speter
92010015Speter	pp = MINOR2PP(mynor);
92110015Speter	tp = pp->sp_tty;
92210015Speter
92310015Speter	DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%x,%x,%x,%x) sp_state:%x\n",
92410015Speter		dev, flag, mode, p, pp->sp_state));
92510015Speter
92610015Speter	/* did we sleep and loose a race? */
92710015Speter	if (pp->sp_state & SS_CLOSING) {
92810015Speter		/* error = ESOMETING? */
92910015Speter		goto out;
93010015Speter	}
93110015Speter
93210015Speter	/* begin race detection.. */
93310015Speter	pp->sp_state |= SS_CLOSING;
93410015Speter
93510015Speter	si_write_enable(pp, 0);		/* block writes for ttywait() */
93610015Speter
93710015Speter	/* THIS MAY SLEEP IN TTYWAIT!!! */
93810015Speter	(*linesw[tp->t_line].l_close)(tp, flag);
93910015Speter
94010015Speter	si_write_enable(pp, 1);
94110015Speter
94210015Speter	/* did we sleep and somebody started another open? */
94310015Speter	if (!(pp->sp_state & SS_CLOSING)) {
94410015Speter		/* error = ESOMETING? */
94510015Speter		goto out;
94610015Speter	}
94710015Speter	/* ok. we are now still on the right track.. nuke the hardware */
94810015Speter
94910015Speter	if (pp->sp_state & SS_LSTART) {
95010015Speter		untimeout((timeout_func_t)si_lstart, (caddr_t)pp);
95110015Speter		pp->sp_state &= ~SS_LSTART;
95210015Speter	}
95310015Speter
95410015Speter	sistop(tp, FREAD | FWRITE);
95510015Speter
95610015Speter	sihardclose(pp);
95710015Speter	ttyclose(tp);
95810015Speter	pp->sp_state &= ~SS_OPEN;
95910015Speter
96010015Speterout:
96110015Speter	DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n"));
96210015Speter	splx(oldspl);
96310015Speter	return(error);
96410015Speter}
96510015Speter
96610015Speterstatic void
96710015Spetersihardclose(pp)
96810015Speter	struct si_port *pp;
96910015Speter{
97010015Speter	int oldspl;
97110015Speter	struct tty *tp;
97210015Speter	volatile struct si_channel *ccbp;
97310015Speter
97410015Speter	oldspl = spltty();
97510015Speter
97610015Speter	tp = pp->sp_tty;
97710015Speter	ccbp = pp->sp_ccb;			/* Find control block */
97810015Speter	if (tp->t_cflag & HUPCL
97910015Speter	    || !pp->sp_active_out
98010015Speter	       && !(ccbp->hi_ip & IP_DCD)
98110015Speter	       && !(pp->sp_iin.c_cflag && CLOCAL)
98210015Speter	    || !(tp->t_state & TS_ISOPEN)) {
98310015Speter
98410015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
98510015Speter		(void) si_command(pp, FCLOSE, SI_NOWAIT);
98610015Speter
98710015Speter		if (pp->sp_dtr_wait != 0) {
98810015Speter			timeout(sidtrwakeup, pp, pp->sp_dtr_wait);
98910015Speter			pp->sp_state |= SS_DTR_OFF;
99010015Speter		}
99110015Speter
99210015Speter	}
99310015Speter	pp->sp_active_out = FALSE;
99410015Speter	wakeup((caddr_t)&pp->sp_active_out);
99510015Speter	wakeup(TSA_CARR_ON(tp));
99610015Speter
99710015Speter	splx(oldspl);
99810015Speter}
99910015Speter
100010015Speter
100110015Speter/*
100210015Speter * called at splsoftclock()...
100310015Speter */
100410015Speterstatic void
100510015Spetersidtrwakeup(chan)
100610015Speter	void *chan;
100710015Speter{
100810015Speter	struct si_port *pp;
100910015Speter	int oldspl;
101010015Speter
101110015Speter	oldspl = spltty();
101210015Speter
101310015Speter	pp = (struct si_port *)chan;
101410015Speter	pp->sp_state &= ~SS_DTR_OFF;
101510015Speter	wakeup(&pp->sp_dtr_wait);
101610015Speter
101710015Speter	splx(oldspl);
101810015Speter}
101910015Speter
102010015Speter/*
102110015Speter * User level stuff - read and write
102210015Speter */
102312675Sjulianstatic	int
102410015Spetersiread(dev, uio, flag)
102510015Speter	register dev_t dev;
102610015Speter	struct uio *uio;
102710015Speter	int flag;
102810015Speter{
102910015Speter	register struct tty *tp;
103010015Speter	int mynor = minor(dev);
103110015Speter
103210015Speter	if (IS_SPECIAL(mynor)) {
103310015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_READ, "siread(CONTROLDEV!!)\n"));
103410015Speter		return(ENODEV);
103510015Speter	}
103610015Speter	tp = MINOR2TP(mynor);
103710015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_READ,
103810015Speter		"siread(%x,%x,%x)\n", dev, uio, flag));
103910015Speter	return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
104010015Speter}
104110015Speter
104210015Speter
104312675Sjulianstatic	int
104410015Spetersiwrite(dev, uio, flag)
104510015Speter	dev_t dev;
104610015Speter	struct uio *uio;
104710015Speter	int flag;
104810015Speter{
104910015Speter	register struct si_port *pp;
105010015Speter	register struct tty *tp;
105110015Speter	int error = 0;
105210015Speter	int mynor = minor(dev);
105310015Speter	int oldspl;
105410015Speter
105510015Speter	if (IS_SPECIAL(mynor)) {
105610015Speter		DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n"));
105710015Speter		return(ENODEV);
105810015Speter	}
105910015Speter	pp = MINOR2PP(mynor);
106010015Speter	tp = pp->sp_tty;
106110015Speter	DPRINT((pp, DBG_WRITE, "siwrite(%x,%x,%x)\n", dev, uio, flag));
106210015Speter
106310015Speter	oldspl = spltty();
106410015Speter	/*
106510015Speter	 * If writes are currently blocked, wait on the "real" tty
106610015Speter	 */
106710015Speter	while (pp->sp_state & SS_BLOCKWRITE) {
106810015Speter		pp->sp_state |= SS_WAITWRITE;
106910015Speter		DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n"));
107010015Speter		if (error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH,
107110015Speter				     "siwrite", 0))
107210015Speter			goto out;
107310015Speter	}
107410015Speter
107510015Speter	error = (*linesw[tp->t_line].l_write)(tp, uio, flag);
107610015Speterout:
107710015Speter	splx(oldspl);
107810015Speter	return (error);
107910015Speter}
108010015Speter
108110015Speter
108212675Sjulianstatic	struct tty *
108310015Spetersidevtotty(dev_t dev)
108410015Speter{
108510015Speter	struct si_port *pp;
108610015Speter	int mynor = minor(dev);
108710015Speter	struct si_softc *sc = &si_softc[SI_CARD(mynor)];
108810015Speter
108910015Speter	if (IS_SPECIAL(mynor))
109010015Speter		return(NULL);
109110015Speter	if (SI_PORT(mynor) >= sc->sc_nport)
109210015Speter		return(NULL);
109310015Speter	pp = MINOR2PP(mynor);
109410015Speter	return (pp->sp_tty);
109510015Speter}
109610015Speter
109712675Sjulianstatic	int
109810015Spetersiioctl(dev, cmd, data, flag, p)
109910015Speter	dev_t dev;
110010015Speter	int cmd;
110110015Speter	caddr_t data;
110210015Speter	int flag;
110310015Speter	struct proc *p;
110410015Speter{
110510015Speter	struct si_port *pp;
110610015Speter	register struct tty *tp;
110710015Speter	int error;
110810015Speter	int mynor = minor(dev);
110910015Speter	int oldspl;
111010015Speter	int blocked = 0;
111110015Speter#if defined(COMPAT_43)
111210015Speter	int oldcmd;
111310015Speter	struct termios term;
111410015Speter#endif
111510015Speter
111610015Speter	if (IS_SI_IOCTL(cmd))
111710015Speter		return(si_Sioctl(dev, cmd, data, flag, p));
111810015Speter
111910015Speter	pp = MINOR2PP(mynor);
112010015Speter	tp = pp->sp_tty;
112110015Speter
112210015Speter	DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%x,%x,%x,%x)\n",
112310015Speter		dev, cmd, data, flag));
112410015Speter	if (IS_STATE(mynor)) {
112510015Speter		struct termios *ct;
112610015Speter
112710015Speter		switch (mynor & SI_STATE_MASK) {
112810015Speter		case SI_INIT_STATE_MASK:
112910015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
113010015Speter			break;
113110015Speter		case SI_LOCK_STATE_MASK:
113210015Speter			ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin;
113310015Speter			break;
113410015Speter		default:
113510015Speter			return (ENODEV);
113610015Speter		}
113710015Speter		switch (cmd) {
113810015Speter		case TIOCSETA:
113910015Speter			error = suser(p->p_ucred, &p->p_acflag);
114010015Speter			if (error != 0)
114110015Speter				return (error);
114210015Speter			*ct = *(struct termios *)data;
114310015Speter			return (0);
114410015Speter		case TIOCGETA:
114510015Speter			*(struct termios *)data = *ct;
114610015Speter			return (0);
114710015Speter		case TIOCGETD:
114810015Speter			*(int *)data = TTYDISC;
114910015Speter			return (0);
115010015Speter		case TIOCGWINSZ:
115110015Speter			bzero(data, sizeof(struct winsize));
115210015Speter			return (0);
115310015Speter		default:
115410015Speter			return (ENOTTY);
115510015Speter		}
115610015Speter	}
115710015Speter	/*
115810015Speter	 * Do the old-style ioctl compat routines...
115910015Speter	 */
116010015Speter#if defined(COMPAT_43)
116110015Speter	term = tp->t_termios;
116210015Speter	oldcmd = cmd;
116310015Speter	error = ttsetcompat(tp, &cmd, data, &term);
116410015Speter	if (error != 0)
116510015Speter		return (error);
116610015Speter	if (cmd != oldcmd)
116710015Speter		data = (caddr_t)&term;
116810015Speter#endif
116910015Speter	/*
117010015Speter	 * Do the initial / lock state business
117110015Speter	 */
117210015Speter	if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
117310015Speter		int     cc;
117410015Speter		struct termios *dt = (struct termios *)data;
117510015Speter		struct termios *lt = mynor & SI_CALLOUT_MASK
117610015Speter				     ? &pp->sp_lout : &pp->sp_lin;
117710015Speter
117810015Speter		dt->c_iflag = (tp->t_iflag & lt->c_iflag)
117910015Speter			| (dt->c_iflag & ~lt->c_iflag);
118010015Speter		dt->c_oflag = (tp->t_oflag & lt->c_oflag)
118110015Speter			| (dt->c_oflag & ~lt->c_oflag);
118210015Speter		dt->c_cflag = (tp->t_cflag & lt->c_cflag)
118310015Speter			| (dt->c_cflag & ~lt->c_cflag);
118410015Speter		dt->c_lflag = (tp->t_lflag & lt->c_lflag)
118510015Speter			| (dt->c_lflag & ~lt->c_lflag);
118610015Speter		for (cc = 0; cc < NCCS; ++cc)
118710015Speter			if (lt->c_cc[cc] != 0)
118810015Speter				dt->c_cc[cc] = tp->t_cc[cc];
118910015Speter		if (lt->c_ispeed != 0)
119010015Speter			dt->c_ispeed = tp->t_ispeed;
119110015Speter		if (lt->c_ospeed != 0)
119210015Speter			dt->c_ospeed = tp->t_ospeed;
119310015Speter	}
119410015Speter
119510015Speter	/*
119610015Speter	 * Block user-level writes to give the ttywait()
119710015Speter	 * a chance to completely drain for commands
119810015Speter	 * that require the port to be in a quiescent state.
119910015Speter	 */
120010015Speter	switch (cmd) {
120110015Speter	case TIOCSETAW: case TIOCSETAF:
120210015Speter	case TIOCDRAIN: case TIOCSETP:
120310015Speter		blocked++;	/* block writes for ttywait() and siparam() */
120410015Speter		si_write_enable(pp, 0);
120510015Speter	}
120610015Speter
120710015Speter	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
120810015Speter	if (error >= 0)
120910015Speter		goto out;
121010015Speter
121110015Speter	oldspl = spltty();
121210015Speter
121310015Speter	error = ttioctl(tp, cmd, data, flag);
121410015Speter	si_disc_optim(tp, &tp->t_termios, pp);
121510015Speter	if (error >= 0)
121610015Speter		goto outspl;
121710015Speter
121810015Speter	switch (cmd) {
121910015Speter	case TIOCSBRK:
122010015Speter		si_command(pp, SBREAK, SI_NOWAIT);
122110015Speter		break;
122210015Speter	case TIOCCBRK:
122310015Speter		si_command(pp, EBREAK, SI_NOWAIT);
122410015Speter		break;
122510015Speter	case TIOCSDTR:
122610015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
122710015Speter		break;
122810015Speter	case TIOCCDTR:
122910015Speter		(void) si_modem(pp, SET, 0);
123010015Speter		break;
123110015Speter	case TIOCMSET:
123210015Speter		(void) si_modem(pp, SET, *(int *)data);
123310015Speter		break;
123410015Speter	case TIOCMBIS:
123510015Speter		(void) si_modem(pp, BIS, *(int *)data);
123610015Speter		break;
123710015Speter	case TIOCMBIC:
123810015Speter		(void) si_modem(pp, BIC, *(int *)data);
123910015Speter		break;
124010015Speter	case TIOCMGET:
124110015Speter		*(int *)data = si_modem(pp, GET, 0);
124210015Speter		break;
124310015Speter	case TIOCMSDTRWAIT:
124410015Speter		/* must be root since the wait applies to following logins */
124510015Speter		error = suser(p->p_ucred, &p->p_acflag);
124610015Speter		if (error != 0) {
124710015Speter			goto outspl;
124810015Speter		}
124910015Speter		pp->sp_dtr_wait = *(int *)data * hz / 100;
125010015Speter		break;
125110015Speter	case TIOCMGDTRWAIT:
125210015Speter		*(int *)data = pp->sp_dtr_wait * 100 / hz;
125310015Speter		break;
125410015Speter
125510015Speter	default:
125610015Speter		error = ENOTTY;
125710015Speter	}
125810015Speter	error = 0;
125910015Speteroutspl:
126010015Speter	splx(oldspl);
126110015Speterout:
126210015Speter	DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error));
126310015Speter	if (blocked)
126410015Speter		si_write_enable(pp, 1);
126510015Speter	return(error);
126610015Speter}
126710015Speter
126810015Speter/*
126910015Speter * Handle the Specialix ioctls. All MUST be called via the CONTROL device
127010015Speter */
127110015Speterstatic int
127210015Spetersi_Sioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
127310015Speter{
127410015Speter	struct si_softc *xsc;
127510015Speter	register struct si_port *xpp;
127610015Speter	volatile struct si_reg *regp;
127710015Speter	struct si_tcsi *dp;
127810044Speter	struct si_pstat *sps;
127911872Sphk	int *ip, error = 0;
128010015Speter	int oldspl;
128110015Speter	int card, port;
128210015Speter	int mynor = minor(dev);
128310015Speter
128410015Speter	DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%x,%x,%x,%x)\n",
128510015Speter		dev, cmd, data, flag));
128610015Speter
128710044Speter#if 1
128810044Speter	DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT));
128910044Speter	DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB));
129010044Speter	DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY));
129110044Speter#endif
129210044Speter
129310015Speter	if (!IS_CONTROLDEV(mynor)) {
129410015Speter		DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n"));
129510015Speter		return(ENODEV);
129610015Speter	}
129710015Speter
129810015Speter	oldspl = spltty();	/* better safe than sorry */
129910015Speter
130010015Speter	ip = (int *)data;
130110015Speter
130210015Speter#define SUCHECK if (error = suser(p->p_ucred, &p->p_acflag)) goto out
130310015Speter
130410015Speter	switch (cmd) {
130510015Speter	case TCSIPORTS:
130610015Speter		*ip = si_Nports;
130710015Speter		goto out;
130810015Speter	case TCSIMODULES:
130910015Speter		*ip = si_Nmodules;
131010015Speter		goto out;
131110015Speter	case TCSISDBG_ALL:
131210015Speter		SUCHECK;
131310015Speter		si_debug = *ip;
131410015Speter		goto out;
131510015Speter	case TCSIGDBG_ALL:
131610015Speter		*ip = si_debug;
131710015Speter		goto out;
131810015Speter	default:
131910015Speter		/*
132010015Speter		 * Check that a controller for this port exists
132110015Speter		 */
132210044Speter
132310044Speter		/* may also be a struct si_pstat, a superset of si_tcsi */
132410044Speter
132510015Speter		dp = (struct si_tcsi *)data;
132610044Speter		sps = (struct si_pstat *)data;
132710015Speter		card = dp->tc_card;
132810015Speter		xsc = &si_softc[card];	/* check.. */
132912174Speter		if (card < 0 || card >= NSI || xsc->sc_type == SIEMPTY) {
133010015Speter			error = ENOENT;
133110015Speter			goto out;
133210015Speter		}
133310015Speter		/*
133410015Speter		 * And check that a port exists
133510015Speter		 */
133610015Speter		port = dp->tc_port;
133710015Speter		if (port < 0 || port >= xsc->sc_nport) {
133810015Speter			error = ENOENT;
133910015Speter			goto out;
134010015Speter		}
134110015Speter		xpp = xsc->sc_ports + port;
134210015Speter		regp = (struct si_reg *)xsc->sc_maddr;
134310015Speter	}
134410015Speter
134510015Speter	switch (cmd) {
134610015Speter	case TCSIDEBUG:
134710015Speter#ifdef	SI_DEBUG
134810015Speter		SUCHECK;
134910015Speter		if (xpp->sp_debug)
135010015Speter			xpp->sp_debug = 0;
135110015Speter		else {
135210015Speter			xpp->sp_debug = DBG_ALL;
135310015Speter			DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n",
135410015Speter				(xpp->sp_debug&DBG_ALL)?"ON":"OFF"));
135510015Speter		}
135610015Speter		break;
135710015Speter#else
135810015Speter		error = ENODEV;
135910015Speter		goto out;
136010015Speter#endif
136110015Speter	case TCSISDBG_LEVEL:
136210015Speter	case TCSIGDBG_LEVEL:
136310015Speter#ifdef	SI_DEBUG
136410015Speter		if (cmd == TCSIGDBG_LEVEL) {
136510015Speter			dp->tc_dbglvl = xpp->sp_debug;
136610015Speter		} else {
136710015Speter			SUCHECK;
136810015Speter			xpp->sp_debug = dp->tc_dbglvl;
136910015Speter		}
137010015Speter		break;
137110015Speter#else
137210015Speter		error = ENODEV;
137310015Speter		goto out;
137410015Speter#endif
137510015Speter	case TCSIGRXIT:
137610015Speter		dp->tc_int = regp->rx_int_count;
137710015Speter		break;
137810015Speter	case TCSIRXIT:
137910015Speter		SUCHECK;
138010015Speter		regp->rx_int_count = dp->tc_int;
138110015Speter		break;
138210015Speter	case TCSIGIT:
138310015Speter		dp->tc_int = regp->int_count;
138410015Speter		break;
138510015Speter	case TCSIIT:
138610015Speter		SUCHECK;
138710015Speter		regp->int_count = dp->tc_int;
138810015Speter		break;
138910044Speter	case TCSISTATE:
139010044Speter		dp->tc_int = xpp->sp_ccb->hi_ip;
139110015Speter		break;
139210044Speter	/* these next three use a different structure */
139310044Speter	case TCSI_PORT:
139410015Speter		SUCHECK;
139510044Speter		sps->tc_siport = *xpp;
139610015Speter		break;
139710044Speter	case TCSI_CCB:
139810044Speter		SUCHECK;
139910044Speter		sps->tc_ccb = *xpp->sp_ccb;
140010015Speter		break;
140110044Speter	case TCSI_TTY:
140210044Speter		SUCHECK;
140310044Speter		sps->tc_tty = *xpp->sp_tty;
140410015Speter		break;
140510015Speter	default:
140610015Speter		error = EINVAL;
140710015Speter		goto out;
140810015Speter	}
140910015Speterout:
141010015Speter	splx(oldspl);
141110015Speter	return(error);		/* success */
141210015Speter}
141310015Speter
141410015Speter/*
141510015Speter *	siparam()	: Configure line params
141610015Speter *	called at spltty();
141710015Speter *	this may sleep, does not flush, nor wait for drain, nor block writes
141810015Speter *	caller must arrange this if it's important..
141910015Speter */
142012724Sphkstatic int
142110015Spetersiparam(tp, t)
142210015Speter	register struct tty *tp;
142310015Speter	register struct termios *t;
142410015Speter{
142510015Speter	register struct si_port *pp = TP2PP(tp);
142610015Speter	volatile struct si_channel *ccbp;
142710015Speter	int oldspl, cflag, iflag, oflag, lflag;
142810015Speter	int error = 0;		/* shutup gcc */
142910015Speter	int ispeed = 0;		/* shutup gcc */
143010015Speter	int ospeed = 0;		/* shutup gcc */
143110161Speter	BYTE val;
143210015Speter
143310015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t));
143410015Speter	cflag = t->c_cflag;
143510015Speter	iflag = t->c_iflag;
143610015Speter	oflag = t->c_oflag;
143710015Speter	lflag = t->c_lflag;
143810044Speter	DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n",
143910044Speter		oflag, cflag, iflag, lflag));
144010015Speter
144110015Speter
144210015Speter	/* if not hung up.. */
144310015Speter	if (t->c_ospeed != 0) {
144410015Speter		/* translate baud rate to firmware values */
144510015Speter		ospeed = ttspeedtab(t->c_ospeed, bdrates);
144610015Speter		ispeed = t->c_ispeed ?
144710015Speter			 ttspeedtab(t->c_ispeed, bdrates) : ospeed;
144810015Speter
144910015Speter		/* enforce legit baud rate */
145010015Speter		if (ospeed < 0 || ispeed < 0)
145110015Speter			return (EINVAL);
145210015Speter	}
145310015Speter
145410015Speter
145510015Speter	oldspl = spltty();
145610015Speter
145710015Speter	ccbp = pp->sp_ccb;
145810015Speter
145910161Speter	/* ========== set hi_break ========== */
146010161Speter	val = 0;
146110161Speter	if (iflag & IGNBRK)		/* Breaks */
146210161Speter		val |= BR_IGN;
146310161Speter	if (iflag & BRKINT)		/* Interrupt on break? */
146410161Speter		val |= BR_INT;
146510161Speter	if (iflag & PARMRK)		/* Parity mark? */
146610161Speter		val |= BR_PARMRK;
146710161Speter	if (iflag & IGNPAR)		/* Ignore chars with parity errors? */
146810161Speter		val |= BR_PARIGN;
146910161Speter	ccbp->hi_break = val;
147010161Speter
147110161Speter	/* ========== set hi_csr ========== */
147210015Speter	/* if not hung up.. */
147310015Speter	if (t->c_ospeed != 0) {
147410015Speter		/* Set I/O speeds */
147510161Speter		 val = (ispeed << 4) | ospeed;
147610015Speter	}
147710161Speter	ccbp->hi_csr = val;
147810015Speter
147910161Speter	/* ========== set hi_mr2 ========== */
148010161Speter	val = 0;
148110015Speter	if (cflag & CSTOPB)				/* Stop bits */
148210161Speter		val |= MR2_2_STOP;
148310015Speter	else
148410161Speter		val |= MR2_1_STOP;
148510161Speter	/*
148610161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
148710161Speter	 * a DCE, hence the reverse sense of RTS and CTS
148810161Speter	 */
148910161Speter	/* Output Flow - RTS must be raised before data can be sent */
149010161Speter	if (cflag & CCTS_OFLOW)
149110161Speter		val |= MR2_RTSCONT;
149210161Speter
149310161Speter	ccbp->hi_mr1 = val;
149410161Speter
149510161Speter	/* ========== set hi_mr1 ========== */
149610161Speter	val = 0;
149710015Speter	if (!(cflag & PARENB))				/* Parity */
149810161Speter		val |= MR1_NONE;
149910015Speter	else
150010161Speter		val |= MR1_WITH;
150110015Speter	if (cflag & PARODD)
150210161Speter		val |= MR1_ODD;
150310015Speter
150410015Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
150510161Speter		val |= MR1_8_BITS;
150610015Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
150710161Speter		val |= MR1_7_BITS;
150810015Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
150910161Speter		val |= MR1_6_BITS;
151010015Speter	} else {					/* Must be 5 */
151110161Speter		val |= MR1_5_BITS;
151210015Speter	}
151310161Speter	/*
151410161Speter	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
151510161Speter	 * a DCE, hence the reverse sense of RTS and CTS
151610161Speter	 */
151710161Speter	/* Input Flow - CTS is raised when port is ready to receive data */
151810161Speter	if (cflag & CRTS_IFLOW)
151910161Speter		val |= MR1_CTSCONT;
152010015Speter
152110161Speter	ccbp->hi_mr1 = val;
152210161Speter
152310161Speter	/* ========== set hi_mask ========== */
152410161Speter	val = 0xff;
152510161Speter	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
152610161Speter		val &= 0xFF;
152710161Speter	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
152810161Speter		val &= 0x7F;
152910161Speter	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
153010161Speter		val &= 0x3F;
153110161Speter	} else {					/* Must be 5 */
153210161Speter		val &= 0x1F;
153310161Speter	}
153410015Speter	if (iflag & ISTRIP)
153510161Speter		val &= 0x7F;
153610015Speter
153710161Speter	ccbp->hi_mask = val;
153810161Speter
153910161Speter	/* ========== set hi_prtcl ========== */
154010161Speter	val = 0;
154110015Speter				/* Monitor DCD etc. if a modem */
154210015Speter	if (!(cflag & CLOCAL))
154310161Speter		val |= SP_DCEN;
154410161Speter	if (iflag & IXANY)
154510161Speter		val |= SP_TANY;
154610161Speter	if (iflag & IXON)
154710161Speter		val |= SP_TXEN;
154810161Speter	if (iflag & IXOFF)
154910161Speter		val |= SP_RXEN;
155010161Speter	if (iflag & INPCK)
155110161Speter		val |= SP_PAEN;
155210015Speter
155310161Speter	ccbp->hi_prtcl = val;
155410161Speter
155510161Speter
155610161Speter	/* ========== set hi_{rx|tx}{on|off} ========== */
155710161Speter	/* XXX: the card TOTALLY shields us from the flow control... */
155810015Speter	ccbp->hi_txon = t->c_cc[VSTART];
155910015Speter	ccbp->hi_txoff = t->c_cc[VSTOP];
156010015Speter
156110015Speter	ccbp->hi_rxon = t->c_cc[VSTART];
156210015Speter	ccbp->hi_rxoff = t->c_cc[VSTOP];
156310015Speter
156410161Speter	/* ========== send settings to the card ========== */
156510015Speter	/* potential sleep here */
156610015Speter	if (ccbp->hi_stat == IDLE_CLOSE)		/* Not yet open */
156710015Speter		si_command(pp, LOPEN, SI_WAIT);		/* open it */
156810015Speter	else
156910015Speter		si_command(pp, CONFIG, SI_WAIT);	/* change params */
157010015Speter
157110161Speter	/* ========== set DTR etc ========== */
157210015Speter	/* Hangup if ospeed == 0 */
157310015Speter	if (t->c_ospeed == 0) {
157410015Speter		(void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS);
157510015Speter	} else {
157610015Speter		/*
157710015Speter		 * If the previous speed was 0, may need to re-enable
157810015Speter	 	 * the modem signals
157910015Speter	 	 */
158010015Speter		(void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS);
158110015Speter	}
158210015Speter
158310044Speter	DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n",
158410044Speter		ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break));
158510015Speter
158610015Speter	splx(oldspl);
158710015Speter	return(error);
158810015Speter}
158910015Speter
159010015Speter/*
159110015Speter * Enable or Disable the writes to this channel...
159210015Speter * "state" ->  enabled = 1; disabled = 0;
159310015Speter */
159410015Speterstatic void
159510015Spetersi_write_enable(pp, state)
159610015Speter	register struct si_port *pp;
159710015Speter	int state;
159810015Speter{
159910015Speter	int oldspl;
160010015Speter
160110015Speter	oldspl = spltty();
160210015Speter
160310015Speter	if (state) {
160410015Speter		pp->sp_state &= ~SS_BLOCKWRITE;
160510015Speter		if (pp->sp_state & SS_WAITWRITE) {
160610015Speter			pp->sp_state &= ~SS_WAITWRITE;
160710015Speter			/* thunder away! */
160810015Speter			wakeup((caddr_t)pp);
160910015Speter		}
161010015Speter	} else {
161110015Speter		pp->sp_state |= SS_BLOCKWRITE;
161210015Speter	}
161310015Speter
161410015Speter	splx(oldspl);
161510015Speter}
161610015Speter
161710015Speter/*
161810015Speter * Set/Get state of modem control lines.
161910015Speter * Due to DCE-like behaviour of the adapter, some signals need translation:
162010015Speter *	TIOCM_DTR	DSR
162110015Speter *	TIOCM_RTS	CTS
162210015Speter */
162310015Speterstatic int
162410015Spetersi_modem(pp, cmd, bits)
162510015Speter	struct si_port *pp;
162610015Speter	enum si_mctl cmd;
162710015Speter	int bits;
162810015Speter{
162910015Speter	volatile struct si_channel *ccbp;
163010015Speter	int x;
163110015Speter
163210015Speter	DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits));
163310015Speter	ccbp = pp->sp_ccb;		/* Find channel address */
163410015Speter	switch (cmd) {
163510015Speter	case GET:
163610015Speter		x = ccbp->hi_ip;
163710015Speter		bits = TIOCM_LE;
163810015Speter		if (x & IP_DCD)		bits |= TIOCM_CAR;
163910015Speter		if (x & IP_DTR)		bits |= TIOCM_DTR;
164010015Speter		if (x & IP_RTS)		bits |= TIOCM_RTS;
164110015Speter		if (x & IP_RI)		bits |= TIOCM_RI;
164210015Speter		return(bits);
164310015Speter	case SET:
164410015Speter		ccbp->hi_op &= ~(OP_DSR|OP_CTS);
164510015Speter		/* fall through */
164610015Speter	case BIS:
164710015Speter		x = 0;
164810015Speter		if (bits & TIOCM_DTR)
164910015Speter			x |= OP_DSR;
165010015Speter		if (bits & TIOCM_RTS)
165110015Speter			x |= OP_CTS;
165210015Speter		ccbp->hi_op |= x;
165310015Speter		break;
165410015Speter	case BIC:
165510015Speter		if (bits & TIOCM_DTR)
165610015Speter			ccbp->hi_op &= ~OP_DSR;
165710015Speter		if (bits & TIOCM_RTS)
165810015Speter			ccbp->hi_op &= ~OP_CTS;
165910015Speter	}
166010015Speter	return 0;
166110015Speter}
166210015Speter
166310015Speter/*
166410015Speter * Handle change of modem state
166510015Speter */
166610015Speterstatic void
166710015Spetersi_modem_state(pp, tp, hi_ip)
166810015Speter	register struct si_port *pp;
166910015Speter	register struct tty *tp;
167010015Speter	register int hi_ip;
167110015Speter{
167210015Speter							/* if a modem dev */
167310015Speter	if (hi_ip & IP_DCD) {
167410015Speter		if ( !(pp->sp_last_hi_ip & IP_DCD)) {
167510015Speter			DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n",
167610015Speter				tp->t_line));
167710015Speter			(void)(*linesw[tp->t_line].l_modem)(tp, 1);
167810015Speter		}
167910015Speter	} else {
168010015Speter		if (pp->sp_last_hi_ip & IP_DCD) {
168110015Speter			DPRINT((pp, DBG_INTR, "modem carr off\n"));
168210015Speter			if ((*linesw[tp->t_line].l_modem)(tp, 0))
168310015Speter				(void) si_modem(pp, SET, 0);
168410015Speter		}
168510015Speter	}
168610015Speter	pp->sp_last_hi_ip = hi_ip;
168710015Speter
168810015Speter}
168910015Speter
169010015Speter/*
169110015Speter * Poller to catch missed interrupts.
169212174Speter *
169312496Speter * Note that the SYSV Specialix drivers poll at 100 times per second to get
169412496Speter * better response.  We could really use a "periodic" version timeout(). :-)
169510015Speter */
169610015Speter#ifdef POLL
169710708Speterstatic void
169810015Spetersi_poll(void *nothing)
169910015Speter{
170010015Speter	register struct si_softc *sc;
170110015Speter	register int i;
170210015Speter	volatile struct si_reg *regp;
170312174Speter	register struct si_port *pp;
170412174Speter	int lost, oldspl, port;
170510015Speter
170610015Speter	DPRINT((0, DBG_POLL, "si_poll()\n"));
170711609Speter	oldspl = spltty();
170810015Speter	if (in_intr)
170910015Speter		goto out;
171010015Speter	lost = 0;
171110015Speter	for (i=0; i<NSI; i++) {
171210015Speter		sc = &si_softc[i];
171312174Speter		if (sc->sc_type == SIEMPTY)
171410015Speter			continue;
171510015Speter		regp = (struct si_reg *)sc->sc_maddr;
171610015Speter		/*
171710015Speter		 * See if there has been a pending interrupt for 2 seconds
171810015Speter		 * or so. The test <int_scounter >= 200) won't correspond
171910015Speter		 * to 2 seconds if int_count gets changed.
172010015Speter		 */
172110015Speter		if (regp->int_pending != 0) {
172210015Speter			if (regp->int_scounter >= 200 &&
172310015Speter			    regp->initstat == 1) {
172412174Speter				printf("si%d: lost intr\n", i);
172510015Speter				lost++;
172610015Speter			}
172710015Speter		} else {
172810015Speter			regp->int_scounter = 0;
172910015Speter		}
173010015Speter
173112174Speter		/*
173212174Speter		 * gripe about no input flow control..
173312174Speter		 */
173412174Speter		pp = sc->sc_ports;
173512174Speter		for (port = 0; port < sc->sc_nport; pp++, port++) {
173612174Speter			if (pp->sp_delta_overflows > 0) {
173712174Speter				printf("si%d: %d tty level buffer overflows\n",
173812174Speter					i, pp->sp_delta_overflows);
173912174Speter				pp->sp_delta_overflows = 0;
174012174Speter			}
174112174Speter		}
174210015Speter	}
174310015Speter	if (lost)
174410015Speter		siintr(-1);	/* call intr with fake vector */
174511609Speterout:
174610015Speter	splx(oldspl);
174710015Speter
174810015Speter	timeout(si_poll, (caddr_t)0L, POLL_INTERVAL);
174910015Speter}
175010015Speter#endif	/* ifdef POLL */
175110015Speter
175210015Speter/*
175310015Speter * The interrupt handler polls ALL ports on ALL adapters each time
175410015Speter * it is called.
175510015Speter */
175610015Speter
175712496Speterstatic BYTE si_rxbuf[SI_BUFFERSIZE];	/* input staging area */
175810015Speter
175910708Spetervoid
176011609Spetersiintr(int unit)
176110015Speter{
176210015Speter	register struct si_softc *sc;
176310015Speter
176410015Speter	register struct si_port *pp;
176510015Speter	volatile struct si_channel *ccbp;
176610015Speter	register struct tty *tp;
176710015Speter	volatile caddr_t maddr;
176811872Sphk	BYTE op, ip;
176912174Speter	int x, card, port, n, i, isopen;
177010015Speter	volatile BYTE *z;
177110015Speter	BYTE c;
177210015Speter
177311609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "siintr(%d)\n", unit));
177411609Speter	if (in_intr) {
177511609Speter		if (unit < 0)	/* should never happen */
177610708Speter			return;
177712174Speter		printf("si%d: Warning interrupt handler re-entered\n",
177811609Speter			unit);
177910708Speter		return;
178010015Speter	}
178110015Speter	in_intr = 1;
178210015Speter
178310015Speter	/*
178410015Speter	 * When we get an int we poll all the channels and do ALL pending
178510015Speter	 * work, not just the first one we find. This allows all cards to
178610015Speter	 * share the same vector.
178710015Speter	 */
178810015Speter	for (card=0; card < NSI; card++) {
178910015Speter		sc = &si_softc[card];
179012174Speter		if (sc->sc_type == SIEMPTY)
179110015Speter			continue;
179212174Speter
179312174Speter		/*
179412174Speter		 * First, clear the interrupt
179512174Speter		 */
179610015Speter		switch(sc->sc_type) {
179710015Speter		case SIHOST :
179810015Speter			maddr = sc->sc_maddr;
179910015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
180010015Speter							/* flag nothing pending */
180110015Speter			*(maddr+SIINTCL) = 0x00;	/* Set IRQ clear */
180210015Speter			*(maddr+SIINTCL_CL) = 0x00;	/* Clear IRQ clear */
180310015Speter			break;
180410015Speter		case SIHOST2:
180510015Speter			maddr = sc->sc_maddr;
180610015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
180710015Speter			*(maddr+SIPLIRQCLR) = 0x00;
180810015Speter			*(maddr+SIPLIRQCLR) = 0x10;
180910015Speter			break;
181010015Speter		case SIEISA:
181110015Speter#if NEISA > 0
181210015Speter			maddr = sc->sc_maddr;
181310015Speter			((volatile struct si_reg *)maddr)->int_pending = 0;
181410015Speter			(void)inb(sc->sc_eisa_iobase+3);
181510015Speter			break;
181610015Speter#endif	/* fall through if not EISA kernel */
181710015Speter		case SIEMPTY:
181810015Speter		default:
181910015Speter			continue;
182010015Speter		}
182110015Speter		((volatile struct si_reg *)maddr)->int_scounter = 0;
182210015Speter
182312174Speter		/*
182412174Speter		 * check each port
182512174Speter		 */
182612174Speter		for (pp=sc->sc_ports,port=0; port < sc->sc_nport; pp++,port++) {
182710015Speter			ccbp = pp->sp_ccb;
182810015Speter			tp = pp->sp_tty;
182910015Speter
183012174Speter
183110015Speter			/*
183210015Speter			 * See if a command has completed ?
183310015Speter			 */
183410015Speter			if (ccbp->hi_stat != pp->sp_pend) {
183510015Speter				DPRINT((pp, DBG_INTR,
183610015Speter					"siintr hi_stat = 0x%x, pend = %d\n",
183710015Speter					ccbp->hi_stat, pp->sp_pend));
183810015Speter				switch(pp->sp_pend) {
183910015Speter				case LOPEN:
184010015Speter				case MPEND:
184110015Speter				case MOPEN:
184210015Speter				case CONFIG:
184310015Speter					pp->sp_pend = ccbp->hi_stat;
184410015Speter						/* sleeping in si_command */
184510015Speter					wakeup(&pp->sp_state);
184610015Speter					break;
184710015Speter				default:
184810015Speter					pp->sp_pend = ccbp->hi_stat;
184910015Speter				}
185010015Speter	 		}
185110015Speter
185210015Speter			/*
185310015Speter			 * Continue on if it's closed
185410015Speter			 */
185510015Speter			if (ccbp->hi_stat == IDLE_CLOSE) {
185610015Speter				continue;
185710015Speter			}
185810015Speter
185910015Speter			/*
186010015Speter			 * Do modem state change if not a local device
186110015Speter			 */
186210015Speter			si_modem_state(pp, tp, ccbp->hi_ip);
186310015Speter
186410015Speter			/*
186512174Speter			 * Check to see if there's we should 'receive'
186612174Speter			 * characters.
186712174Speter			 */
186812174Speter			if (tp->t_state & TS_CONNECTED &&
186912174Speter			    tp->t_state & TS_ISOPEN)
187012174Speter				isopen = 1;
187112174Speter			else
187212174Speter				isopen = 0;
187312174Speter
187412174Speter			/*
187510015Speter			 * Do break processing
187610015Speter			 */
187710015Speter			if (ccbp->hi_state & ST_BREAK) {
187812174Speter				if (isopen) {
187912174Speter				    (*linesw[tp->t_line].l_rint)(TTY_BI, tp);
188010015Speter				}
188110015Speter				ccbp->hi_state &= ~ST_BREAK;   /* A Bit iffy this */
188210015Speter				DPRINT((pp, DBG_INTR, "si_intr break\n"));
188310015Speter			}
188410015Speter
188510015Speter			/*
188612174Speter			 * Do RX stuff - if not open then dump any characters.
188712174Speter			 * XXX: This is VERY messy and needs to be cleaned up.
188812174Speter			 *
188912174Speter			 * XXX: can we leave data in the host adapter buffer
189012174Speter			 * when the clists are full?  That may be dangerous
189112174Speter			 * if the user cannot get an interrupt signal through.
189210015Speter			 */
189310015Speter
189412174Speter	more_rx:	/* XXX Sorry. the nesting was driving me bats! :-( */
189512174Speter
189612174Speter			if (!isopen) {
189710015Speter				ccbp->hi_rxopos = ccbp->hi_rxipos;
189812174Speter				goto end_rx;
189912174Speter			}
190010015Speter
190112174Speter			/*
190212174Speter			 * Process read characters if not skipped above
190312174Speter			 */
190412174Speter			c = ccbp->hi_rxipos - ccbp->hi_rxopos;
190512174Speter			if (c == 0) {
190612174Speter				goto end_rx;
190712174Speter			}
190810015Speter
190912174Speter			op = ccbp->hi_rxopos;
191012174Speter			ip = ccbp->hi_rxipos;
191112174Speter			n = c & 0xff;
191212174Speter
191312174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
191410015Speter						n, op, ip));
191510015Speter
191612174Speter			/*
191712174Speter			 * Suck characters out of host card buffer into the
191812174Speter			 * "input staging buffer" - so that we dont leave the
191912174Speter			 * host card in limbo while we're possibly echoing
192012174Speter			 * characters and possibly flushing input inside the
192112174Speter			 * ldisc l_rint() routine.
192212174Speter			 */
192312496Speter			if (n <= SI_BUFFERSIZE - op) {
192410015Speter
192512174Speter				DPRINT((pp, DBG_INTR, "\tsingle copy\n"));
192612174Speter				z = ccbp->hi_rxbuf + op;
192712174Speter				bcopy((caddr_t)z, si_rxbuf, n);
192810015Speter
192912174Speter				op += n;
193012174Speter			} else {
193112496Speter				x = SI_BUFFERSIZE - op;
193210015Speter
193312174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x));
193412174Speter				z = ccbp->hi_rxbuf + op;
193512174Speter				bcopy((caddr_t)z, si_rxbuf, x);
193610015Speter
193712174Speter				DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n-x));
193812174Speter				z = ccbp->hi_rxbuf;
193912174Speter				bcopy((caddr_t)z, si_rxbuf+x, n-x);
194010015Speter
194112174Speter				op += n;
194212174Speter			}
194310015Speter
194412174Speter			/* clear collected characters from buffer */
194512174Speter			ccbp->hi_rxopos = op;
194612174Speter
194712174Speter			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
194810015Speter						n, op, ip));
194910015Speter
195012174Speter			/*
195112174Speter			 * at this point...
195212174Speter			 * n = number of chars placed in si_rxbuf
195312174Speter			 */
195410015Speter
195512174Speter			/*
195612174Speter			 * Avoid the grotesquely inefficient lineswitch
195712174Speter			 * routine (ttyinput) in "raw" mode. It usually
195812174Speter			 * takes about 450 instructions (that's without
195912174Speter			 * canonical processing or echo!). slinput is
196012174Speter			 * reasonably fast (usually 40 instructions
196112174Speter			 * plus call overhead).
196212174Speter			 */
196312174Speter			if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
196410015Speter
196512174Speter				/* block if the driver supports it */
196612174Speter				if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER
196712174Speter				    && (tp->t_cflag & CRTS_IFLOW
196812174Speter					|| tp->t_iflag & IXOFF)
196912174Speter				    && !(tp->t_state & TS_TBLOCK))
197012174Speter					ttyblock(tp);
197110015Speter
197212174Speter				tk_nin += n;
197312174Speter				tk_rawcc += n;
197412174Speter				tp->t_rawcc += n;
197512174Speter
197612174Speter				pp->sp_delta_overflows +=
197712174Speter				    b_to_q((char *)si_rxbuf, n, &tp->t_rawq);
197812174Speter
197912174Speter				ttwakeup(tp);
198012174Speter				if (tp->t_state & TS_TTSTOP
198112174Speter				    && (tp->t_iflag & IXANY
198212174Speter					|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
198312174Speter					tp->t_state &= ~TS_TTSTOP;
198412174Speter					tp->t_lflag &= ~FLUSHO;
198512174Speter					si_start(tp);
198612174Speter				}
198712174Speter			} else {
198812174Speter				/*
198912174Speter				 * It'd be nice to not have to go through the
199012174Speter				 * function call overhead for each char here.
199112174Speter				 * It'd be nice to block input it, saving a
199212174Speter				 * loop here and the call/return overhead.
199312174Speter				 */
199412174Speter				for(x = 0; x < n; x++) {
199512174Speter					i = si_rxbuf[x];
199612174Speter					if ((*linesw[tp->t_line].l_rint)(i, tp)
199712174Speter					     == -1) {
199812174Speter						pp->sp_delta_overflows++;
199910015Speter					}
200012174Speter					/*
200112174Speter					 * doesn't seem to be much point doing
200212174Speter					 * this here.. this driver has no
200312174Speter					 * softtty processing! ??
200412174Speter					 */
200512174Speter					if (pp->sp_hotchar && i == pp->sp_hotchar) {
200612174Speter						setsofttty();
200712174Speter					}
200812174Speter				}
200912174Speter			}
201012174Speter			goto more_rx;	/* try for more until RXbuf is empty */
201110015Speter
201212174Speter	end_rx:		/* XXX: Again, sorry about the gotos.. :-) */
201310015Speter
201410015Speter			/*
201510015Speter			 * Do TX stuff
201610015Speter			 */
201710015Speter			(*linesw[tp->t_line].l_start)(tp);
201810015Speter
201910015Speter		} /* end of for (all ports on this controller) */
202010015Speter	} /* end of for (all controllers) */
202110015Speter
202211609Speter	in_intr = 0;
202311609Speter	DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "end siintr(%d)\n", unit));
202410015Speter}
202510015Speter
202610015Speter/*
202710015Speter * Nudge the transmitter...
202812174Speter *
202912174Speter * XXX: I inherited some funny code here.  It implies the host card only
203012174Speter * interrupts when the transmit buffer reaches the low-water-mark, and does
203112174Speter * not interrupt when it's actually hits empty.  In some cases, we have
203212174Speter * processes waiting for complete drain, and we need to simulate an interrupt
203312174Speter * about when we think the buffer is going to be empty (and retry if not).
203412174Speter * I really am not certain about this...  I *need* the hardware manuals.
203510015Speter */
203610015Speterstatic void
203710015Spetersi_start(tp)
203810015Speter	register struct tty *tp;
203910015Speter{
204010015Speter	struct si_port *pp;
204110015Speter	volatile struct si_channel *ccbp;
204210015Speter	register struct clist *qp;
204310015Speter	register char *dptr;
204410015Speter	BYTE ipos;
204510015Speter	int nchar;
204610015Speter	int oldspl, count, n, amount, buffer_full;
204710015Speter	int do_exitproc;
204810015Speter
204910015Speter	oldspl = spltty();
205010015Speter
205110015Speter	qp = &tp->t_outq;
205210015Speter	pp = TP2PP(tp);
205310015Speter
205410015Speter	DPRINT((pp, DBG_ENTRY|DBG_START,
205510015Speter		"si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n",
205610015Speter		tp, tp->t_state, pp->sp_state, qp->c_cc));
205710015Speter
205810015Speter	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
205910015Speter		goto out;
206010015Speter
206110015Speter	do_exitproc = 0;
206210015Speter	buffer_full = 0;
206310015Speter	ccbp = pp->sp_ccb;
206410015Speter
206510015Speter	/*
206610015Speter	 * Handle the case where ttywait() is called on process exit
206710015Speter	 * this may be BSDI specific, I dont know...
206810015Speter	 */
206910015Speter	if (tp->t_session != NULL && tp->t_session->s_leader != NULL &&
207010015Speter	    (tp->t_session->s_leader->p_flag & P_WEXIT)) {
207110015Speter		do_exitproc++;
207210015Speter	}
207310015Speter
207410015Speter	count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
207510015Speter	DPRINT((pp, DBG_START, "count %d\n", (BYTE)count));
207610015Speter
207710015Speter	dptr = (char *)ccbp->hi_txbuf;	/* data buffer */
207810015Speter
207910015Speter	while ((nchar = qp->c_cc) > 0) {
208010015Speter		if ((BYTE)count >= 255) {
208110015Speter			buffer_full++;
208210015Speter			break;
208310015Speter		}
208410015Speter		amount = min(nchar, (255 - (BYTE)count));
208510015Speter		ipos = (unsigned int)ccbp->hi_txipos;
208610015Speter		/* will it fit in one lump? */
208712496Speter		if ((SI_BUFFERSIZE - ipos) >= amount) {
208810015Speter			n = q_to_b(&tp->t_outq,
208910015Speter				(char *)&ccbp->hi_txbuf[ipos], amount);
209010015Speter		} else {
209110015Speter			n = q_to_b(&tp->t_outq,
209210015Speter				(char *)&ccbp->hi_txbuf[ipos],
209312496Speter				SI_BUFFERSIZE-ipos);
209412496Speter			if (n == SI_BUFFERSIZE-ipos) {
209510015Speter				n += q_to_b(&tp->t_outq,
209610015Speter					(char *)&ccbp->hi_txbuf[0],
209712496Speter					amount - (SI_BUFFERSIZE-ipos));
209810015Speter			}
209910015Speter		}
210010015Speter		ccbp->hi_txipos += n;
210110015Speter		count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
210210015Speter	}
210310015Speter
210410015Speter	if (count != 0 && nchar == 0) {
210510015Speter		tp->t_state |= TS_BUSY;
210610015Speter	} else {
210710015Speter		tp->t_state &= ~TS_BUSY;
210810015Speter	}
210910015Speter
211010015Speter	/* wakeup time? */
211110015Speter	ttwwakeup(tp);
211210015Speter
211310015Speter	DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n",
211410015Speter		(BYTE)count, nchar, tp->t_state));
211510015Speter
211610015Speter	if ((tp->t_state & TS_BUSY) || do_exitproc)
211710015Speter	{
211810015Speter		int time;
211910015Speter
212010015Speter		if (do_exitproc != 0) {
212110015Speter			time = hz / 10;
212210015Speter		} else {
212310015Speter			time = ttspeedtab(tp->t_ospeed, chartimes);
212410015Speter
212510015Speter			if (time > 0) {
212610015Speter				if (time < nchar)
212710015Speter					time = nchar / time;
212810015Speter				else
212910015Speter					time = 2;
213010015Speter			} else {
213112174Speter				printf("si%d: bad char time value!!\n",
213212174Speter					(int)SI_CARD(tp->t_dev));
213310015Speter				goto out;
213410015Speter			}
213510015Speter		}
213610015Speter
213710015Speter		if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) {
213810015Speter			untimeout((timeout_func_t)si_lstart, (caddr_t)pp);
213910015Speter		} else {
214010015Speter			pp->sp_state |= SS_LSTART;
214110015Speter		}
214210015Speter		DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time));
214310015Speter		timeout((timeout_func_t)si_lstart, (caddr_t)pp, time);
214410015Speter	}
214510015Speter
214610015Speterout:
214710015Speter	splx(oldspl);
214810015Speter	DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n"));
214910015Speter}
215010015Speter
215110015Speter/*
215210015Speter * Note: called at splsoftclock from the timeout code
215310015Speter * This has to deal with two things...  cause wakeups while waiting for
215410015Speter * tty drains on last process exit, and call l_start at about the right
215510015Speter * time for protocols like ppp.
215610015Speter */
215710015Speterstatic void
215810015Spetersi_lstart(pp)
215910015Speter	register struct si_port *pp;
216010015Speter{
216110015Speter	register struct tty *tp;
216210015Speter	int oldspl;
216310015Speter
216410015Speter	DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n",
216510015Speter		pp, pp->sp_state));
216610015Speter
216710015Speter	oldspl = spltty();
216810015Speter
216910015Speter	if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) {
217010015Speter		splx(oldspl);
217110015Speter		return;
217210015Speter	}
217310015Speter	pp->sp_state &= ~SS_LSTART;
217410015Speter	pp->sp_state |= SS_INLSTART;
217510015Speter
217610015Speter	tp = pp->sp_tty;
217710015Speter
217810015Speter	/* deal with the process exit case */
217910015Speter	ttwwakeup(tp);
218010015Speter
218112174Speter	/* nudge protocols - eg: ppp */
218210015Speter	(*linesw[tp->t_line].l_start)(tp);
218310015Speter
218410015Speter	pp->sp_state &= ~SS_INLSTART;
218510015Speter	splx(oldspl);
218610015Speter}
218710015Speter
218810015Speter/*
218910015Speter * Stop output on a line. called at spltty();
219010015Speter */
219110015Spetervoid
219210015Spetersistop(tp, rw)
219310015Speter	register struct tty *tp;
219410015Speter	int rw;
219510015Speter{
219610015Speter	volatile struct si_channel *ccbp;
219710015Speter	struct si_port *pp;
219810015Speter
219910015Speter	pp = TP2PP(tp);
220010015Speter	ccbp = pp->sp_ccb;
220110015Speter
220210015Speter	DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "sistop(%x,%x)\n", tp, rw));
220310015Speter
220410015Speter	/* XXX: must check (rw & FWRITE | FREAD) etc flushing... */
220510015Speter	if (rw & FWRITE) {
220610015Speter		/* what level are we meant to be flushing anyway? */
220710015Speter		if (tp->t_state & TS_BUSY) {
220810015Speter			si_command(TP2PP(tp), WFLUSH, SI_NOWAIT);
220910015Speter			tp->t_state &= ~TS_BUSY;
221010015Speter			ttwwakeup(tp);	/* Bruce???? */
221110015Speter		}
221210015Speter	}
221312174Speter#if 1	/* XXX: this doesn't work right yet.. */
221412174Speter	/* XXX: this may have been failing because we used to call l_rint()
221512174Speter	 * while we were looping based on these two counters. Now, we collect
221612174Speter	 * the data and then loop stuffing it into l_rint(), making this
221712174Speter	 * useless.  Should we cause this to blow away the staging buffer?
221812174Speter	 */
221910015Speter	if (rw & FREAD) {
222010015Speter		ccbp->hi_rxopos = ccbp->hi_rxipos;
222110015Speter	}
222210015Speter#endif
222310015Speter}
222410015Speter
222510015Speter/*
222610015Speter * Issue a command to the Z280 host card CPU.
222710015Speter */
222810015Speter
222910015Speterstatic void
223010015Spetersi_command(pp, cmd, waitflag)
223110015Speter	struct si_port *pp;		/* port control block (local) */
223210015Speter	int cmd;
223310015Speter	int waitflag;
223410015Speter{
223510015Speter	int oldspl;
223610015Speter	volatile struct si_channel *ccbp = pp->sp_ccb;
223710015Speter	int x;
223810015Speter
223910015Speter	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n",
224010015Speter		pp, cmd, waitflag, ccbp->hi_stat));
224110015Speter
224210015Speter	oldspl = spltty();		/* Keep others out */
224310015Speter
224410015Speter	/* wait until it's finished what it was doing.. */
224510015Speter	while((x = ccbp->hi_stat) != IDLE_OPEN &&
224610015Speter			x != IDLE_CLOSE &&
224710015Speter			x != cmd) {
224810015Speter		if (in_intr) {			/* Prevent sleep in intr */
224910015Speter			DPRINT((pp, DBG_PARAM,
225010015Speter				"cmd intr collision - completing %d\trequested %d\n",
225110015Speter				x, cmd));
225210015Speter			splx(oldspl);
225310015Speter			return;
225410015Speter		} else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
225510015Speter				"sicmd1", 1)) {
225610015Speter			splx(oldspl);
225710015Speter			return;
225810015Speter		}
225910015Speter	}
226010015Speter	/* it should now be in IDLE_OPEN, IDLE_CLOSE, or "cmd" */
226110015Speter
226210015Speter	/* if there was a pending command, cause a state-change wakeup */
226310015Speter	if (pp->sp_pend != IDLE_OPEN) {
226410015Speter		switch(pp->sp_pend) {
226510015Speter		case LOPEN:
226610015Speter		case MPEND:
226710015Speter		case MOPEN:
226810015Speter		case CONFIG:
226910015Speter			wakeup(&pp->sp_state);
227010015Speter			break;
227110015Speter		default:
227210015Speter			break;
227310015Speter		}
227410015Speter	}
227510015Speter
227610015Speter	pp->sp_pend = cmd;		/* New command pending */
227710015Speter	ccbp->hi_stat = cmd;		/* Post it */
227810015Speter
227910015Speter	if (waitflag) {
228010015Speter		if (in_intr) {		/* If in interrupt handler */
228110015Speter			DPRINT((pp, DBG_PARAM,
228210015Speter				"attempt to sleep in si_intr - cmd req %d\n",
228310015Speter				cmd));
228410015Speter			splx(oldspl);
228510015Speter			return;
228610015Speter		} else while(ccbp->hi_stat != IDLE_OPEN) {
228710015Speter			if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
228810015Speter			    "sicmd2", 0))
228910015Speter				break;
229010015Speter		}
229110015Speter	}
229210015Speter	splx(oldspl);
229310015Speter}
229410015Speter
229510015Speterstatic void
229610015Spetersi_disc_optim(tp, t, pp)
229710015Speter	struct tty	*tp;
229810015Speter	struct termios	*t;
229910015Speter	struct si_port	*pp;
230010015Speter{
230110015Speter	/*
230210015Speter	 * XXX can skip a lot more cases if Smarts.  Maybe
230310015Speter	 * (IGNCR | ISTRIP | IXON) in c_iflag.  But perhaps we
230410015Speter	 * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state.
230510015Speter	 */
230610015Speter	if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
230710015Speter	    && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
230810015Speter	    && (!(t->c_iflag & PARMRK)
230910015Speter		|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
231010015Speter	    && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
231110015Speter	    && linesw[tp->t_line].l_rint == ttyinput)
231210015Speter		tp->t_state |= TS_CAN_BYPASS_L_RINT;
231310015Speter	else
231410015Speter		tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
231510161Speter
231610015Speter	/*
231710015Speter	 * Prepare to reduce input latency for packet
231810015Speter	 * discplines with a end of packet character.
231910015Speter	 */
232010015Speter	if (tp->t_line == SLIPDISC)
232110015Speter		pp->sp_hotchar = 0xc0;
232210015Speter	else if (tp->t_line == PPPDISC)
232310015Speter		pp->sp_hotchar = 0x7e;
232410015Speter	else
232510015Speter		pp->sp_hotchar = 0;
232610161Speter
232710161Speter	DPRINT((pp, DBG_OPTIM, "bypass: %s, hotchar: %x\n",
232810161Speter		(tp->t_state & TS_CAN_BYPASS_L_RINT) ? "on" : "off",
232910161Speter		pp->sp_hotchar));
233010015Speter}
233110015Speter
233210015Speter
233310015Speter#ifdef	SI_DEBUG
233413353Speter
233510015Speterstatic void
233613353Speter#ifdef __STDC__
233713353Spetersi_dprintf(struct si_port *pp, int flags, const char *fmt, ...)
233813353Speter#else
233913353Spetersi_dprintf(pp, flags, fmt, va_alist)
234010015Speter	struct si_port *pp;
234110015Speter	int flags;
234213353Speter	char *fmt;
234313353Speter#endif
234410015Speter{
234513353Speter	va_list ap;
234610015Speter	if ((pp == NULL && (si_debug&flags)) ||
234710015Speter	    (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) {
234813353Speter		va_start(ap, fmt);
234910015Speter	    	if (pp != NULL)
235012496Speter	    		printf("%ci%d(%d): ", 's',
235112174Speter	    			(int)SI_CARD(pp->sp_tty->t_dev),
235212174Speter	    			(int)SI_PORT(pp->sp_tty->t_dev));
235313353Speter		printf("%r", fmt, ap);
235413353Speter		va_end(ap);
235510015Speter	}
235610015Speter}
235710015Speter
235810015Speterstatic char *
235910015Spetersi_mctl2str(cmd)
236010015Speter	enum si_mctl cmd;
236110015Speter{
236210015Speter	switch (cmd) {
236310015Speter	case GET:	return("GET");
236410015Speter	case SET:	return("SET");
236510015Speter	case BIS:	return("BIS");
236610015Speter	case BIC:	return("BIC");
236710015Speter	}
236810015Speter	return("BAD");
236910015Speter}
237012502Sjulian
237112624Speter#endif	/* DEBUG */
237212502Sjulian
237312624Speter
237412502Sjulian
237512502Sjulianstatic si_devsw_installed = 0;
237612502Sjulian
237712517Sjulianstatic void 	si_drvinit(void *unused)
237812502Sjulian{
237912517Sjulian	dev_t dev;
238012517Sjulian
238112502Sjulian	if( ! si_devsw_installed ) {
238212675Sjulian		dev = makedev(CDEV_MAJOR, 0);
238312675Sjulian		cdevsw_add(&dev,&si_cdevsw, NULL);
238412502Sjulian		si_devsw_installed = 1;
238512517Sjulian    	}
238612502Sjulian}
238712517Sjulian
238812517SjulianSYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,si_drvinit,NULL)
238912517Sjulian
2390