si.c revision 151383
1218822Sdim/*-
2218822Sdim * Device driver for Specialix range (SI/XIO) of serial line multiplexors.
333965Sjdp *
4218822Sdim * Copyright (C) 1990, 1992, 1998 Specialix International,
5218822Sdim * Copyright (C) 1993, Andy Rutter <andy@acronym.co.uk>
638889Sjdp * Copyright (C) 2000, Peter Wemm <peter@netplex.com.au>
738889Sjdp *
838889Sjdp * Originally derived from:	SunOS 4.x version
933965Sjdp * Ported from BSDI version to FreeBSD by Peter Wemm.
1033965Sjdp *
1138889Sjdp * Redistribution and use in source and binary forms, with or without
1238889Sjdp * modification, are permitted provided that the following conditions
1338889Sjdp * are met:
1433965Sjdp * 1. Redistributions of source code must retain the above copyright
15218822Sdim *    notices, this list of conditions and the following disclaimer.
1638889Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1738889Sjdp *    notices, this list of conditions and the following disclaimer in the
1838889Sjdp *    documentation and/or other materials provided with the distribution.
1933965Sjdp * 3. All advertising materials mentioning features or use of this software
2038889Sjdp *    must display the following acknowledgement:
2138889Sjdp *	This product includes software developed by Andy Rutter of
2238889Sjdp *	Advanced Methods and Tools Ltd. based on original information
2338889Sjdp *	from Specialix International.
24218822Sdim * 4. Neither the name of Advanced Methods and Tools, nor Specialix
2538889Sjdp *    International may be used to endorse or promote products derived from
26218822Sdim *    this software without specific prior written permission.
27218822Sdim *
28218822Sdim * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
29218822Sdim * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30218822Sdim * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
3138889Sjdp * NO EVENT SHALL THE AUTHORS BE LIABLE.
3238889Sjdp *
3338889Sjdp */
3438889Sjdp
3538889Sjdp#include <sys/cdefs.h>
3638889Sjdp__FBSDID("$FreeBSD: head/sys/dev/si/si.c 151383 2005-10-16 20:22:56Z phk $");
3738889Sjdp
3838889Sjdp#ifndef lint
3938889Sjdpstatic const char si_copyright1[] =  "@(#) Copyright (C) Specialix International, 1990,1992,1998",
40218822Sdim		  si_copyright2[] =  "@(#) Copyright (C) Andy Rutter 1993",
41218822Sdim		  si_copyright3[] =  "@(#) Copyright (C) Peter Wemm 2000";
42218822Sdim#endif	/* not lint */
43218822Sdim
44218822Sdim#include "opt_compat.h"
45218822Sdim#include "opt_debug_si.h"
46218822Sdim#include "opt_tty.h"
47218822Sdim
48218822Sdim#include <sys/param.h>
49218822Sdim#include <sys/systm.h>
50218822Sdim#include <sys/serial.h>
51218822Sdim#include <sys/tty.h>
52218822Sdim#include <sys/conf.h>
53218822Sdim#include <sys/fcntl.h>
54218822Sdim#include <sys/kernel.h>
55218822Sdim#include <sys/malloc.h>
56218822Sdim#include <sys/sysctl.h>
57218822Sdim#include <sys/bus.h>
58218822Sdim#include <machine/bus.h>
59218822Sdim#include <sys/rman.h>
60218822Sdim#include <machine/resource.h>
61218822Sdim
62218822Sdim
63218822Sdim#include <vm/vm.h>
64218822Sdim#include <vm/pmap.h>
65218822Sdim
66218822Sdim#include <machine/stdarg.h>
67218822Sdim
68218822Sdim#include <dev/si/sireg.h>
69218822Sdim#include <dev/si/sivar.h>
70218822Sdim#include <dev/si/si.h>
71218822Sdim
72218822Sdim/*
73218822Sdim * This device driver is designed to interface the Specialix International
74218822Sdim * SI, XIO and SX range of serial multiplexor cards to FreeBSD on an ISA,
75218822Sdim * EISA or PCI bus machine.
76218822Sdim *
77218822Sdim * The controller is interfaced to the host via dual port RAM
78218822Sdim * and an interrupt.
79218822Sdim *
80218822Sdim * The code for the Host 1 (very old ISA cards) has not been tested.
81218822Sdim */
82218822Sdim
83218822Sdim#define	POLL		/* turn on poller to scan for lost interrupts */
84218822Sdim#define REALPOLL	/* on each poll, scan for work regardless */
85218822Sdim#define POLLHZ	(hz/10)	/* 10 times per second */
86218822Sdim#define SI_I_HIGH_WATER	(TTYHOG - 2 * SI_BUFFERSIZE)
87218822Sdim#define INT_COUNT 25000		/* max of 125 ints per second */
88218822Sdim#define JET_INT_COUNT 100	/* max of 100 ints per second */
89218822Sdim#define RXINT_COUNT 1	/* one rxint per 10 milliseconds */
90218822Sdim
91218822Sdimstatic void si_command(struct si_port *, int, int);
92218822Sdimstatic int si_Sioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
93218822Sdimstatic void si_start(struct tty *);
94218822Sdimstatic void si_stop(struct tty *, int);
95218822Sdimstatic timeout_t si_lstart;
96218822Sdim
97218822Sdimstatic t_break_t sibreak;
98218822Sdimstatic t_close_t siclose;
99218822Sdimstatic t_modem_t simodem;
100218822Sdimstatic t_open_t siopen;
101218822Sdim
102218822Sdimstatic int	siparam(struct tty *, struct termios *);
103218822Sdim
104218822Sdimstatic void	si_modem_state(struct si_port *pp, struct tty *tp, int hi_ip);
105218822Sdimstatic char *	si_modulename(int host_type, int uart_type);
106218822Sdim
107218822Sdimstatic struct cdevsw si_Scdevsw = {
108218822Sdim	.d_version =	D_VERSION,
109218822Sdim	.d_ioctl =	si_Sioctl,
110218822Sdim	.d_name =	"si",
111218822Sdim	.d_flags =	D_TTY | D_NEEDGIANT,
112218822Sdim};
113218822Sdim
114218822Sdimstatic int si_Nports;
115218822Sdimstatic int si_Nmodules;
116218822Sdimstatic int si_debug = 0;	/* data, not bss, so it's patchable */
117218822Sdim
118218822SdimSYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RW, &si_debug, 0, "");
119218822SdimTUNABLE_INT("machdep.si_debug", &si_debug);
120218822Sdim
121218822Sdimstatic int si_numunits;
122218822Sdim
123218822Sdimdevclass_t si_devclass;
124218822Sdim
125218822Sdim#ifndef B2000	/* not standard, but the hardware knows it. */
126218822Sdim# define B2000 2000
127218822Sdim#endif
128218822Sdimstatic struct speedtab bdrates[] = {
129218822Sdim	{ B75,		CLK75, },	/* 0x0 */
130218822Sdim	{ B110,		CLK110, },	/* 0x1 */
131218822Sdim	{ B150,		CLK150, },	/* 0x3 */
132218822Sdim	{ B300,		CLK300, },	/* 0x4 */
133218822Sdim	{ B600,		CLK600, },	/* 0x5 */
134218822Sdim	{ B1200,	CLK1200, },	/* 0x6 */
135218822Sdim	{ B2000,	CLK2000, },	/* 0x7 */
136218822Sdim	{ B2400,	CLK2400, },	/* 0x8 */
137218822Sdim	{ B4800,	CLK4800, },	/* 0x9 */
138218822Sdim	{ B9600,	CLK9600, },	/* 0xb */
139218822Sdim	{ B19200,	CLK19200, },	/* 0xc */
140218822Sdim	{ B38400,	CLK38400, },	/* 0x2 (out of order!) */
141218822Sdim	{ B57600,	CLK57600, },	/* 0xd */
142218822Sdim	{ B115200,	CLK110, },	/* 0x1 (dupe!, 110 baud on "si") */
143218822Sdim	{ -1,		-1 },
144218822Sdim};
145218822Sdim
146218822Sdim
147218822Sdim/* populated with approx character/sec rates - translated at card
148218822Sdim * initialisation time to chars per tick of the clock */
149218822Sdimstatic int done_chartimes = 0;
150218822Sdimstatic struct speedtab chartimes[] = {
151218822Sdim	{ B75,		8, },
152218822Sdim	{ B110,		11, },
153218822Sdim	{ B150,		15, },
154218822Sdim	{ B300,		30, },
155218822Sdim	{ B600,		60, },
156218822Sdim	{ B1200,	120, },
157218822Sdim	{ B2000,	200, },
158218822Sdim	{ B2400,	240, },
159218822Sdim	{ B4800,	480, },
160218822Sdim	{ B9600,	960, },
161218822Sdim	{ B19200,	1920, },
162218822Sdim	{ B38400,	3840, },
163218822Sdim	{ B57600,	5760, },
164218822Sdim	{ B115200,	11520, },
165218822Sdim	{ -1,		-1 },
166218822Sdim};
167218822Sdimstatic volatile int in_intr = 0;	/* Inside interrupt handler? */
168218822Sdim
169218822Sdim#ifdef POLL
170218822Sdimstatic int si_pollrate;			/* in addition to irq */
171218822Sdimstatic int si_realpoll = 0;		/* poll HW on timer */
172218822Sdim
173218822SdimSYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, "");
174218822SdimSYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, "");
175218822Sdim
176218822Sdimstatic int init_finished = 0;
177218822Sdimstatic void si_poll(void *);
178218822Sdim#endif
179218822Sdim
180218822Sdim/*
181218822Sdim * Array of adapter types and the corresponding RAM size. The order of
182218822Sdim * entries here MUST match the ordinal of the adapter type.
183218822Sdim */
184218822Sdimstatic const char *si_type[] = {
185218822Sdim	"EMPTY",
186218822Sdim	"SIHOST",
187218822Sdim	"SIMCA",		/* FreeBSD does not support Microchannel */
188218822Sdim	"SIHOST2",
189218822Sdim	"SIEISA",
190218822Sdim	"SIPCI",
191218822Sdim	"SXPCI",
192218822Sdim	"SXISA",
193218822Sdim};
194218822Sdim
195218822Sdim/*
196218822Sdim * We have to make an 8 bit version of bcopy, since some cards can't
197218822Sdim * deal with 32 bit I/O
198218822Sdim */
199218822Sdimstatic void __inline
200218822Sdimsi_bcopy(const void *src, void *dst, size_t len)
201218822Sdim{
202218822Sdim	u_char *d;
203218822Sdim	const u_char *s;
204218822Sdim
205218822Sdim	d = dst;
206218822Sdim	s = src;
207218822Sdim	while (len--)
208218822Sdim		*d++ = *s++;
209218822Sdim}
210218822Sdimstatic void __inline
211218822Sdimsi_vbcopy(const volatile void *src, void *dst, size_t len)
212218822Sdim{
213218822Sdim	u_char *d;
21438889Sjdp	const volatile u_char *s;
215218822Sdim
216218822Sdim	d = dst;
217218822Sdim	s = src;
218218822Sdim	while (len--)
21938889Sjdp		*d++ = *s++;
22060484Sobrien}
221218822Sdimstatic void __inline
22260484Sobriensi_bcopyv(const void *src, volatile void *dst, size_t len)
22338889Sjdp{
22438889Sjdp	volatile u_char *d;
225218822Sdim	const u_char *s;
22638889Sjdp
22760484Sobrien	d = dst;
22860484Sobrien	s = src;
22933965Sjdp	while (len--)
230218822Sdim		*d++ = *s++;
231218822Sdim}
232218822Sdim
233218822Sdim/*
234218822Sdim * Attach the device.  Initialize the card.
235218822Sdim */
23660484Sobrienint
237218822Sdimsiattach(device_t dev)
23860484Sobrien{
239218822Sdim	int unit;
24038889Sjdp	struct si_softc *sc;
241218822Sdim	struct si_port *pp;
242218822Sdim	struct tty *tp;
243218822Sdim	volatile struct si_channel *ccbp;
244218822Sdim	volatile struct si_reg *regp;
245218822Sdim	volatile caddr_t maddr;
246104834Sobrien	struct si_module *modp;
247104834Sobrien	struct speedtab *spt;
24838889Sjdp	int nmodule, nport, x, y;
249218822Sdim	int uart_type;
250218822Sdim
251218822Sdim	sc = device_get_softc(dev);
252218822Sdim	unit = device_get_unit(dev);
253218822Sdim
25460484Sobrien	sc->sc_typename = si_type[sc->sc_type];
255218822Sdim	if (si_numunits < unit + 1)
25638889Sjdp		si_numunits = unit + 1;
257218822Sdim
258218822Sdim	DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", unit));
259218822Sdim
260218822Sdim#ifdef POLL
261218822Sdim	if (si_pollrate == 0) {
26260484Sobrien		si_pollrate = POLLHZ;		/* in addition to irq */
263218822Sdim#ifdef REALPOLL
264218822Sdim		si_realpoll = 1;		/* scan always */
265218822Sdim#endif
266218822Sdim	}
267218822Sdim#endif
268218822Sdim
269218822Sdim	DPRINT((0, DBG_AUTOBOOT, "si%d: type: %s paddr: %x maddr: %x\n", unit,
270218822Sdim		sc->sc_typename, sc->sc_paddr, sc->sc_maddr));
271218822Sdim
272218822Sdim	sc->sc_ports = NULL;			/* mark as uninitialised */
27338889Sjdp
27438889Sjdp	maddr = sc->sc_maddr;
275218822Sdim
276218822Sdim	/* Stop the CPU first so it won't stomp around while we load */
27738889Sjdp
278218822Sdim	switch (sc->sc_type) {
279218822Sdim		case SIEISA:
28038889Sjdp			outb(sc->sc_iobase + 2, sc->sc_irq << 4);
28160484Sobrien		break;
28260484Sobrien		case SIPCI:
283218822Sdim			*(maddr+SIPCIRESET) = 0;
28438889Sjdp		break;
285218822Sdim		case SIJETPCI: /* fall through to JET ISA */
286218822Sdim		case SIJETISA:
28777298Sobrien			*(maddr+SIJETCONFIG) = 0;
28877298Sobrien		break;
28938889Sjdp		case SIHOST2:
290218822Sdim			*(maddr+SIPLRESET) = 0;
291218822Sdim		break;
292218822Sdim		case SIHOST:
293218822Sdim			*(maddr+SIRESET) = 0;
294218822Sdim		break;
295218822Sdim		default: /* this should never happen */
29660484Sobrien			printf("si%d: unsupported configuration\n", unit);
29738889Sjdp			return EINVAL;
298218822Sdim		break;
299218822Sdim	}
300218822Sdim
30177298Sobrien	/* OK, now lets download the download code */
30260484Sobrien
30338889Sjdp	if (SI_ISJET(sc->sc_type)) {
304218822Sdim		DPRINT((0, DBG_DOWNLOAD, "si%d: jet_download: nbytes %d\n",
305218822Sdim			unit, si3_t225_dsize));
306218822Sdim		si_bcopy(si3_t225_download, maddr + si3_t225_downloadaddr,
307218822Sdim			si3_t225_dsize);
308218822Sdim		DPRINT((0, DBG_DOWNLOAD,
309218822Sdim			"si%d: jet_bootstrap: nbytes %d -> %x\n",
310218822Sdim			unit, si3_t225_bsize, si3_t225_bootloadaddr));
311218822Sdim		si_bcopy(si3_t225_bootstrap, maddr + si3_t225_bootloadaddr,
312218822Sdim			si3_t225_bsize);
313218822Sdim	} else {
314218822Sdim		DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n",
315218822Sdim			unit, si2_z280_dsize));
316218822Sdim		si_bcopy(si2_z280_download, maddr + si2_z280_downloadaddr,
317218822Sdim			si2_z280_dsize);
318218822Sdim	}
319218822Sdim
320218822Sdim	/* Now start the CPU */
321218822Sdim
322218822Sdim	switch (sc->sc_type) {
323218822Sdim	case SIEISA:
324218822Sdim		/* modify the download code to tell it that it's on an EISA */
325218822Sdim		*(maddr + 0x42) = 1;
326218822Sdim		outb(sc->sc_iobase + 2, (sc->sc_irq << 4) | 4);
327218822Sdim		(void)inb(sc->sc_iobase + 3); /* reset interrupt */
328218822Sdim		break;
329218822Sdim	case SIPCI:
330218822Sdim		/* modify the download code to tell it that it's on a PCI */
331218822Sdim		*(maddr+0x42) = 1;
332218822Sdim		*(maddr+SIPCIRESET) = 1;
333218822Sdim		*(maddr+SIPCIINTCL) = 0;
334218822Sdim		break;
335218822Sdim	case SIJETPCI:
336218822Sdim		*(maddr+SIJETRESET) = 0;
337218822Sdim		*(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN;
338218822Sdim		break;
339218822Sdim	case SIJETISA:
340218822Sdim		*(maddr+SIJETRESET) = 0;
341218822Sdim		switch (sc->sc_irq) {
342218822Sdim		case 9:
343218822Sdim			*(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0x90;
344218822Sdim			break;
345218822Sdim		case 10:
346218822Sdim			*(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xa0;
347218822Sdim			break;
348218822Sdim		case 11:
349218822Sdim			*(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xb0;
350218822Sdim			break;
351218822Sdim		case 12:
352218822Sdim			*(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xc0;
353218822Sdim			break;
354218822Sdim		case 15:
35538889Sjdp			*(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xf0;
356218822Sdim			break;
35789857Sobrien		}
35838889Sjdp		break;
35938889Sjdp	case SIHOST:
36077298Sobrien		*(maddr+SIRESET_CL) = 0;
36177298Sobrien		*(maddr+SIINTCL_CL) = 0;
36233965Sjdp		break;
36360484Sobrien	case SIHOST2:
36460484Sobrien		*(maddr+SIPLRESET) = 0x10;
36533965Sjdp		switch (sc->sc_irq) {
36633965Sjdp		case 11:
36760484Sobrien			*(maddr+SIPLIRQ11) = 0x10;
36860484Sobrien			break;
36960484Sobrien		case 12:
37060484Sobrien			*(maddr+SIPLIRQ12) = 0x10;
37160484Sobrien			break;
37233965Sjdp		case 15:
37360484Sobrien			*(maddr+SIPLIRQ15) = 0x10;
37460484Sobrien			break;
37560484Sobrien		}
37633965Sjdp		*(maddr+SIPLIRQCLR) = 0x10;
37733965Sjdp		break;
37833965Sjdp	default: /* this should _REALLY_ never happen */
37960484Sobrien		printf("si%d: Uh, it was supported a second ago...\n", unit);
38060484Sobrien		return EINVAL;
38160484Sobrien	}
38260484Sobrien
38360484Sobrien	DELAY(1000000);			/* wait around for a second */
384218822Sdim
38560484Sobrien	regp = (struct si_reg *)maddr;
386218822Sdim	y = 0;
387218822Sdim					/* wait max of 5 sec for init OK */
38833965Sjdp	while (regp->initstat == 0 && y++ < 10) {
389104834Sobrien		DELAY(500000);
390218822Sdim	}
39133965Sjdp	switch (regp->initstat) {
39233965Sjdp	case 0:
39360484Sobrien		printf("si%d: startup timeout - aborting\n", unit);
39460484Sobrien		sc->sc_type = SIEMPTY;
395104834Sobrien		return EINVAL;
396104834Sobrien	case 1:
397104834Sobrien		if (SI_ISJET(sc->sc_type)) {
398218822Sdim			/* set throttle to 100 times per second */
399218822Sdim			regp->int_count = JET_INT_COUNT;
400104834Sobrien			/* rx_intr_count is a NOP in Jet */
40133965Sjdp		} else {
402104834Sobrien			/* set throttle to 125 times per second */
403218822Sdim			regp->int_count = INT_COUNT;
404104834Sobrien			/* rx intr max of 25 times per second */
405218822Sdim			regp->rx_int_count = RXINT_COUNT;
406218822Sdim		}
40733965Sjdp		regp->int_pending = 0;		/* no intr pending */
408218822Sdim		regp->int_scounter = 0;	/* reset counter */
409104834Sobrien		break;
410218822Sdim	case 0xff:
411218822Sdim		/*
412218822Sdim		 * No modules found, so give up on this one.
413104834Sobrien		 */
414104834Sobrien		printf("si%d: %s - no ports found\n", unit,
415104834Sobrien			si_type[sc->sc_type]);
416104834Sobrien		return 0;
417218822Sdim	default:
418104834Sobrien		printf("si%d: download code version error - initstat %x\n",
419104834Sobrien			unit, regp->initstat);
420218822Sdim		return EINVAL;
421218822Sdim	}
42233965Sjdp
42377298Sobrien	/*
424130561Sobrien	 * First time around the ports just count them in order
425218822Sdim	 * to allocate some memory.
42633965Sjdp	 */
42738889Sjdp	nport = 0;
42838889Sjdp	modp = (struct si_module *)(maddr + 0x80);
42933965Sjdp	for (;;) {
43033965Sjdp		DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp));
43138889Sjdp		switch (modp->sm_type) {
43238889Sjdp		case TA4:
43338889Sjdp			DPRINT((0, DBG_DOWNLOAD,
43438889Sjdp				"si%d: Found old TA4 module, 4 ports\n",
43560484Sobrien				unit));
436218822Sdim			x = 4;
437218822Sdim			break;
43877298Sobrien		case TA8:
43977298Sobrien			DPRINT((0, DBG_DOWNLOAD,
44077298Sobrien				"si%d: Found old TA8 module, 8 ports\n",
44177298Sobrien				unit));
44277298Sobrien			x = 8;
44377298Sobrien			break;
44477298Sobrien		case TA4_ASIC:
44577298Sobrien			DPRINT((0, DBG_DOWNLOAD,
44677298Sobrien				"si%d: Found ASIC TA4 module, 4 ports\n",
44777298Sobrien				unit));
44877298Sobrien			x = 4;
44977298Sobrien			break;
45077298Sobrien		case TA8_ASIC:
45177298Sobrien			DPRINT((0, DBG_DOWNLOAD,
45233965Sjdp				"si%d: Found ASIC TA8 module, 8 ports\n",
45360484Sobrien				unit));
454218822Sdim			x = 8;
455218822Sdim			break;
456218822Sdim		case MTA:
457218822Sdim			DPRINT((0, DBG_DOWNLOAD,
458218822Sdim				"si%d: Found CD1400 module, 8 ports\n",
459218822Sdim				unit));
460218822Sdim			x = 8;
461218822Sdim			break;
462218822Sdim		case SXDC:
463218822Sdim			DPRINT((0, DBG_DOWNLOAD,
464218822Sdim				"si%d: Found SXDC module, 8 ports\n",
465218822Sdim				unit));
466218822Sdim			x = 8;
467218822Sdim			break;
468218822Sdim		default:
469218822Sdim			printf("si%d: unknown module type %d\n",
470218822Sdim				unit, modp->sm_type);
471218822Sdim			goto try_next;
472218822Sdim		}
473218822Sdim
474218822Sdim		/* this was limited in firmware and is also a driver issue */
475218822Sdim		if ((nport + x) > SI_MAXPORTPERCARD) {
476218822Sdim			printf("si%d: extra ports ignored\n", unit);
47738889Sjdp			goto try_next;
47860484Sobrien		}
47938889Sjdp
480218822Sdim		nport += x;
481218822Sdim		si_Nports += x;
48260484Sobrien		si_Nmodules++;
483218822Sdim
484218822Sdimtry_next:
485218822Sdim		if (modp->sm_next == 0)
486130561Sobrien			break;
487104834Sobrien		modp = (struct si_module *)
488104834Sobrien			(maddr + (unsigned)(modp->sm_next & 0x7fff));
489104834Sobrien	}
490218822Sdim	sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport,
491104834Sobrien		M_DEVBUF, M_NOWAIT | M_ZERO);
492104834Sobrien	if (sc->sc_ports == 0) {
493104834Sobrien		printf("si%d: fail to malloc memory for port structs\n",
494218822Sdim			unit);
495218822Sdim		return EINVAL;
49638889Sjdp	}
49768765Sobrien	sc->sc_nport = nport;
498218822Sdim
49938889Sjdp	/*
50038889Sjdp	 * Scan round the ports again, this time initialising.
50138889Sjdp	 */
502218822Sdim	pp = sc->sc_ports;
50377298Sobrien	nmodule = 0;
50433965Sjdp	modp = (struct si_module *)(maddr + 0x80);
505218822Sdim	uart_type = 1000;	/* arbitary, > uchar_max */
506218822Sdim	for (;;) {
507218822Sdim		switch (modp->sm_type) {
50860484Sobrien		case TA4:
509218822Sdim			nport = 4;
51068765Sobrien			break;
511218822Sdim		case TA8:
51289857Sobrien			nport = 8;
513218822Sdim			break;
514218822Sdim		case TA4_ASIC:
51533965Sjdp			nport = 4;
516218822Sdim			break;
51760484Sobrien		case TA8_ASIC:
51889857Sobrien			nport = 8;
519218822Sdim			break;
520218822Sdim		case MTA:
52160484Sobrien			nport = 8;
52277298Sobrien			break;
523218822Sdim		case SXDC:
524218822Sdim			nport = 8;
52538889Sjdp			break;
52638889Sjdp		default:
527218822Sdim			goto try_next2;
528218822Sdim		}
529218822Sdim		nmodule++;
530218822Sdim		ccbp = (struct si_channel *)((char *)modp + 0x100);
531218822Sdim		if (uart_type == 1000)
532218822Sdim			uart_type = ccbp->type;
533218822Sdim		else if (uart_type != ccbp->type)
534218822Sdim			printf("si%d: Warning: module %d mismatch! (%d%s != %d%s)\n",
535218822Sdim			    unit, nmodule,
536218822Sdim			    ccbp->type, si_modulename(sc->sc_type, ccbp->type),
537218822Sdim			    uart_type, si_modulename(sc->sc_type, uart_type));
538218822Sdim
539218822Sdim		for (x = 0; x < nport; x++, pp++, ccbp++) {
540218822Sdim			pp->sp_ccb = ccbp;	/* save the address */
541218822Sdim			pp->sp_pend = IDLE_CLOSE;
542218822Sdim			pp->sp_state = 0;	/* internal flag */
543218822Sdim#ifdef SI_DEBUG
544218822Sdim			sprintf(pp->sp_name, "si%r%r", unit, x);
545218822Sdim#endif
546218822Sdim			tp = pp->sp_tty = ttyalloc();
547218822Sdim			tp->t_sc = pp;
548218822Sdim			tp->t_break = sibreak;
549218822Sdim			tp->t_close = siclose;
550218822Sdim			tp->t_modem = simodem;
551218822Sdim			tp->t_open = siopen;
552218822Sdim			tp->t_oproc = si_start;
55338889Sjdp			tp->t_param = siparam;
554218822Sdim			tp->t_stop = si_stop;
555218822Sdim			ttycreate(tp, TS_CALLOUT, "A%r%r", unit, x);
55638889Sjdp		}
557218822Sdimtry_next2:
55838889Sjdp		if (modp->sm_next == 0) {
559218822Sdim			printf("si%d: card: %s, ports: %d, modules: %d, type: %d%s\n",
560218822Sdim				unit,
56138889Sjdp				sc->sc_typename,
562218822Sdim				sc->sc_nport,
56360484Sobrien				nmodule,
564218822Sdim				uart_type,
565218822Sdim				si_modulename(sc->sc_type, uart_type));
56660484Sobrien			break;
567218822Sdim		}
568218822Sdim		modp = (struct si_module *)
569218822Sdim			(maddr + (unsigned)(modp->sm_next & 0x7fff));
570218822Sdim	}
571218822Sdim	if (done_chartimes == 0) {
57238889Sjdp		for (spt = chartimes ; spt->sp_speed != -1; spt++) {
573218822Sdim			if ((spt->sp_code /= hz) == 0)
574218822Sdim				spt->sp_code = 1;
57538889Sjdp		}
57638889Sjdp		done_chartimes = 1;
577218822Sdim	}
578218822Sdim
579218822Sdim	make_dev(&si_Scdevsw, 0, 0, 0, 0600, "si_control");
58038889Sjdp	return (0);
58138889Sjdp}
582218822Sdim
58338889Sjdpstatic	int
584218822Sdimsiopen(struct tty *tp, struct cdev *dev)
585218822Sdim{
586218822Sdim
587218822Sdim#ifdef	POLL
588218822Sdim	/*
589218822Sdim	 * We've now got a device, so start the poller.
590218822Sdim	 */
59138889Sjdp	if (init_finished == 0) {
59238889Sjdp		timeout(si_poll, (caddr_t)0L, si_pollrate);
59338889Sjdp		init_finished = 1;
59438889Sjdp	}
59560484Sobrien#endif
596218822Sdim	return(0);
597218822Sdim}
598218822Sdim
599218822Sdimstatic void
60038889Sjdpsiclose(struct tty *tp)
60138889Sjdp{
602218822Sdim	struct si_port *pp;
603218822Sdim
604218822Sdim	pp = tp->t_sc;
605218822Sdim	(void) si_command(pp, FCLOSE, SI_NOWAIT);
606218822Sdim}
607218822Sdim
60838889Sjdpstatic void
60938889Sjdpsibreak(struct tty *tp, int sig)
610218822Sdim{
611218822Sdim	struct si_port *pp;
612218822Sdim
613218822Sdim	pp = tp->t_sc;
614218822Sdim	if (sig)
615218822Sdim		si_command(pp, SBREAK, SI_WAIT);
616218822Sdim	else
617218822Sdim		si_command(pp, EBREAK, SI_WAIT);
618218822Sdim}
619218822Sdim
620218822Sdim
621218822Sdim/*
622218822Sdim * Handle the Specialix ioctls on the control dev.
623218822Sdim */
624218822Sdimstatic int
625218822Sdimsi_Sioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
626218822Sdim{
627218822Sdim	struct si_softc *xsc;
628218822Sdim	struct si_port *xpp;
629218822Sdim	volatile struct si_reg *regp;
630218822Sdim	struct si_tcsi *dp;
631218822Sdim	struct si_pstat *sps;
632218822Sdim	int *ip, error = 0;
633218822Sdim	int oldspl;
634218822Sdim	int card, port;
635218822Sdim
636218822Sdim	DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%s,%lx,%x,%x)\n",
637218822Sdim		devtoname(dev), cmd, data, flag));
638218822Sdim
639218822Sdim#if 1
640218822Sdim	DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT));
641218822Sdim	DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB));
642218822Sdim	DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY));
643218822Sdim#endif
644218822Sdim
645218822Sdim	oldspl = spltty();	/* better safe than sorry */
646218822Sdim
647218822Sdim	ip = (int *)data;
648218822Sdim
649218822Sdim#define SUCHECK if ((error = suser(td))) goto out
650218822Sdim
651218822Sdim	switch (cmd) {
652218822Sdim	case TCSIPORTS:
653218822Sdim		*ip = si_Nports;
654218822Sdim		goto out;
655218822Sdim	case TCSIMODULES:
656218822Sdim		*ip = si_Nmodules;
657218822Sdim		goto out;
658218822Sdim	case TCSISDBG_ALL:
659218822Sdim		SUCHECK;
660218822Sdim		si_debug = *ip;
661218822Sdim		goto out;
662218822Sdim	case TCSIGDBG_ALL:
663218822Sdim		*ip = si_debug;
664218822Sdim		goto out;
665218822Sdim	default:
666218822Sdim		/*
667218822Sdim		 * Check that a controller for this port exists
668218822Sdim		 */
669218822Sdim
670218822Sdim		/* may also be a struct si_pstat, a superset of si_tcsi */
671218822Sdim
67238889Sjdp		dp = (struct si_tcsi *)data;
673218822Sdim		sps = (struct si_pstat *)data;
674218822Sdim		card = dp->tc_card;
67538889Sjdp		xsc = devclass_get_softc(si_devclass, card);	/* check.. */
676218822Sdim		if (xsc == NULL || xsc->sc_type == SIEMPTY) {
677218822Sdim			error = ENOENT;
67838889Sjdp			goto out;
67938889Sjdp		}
68038889Sjdp		/*
68138889Sjdp		 * And check that a port exists
68277298Sobrien		 */
683218822Sdim		port = dp->tc_port;
68477298Sobrien		if (port < 0 || port >= xsc->sc_nport) {
68538889Sjdp			error = ENOENT;
686218822Sdim			goto out;
68738889Sjdp		}
688218822Sdim		xpp = xsc->sc_ports + port;
689218822Sdim		regp = (struct si_reg *)xsc->sc_maddr;
69038889Sjdp	}
691218822Sdim
692218822Sdim	switch (cmd) {
69338889Sjdp	case TCSIDEBUG:
69438889Sjdp#ifdef	SI_DEBUG
69538889Sjdp		SUCHECK;
69638889Sjdp		if (xpp->sp_debug)
69738889Sjdp			xpp->sp_debug = 0;
69838889Sjdp		else {
69938889Sjdp			xpp->sp_debug = DBG_ALL;
70038889Sjdp			DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n",
701218822Sdim				(xpp->sp_debug&DBG_ALL)?"ON":"OFF"));
702218822Sdim		}
70338889Sjdp		break;
70460484Sobrien#else
70560484Sobrien		error = ENODEV;
70660484Sobrien		goto out;
70760484Sobrien#endif
70860484Sobrien	case TCSISDBG_LEVEL:
70960484Sobrien	case TCSIGDBG_LEVEL:
710218822Sdim#ifdef	SI_DEBUG
711218822Sdim		if (cmd == TCSIGDBG_LEVEL) {
712218822Sdim			dp->tc_dbglvl = xpp->sp_debug;
713218822Sdim		} else {
714218822Sdim			SUCHECK;
715218822Sdim			xpp->sp_debug = dp->tc_dbglvl;
716218822Sdim		}
717218822Sdim		break;
71860484Sobrien#else
71960484Sobrien		error = ENODEV;
72060484Sobrien		goto out;
72160484Sobrien#endif
72260484Sobrien	case TCSIGRXIT:
72360484Sobrien		dp->tc_int = regp->rx_int_count;
72460484Sobrien		break;
72560484Sobrien	case TCSIRXIT:
72660484Sobrien		SUCHECK;
72760484Sobrien		regp->rx_int_count = dp->tc_int;
72860484Sobrien		break;
729218822Sdim	case TCSIGIT:
73060484Sobrien		dp->tc_int = regp->int_count;
73160484Sobrien		break;
73260484Sobrien	case TCSIIT:
73360484Sobrien		SUCHECK;
73460484Sobrien		regp->int_count = dp->tc_int;
73560484Sobrien		break;
73660484Sobrien	case TCSISTATE:
737218822Sdim		dp->tc_int = xpp->sp_ccb->hi_ip;
738218822Sdim		break;
739218822Sdim	/* these next three use a different structure */
740218822Sdim	case TCSI_PORT:
741218822Sdim		SUCHECK;
742218822Sdim		si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport));
743218822Sdim		break;
74460484Sobrien	case TCSI_CCB:
745218822Sdim		SUCHECK;
746218822Sdim		si_vbcopy(xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb));
747218822Sdim		break;
748218822Sdim	case TCSI_TTY:
749218822Sdim		SUCHECK;
750218822Sdim		si_bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty));
751218822Sdim		break;
752218822Sdim	default:
75360484Sobrien		error = EINVAL;
754218822Sdim		goto out;
75560484Sobrien	}
75660484Sobrienout:
75760484Sobrien	splx(oldspl);
75860484Sobrien	return(error);		/* success */
75960484Sobrien}
76060484Sobrien
76160484Sobrien/*
76260484Sobrien *	siparam()	: Configure line params
76360484Sobrien *	called at spltty();
764218822Sdim *	this may sleep, does not flush, nor wait for drain, nor block writes
76560484Sobrien *	caller must arrange this if it's important..
76660484Sobrien */
76760484Sobrienstatic int
76860484Sobriensiparam(struct tty *tp, struct termios *t)
76960484Sobrien{
770218822Sdim	struct si_port *pp = tp->t_sc;
771218822Sdim	volatile struct si_channel *ccbp;
772218822Sdim	int oldspl, cflag, iflag, oflag, lflag;
773218822Sdim	int error = 0;		/* shutup gcc */
77460484Sobrien	int ispeed = 0;		/* shutup gcc */
775218822Sdim	int ospeed = 0;		/* shutup gcc */
776218822Sdim	BYTE val;
777218822Sdim
778218822Sdim	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t));
779218822Sdim	cflag = t->c_cflag;
780218822Sdim	iflag = t->c_iflag;
781218822Sdim	oflag = t->c_oflag;
782218822Sdim	lflag = t->c_lflag;
78338889Sjdp	DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n",
78438889Sjdp		oflag, cflag, iflag, lflag));
785218822Sdim
786218822Sdim	/* XXX - if Jet host and SXDC module, use extended baud rates */
78738889Sjdp
78838889Sjdp	/* if not hung up.. */
789218822Sdim	if (t->c_ospeed != 0) {
790218822Sdim		/* translate baud rate to firmware values */
791218822Sdim		ospeed = ttspeedtab(t->c_ospeed, bdrates);
792218822Sdim		ispeed = t->c_ispeed ?
793218822Sdim			 ttspeedtab(t->c_ispeed, bdrates) : ospeed;
794218822Sdim
795218822Sdim		/* enforce legit baud rate */
79660484Sobrien		if (ospeed < 0 || ispeed < 0)
797218822Sdim			return (EINVAL);
798218822Sdim	}
799218822Sdim
800218822Sdim	oldspl = spltty();
80160484Sobrien
802218822Sdim	ccbp = pp->sp_ccb;
803218822Sdim
804218822Sdim	/* ========== set hi_break ========== */
805218822Sdim	val = 0;
806218822Sdim	if (iflag & IGNBRK)		/* Breaks */
80738889Sjdp		val |= BR_IGN;
808218822Sdim	if (iflag & BRKINT)		/* Interrupt on break? */
809218822Sdim		val |= BR_INT;
810218822Sdim	if (iflag & PARMRK)		/* Parity mark? */
811218822Sdim		val |= BR_PARMRK;
812218822Sdim	if (iflag & IGNPAR)		/* Ignore chars with parity errors? */
813218822Sdim		val |= BR_PARIGN;
814218822Sdim	ccbp->hi_break = val;
815218822Sdim
816218822Sdim	/* ========== set hi_csr ========== */
817218822Sdim	/* if not hung up.. */
818218822Sdim	if (t->c_ospeed != 0) {
819218822Sdim		/* Set I/O speeds */
820218822Sdim		 val = (ispeed << 4) | ospeed;
821218822Sdim	}
822218822Sdim	ccbp->hi_csr = val;
823218822Sdim
824218822Sdim	/* ========== set hi_mr2 ========== */
825218822Sdim	val = 0;
826218822Sdim	if (cflag & CSTOPB)				/* Stop bits */
82738889Sjdp		val |= MR2_2_STOP;
828218822Sdim	else
829218822Sdim		val |= MR2_1_STOP;
830218822Sdim	/*
831218822Sdim	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
83238889Sjdp	 * a DCE, hence the reverse sense of RTS and CTS
83338889Sjdp	 */
834218822Sdim	/* Output Flow - RTS must be raised before data can be sent */
83538889Sjdp	if (cflag & CCTS_OFLOW)
83638889Sjdp		val |= MR2_RTSCONT;
837218822Sdim
838218822Sdim	ccbp->hi_mr2 = val;
839218822Sdim
840218822Sdim	/* ========== set hi_mr1 ========== */
841218822Sdim	val = 0;
842218822Sdim	if (!(cflag & PARENB))				/* Parity */
843218822Sdim		val |= MR1_NONE;
844218822Sdim	else
845218822Sdim		val |= MR1_WITH;
846218822Sdim	if (cflag & PARODD)
847218822Sdim		val |= MR1_ODD;
848218822Sdim
849218822Sdim	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
850218822Sdim		val |= MR1_8_BITS;
851218822Sdim	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
85260484Sobrien		val |= MR1_7_BITS;
853218822Sdim	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
854218822Sdim		val |= MR1_6_BITS;
855218822Sdim	} else {					/* Must be 5 */
856218822Sdim		val |= MR1_5_BITS;
857218822Sdim	}
858218822Sdim	/*
859218822Sdim	 * Enable H/W RTS/CTS handshaking. The default TA/MTA is
86060484Sobrien	 * a DCE, hence the reverse sense of RTS and CTS
86160484Sobrien	 */
86260484Sobrien	/* Input Flow - CTS is raised when port is ready to receive data */
863218822Sdim	if (cflag & CRTS_IFLOW)
864218822Sdim		val |= MR1_CTSCONT;
865218822Sdim
866218822Sdim	ccbp->hi_mr1 = val;
867218822Sdim
868218822Sdim	/* ========== set hi_mask ========== */
869218822Sdim	val = 0xff;
87060484Sobrien	if ((cflag & CS8) == CS8) {			/* 8 data bits? */
87160484Sobrien		val &= 0xFF;
872218822Sdim	} else if ((cflag & CS7) == CS7) {		/* 7 data bits? */
87338889Sjdp		val &= 0x7F;
87460484Sobrien	} else if ((cflag & CS6) == CS6) {		/* 6 data bits? */
87560484Sobrien		val &= 0x3F;
87638889Sjdp	} else {					/* Must be 5 */
877218822Sdim		val &= 0x1F;
878218822Sdim	}
879218822Sdim	if (iflag & ISTRIP)
880218822Sdim		val &= 0x7F;
88138889Sjdp
88238889Sjdp	ccbp->hi_mask = val;
88338889Sjdp
88438889Sjdp	/* ========== set hi_prtcl ========== */
88538889Sjdp	val = SP_DCEN;		/* Monitor DCD always, or TIOCMGET misses it */
88638889Sjdp	if (iflag & IXANY)
88738889Sjdp		val |= SP_TANY;
888218822Sdim	if (iflag & IXON)
88960484Sobrien		val |= SP_TXEN;
89038889Sjdp	if (iflag & IXOFF)
89138889Sjdp		val |= SP_RXEN;
892218822Sdim	if (iflag & INPCK)
893218822Sdim		val |= SP_PAEN;
894218822Sdim
895218822Sdim	ccbp->hi_prtcl = val;
896218822Sdim
897218822Sdim
898218822Sdim	/* ========== set hi_{rx|tx}{on|off} ========== */
899218822Sdim	/* XXX: the card TOTALLY shields us from the flow control... */
900218822Sdim	ccbp->hi_txon = t->c_cc[VSTART];
901218822Sdim	ccbp->hi_txoff = t->c_cc[VSTOP];
902218822Sdim
903218822Sdim	ccbp->hi_rxon = t->c_cc[VSTART];
904218822Sdim	ccbp->hi_rxoff = t->c_cc[VSTOP];
905218822Sdim
906218822Sdim	/* ========== send settings to the card ========== */
90738889Sjdp	/* potential sleep here */
908218822Sdim	if (ccbp->hi_stat == IDLE_CLOSE)		/* Not yet open */
909218822Sdim		si_command(pp, LOPEN, SI_WAIT);		/* open it */
91038889Sjdp	else
911218822Sdim		si_command(pp, CONFIG, SI_WAIT);	/* change params */
912218822Sdim
913218822Sdim	/* ========== set DTR etc ========== */
914218822Sdim	/* Hangup if ospeed == 0 */
915218822Sdim	if (t->c_ospeed == 0) {
916218822Sdim		(void) simodem(tp, 0, SER_DTR | SER_RTS);
91760484Sobrien	} else {
918218822Sdim		/*
91960484Sobrien		 * If the previous speed was 0, may need to re-enable
920218822Sdim		 * the modem signals
92138889Sjdp		 */
922218822Sdim		(void) simodem(tp, SER_DTR | SER_RTS, 0);
92360484Sobrien	}
924218822Sdim
92560484Sobrien	DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n",
926218822Sdim		ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break));
927218822Sdim
928218822Sdim	splx(oldspl);
929218822Sdim	return(error);
930218822Sdim}
931218822Sdim
932218822Sdim/*
933218822Sdim * Set/Get state of modem control lines.
934218822Sdim * Due to DCE-like behaviour of the adapter, some signals need translation:
935218822Sdim *	TIOCM_DTR	DSR
936218822Sdim *	TIOCM_RTS	CTS
937218822Sdim */
93860484Sobrienstatic int
939218822Sdimsimodem(struct tty *tp, int sigon, int sigoff)
940218822Sdim{
941218822Sdim	struct si_port *pp;
942218822Sdim	volatile struct si_channel *ccbp;
94338889Sjdp	int x;
944218822Sdim
94538889Sjdp	pp = tp->t_sc;
946218822Sdim	DPRINT((pp, DBG_ENTRY|DBG_MODEM, "simodem(%x,%x)\n", sigon, sigoff));
947218822Sdim	ccbp = pp->sp_ccb;		/* Find channel address */
948218822Sdim	if (sigon == 0 && sigoff == 0) {
949218822Sdim		x = ccbp->hi_ip;
950218822Sdim		/*
951218822Sdim		 * XXX: not sure this is correct, should it be CTS&DSR ?
952218822Sdim		 * XXX: or do we (just) miss CTS & DSR ?
953218822Sdim		 */
954218822Sdim		if (x & IP_DCD)		sigon |= SER_DCD;
955218822Sdim		if (x & IP_DTR)		sigon |= SER_DTR;
956218822Sdim		if (x & IP_RTS)		sigon |= SER_RTS;
957218822Sdim		if (x & IP_RI)		sigon |= SER_RI;
958218822Sdim		return (sigon);
959218822Sdim	}
960218822Sdim
961218822Sdim	x = ccbp->hi_op;
962218822Sdim	if (sigon & SER_DTR)
963218822Sdim		x |= OP_DSR;
964218822Sdim	if (sigoff & SER_DTR)
965218822Sdim		x &= ~OP_DSR;
966218822Sdim	if (sigon & SER_RTS)
967218822Sdim		x |= OP_CTS;
968218822Sdim	if (sigoff & SER_RTS)
969218822Sdim		x &= ~OP_CTS;
970218822Sdim	ccbp->hi_op = x;
971218822Sdim	return 0;
972218822Sdim}
973218822Sdim
974218822Sdim/*
975218822Sdim * Handle change of modem state
976218822Sdim */
977218822Sdimstatic void
978218822Sdimsi_modem_state(struct si_port *pp, struct tty *tp, int hi_ip)
979218822Sdim{
980218822Sdim							/* if a modem dev */
981218822Sdim	if (hi_ip & IP_DCD) {
982218822Sdim		if (!(pp->sp_last_hi_ip & IP_DCD)) {
983218822Sdim			DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n",
984218822Sdim				tp->t_line));
985218822Sdim			(void)ttyld_modem(tp, 1);
986218822Sdim		}
987218822Sdim	} else {
988218822Sdim		if (pp->sp_last_hi_ip & IP_DCD) {
989218822Sdim			DPRINT((pp, DBG_INTR, "modem carr off\n"));
990218822Sdim			if (ttyld_modem(tp, 0))
991218822Sdim				(void) simodem(tp, 0, SER_DTR | SER_RTS);
992218822Sdim		}
993218822Sdim	}
994218822Sdim	pp->sp_last_hi_ip = hi_ip;
995218822Sdim
996218822Sdim}
997218822Sdim
998218822Sdim/*
999218822Sdim * Poller to catch missed interrupts.
1000218822Sdim *
1001218822Sdim * Note that the SYSV Specialix drivers poll at 100 times per second to get
1002218822Sdim * better response.  We could really use a "periodic" version timeout(). :-)
1003218822Sdim */
1004218822Sdim#ifdef POLL
1005218822Sdimstatic void
100660484Sobriensi_poll(void *nothing)
1007130561Sobrien{
100860484Sobrien	struct si_softc *sc;
100938889Sjdp	int i;
101038889Sjdp	volatile struct si_reg *regp;
101138889Sjdp	struct si_port *pp;
101238889Sjdp	int lost, oldspl, port;
101338889Sjdp
101438889Sjdp	DPRINT((0, DBG_POLL, "si_poll()\n"));
101538889Sjdp	oldspl = spltty();
101689857Sobrien	if (in_intr)
101789857Sobrien		goto out;
101889857Sobrien	lost = 0;
101938889Sjdp	for (i = 0; i < si_numunits; i++) {
102038889Sjdp		sc = devclass_get_softc(si_devclass, i);
102138889Sjdp		if (sc == NULL || sc->sc_type == SIEMPTY)
102238889Sjdp			continue;
102338889Sjdp		regp = (struct si_reg *)sc->sc_maddr;
102438889Sjdp
102577298Sobrien		/*
102677298Sobrien		 * See if there has been a pending interrupt for 2 seconds
102777298Sobrien		 * or so. The test (int_scounter >= 200) won't correspond
102860484Sobrien		 * to 2 seconds if int_count gets changed.
102938889Sjdp		 */
103038889Sjdp		if (regp->int_pending != 0) {
103138889Sjdp			if (regp->int_scounter >= 200 &&
103238889Sjdp			    regp->initstat == 1) {
103338889Sjdp				printf("si%d: lost intr\n", i);
103438889Sjdp				lost++;
103538889Sjdp			}
103638889Sjdp		} else {
103738889Sjdp			regp->int_scounter = 0;
103838889Sjdp		}
103938889Sjdp
104038889Sjdp		/*
104138889Sjdp		 * gripe about no input flow control..
104238889Sjdp		 */
104338889Sjdp		pp = sc->sc_ports;
104438889Sjdp		for (port = 0; port < sc->sc_nport; pp++, port++) {
104538889Sjdp			if (pp->sp_delta_overflows > 0) {
1046218822Sdim				printf("si%d: %d tty level buffer overflows\n",
104738889Sjdp					i, pp->sp_delta_overflows);
104838889Sjdp				pp->sp_delta_overflows = 0;
104938889Sjdp			}
105038889Sjdp		}
105138889Sjdp	}
105238889Sjdp	if (lost || si_realpoll)
105338889Sjdp		si_intr(NULL);	/* call intr with fake vector */
105438889Sjdpout:
1055218822Sdim	splx(oldspl);
105633965Sjdp
1057218822Sdim	timeout(si_poll, (caddr_t)0L, si_pollrate);
105833965Sjdp}
1059218822Sdim#endif	/* ifdef POLL */
106033965Sjdp
1061218822Sdim/*
106233965Sjdp * The interrupt handler polls ALL ports on ALL adapters each time
106333965Sjdp * it is called.
1064218822Sdim */
106533965Sjdp
1066218822Sdimstatic BYTE si_rxbuf[SI_BUFFERSIZE];	/* input staging area */
106733965Sjdpstatic BYTE si_txbuf[SI_BUFFERSIZE];	/* output staging area */
1068218822Sdim
106933965Sjdpvoid
107033965Sjdpsi_intr(void *arg)
1071218822Sdim{
1072218822Sdim	struct si_softc *sc;
1073218822Sdim	struct si_port *pp;
1074218822Sdim	volatile struct si_channel *ccbp;
1075218822Sdim	struct tty *tp;
1076218822Sdim	volatile caddr_t maddr;
1077218822Sdim	BYTE op, ip;
107860484Sobrien	int x, card, port, n, i, isopen;
107960484Sobrien	volatile BYTE *z;
108060484Sobrien	BYTE c;
108160484Sobrien
108260484Sobrien	sc = arg;
1083218822Sdim
108460484Sobrien	DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "si_intr\n"));
108560484Sobrien	if (in_intr)
1086218822Sdim		return;
1087218822Sdim	in_intr = 1;
1088218822Sdim
1089218822Sdim	/*
1090218822Sdim	 * When we get an int we poll all the channels and do ALL pending
1091218822Sdim	 * work, not just the first one we find. This allows all cards to
1092218822Sdim	 * share the same vector.
1093218822Sdim	 *
1094218822Sdim	 * XXX - But if we're sharing the vector with something that's NOT
1095218822Sdim	 * a SI/XIO/SX card, we may be making more work for ourselves.
1096218822Sdim	 */
1097218822Sdim	for (card = 0; card < si_numunits; card++) {
1098218822Sdim		sc = devclass_get_softc(si_devclass, card);
1099218822Sdim		if (sc == NULL || sc->sc_type == SIEMPTY)
1100218822Sdim			continue;
1101218822Sdim
1102218822Sdim		/*
1103218822Sdim		 * First, clear the interrupt
1104218822Sdim		 */
1105218822Sdim		switch(sc->sc_type) {
1106218822Sdim		case SIHOST:
1107218822Sdim			maddr = sc->sc_maddr;
1108218822Sdim			((volatile struct si_reg *)maddr)->int_pending = 0;
1109218822Sdim							/* flag nothing pending */
111038889Sjdp			*(maddr+SIINTCL) = 0x00;	/* Set IRQ clear */
111133965Sjdp			*(maddr+SIINTCL_CL) = 0x00;	/* Clear IRQ clear */
1112218822Sdim			break;
111360484Sobrien		case SIHOST2:
111460484Sobrien			maddr = sc->sc_maddr;
111533965Sjdp			((volatile struct si_reg *)maddr)->int_pending = 0;
111633965Sjdp			*(maddr+SIPLIRQCLR) = 0x00;
111733965Sjdp			*(maddr+SIPLIRQCLR) = 0x10;
111838889Sjdp			break;
111938889Sjdp		case SIPCI:
112033965Sjdp			maddr = sc->sc_maddr;
112189857Sobrien			((volatile struct si_reg *)maddr)->int_pending = 0;
1122218822Sdim			*(maddr+SIPCIINTCL) = 0x0;
112333965Sjdp			break;
112433965Sjdp		case SIJETPCI:	/* fall through to JETISA case */
112560484Sobrien		case SIJETISA:
112660484Sobrien			maddr = sc->sc_maddr;
112760484Sobrien			((volatile struct si_reg *)maddr)->int_pending = 0;
112860484Sobrien			*(maddr+SIJETINTCL) = 0x0;
112977298Sobrien			break;
113077298Sobrien		case SIEISA:
113177298Sobrien			maddr = sc->sc_maddr;
113277298Sobrien			((volatile struct si_reg *)maddr)->int_pending = 0;
113377298Sobrien			(void)inb(sc->sc_iobase + 3);
113477298Sobrien			break;
113577298Sobrien		case SIEMPTY:
113633965Sjdp		default:
113760484Sobrien			continue;
113860484Sobrien		}
113960484Sobrien		((volatile struct si_reg *)maddr)->int_scounter = 0;
114060484Sobrien
114160484Sobrien		/*
114233965Sjdp		 * check each port
114333965Sjdp		 */
114438889Sjdp		for (pp = sc->sc_ports, port = 0; port < sc->sc_nport;
114533965Sjdp		     pp++, port++) {
114633965Sjdp			ccbp = pp->sp_ccb;
114733965Sjdp			tp = pp->sp_tty;
114838889Sjdp
114994536Sobrien			/*
115094536Sobrien			 * See if a command has completed ?
115133965Sjdp			 */
115260484Sobrien			if (ccbp->hi_stat != pp->sp_pend) {
115333965Sjdp				DPRINT((pp, DBG_INTR,
115460484Sobrien					"si_intr hi_stat = 0x%x, pend = %d\n",
115533965Sjdp					ccbp->hi_stat, pp->sp_pend));
115633965Sjdp				switch(pp->sp_pend) {
115760484Sobrien				case LOPEN:
115833965Sjdp				case MPEND:
115960484Sobrien				case MOPEN:
116033965Sjdp				case CONFIG:
116133965Sjdp				case SBREAK:
116260484Sobrien				case EBREAK:
116338889Sjdp					pp->sp_pend = ccbp->hi_stat;
116460484Sobrien						/* sleeping in si_command */
116538889Sjdp					wakeup(&pp->sp_state);
116633965Sjdp					break;
116738889Sjdp				default:
116838889Sjdp					pp->sp_pend = ccbp->hi_stat;
116933965Sjdp				}
117033965Sjdp			}
117138889Sjdp
117233965Sjdp			/*
117360484Sobrien			 * Continue on if it's closed
1174218822Sdim			 */
1175218822Sdim			if (ccbp->hi_stat == IDLE_CLOSE) {
1176218822Sdim				continue;
1177218822Sdim			}
117838889Sjdp
117933965Sjdp			/*
1180104834Sobrien			 * Do modem state change if not a local device
118138889Sjdp			 */
118238889Sjdp			si_modem_state(pp, tp, ccbp->hi_ip);
118338889Sjdp
118438889Sjdp			/*
1185130561Sobrien			 * Check to see if we should 'receive' characters.
1186104834Sobrien			 */
1187104834Sobrien			if (tp->t_state & TS_CONNECTED &&
1188104834Sobrien			    tp->t_state & TS_ISOPEN)
118938889Sjdp				isopen = 1;
119033965Sjdp			else
119133965Sjdp				isopen = 0;
119233965Sjdp
119333965Sjdp			/*
119433965Sjdp			 * Do input break processing
119533965Sjdp			 */
119633965Sjdp			if (ccbp->hi_state & ST_BREAK) {
119733965Sjdp				if (isopen) {
1198218822Sdim				    ttyld_rint(tp, TTY_BI);
1199218822Sdim				}
1200218822Sdim				ccbp->hi_state &= ~ST_BREAK;   /* A Bit iffy this */
1201218822Sdim				DPRINT((pp, DBG_INTR, "si_intr break\n"));
1202218822Sdim			}
1203218822Sdim
1204218822Sdim			/*
1205218822Sdim			 * Do RX stuff - if not open then dump any characters.
1206218822Sdim			 * XXX: This is VERY messy and needs to be cleaned up.
1207218822Sdim			 *
1208104834Sobrien			 * XXX: can we leave data in the host adapter buffer
1209218822Sdim			 * when the clists are full?  That may be dangerous
1210218822Sdim			 * if the user cannot get an interrupt signal through.
1211218822Sdim			 */
1212218822Sdim
1213218822Sdim	more_rx:	/* XXX Sorry. the nesting was driving me bats! :-( */
1214218822Sdim
1215218822Sdim			if (!isopen) {
1216218822Sdim				ccbp->hi_rxopos = ccbp->hi_rxipos;
1217218822Sdim				goto end_rx;
1218218822Sdim			}
1219218822Sdim
1220218822Sdim			/*
1221218822Sdim			 * If the tty input buffers are blocked, stop emptying
1222218822Sdim			 * the incoming buffers and let the auto flow control
1223218822Sdim			 * assert..
1224218822Sdim			 */
1225218822Sdim			if (tp->t_state & TS_TBLOCK) {
1226218822Sdim				goto end_rx;
1227218822Sdim			}
1228218822Sdim
1229218822Sdim			/*
1230218822Sdim			 * Process read characters if not skipped above
1231218822Sdim			 */
1232218822Sdim			op = ccbp->hi_rxopos;
1233218822Sdim			ip = ccbp->hi_rxipos;
1234218822Sdim			c = ip - op;
1235218822Sdim			if (c == 0) {
1236218822Sdim				goto end_rx;
1237218822Sdim			}
1238218822Sdim
1239218822Sdim			n = c & 0xff;
1240218822Sdim			if (n > 250)
1241218822Sdim				n = 250;
1242218822Sdim
1243218822Sdim			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
1244218822Sdim						n, op, ip));
1245218822Sdim
1246218822Sdim			/*
1247218822Sdim			 * Suck characters out of host card buffer into the
1248218822Sdim			 * "input staging buffer" - so that we dont leave the
1249218822Sdim			 * host card in limbo while we're possibly echoing
1250218822Sdim			 * characters and possibly flushing input inside the
1251218822Sdim			 * ldisc l_rint() routine.
1252218822Sdim			 */
1253218822Sdim			if (n <= SI_BUFFERSIZE - op) {
1254218822Sdim
1255218822Sdim				DPRINT((pp, DBG_INTR, "\tsingle copy\n"));
1256218822Sdim				z = ccbp->hi_rxbuf + op;
1257218822Sdim				si_vbcopy(z, si_rxbuf, n);
1258218822Sdim
1259218822Sdim				op += n;
1260218822Sdim			} else {
1261218822Sdim				x = SI_BUFFERSIZE - op;
1262218822Sdim
1263218822Sdim				DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x));
1264218822Sdim				z = ccbp->hi_rxbuf + op;
1265218822Sdim				si_vbcopy(z, si_rxbuf, x);
1266218822Sdim
1267218822Sdim				DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n",
1268218822Sdim					n - x));
1269218822Sdim				z = ccbp->hi_rxbuf;
1270218822Sdim				si_vbcopy(z, si_rxbuf + x, n - x);
127133965Sjdp
127233965Sjdp				op += n;
127333965Sjdp			}
127433965Sjdp
1275218822Sdim			/* clear collected characters from buffer */
1276218822Sdim			ccbp->hi_rxopos = op;
1277218822Sdim
1278218822Sdim			DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n",
1279218822Sdim						n, op, ip));
1280218822Sdim
1281218822Sdim			/*
1282218822Sdim			 * at this point...
1283218822Sdim			 * n = number of chars placed in si_rxbuf
1284104834Sobrien			 */
1285218822Sdim
1286218822Sdim			/*
1287218822Sdim			 * Avoid the grotesquely inefficient lineswitch
1288218822Sdim			 * routine (ttyinput) in "raw" mode. It usually
128933965Sjdp			 * takes about 450 instructions (that's without
129033965Sjdp			 * canonical processing or echo!). slinput is
1291218822Sdim			 * reasonably fast (usually 40 instructions
1292218822Sdim			 * plus call overhead).
1293218822Sdim			 */
1294218822Sdim			if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
1295218822Sdim
1296218822Sdim				/* block if the driver supports it */
1297218822Sdim				if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER &&
1298218822Sdim				    (tp->t_cflag & CRTS_IFLOW ||
1299218822Sdim				     tp->t_iflag & IXOFF) &&
1300218822Sdim				    !(tp->t_state & TS_TBLOCK))
1301218822Sdim					ttyblock(tp);
1302218822Sdim
1303218822Sdim				tk_nin += n;
1304130561Sobrien				tk_rawcc += n;
1305218822Sdim				tp->t_rawcc += n;
1306218822Sdim
1307218822Sdim				pp->sp_delta_overflows +=
1308218822Sdim				    b_to_q((char *)si_rxbuf, n, &tp->t_rawq);
130989857Sobrien
1310218822Sdim				ttwakeup(tp);
1311218822Sdim				if (tp->t_state & TS_TTSTOP &&
1312218822Sdim				    (tp->t_iflag & IXANY ||
1313218822Sdim				     tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
1314218822Sdim					tp->t_state &= ~TS_TTSTOP;
1315218822Sdim					tp->t_lflag &= ~FLUSHO;
1316218822Sdim					si_start(tp);
1317218822Sdim				}
1318104834Sobrien			} else {
1319218822Sdim				/*
1320218822Sdim				 * It'd be nice to not have to go through the
1321218822Sdim				 * function call overhead for each char here.
1322218822Sdim				 * It'd be nice to block input it, saving a
1323218822Sdim				 * loop here and the call/return overhead.
1324218822Sdim				 */
1325218822Sdim				for(x = 0; x < n; x++) {
1326218822Sdim					i = si_rxbuf[x];
1327218822Sdim					if (ttyld_rint(tp, i)
1328218822Sdim					     == -1) {
1329218822Sdim						pp->sp_delta_overflows++;
1330218822Sdim					}
1331218822Sdim				}
1332130561Sobrien			}
1333130561Sobrien			goto more_rx;	/* try for more until RXbuf is empty */
1334218822Sdim
1335218822Sdim	end_rx:		/* XXX: Again, sorry about the gotos.. :-) */
1336218822Sdim
1337218822Sdim			/*
1338218822Sdim			 * Do TX stuff
1339218822Sdim			 */
1340218822Sdim			ttyld_start(tp);
1341218822Sdim
1342218822Sdim		} /* end of for (all ports on this controller) */
1343218822Sdim	} /* end of for (all controllers) */
1344218822Sdim
1345218822Sdim	in_intr = 0;
1346218822Sdim	DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "end si_intr\n"));
1347218822Sdim}
1348218822Sdim
1349218822Sdim/*
1350218822Sdim * Nudge the transmitter...
1351218822Sdim *
1352218822Sdim * XXX: I inherited some funny code here.  It implies the host card only
1353218822Sdim * interrupts when the transmit buffer reaches the low-water-mark, and does
1354218822Sdim * not interrupt when it's actually hits empty.  In some cases, we have
1355218822Sdim * processes waiting for complete drain, and we need to simulate an interrupt
1356218822Sdim * about when we think the buffer is going to be empty (and retry if not).
1357218822Sdim * I really am not certain about this...  I *need* the hardware manuals.
1358218822Sdim */
1359218822Sdimstatic void
1360218822Sdimsi_start(struct tty *tp)
1361218822Sdim{
1362218822Sdim	struct si_port *pp;
1363218822Sdim	volatile struct si_channel *ccbp;
1364218822Sdim	struct clist *qp;
1365218822Sdim	BYTE ipos;
1366218822Sdim	int nchar;
1367218822Sdim	int oldspl, count, n, amount, buffer_full;
1368218822Sdim
1369218822Sdim	oldspl = spltty();
1370218822Sdim
1371218822Sdim	qp = &tp->t_outq;
1372218822Sdim	pp = tp->t_sc;
1373218822Sdim
1374218822Sdim	DPRINT((pp, DBG_ENTRY|DBG_START,
1375218822Sdim		"si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n",
1376218822Sdim		tp, tp->t_state, pp->sp_state, qp->c_cc));
1377218822Sdim
1378218822Sdim	if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
1379218822Sdim		goto out;
1380218822Sdim
1381218822Sdim	buffer_full = 0;
1382218822Sdim	ccbp = pp->sp_ccb;
1383218822Sdim
1384218822Sdim	count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
1385218822Sdim	DPRINT((pp, DBG_START, "count %d\n", (BYTE)count));
1386218822Sdim
1387218822Sdim	while ((nchar = qp->c_cc) > 0) {
1388218822Sdim		if ((BYTE)count >= 255) {
1389218822Sdim			buffer_full++;
1390218822Sdim			break;
1391218822Sdim		}
1392218822Sdim		amount = min(nchar, (255 - (BYTE)count));
1393218822Sdim		ipos = (unsigned int)ccbp->hi_txipos;
1394218822Sdim		n = q_to_b(&tp->t_outq, si_txbuf, amount);
1395218822Sdim		/* will it fit in one lump? */
1396218822Sdim		if ((SI_BUFFERSIZE - ipos) >= n) {
1397218822Sdim			si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], n);
1398218822Sdim		} else {
1399218822Sdim			si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos],
1400218822Sdim				SI_BUFFERSIZE - ipos);
1401218822Sdim			si_bcopyv(si_txbuf + (SI_BUFFERSIZE - ipos),
1402218822Sdim				&ccbp->hi_txbuf[0], n - (SI_BUFFERSIZE - ipos));
1403218822Sdim		}
1404218822Sdim		ccbp->hi_txipos += n;
1405104834Sobrien		count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos;
1406104834Sobrien	}
1407218822Sdim
1408218822Sdim	if (count != 0 && nchar == 0) {
1409218822Sdim		tp->t_state |= TS_BUSY;
1410218822Sdim	} else {
1411218822Sdim		tp->t_state &= ~TS_BUSY;
1412218822Sdim	}
1413218822Sdim
1414218822Sdim	/* wakeup time? */
1415218822Sdim	ttwwakeup(tp);
1416218822Sdim
1417218822Sdim	DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n",
1418218822Sdim		(BYTE)count, nchar, tp->t_state));
141994536Sobrien
1420218822Sdim	if (tp->t_state & TS_BUSY)
142133965Sjdp	{
142233965Sjdp		int time;
1423218822Sdim
1424218822Sdim		time = ttspeedtab(tp->t_ospeed, chartimes);
1425218822Sdim
1426218822Sdim		if (time > 0) {
1427218822Sdim			if (time < nchar)
142838889Sjdp				time = nchar / time;
1429218822Sdim			else
1430218822Sdim				time = 2;
1431218822Sdim		} else {
1432218822Sdim			DPRINT((pp, DBG_START,
1433218822Sdim				"bad char time value! %d\n", time));
143489857Sobrien			time = hz/10;
1435218822Sdim		}
1436218822Sdim
1437218822Sdim		if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) {
1438218822Sdim			untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch);
1439218822Sdim		} else {
1440218822Sdim			pp->sp_state |= SS_LSTART;
1441218822Sdim		}
1442218822Sdim		DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time));
1443218822Sdim		pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time);
144433965Sjdp	}
144538889Sjdp
144638889Sjdpout:
144738889Sjdp	splx(oldspl);
1448	DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n"));
1449}
1450
1451/*
1452 * Note: called at splsoftclock from the timeout code
1453 * This has to deal with two things...  cause wakeups while waiting for
1454 * tty drains on last process exit, and call l_start at about the right
1455 * time for protocols like ppp.
1456 */
1457static void
1458si_lstart(void *arg)
1459{
1460	struct si_port *pp = arg;
1461	struct tty *tp;
1462	int oldspl;
1463
1464	DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n",
1465		pp, pp->sp_state));
1466
1467	oldspl = spltty();
1468	tp = pp->sp_tty;
1469
1470	if ((tp->t_state & TS_ISOPEN) == 0 ||
1471	    (pp->sp_state & SS_LSTART) == 0) {
1472		splx(oldspl);
1473		return;
1474	}
1475	pp->sp_state &= ~SS_LSTART;
1476	pp->sp_state |= SS_INLSTART;
1477
1478
1479	/* deal with the process exit case */
1480	ttwwakeup(tp);
1481
1482	/* nudge protocols - eg: ppp */
1483	ttyld_start(tp);
1484
1485	pp->sp_state &= ~SS_INLSTART;
1486	splx(oldspl);
1487}
1488
1489/*
1490 * Stop output on a line. called at spltty();
1491 */
1492static void
1493si_stop(struct tty *tp, int rw)
1494{
1495	volatile struct si_channel *ccbp;
1496	struct si_port *pp;
1497
1498	pp = tp->t_sc;
1499	ccbp = pp->sp_ccb;
1500
1501	DPRINT((pp, DBG_ENTRY|DBG_STOP, "si_stop(%x,%x)\n", tp, rw));
1502
1503	/* XXX: must check (rw & FWRITE | FREAD) etc flushing... */
1504	if (rw & FWRITE) {
1505		/* what level are we meant to be flushing anyway? */
1506		if (tp->t_state & TS_BUSY) {
1507			si_command(pp, WFLUSH, SI_NOWAIT);
1508			tp->t_state &= ~TS_BUSY;
1509			ttwwakeup(tp);	/* Bruce???? */
1510		}
1511	}
1512#if 1	/* XXX: this doesn't work right yet.. */
1513	/* XXX: this may have been failing because we used to call l_rint()
1514	 * while we were looping based on these two counters. Now, we collect
1515	 * the data and then loop stuffing it into l_rint(), making this
1516	 * useless.  Should we cause this to blow away the staging buffer?
1517	 */
1518	if (rw & FREAD) {
1519		ccbp->hi_rxopos = ccbp->hi_rxipos;
1520	}
1521#endif
1522}
1523
1524/*
1525 * Issue a command to the host card CPU.
1526 */
1527
1528static void
1529si_command(struct si_port *pp, int cmd, int waitflag)
1530{
1531	int oldspl;
1532	volatile struct si_channel *ccbp = pp->sp_ccb;
1533	int x;
1534
1535	DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n",
1536		pp, cmd, waitflag, ccbp->hi_stat));
1537
1538	oldspl = spltty();		/* Keep others out */
1539
1540	/* wait until it's finished what it was doing.. */
1541	/* XXX: sits in IDLE_BREAK until something disturbs it or break
1542	 * is turned off. */
1543	while((x = ccbp->hi_stat) != IDLE_OPEN &&
1544			x != IDLE_CLOSE &&
1545			x != IDLE_BREAK &&
1546			x != cmd) {
1547		if (in_intr) {			/* Prevent sleep in intr */
1548			DPRINT((pp, DBG_PARAM,
1549				"cmd intr collision - completing %d\trequested %d\n",
1550				x, cmd));
1551			splx(oldspl);
1552			return;
1553		} else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
1554				"sicmd1", 1)) {
1555			splx(oldspl);
1556			return;
1557		}
1558	}
1559	/* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */
1560
1561	/* if there was a pending command, cause a state-change wakeup */
1562	switch(pp->sp_pend) {
1563	case LOPEN:
1564	case MPEND:
1565	case MOPEN:
1566	case CONFIG:
1567	case SBREAK:
1568	case EBREAK:
1569		wakeup(&pp->sp_state);
1570		break;
1571	default:
1572		break;
1573	}
1574
1575	pp->sp_pend = cmd;		/* New command pending */
1576	ccbp->hi_stat = cmd;		/* Post it */
1577
1578	if (waitflag) {
1579		if (in_intr) {		/* If in interrupt handler */
1580			DPRINT((pp, DBG_PARAM,
1581				"attempt to sleep in si_intr - cmd req %d\n",
1582				cmd));
1583			splx(oldspl);
1584			return;
1585		} else while(ccbp->hi_stat != IDLE_OPEN &&
1586			     ccbp->hi_stat != IDLE_BREAK) {
1587			if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH,
1588			    "sicmd2", 0))
1589				break;
1590		}
1591	}
1592	splx(oldspl);
1593}
1594
1595
1596#ifdef	SI_DEBUG
1597
1598void
1599si_dprintf(struct si_port *pp, int flags, const char *fmt, ...)
1600{
1601	va_list ap;
1602
1603	if ((pp == NULL && (si_debug&flags)) ||
1604	    (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) {
1605		if (pp != NULL)
1606			printf("%s: ", pp->sp_name);
1607		va_start(ap, fmt);
1608		vprintf(fmt, ap);
1609		va_end(ap);
1610	}
1611}
1612
1613#endif	/* DEBUG */
1614
1615static char *
1616si_modulename(int host_type, int uart_type)
1617{
1618	switch (host_type) {
1619	/* Z280 based cards */
1620	case SIEISA:
1621	case SIHOST2:
1622	case SIHOST:
1623	case SIPCI:
1624		switch (uart_type) {
1625		case 0:
1626			return(" (XIO)");
1627		case 1:
1628			return(" (SI)");
1629		}
1630		break;
1631	/* T225 based hosts */
1632	case SIJETPCI:
1633	case SIJETISA:
1634		switch (uart_type) {
1635		case 0:
1636			return(" (SI)");
1637		case 40:
1638			return(" (XIO)");
1639		case 72:
1640			return(" (SXDC)");
1641		}
1642		break;
1643	}
1644	return("");
1645}
1646