From Ache
[unix-history] / sys / i386 / isa / sio.c
CommitLineData
15637ed4
RG
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
d7136515 33 * from: @(#)com.c 7.5 (Berkeley) 5/16/91
45fab98c 34 * $Id: sio.c,v 1.52 1994/05/31 18:18:46 ache Exp $
15637ed4 35 */
15637ed4
RG
36
37#include "sio.h"
38#if NSIO > 0
39/*
55116fbc 40 * Serial driver, based on 386BSD-0.1 com driver.
15637ed4
RG
41 * Mostly rewritten to use pseudo-DMA.
42 * Works for National Semiconductor NS8250-NS16550AF UARTs.
55116fbc 43 * COM driver, based on HP dca driver.
15637ed4
RG
44 */
45#include "param.h"
46#include "systm.h"
47#include "ioctl.h"
48#include "tty.h"
49#include "proc.h"
50#include "user.h"
51#include "conf.h"
8a86c29c 52#include "dkstat.h"
15637ed4
RG
53#include "file.h"
54#include "uio.h"
55#include "kernel.h"
8a86c29c 56#include "malloc.h"
15637ed4
RG
57#include "syslog.h"
58
45fab98c 59#include "i386/isa/icu.h" /* XXX */
15637ed4
RG
60#include "i386/isa/isa.h"
61#include "i386/isa/isa_device.h"
62#include "i386/isa/comreg.h"
63#include "i386/isa/ic/ns16550.h"
64
15637ed4
RG
65#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
66#define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE)
67#define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8)
68#define RS_IBUFSIZE 256
15637ed4
RG
69#define TTY_BI TTY_FE /* XXX */
70#define TTY_OE TTY_PE /* XXX */
55116fbc 71
8a86c29c
AC
72#define CALLOUT_MASK 0x80
73#define CONTROL_MASK 0x60
74#define CONTROL_INIT_STATE 0x20
75#define CONTROL_LOCK_STATE 0x40
76#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev)))
77#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK)
78#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK)
0a817a83 79
15637ed4
RG
80#ifdef COM_MULTIPORT
81/* checks in flags for multiport and which is multiport "master chip"
82 * for a given card
83 */
0a817a83
AC
84#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01)
85#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff)
f36101c6 86#define COM_NOMASTER(dev) ((dev)->id_flags & 0x04)
15637ed4 87#endif /* COM_MULTIPORT */
0a817a83
AC
88
89#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
45fab98c 90#define COM_QUIET(dev) ((dev)->id_flags & 0x80)
15637ed4
RG
91
92#define com_scr 7 /* scratch register for 16450-16550 (R/W) */
15637ed4
RG
93
94/*
95 * Input buffer watermarks.
96 * The external device is asked to stop sending when the buffer exactly reaches
97 * high water, or when the high level requests it.
98 * The high level is notified immediately (rather than at a later clock tick)
99 * when this watermark is reached.
100 * The buffer size is chosen so the watermark should almost never be reached.
101 * The low watermark is invisibly 0 since the buffer is always emptied all at
102 * once.
103 */
104#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
105
106/*
107 * com state bits.
108 * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
109 * than the other bits so that they can be tested as a group without masking
110 * off the low bits.
111 *
112 * The following com and tty flags correspond closely:
113 * TS_BUSY = CS_BUSY (maintained by comstart() and comflush())
114 * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop())
115 * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam())
116 * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam())
117 * TS_FLUSH is not used.
8a86c29c
AC
118 * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
119 * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state).
15637ed4
RG
120 */
121#define CS_BUSY 0x80 /* output in progress */
122#define CS_TTGO 0x40 /* output not stopped by XOFF */
123#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */
124#define CS_CHECKMSR 1 /* check of MSR scheduled */
125#define CS_CTS_OFLOW 2 /* use CTS output flow control */
8a86c29c 126#define CS_DTR_OFF 0x10 /* DTR held off */
15637ed4
RG
127#define CS_ODONE 4 /* output completed */
128#define CS_RTS_IFLOW 8 /* use RTS input flow control */
129
8a86c29c 130static char const * const error_desc[] = {
15637ed4
RG
131#define CE_OVERRUN 0
132 "silo overflow",
133#define CE_INTERRUPT_BUF_OVERFLOW 1
134 "interrupt-level buffer overflow",
135#define CE_TTY_BUF_OVERFLOW 2
136 "tty-level buffer overflow",
137};
138
139#define CE_NTYPES 3
140#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
141
142/* types. XXX - should be elsewhere */
143typedef u_int Port_t; /* hardware port */
15637ed4
RG
144typedef u_char bool_t; /* boolean */
145
146/* com device structure */
147struct com_s {
148 u_char state; /* miscellaneous flag bits */
8a86c29c 149 bool_t active_out; /* nonzero if the callout device is open */
15637ed4 150 u_char cfcr_image; /* copy of value written to CFCR */
8a86c29c
AC
151 u_char ftl; /* current rx fifo trigger level */
152 u_char ftl_init; /* ftl_max for next open() */
153 u_char ftl_max; /* maximum ftl for curent open() */
15637ed4
RG
154 bool_t hasfifo; /* nonzero for 16550 UARTs */
155 u_char mcr_image; /* copy of value written to MCR */
15637ed4
RG
156#ifdef COM_MULTIPORT
157 bool_t multiport; /* is this unit part of a multiport device? */
158#endif /* COM_MULTIPORT */
8a86c29c 159 int dtr_wait; /* time to hold DTR down on close (* 1/hz) */
beb52b92 160 u_int tx_fifo_size;
8a86c29c 161 u_int wopeners; /* # processes waiting for DCD in open() */
15637ed4
RG
162
163 /*
164 * The high level of the driver never reads status registers directly
165 * because there would be too many side effects to handle conveniently.
166 * Instead, it reads copies of the registers stored here by the
167 * interrupt handler.
168 */
169 u_char last_modem_status; /* last MSR read by intr handler */
170 u_char prev_modem_status; /* last MSR handled by high level */
171
8a86c29c 172 u_char hotchar; /* ldisc-specific char to be handled ASAP */
15637ed4
RG
173 u_char *ibuf; /* start of input buffer */
174 u_char *ibufend; /* end of input buffer */
175 u_char *ihighwater; /* threshold in input buffer */
176 u_char *iptr; /* next free spot in input buffer */
177
178 u_char *obufend; /* end of output buffer */
179 int ocount; /* original count for current output */
180 u_char *optr; /* next char to output */
181
182 Port_t data_port; /* i/o ports */
183 Port_t int_id_port;
184 Port_t iobase;
185 Port_t modem_ctl_port;
186 Port_t line_status_port;
187 Port_t modem_status_port;
188
189 struct tty *tp; /* cross reference */
190
8a86c29c
AC
191 /* Initial state. */
192 struct termios it_in; /* should be in struct tty */
193 struct termios it_out;
194
195 /* Lock state. */
196 struct termios lt_in; /* should be in struct tty */
197 struct termios lt_out;
198
0a817a83
AC
199#ifdef TIOCTIMESTAMP
200 bool_t do_timestamp;
201 struct timeval timestamp;
202#endif
203
15637ed4
RG
204 u_long bytes_in; /* statistics */
205 u_long bytes_out;
206 u_int delta_error_counts[CE_NTYPES];
207 u_int error_counts[CE_NTYPES];
208
209 /*
210 * Ping-pong input buffers. The extra factor of 2 in the sizes is
211 * to allow for an error byte for each input byte.
212 */
213#define CE_INPUT_OFFSET RS_IBUFSIZE
214 u_char ibuf1[2 * RS_IBUFSIZE];
215 u_char ibuf2[2 * RS_IBUFSIZE];
216};
217
15637ed4 218/*
0a817a83
AC
219 * The public functions in the com module ought to be declared in a com-driver
220 * system header.
15637ed4
RG
221 */
222#define Dev_t int /* promoted dev_t */
15637ed4 223
0a817a83
AC
224/* Interrupt handling entry points. */
225void siointr __P((int unit));
226void siopoll __P((void));
227
228/* Device switch entry points. */
229int sioopen __P((Dev_t dev, int oflags, int devtype,
230 struct proc *p));
15637ed4
RG
231int sioclose __P((Dev_t dev, int fflag, int devtype,
232 struct proc *p));
0a817a83
AC
233int sioread __P((Dev_t dev, struct uio *uio, int ioflag));
234int siowrite __P((Dev_t dev, struct uio *uio, int ioflag));
15637ed4
RG
235int sioioctl __P((Dev_t dev, int cmd, caddr_t data,
236 int fflag, struct proc *p));
0a817a83
AC
237void siostop __P((struct tty *tp, int rw));
238#define sioreset noreset
239int sioselect __P((Dev_t dev, int rw, struct proc *p));
240#define siommap nommap
241#define siostrategy nostrategy
242
243/* Console device entry points. */
15637ed4 244int siocngetc __P((Dev_t dev));
0a817a83 245struct consdev;
15637ed4
RG
246void siocninit __P((struct consdev *cp));
247void siocnprobe __P((struct consdev *cp));
248void siocnputc __P((Dev_t dev, int c));
15637ed4
RG
249
250static int sioattach __P((struct isa_device *dev));
8a86c29c 251static void siodtrwakeup __P((caddr_t chan, int ticks));
15637ed4
RG
252static void comflush __P((struct com_s *com));
253static void comhardclose __P((struct com_s *com));
0a817a83 254static void siointr1 __P((struct com_s *com));
142126f9 255static void commctl __P((struct com_s *com, int bits, int how));
15637ed4
RG
256static int comparam __P((struct tty *tp, struct termios *t));
257static int sioprobe __P((struct isa_device *dev));
0a817a83
AC
258static void comstart __P((struct tty *tp));
259static void comwakeup __P((caddr_t chan, int ticks));
55116fbc 260static int tiocm_xxx2mcr __P((int tiocm_xxx));
15637ed4
RG
261
262/* table and macro for fast conversion from a unit number to its com struct */
263static struct com_s *p_com_addr[NSIO];
264#define com_addr(unit) (p_com_addr[unit])
265
0a817a83
AC
266#ifdef TIOCTIMESTAMP
267static struct timeval intr_timestamp;
268#endif
01a70cf7 269
15637ed4
RG
270struct isa_driver siodriver = {
271 sioprobe, sioattach, "sio"
272};
273
274#ifdef COMCONSOLE
275static int comconsole = COMCONSOLE;
276#else
277static int comconsole = -1;
278#endif
15637ed4
RG
279static speed_t comdefaultrate = TTYDEF_SPEED;
280static u_int com_events; /* input chars + weighted output completions */
281static int commajor;
6cc15668 282struct tty *sio_tty[NSIO];
8a86c29c 283extern struct tty *constty; /* XXX */
15637ed4
RG
284
285#ifdef KGDB
286#include "machine/remote-sl.h"
287
288extern int kgdb_dev;
289extern int kgdb_rate;
290extern int kgdb_debug_init;
291#endif
292
293static struct speedtab comspeedtab[] = {
294 0, 0,
295 50, COMBRD(50),
296 75, COMBRD(75),
297 110, COMBRD(110),
298 134, COMBRD(134),
299 150, COMBRD(150),
300 200, COMBRD(200),
301 300, COMBRD(300),
302 600, COMBRD(600),
303 1200, COMBRD(1200),
304 1800, COMBRD(1800),
305 2400, COMBRD(2400),
306 4800, COMBRD(4800),
307 9600, COMBRD(9600),
308 19200, COMBRD(19200),
309 38400, COMBRD(38400),
310 57600, COMBRD(57600),
311 115200, COMBRD(115200),
312 -1, -1
313};
314
315/* XXX - configure this list */
316static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
317
318static int
319sioprobe(dev)
320 struct isa_device *dev;
321{
142126f9 322 static bool_t already_init;
15637ed4 323 Port_t *com_ptr;
45fab98c
AC
324 bool_t failures[10];
325 int fn;
326 struct isa_device *idev;
15637ed4 327 Port_t iobase;
8a86c29c 328 u_char mcr_image;
15637ed4
RG
329 int result;
330
331 if (!already_init) {
332 /*
333 * Turn off MCR_IENABLE for all likely serial ports. An unused
334 * port with its MCR_IENABLE gate open will inhibit interrupts
335 * from any used port that shares the interrupt vector.
8a86c29c 336 * XXX the gate enable is elsewhere for some multiports.
15637ed4
RG
337 */
338 for (com_ptr = likely_com_ports;
339 com_ptr < &likely_com_ports[sizeof likely_com_ports
340 / sizeof likely_com_ports[0]];
341 ++com_ptr)
342 outb(*com_ptr + com_mcr, 0);
343 already_init = TRUE;
344 }
8a86c29c
AC
345
346 /*
347 * If the port is on a multiport card and has a master port,
348 * initialize the common interrupt control register in the
45fab98c
AC
349 * master and prepare to leave MCR_IENABLE clear in the mcr.
350 * Otherwise, prepare to set MCR_IENABLE in the mcr.
351 * Point idev to the device struct giving the correct id_irq.
352 * This is the struct for the master device if there is one.
8a86c29c 353 */
45fab98c 354 idev = dev;
8a86c29c
AC
355 mcr_image = MCR_IENABLE;
356#ifdef COM_MULTIPORT
45fab98c
AC
357 if (COM_ISMULTIPORT(dev)) {
358 if (!COM_NOMASTER(dev)) {
359 idev = find_isadev(isa_devtab_tty, &siodriver,
360 COM_MPMASTER(dev));
361 if (idev == NULL) {
362 printf("sio%d: master device %d not found\n",
363 dev->id_unit, COM_MPMASTER(dev));
364 return (0);
365 }
366 if (idev->id_irq == 0) {
367 printf("sio%d: master device %d irq not configured\n",
368 dev->id_unit, COM_MPMASTER(dev));
369 return (0);
370 }
371 outb(idev->id_iobase + com_scr, 0x80);
8a86c29c 372 mcr_image = 0;
45fab98c 373 }
8a86c29c 374 }
45fab98c 375 else
8a86c29c 376#endif /* COM_MULTIPORT */
45fab98c
AC
377 if (idev->id_irq == 0) {
378 printf("sio%d: irq not configured\n", dev->id_unit);
379 return (0);
380 }
8a86c29c 381
45fab98c 382 bzero(failures, sizeof failures);
15637ed4 383 iobase = dev->id_iobase;
15637ed4
RG
384
385 /*
386 * We don't want to get actual interrupts, just masked ones.
387 * Interrupts from this line should already be masked in the ICU,
388 * but mask them in the processor as well in case there are some
389 * (misconfigured) shared interrupts.
390 */
391 disable_intr();
392
45fab98c
AC
393 /*
394 * XXX DELAY() reenables CPU interrupts. This is a problem for
395 * shared interrupts after the first device using one has been
396 * successfully probed - config_isadev() has enabled the interrupt
397 * in the ICU.
398 */
399 outb(IO_ICU1 + 1, 0xff);
400
15637ed4 401 /*
8a86c29c
AC
402 * Initialize the speed and the word size and wait long enough to
403 * drain the maximum of 16 bytes of junk in device output queues.
404 * The speed is undefined after a master reset and must be set
405 * before relying on anything related to output. There may be
406 * junk after a (very fast) soft reboot and (apparently) after
407 * master reset.
408 * XXX what about the UART bug avoided by waiting in comparam()?
409 * We don't want to to wait long enough to drain at 2 bps.
410 */
411 outb(iobase + com_cfcr, CFCR_DLAB);
412 outb(iobase + com_dlbl, COMBRD(9600) & 0xff);
413 outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8);
414 outb(iobase + com_cfcr, CFCR_8BITS);
415 DELAY((16 + 1) * 9600 / 10);
134f8c6e 416
8a86c29c
AC
417 /*
418 * Enable the interrupt gate and disable device interupts. This
419 * should leave the device driving the interrupt line low and
420 * guarantee an edge trigger if an interrupt can be generated.
0d532fc2
AC
421 */
422 outb(iobase + com_mcr, mcr_image);
423 outb(iobase + com_ier, 0);
424
425 /*
8a86c29c
AC
426 * Attempt to set loopback mode so that we can send a null byte
427 * without annoying any external device.
428 */
0d532fc2 429 outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK);
8a86c29c
AC
430
431 /*
432 * Attempt to generate an output interrupt. On 8250's, setting
433 * IER_ETXRDY generates an interrupt independent of the current
434 * setting and independent of whether the THR is empty. On 16450's,
435 * setting IER_ETXRDY generates an interrupt independent of the
436 * current setting. On 16550A's, setting IER_ETXRDY only
437 * generates an interrupt when IER_ETXRDY is not already set.
438 */
439 outb(iobase + com_ier, IER_ETXRDY);
440
441 /*
442 * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate
443 * an interrupt. They'd better generate one for actually doing
444 * output. Loopback may be broken on the same incompatibles but
445 * it's unlikely to do more than allow the null byte out.
446 */
447 outb(iobase + com_data, 0);
448 DELAY((1 + 1) * 9600 / 10);
449
450 /*
451 * Turn off loopback mode so that the interrupt gate works again
452 * (MCR_IENABLE was hidden). This should leave the device driving
453 * an interrupt line high. It doesn't matter if the interrupt
454 * line oscillates while we are not looking at it, since interrupts
455 * are disabled.
8a86c29c
AC
456 */
457 outb(iobase + com_mcr, mcr_image);
134f8c6e 458
8a86c29c
AC
459 /*
460 * Check that
15637ed4
RG
461 * o the CFCR, IER and MCR in UART hold the values written to them
462 * (the values happen to be all distinct - this is good for
463 * avoiding false positive tests from bus echoes).
464 * o an output interrupt is generated and its vector is correct.
465 * o the interrupt goes away when the IIR in the UART is read.
466 */
45fab98c
AC
467 failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS;
468 failures[1] = inb(iobase + com_ier) - IER_ETXRDY;
469 failures[2] = inb(iobase + com_mcr) - mcr_image;
470 if (idev->id_irq != 0)
471 failures[3] = isa_irq_pending(idev) ? 0 : 1;
472 failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY;
473 failures[5] = isa_irq_pending(idev) ? 1 : 0;
474 failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
15637ed4
RG
475
476 /*
477 * Turn off all device interrupts and check that they go off properly.
8a86c29c
AC
478 * Leave MCR_IENABLE alone. For ports without a master port, it gates
479 * the OUT2 output of the UART to
15637ed4
RG
480 * the ICU input. Closing the gate would give a floating ICU input
481 * (unless there is another device driving at) and spurious interrupts.
482 * (On the system that this was first tested on, the input floats high
483 * and gives a (masked) interrupt as soon as the gate is closed.)
484 */
485 outb(iobase + com_ier, 0);
8a86c29c 486 outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */
45fab98c
AC
487 failures[7] = inb(iobase + com_ier);
488 failures[8] = isa_irq_pending(idev) ? 1 : 0;
489 failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
15637ed4 490
45fab98c 491 outb(IO_ICU1 + 1, imen); /* XXX */
15637ed4 492 enable_intr();
45fab98c
AC
493
494 result = IO_COMSIZE;
495 for (fn = 0; fn < sizeof failures; ++fn)
496 if (failures[fn]) {
497 outb(iobase + com_mcr, 0);
498 result = 0;
499 if (!COM_QUIET(dev))
500 printf("sio%d: probe test %d failed\n",
501 dev->id_unit, fn);
502 }
15637ed4
RG
503 return (result);
504}
505
beb52b92 506static int
15637ed4
RG
507sioattach(isdp)
508 struct isa_device *isdp;
509{
510 struct com_s *com;
511 static bool_t comwakeup_started = FALSE;
512 Port_t iobase;
513 int s;
15637ed4
RG
514 int unit;
515
516 iobase = isdp->id_iobase;
517 unit = isdp->id_unit;
8a86c29c
AC
518 com = malloc(sizeof *com, M_TTYS, M_NOWAIT);
519 if (com == NULL)
520 return (0);
15637ed4
RG
521
522 /*
523 * sioprobe() has initialized the device registers as follows:
524 * o cfcr = CFCR_8BITS.
525 * It is most important that CFCR_DLAB is off, so that the
526 * data port is not hidden when we enable interrupts.
527 * o ier = 0.
528 * Interrupts are only enabled when the line is open.
8a86c29c 529 * o mcr = MCR_IENABLE, or 0 if the port has a master port.
15637ed4
RG
530 * Keeping MCR_DTR and MCR_RTS off might stop the external
531 * device from sending before we are ready.
532 */
8a86c29c 533 bzero(com, sizeof *com);
15637ed4 534 com->cfcr_image = CFCR_8BITS;
5f666b8d 535 com->dtr_wait = 3 * hz;
beb52b92 536 com->tx_fifo_size = 1;
15637ed4
RG
537 com->iptr = com->ibuf = com->ibuf1;
538 com->ibufend = com->ibuf1 + RS_IBUFSIZE;
539 com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
540 com->iobase = iobase;
541 com->data_port = iobase + com_data;
542 com->int_id_port = iobase + com_iir;
543 com->modem_ctl_port = iobase + com_mcr;
8a86c29c 544 com->mcr_image = inb(com->modem_ctl_port);
15637ed4
RG
545 com->line_status_port = iobase + com_lsr;
546 com->modem_status_port = iobase + com_msr;
8a86c29c
AC
547
548 /*
549 * We don't use all the flags from <sys/ttydefaults.h> since they
550 * are only relevant for logins. It's important to have echo off
551 * initially so that the line doesn't start blathering before the
552 * echo flag can be turned off.
553 */
554 com->it_in.c_iflag = 0;
555 com->it_in.c_oflag = 0;
556 com->it_in.c_cflag = TTYDEF_CFLAG;
557 com->it_in.c_lflag = 0;
558 if (unit == comconsole) {
559 com->it_in.c_iflag = TTYDEF_IFLAG;
560 com->it_in.c_oflag = TTYDEF_OFLAG;
561 com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL;
562 com->it_in.c_lflag = TTYDEF_LFLAG;
563 com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
564 }
565 termioschars(&com->it_in);
566 com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
567 com->it_out = com->it_in;
15637ed4
RG
568
569 /* attempt to determine UART type */
87362236 570 printf("sio%d: type", unit);
482dcdb3 571#ifdef COM_MULTIPORT
55116fbc 572 if (!COM_ISMULTIPORT(isdp))
482dcdb3 573#endif
55116fbc
AC
574 {
575 u_char scr;
576 u_char scr1;
577 u_char scr2;
578
579 scr = inb(iobase + com_scr);
580 outb(iobase + com_scr, 0xa5);
581 scr1 = inb(iobase + com_scr);
582 outb(iobase + com_scr, 0x5a);
583 scr2 = inb(iobase + com_scr);
584 outb(iobase + com_scr, scr);
585 if (scr1 != 0xa5 || scr2 != 0x5a) {
142126f9 586 printf(" 8250");
55116fbc 587 goto determined_type;
15637ed4 588 }
15637ed4 589 }
55116fbc
AC
590 outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14);
591 DELAY(100);
142126f9 592 switch (inb(com->int_id_port) & IIR_FIFO_MASK) {
55116fbc 593 case FIFO_TRIGGER_1:
142126f9 594 printf(" 16450");
55116fbc
AC
595 break;
596 case FIFO_TRIGGER_4:
142126f9 597 printf(" 16450?");
55116fbc
AC
598 break;
599 case FIFO_TRIGGER_8:
142126f9 600 printf(" 16550?");
55116fbc
AC
601 break;
602 case FIFO_TRIGGER_14:
142126f9 603 printf(" 16550A");
0a817a83
AC
604 if (COM_NOFIFO(isdp))
605 printf(" fifo disabled");
606 else {
7a4091d6 607 com->hasfifo = TRUE;
8a86c29c 608 com->ftl_init = FIFO_TRIGGER_14;
beb52b92 609 com->tx_fifo_size = 16;
7a4091d6 610 }
55116fbc
AC
611 break;
612 }
613 outb(iobase + com_fifo, 0);
614determined_type: ;
615
15637ed4
RG
616#ifdef COM_MULTIPORT
617 if (COM_ISMULTIPORT(isdp)) {
36f3e249 618 com->multiport = TRUE;
8a86c29c 619 printf(" (multiport");
45fab98c 620 if (!COM_NOMASTER(isdp) && unit == COM_MPMASTER(isdp))
36f3e249 621 printf(" master");
8a86c29c 622 printf(")");
36f3e249 623 }
15637ed4 624#endif /* COM_MULTIPORT */
87362236 625 printf("\n");
15637ed4
RG
626
627#ifdef KGDB
628 if (kgdb_dev == makedev(commajor, unit)) {
45fab98c 629 if (unit == comconsole)
15637ed4
RG
630 kgdb_dev = -1; /* can't debug over console port */
631 else {
0a817a83
AC
632 int divisor;
633
634 /*
635 * XXX now unfinished and broken. Need to do
636 * something more like a full open(). There's no
637 * suitable interrupt handler so don't enable device
638 * interrupts. Watch out for null tp's.
639 */
640 outb(iobase + com_cfcr, CFCR_DLAB);
641 divisor = ttspeedtab(kgdb_rate, comspeedtab);
642 outb(iobase + com_dlbl, divisor & 0xFF);
643 outb(iobase + com_dlbh, (u_int) divisor >> 8);
644 outb(iobase + com_cfcr, CFCR_8BITS);
645 outb(com->modem_status_port,
646 com->mcr_image |= MCR_DTR | MCR_RTS);
647
15637ed4
RG
648 if (kgdb_debug_init) {
649 /*
650 * Print prefix of device name,
651 * let kgdb_connect print the rest.
652 */
55116fbc 653 printf("sio%d: ", unit);
15637ed4 654 kgdb_connect(1);
beb52b92 655 } else
55116fbc 656 printf("sio%d: kgdb enabled\n", unit);
15637ed4
RG
657 }
658 }
659#endif
660
8a86c29c 661 s = spltty();
15637ed4 662 com_addr(unit) = com;
15637ed4 663 splx(s);
15637ed4 664 if (!comwakeup_started) {
8a86c29c 665 comwakeup((caddr_t)NULL, 0);
15637ed4
RG
666 comwakeup_started = TRUE;
667 }
15637ed4
RG
668 return (1);
669}
670
671/* ARGSUSED */
672int
673sioopen(dev, flag, mode, p)
674 dev_t dev;
675 int flag;
676 int mode;
677 struct proc *p;
678{
679 struct com_s *com;
8a86c29c 680 int error;
15637ed4 681 Port_t iobase;
8a86c29c 682 int mynor;
15637ed4
RG
683 int s;
684 struct tty *tp;
142126f9
AC
685 int unit;
686
8a86c29c
AC
687 mynor = minor(dev);
688 unit = MINOR_TO_UNIT(mynor);
15637ed4
RG
689 if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
690 return (ENXIO);
8a86c29c
AC
691 if (mynor & CONTROL_MASK)
692 return (0);
693 tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]);
15637ed4 694 s = spltty();
8a86c29c
AC
695 /*
696 * We jump to this label after all non-interrupted sleeps to pick
697 * up any changes of the device state.
698 */
699open_top:
700 while (com->state & CS_DTR_OFF) {
701 error = tsleep((caddr_t)&com->dtr_wait, TTIPRI | PCATCH,
702 "siodtr", 0);
703 if (error != 0)
704 goto out;
705 }
706 if (tp->t_state & TS_ISOPEN) {
707 /*
708 * The device is open, so everything has been initialized.
709 * Handle conflicts.
710 */
711 if (mynor & CALLOUT_MASK) {
712 if (!com->active_out) {
713 error = EBUSY;
714 goto out;
15637ed4
RG
715 }
716 } else {
717 if (com->active_out) {
15637ed4 718 if (flag & O_NONBLOCK) {
8a86c29c
AC
719 error = EBUSY;
720 goto out;
721 }
d74ba8f9 722 error = tsleep((caddr_t)&com->active_out,
8a86c29c
AC
723 TTIPRI | PCATCH, "siobi", 0);
724 if (error != 0)
725 goto out;
726 goto open_top;
d74ba8f9
AC
727 }
728 }
8a86c29c
AC
729 if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
730 error = EBUSY;
731 goto out;
d74ba8f9
AC
732 }
733 } else {
8a86c29c
AC
734 /*
735 * The device isn't open, so there are no conflicts.
736 * Initialize it. Initialization is done twice in many
737 * cases: to preempt sleeping callin opens if we are
738 * callout, and to complete a callin open after DCD rises.
739 */
d74ba8f9
AC
740 tp->t_oproc = comstart;
741 tp->t_param = comparam;
742 tp->t_dev = dev;
8a86c29c
AC
743 tp->t_termios = mynor & CALLOUT_MASK
744 ? com->it_out : com->it_in;
142126f9 745 commctl(com, MCR_DTR | MCR_RTS, DMSET);
8a86c29c
AC
746 com->ftl_max = com->ftl_init;
747 ++com->wopeners;
15637ed4 748 error = comparam(tp, &tp->t_termios);
8a86c29c 749 --com->wopeners;
15637ed4
RG
750 if (error != 0)
751 goto out;
8a86c29c
AC
752 /*
753 * XXX we should goto open_top if comparam() slept.
754 */
15637ed4
RG
755 ttsetwater(tp);
756 iobase = com->iobase;
142126f9 757 if (com->hasfifo) {
8a86c29c
AC
758 /* Drain fifo. */
759 outb(iobase + com_fifo,
760 FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST
761 | com->ftl);
142126f9
AC
762 DELAY(100);
763 }
764 disable_intr();
15637ed4
RG
765 (void) inb(com->line_status_port);
766 (void) inb(com->data_port);
0a817a83
AC
767 com->prev_modem_status =
768 com->last_modem_status = inb(com->modem_status_port);
15637ed4
RG
769 outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
770 | IER_EMSC);
771 enable_intr();
8a86c29c
AC
772 /*
773 * Handle initial DCD. Callout devices get a fake initial
774 * DCD (trapdoor DCD). If we are callout, then any sleeping
775 * callin opens get woken up and resume sleeping on "siobi"
776 * instead of "siodcd".
15637ed4 777 */
8a86c29c
AC
778 if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)
779 (*linesw[tp->t_line].l_modem)(tp, 1);
780 }
781 /*
782 * Wait for DCD if necessary.
783 */
784 if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
785 && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
786 ++com->wopeners;
787 error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0);
788 --com->wopeners;
15637ed4 789 if (error != 0)
8a86c29c
AC
790 goto out;
791 goto open_top;
15637ed4 792 }
d74ba8f9 793 error = (*linesw[tp->t_line].l_open)(dev, tp, 0);
8a86c29c
AC
794 if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
795 com->active_out = TRUE;
796out:
4f7dcad8 797 splx(s);
8a86c29c 798 if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)
15637ed4 799 comhardclose(com);
15637ed4
RG
800 return (error);
801}
802
803/*ARGSUSED*/
804int
805sioclose(dev, flag, mode, p)
806 dev_t dev;
807 int flag;
808 int mode;
809 struct proc *p;
810{
811 struct com_s *com;
8a86c29c 812 int mynor;
0a817a83 813 int s;
15637ed4
RG
814 struct tty *tp;
815
8a86c29c
AC
816 mynor = minor(dev);
817 if (mynor & CONTROL_MASK)
818 return (0);
819 com = com_addr(MINOR_TO_UNIT(mynor));
15637ed4 820 tp = com->tp;
4f7dcad8 821 s = spltty();
15637ed4 822 (*linesw[tp->t_line].l_close)(tp, flag);
0a817a83 823 siostop(tp, FREAD | FWRITE);
15637ed4 824 comhardclose(com);
4f7dcad8 825 ttyclose(tp);
5f666b8d 826 splx(s);
15637ed4
RG
827 return (0);
828}
829
55116fbc 830static void
15637ed4
RG
831comhardclose(com)
832 struct com_s *com;
833{
834 Port_t iobase;
835 int s;
836 struct tty *tp;
beb52b92 837 int unit;
15637ed4 838
8a86c29c 839 unit = DEV_TO_UNIT(com->tp->t_dev);
15637ed4 840 iobase = com->iobase;
0a817a83
AC
841 s = spltty();
842#ifdef TIOCTIMESTAMP
01a70cf7 843 com->do_timestamp = 0;
0a817a83 844#endif
15637ed4
RG
845 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
846#ifdef KGDB
142126f9 847 /* do not disable interrupts or hang up if debugging */
beb52b92 848 if (kgdb_dev != makedev(commajor, unit))
15637ed4 849#endif
142126f9 850 {
15637ed4 851 outb(iobase + com_ier, 0);
142126f9 852 tp = com->tp;
8a86c29c 853 if (tp->t_cflag & HUPCL
0a817a83
AC
854 /*
855 * XXX we will miss any carrier drop between here and the
856 * next open. Perhaps we should watch DCD even when the
857 * port is closed; it is not sufficient to check it at
858 * the next open because it might go up and down while
8a86c29c 859 * we're not watching.
0a817a83 860 */
8a86c29c
AC
861 || !com->active_out
862 && !(com->prev_modem_status & MSR_DCD)
863 && !(com->it_in.c_cflag & CLOCAL)
142126f9
AC
864 || !(tp->t_state & TS_ISOPEN)) {
865 commctl(com, MCR_RTS, DMSET);
8a86c29c
AC
866 if (com->dtr_wait != 0) {
867 timeout(siodtrwakeup, (caddr_t)com,
868 com->dtr_wait);
869 com->state |= CS_DTR_OFF;
d74ba8f9 870 }
142126f9 871 }
55116fbc 872 }
8a86c29c
AC
873 com->active_out = FALSE;
874 wakeup((caddr_t)&com->active_out);
875 wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */
15637ed4
RG
876 splx(s);
877}
878
879int
880sioread(dev, uio, flag)
881 dev_t dev;
882 struct uio *uio;
883 int flag;
884{
8a86c29c
AC
885 int mynor;
886 struct tty *tp;
15637ed4 887
8a86c29c
AC
888 mynor = minor(dev);
889 if (mynor & CONTROL_MASK)
890 return (ENODEV);
891 tp = com_addr(MINOR_TO_UNIT(mynor))->tp;
15637ed4
RG
892 return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
893}
894
895int
896siowrite(dev, uio, flag)
897 dev_t dev;
898 struct uio *uio;
899 int flag;
900{
8a86c29c
AC
901 int mynor;
902 struct tty *tp;
903 int unit;
15637ed4 904
8a86c29c
AC
905 mynor = minor(dev);
906 if (mynor & CONTROL_MASK)
907 return (ENODEV);
908 unit = MINOR_TO_UNIT(mynor);
909 tp = com_addr(unit)->tp;
15637ed4
RG
910 /*
911 * (XXX) We disallow virtual consoles if the physical console is
912 * a serial port. This is in case there is a display attached that
913 * is not the console. In that situation we don't need/want the X
914 * server taking over the console.
915 */
916 if (constty && unit == comconsole)
917 constty = NULL;
918 return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
919}
920
8a86c29c
AC
921static void
922siodtrwakeup(chan, ticks)
923 caddr_t chan;
924 int ticks;
925{
926 struct com_s *com;
927
928 com = (struct com_s *)chan;
929 com->state &= ~CS_DTR_OFF;
930 wakeup((caddr_t)&com->dtr_wait);
931}
932
0a817a83 933#ifdef TIOCTIMESTAMP
01a70cf7
PHK
934/* Interrupt routine for timekeeping purposes */
935void
0a817a83
AC
936siointrts(unit)
937 int unit;
01a70cf7
PHK
938{
939 microtime(&intr_timestamp);
940 siointr(unit);
941}
0a817a83 942#endif
01a70cf7 943
15637ed4
RG
944void
945siointr(unit)
946 int unit;
947{
fe4b242a 948#ifndef COM_MULTIPORT
0a817a83 949 siointr1(com_addr(unit));
15637ed4 950#else /* COM_MULTIPORT */
4aa03e05 951 struct com_s *com;
8a86c29c 952 bool_t possibly_more_intrs;
15637ed4 953
55116fbc
AC
954 /*
955 * Loop until there is no activity on any port. This is necessary
956 * to get an interrupt edge more than to avoid another interrupt.
957 * If the IRQ signal is just an OR of the IRQ signals from several
958 * devices, then the edge from one may be lost because another is
142126f9 959 * on.
55116fbc 960 */
15637ed4 961 do {
142126f9
AC
962 possibly_more_intrs = FALSE;
963 for (unit = 0; unit < NSIO; ++unit) {
964 com = com_addr(unit);
6460ff3d 965 if (com != NULL
0a817a83
AC
966 && (inb(com->int_id_port) & IIR_IMASK)
967 != IIR_NOPEND) {
968 siointr1(com);
6460ff3d 969 possibly_more_intrs = TRUE;
15637ed4
RG
970 }
971 }
142126f9 972 } while (possibly_more_intrs);
0a817a83 973#endif /* COM_MULTIPORT */
15637ed4 974}
55116fbc 975
beb52b92 976static void
0a817a83 977siointr1(com)
55116fbc 978 struct com_s *com;
15637ed4 979{
0a817a83
AC
980 u_char line_status;
981 u_char modem_status;
982 u_char *ioptr;
983 u_char recv_data;
984
985#ifdef TIOCTIMESTAMP
986 if (com->do_timestamp)
987 /* XXX a little bloat here... */
01a70cf7 988 com->timestamp = intr_timestamp;
0a817a83 989#endif
15637ed4
RG
990 while (TRUE) {
991 line_status = inb(com->line_status_port);
992
993 /* input event? (check first to help avoid overruns) */
994 while (line_status & LSR_RCV_MASK) {
995 /* break/unnattached error bits or real input? */
15637ed4
RG
996 if (!(line_status & LSR_RXRDY))
997 recv_data = 0;
998 else
999 recv_data = inb(com->data_port);
1000 ++com->bytes_in;
8a86c29c 1001 if (com->hotchar != 0 && recv_data == com->hotchar)
0a817a83 1002 setsofttty();
15637ed4
RG
1003#ifdef KGDB
1004 /* trap into kgdb? (XXX - needs testing and optim) */
1005 if (recv_data == FRAME_END
1006 && !(com->tp->t_state & TS_ISOPEN)
1007 && kgdb_dev == makedev(commajor, unit)) {
1008 kgdb_connect(0);
1009 continue;
1010 }
1011#endif /* KGDB */
1012 ioptr = com->iptr;
1013 if (ioptr >= com->ibufend)
1014 CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
1015 else {
1016 ++com_events;
beb52b92 1017#if 0 /* for testing input latency vs efficiency */
142126f9 1018if (com->iptr - com->ibuf == 8)
beb52b92 1019 setsofttty();
142126f9 1020#endif
15637ed4
RG
1021 ioptr[0] = recv_data;
1022 ioptr[CE_INPUT_OFFSET] = line_status;
1023 com->iptr = ++ioptr;
1024 if (ioptr == com->ihighwater
1025 && com->state & CS_RTS_IFLOW)
1026 outb(com->modem_ctl_port,
1027 com->mcr_image &= ~MCR_RTS);
55116fbc
AC
1028 /* XXX - move this out of isr */
1029 if (line_status & LSR_OE)
1030 CE_RECORD(com, CE_OVERRUN);
15637ed4
RG
1031 }
1032
1033 /*
1034 * "& 0x7F" is to avoid the gcc-1.40 generating a slow
1035 * jump from the top of the loop to here
1036 */
1037 line_status = inb(com->line_status_port) & 0x7F;
1038 }
1039
1040 /* modem status change? (always check before doing output) */
1041 modem_status = inb(com->modem_status_port);
1042 if (modem_status != com->last_modem_status) {
1043 /*
1044 * Schedule high level to handle DCD changes. Note
1045 * that we don't use the delta bits anywhere. Some
1046 * UARTs mess them up, and it's easy to remember the
1047 * previous bits and calculate the delta.
1048 */
15637ed4
RG
1049 com->last_modem_status = modem_status;
1050 if (!(com->state & CS_CHECKMSR)) {
1051 com_events += LOTS_OF_EVENTS;
1052 com->state |= CS_CHECKMSR;
beb52b92 1053 setsofttty();
15637ed4
RG
1054 }
1055
1056 /* handle CTS change immediately for crisp flow ctl */
1057 if (com->state & CS_CTS_OFLOW) {
1058 if (modem_status & MSR_CTS)
1059 com->state |= CS_ODEVREADY;
1060 else
1061 com->state &= ~CS_ODEVREADY;
1062 }
1063 }
1064
1065 /* output queued and everything ready? */
1066 if (line_status & LSR_TXRDY
1067 && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) {
15637ed4 1068 ioptr = com->optr;
beb52b92
AC
1069 if (com->tx_fifo_size > 1) {
1070 u_int ocount;
1071
1072 ocount = com->obufend - ioptr;
1073 if (ocount > com->tx_fifo_size)
1074 ocount = com->tx_fifo_size;
1075 com->bytes_out += ocount;
1076 do
1077 outb(com->data_port, *ioptr++);
1078 while (--ocount != 0);
1079 } else {
1080 outb(com->data_port, *ioptr++);
0a817a83 1081 ++com->bytes_out;
beb52b92
AC
1082 }
1083 com->optr = ioptr;
15637ed4
RG
1084 if (ioptr >= com->obufend) {
1085 /* output just completed */
1086 com_events += LOTS_OF_EVENTS;
1087 com->state ^= (CS_ODONE | CS_BUSY);
beb52b92 1088 setsofttty(); /* handle at high level ASAP */
15637ed4
RG
1089 }
1090 }
1091
1092 /* finished? */
beb52b92 1093#ifndef COM_MULTIPORT
0a817a83 1094 if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
15637ed4 1095#endif /* COM_MULTIPORT */
beb52b92 1096 return;
15637ed4
RG
1097 }
1098}
1099
70a6bf52 1100static int
55116fbc 1101tiocm_xxx2mcr(tiocm_xxx)
0a817a83 1102 int tiocm_xxx;
70a6bf52 1103{
0a817a83 1104 int mcr;
55116fbc
AC
1105
1106 mcr = 0;
1107 if (tiocm_xxx & TIOCM_DTR)
1108 mcr |= MCR_DTR;
1109 if (tiocm_xxx & TIOCM_RTS)
1110 mcr |= MCR_RTS;
1111 return (mcr);
70a6bf52 1112}
55116fbc 1113
15637ed4
RG
1114int
1115sioioctl(dev, cmd, data, flag, p)
1116 dev_t dev;
1117 int cmd;
1118 caddr_t data;
1119 int flag;
1120 struct proc *p;
1121{
1122 struct com_s *com;
1123 int error;
1124 Port_t iobase;
55116fbc
AC
1125 int mcr;
1126 int msr;
8a86c29c 1127 int mynor;
15637ed4 1128 int s;
55116fbc 1129 int tiocm_xxx;
15637ed4
RG
1130 struct tty *tp;
1131
8a86c29c
AC
1132 mynor = minor(dev);
1133 com = com_addr(MINOR_TO_UNIT(mynor));
1134 if (mynor & CONTROL_MASK) {
1135 struct termios *ct;
1136
1137 switch (mynor & CONTROL_MASK) {
1138 case CONTROL_INIT_STATE:
1139 ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in;
1140 break;
1141 case CONTROL_LOCK_STATE:
1142 ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in;
1143 break;
1144 default:
1145 return (ENODEV); /* /dev/nodev */
1146 }
1147 switch (cmd) {
1148 case TIOCSETA:
1149 error = suser(p->p_ucred, &p->p_acflag);
1150 if (error)
1151 return (error);
1152 *ct = *(struct termios *)data;
1153 return (0);
1154 case TIOCGETA:
1155 *(struct termios *)data = *ct;
1156 return (0);
1157 case TIOCGETD:
1158 *(int *)data = TTYDISC;
1159 return (0);
1160 case TIOCGWINSZ:
1161 bzero(data, sizeof(struct winsize));
1162 return (0);
1163 default:
1164 return (ENOTTY);
1165 }
1166 }
15637ed4 1167 tp = com->tp;
8a86c29c
AC
1168 if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
1169 int cc;
1170 struct termios *dt = (struct termios *)data;
1171 struct termios *lt = mynor & CALLOUT_MASK
1172 ? &com->lt_out : &com->lt_in;
1173
1174 dt->c_iflag = (tp->t_iflag & lt->c_iflag)
1175 | (dt->c_iflag & ~lt->c_iflag);
1176 dt->c_oflag = (tp->t_oflag & lt->c_oflag)
1177 | (dt->c_oflag & ~lt->c_oflag);
1178 dt->c_cflag = (tp->t_cflag & lt->c_cflag)
1179 | (dt->c_cflag & ~lt->c_cflag);
1180 dt->c_lflag = (tp->t_lflag & lt->c_lflag)
1181 | (dt->c_lflag & ~lt->c_lflag);
1182 for (cc = 0; cc < NCCS; ++cc)
1183 if (lt->c_cc[cc] != 0)
1184 dt->c_cc[cc] = tp->t_cc[cc];
1185 if (lt->c_ispeed != 0)
1186 dt->c_ispeed = tp->t_ispeed;
1187 if (lt->c_ospeed != 0)
1188 dt->c_ospeed = tp->t_ospeed;
1189 }
15637ed4
RG
1190 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
1191 if (error >= 0)
1192 return (error);
1193 error = ttioctl(tp, cmd, data, flag);
beb52b92 1194 if (error >= 0)
142126f9 1195 return (error);
142126f9
AC
1196 iobase = com->iobase;
1197 s = spltty();
15637ed4
RG
1198 switch (cmd) {
1199 case TIOCSBRK:
1200 outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK);
1201 break;
1202 case TIOCCBRK:
1203 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
1204 break;
1205 case TIOCSDTR:
142126f9 1206 commctl(com, MCR_DTR, DMBIS);
15637ed4
RG
1207 break;
1208 case TIOCCDTR:
142126f9 1209 commctl(com, MCR_DTR, DMBIC);
15637ed4
RG
1210 break;
1211 case TIOCMSET:
142126f9 1212 commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET);
15637ed4
RG
1213 break;
1214 case TIOCMBIS:
142126f9 1215 commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS);
15637ed4
RG
1216 break;
1217 case TIOCMBIC:
142126f9 1218 commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC);
15637ed4
RG
1219 break;
1220 case TIOCMGET:
55116fbc
AC
1221 tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */
1222 mcr = com->mcr_image;
1223 if (mcr & MCR_DTR)
1224 tiocm_xxx |= TIOCM_DTR;
1225 if (mcr & MCR_RTS)
1226 tiocm_xxx |= TIOCM_RTS;
1227 msr = com->prev_modem_status;
1228 if (msr & MSR_CTS)
1229 tiocm_xxx |= TIOCM_CTS;
1230 if (msr & MSR_DCD)
1231 tiocm_xxx |= TIOCM_CD;
1232 if (msr & MSR_DSR)
1233 tiocm_xxx |= TIOCM_DSR;
142126f9
AC
1234 /*
1235 * XXX - MSR_RI is naturally volatile, and we make MSR_TERI
1236 * more volatile by reading the modem status a lot. Perhaps
1237 * we should latch both bits until the status is read here.
1238 */
55116fbc
AC
1239 if (msr & (MSR_RI | MSR_TERI))
1240 tiocm_xxx |= TIOCM_RI;
1241 *(int *)data = tiocm_xxx;
15637ed4 1242 break;
55116fbc 1243 case TIOCMSDTRWAIT:
142126f9
AC
1244 /* must be root since the wait applies to following logins */
1245 error = suser(p->p_ucred, &p->p_acflag);
1246 if (error != 0) {
1247 splx(s);
8a86c29c 1248 return (EPERM);
142126f9 1249 }
8a86c29c 1250 com->dtr_wait = *(int *)data * 100 / hz;
55116fbc
AC
1251 break;
1252 case TIOCMGDTRWAIT:
1253 *(int *)data = com->dtr_wait;
1254 break;
0a817a83 1255#ifdef TIOCTIMESTAMP
01a70cf7 1256 case TIOCTIMESTAMP:
0a817a83 1257 com->do_timestamp = TRUE;
01a70cf7
PHK
1258 *(struct timeval *)data = com->timestamp;
1259 break;
0a817a83 1260#endif
15637ed4
RG
1261 default:
1262 splx(s);
1263 return (ENOTTY);
1264 }
1265 splx(s);
1266 return (0);
1267}
1268
1269/* cancel pending output */
1270static void
1271comflush(com)
1272 struct com_s *com;
1273{
1274 struct ringb *rbp;
1275
1276 disable_intr();
1277 if (com->state & CS_ODONE)
1278 com_events -= LOTS_OF_EVENTS;
1279 com->state &= ~(CS_ODONE | CS_BUSY);
1280 enable_intr();
8a86c29c 1281 rbp = com->tp->t_out;
15637ed4
RG
1282 rbp->rb_hd += com->ocount;
1283 rbp->rb_hd = RB_ROLLOVER(rbp, rbp->rb_hd);
1284 com->ocount = 0;
1285 com->tp->t_state &= ~TS_BUSY;
1286}
1287
beb52b92
AC
1288void
1289siopoll()
15637ed4 1290{
15637ed4
RG
1291 int unit;
1292
1293 if (com_events == 0)
1294 return;
15637ed4
RG
1295repeat:
1296 for (unit = 0; unit < NSIO; ++unit) {
0a817a83
AC
1297 u_char *buf;
1298 struct com_s *com;
15637ed4
RG
1299 u_char *ibuf;
1300 int incc;
1301 struct tty *tp;
1302
1303 com = com_addr(unit);
1304 if (com == NULL)
1305 continue;
1306 tp = com->tp;
0a817a83
AC
1307 if (tp == NULL)
1308 continue;
15637ed4
RG
1309
1310 /* switch the role of the low-level input buffers */
beb52b92
AC
1311 if (com->iptr == (ibuf = com->ibuf)) {
1312 buf = NULL; /* not used, but compiler can't tell */
15637ed4 1313 incc = 0;
beb52b92 1314 } else {
8a86c29c
AC
1315 /*
1316 * Prepare to reduce input latency for packet
1317 * discplines with a end of packet character.
1318 * XXX should be elsewhere.
1319 */
1320 if (tp->t_line == SLIPDISC)
1321 com->hotchar = 0xc0;
1322 else if (tp->t_line == PPPDISC)
1323 com->hotchar = 0x7e;
1324 else
1325 com->hotchar = 0;
15637ed4
RG
1326 buf = ibuf;
1327 disable_intr();
1328 incc = com->iptr - buf;
1329 com_events -= incc;
1330 if (ibuf == com->ibuf1)
1331 ibuf = com->ibuf2;
1332 else
1333 ibuf = com->ibuf1;
1334 com->ibufend = ibuf + RS_IBUFSIZE;
1335 com->ihighwater = ibuf + RS_IHIGHWATER;
1336 com->iptr = ibuf;
1337
1338 /*
1339 * There is now room for another low-level buffer full
1340 * of input, so enable RTS if it is now disabled and
1341 * there is room in the high-level buffer.
1342 */
0a817a83
AC
1343 /*
1344 * XXX this used not to look at CS_RTS_IFLOW. The
1345 * change is to allow full control of MCR_RTS via
1346 * ioctls after turning CS_RTS_IFLOW off. Check
1347 * for races. We shouldn't allow the ioctls while
1348 * CS_RTS_IFLOW is on.
1349 */
24de565e
PHK
1350 if ((com->state & CS_RTS_IFLOW)
1351 && !(com->mcr_image & MCR_RTS)
55116fbc 1352 && !(tp->t_state & TS_RTS_IFLOW))
15637ed4
RG
1353 outb(com->modem_ctl_port,
1354 com->mcr_image |= MCR_RTS);
1355 enable_intr();
1356 com->ibuf = ibuf;
1357 }
1358
1359 if (com->state & CS_CHECKMSR) {
1360 u_char delta_modem_status;
1361
1362 disable_intr();
1363 delta_modem_status = com->last_modem_status
1364 ^ com->prev_modem_status;
1365 com->prev_modem_status = com->last_modem_status;
1366 com_events -= LOTS_OF_EVENTS;
1367 com->state &= ~CS_CHECKMSR;
1368 enable_intr();
8a86c29c
AC
1369 if (delta_modem_status & MSR_DCD)
1370 (*linesw[tp->t_line].l_modem)
1371 (tp, com->prev_modem_status & MSR_DCD);
15637ed4
RG
1372 }
1373
1374 /* XXX */
1375 if (TRUE) {
0a817a83
AC
1376 u_int delta;
1377 int errnum;
1378 u_long total;
15637ed4 1379
beb52b92 1380 for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
0a817a83 1381 disable_intr();
beb52b92
AC
1382 delta = com->delta_error_counts[errnum];
1383 com->delta_error_counts[errnum] = 0;
0a817a83 1384 enable_intr();
8a86c29c
AC
1385 if (delta == 0 || !(tp->t_state & TS_ISOPEN))
1386 continue;
1387 total = com->error_counts[errnum] += delta;
15637ed4 1388 log(LOG_WARNING,
55116fbc 1389 "sio%d: %u more %s%s (total %lu)\n",
15637ed4
RG
1390 unit, delta, error_desc[errnum],
1391 delta == 1 ? "" : "s", total);
8a86c29c
AC
1392 if (errnum == CE_OVERRUN && com->hasfifo
1393 && com->ftl > FIFO_TRIGGER_1) {
1394 static u_char ftl_in_bytes[] =
1395 { 1, 4, 8, 14, };
1396
1397 com->ftl_init = FIFO_TRIGGER_8;
1398#define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4
1399 com->ftl_max =
1400 com->ftl -= FIFO_TRIGGER_DELTA;
1401 outb(com->iobase + com_fifo,
1402 FIFO_ENABLE | com->ftl);
1403 log(LOG_WARNING,
1404 "sio%d: reduced fifo trigger level to %d\n",
1405 unit,
1406 ftl_in_bytes[com->ftl
1407 / FIFO_TRIGGER_DELTA]);
15637ed4
RG
1408 }
1409 }
1410 }
1411 if (com->state & CS_ODONE) {
1412 comflush(com);
1413 /* XXX - why isn't the table used for t_line == 0? */
1414 if (tp->t_line != 0)
1415 (*linesw[tp->t_line].l_start)(tp);
1416 else
1417 comstart(tp);
1418 }
1419 if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
1420 continue;
1421 if (com->state & CS_RTS_IFLOW
8a86c29c 1422 && RB_LEN(tp->t_raw) + incc >= RB_I_HIGH_WATER
55116fbc 1423 && !(tp->t_state & TS_RTS_IFLOW)
15637ed4
RG
1424 /*
1425 * XXX - need RTS flow control for all line disciplines.
1426 * Only have it in standard one now.
1427 */
1428 && linesw[tp->t_line].l_rint == ttyinput) {
55116fbc 1429 tp->t_state |= TS_RTS_IFLOW;
15637ed4
RG
1430 ttstart(tp);
1431 }
1432 /*
1433 * Avoid the grotesquely inefficient lineswitch routine
1434 * (ttyinput) in "raw" mode. It usually takes about 450
1435 * instructions (that's without canonical processing or echo!).
1436 * slinput is reasonably fast (usually 40 instructions plus
1437 * call overhead).
1438 */
1439 if (!(tp->t_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP
1440 | IXOFF | IXON))
1441 && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG
1442 | PENDIN))
1443 && !(tp->t_state & (TS_CNTTB | TS_LNCH))
1444 && linesw[tp->t_line].l_rint == ttyinput) {
1445 tk_nin += incc;
1446 tk_rawcc += incc;
1447 tp->t_rawcc += incc;
1448 com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
8a86c29c 1449 += incc - rb_write(tp->t_raw, (char *) buf,
15637ed4
RG
1450 incc);
1451 ttwakeup(tp);
1452 if (tp->t_state & TS_TTSTOP
1453 && (tp->t_iflag & IXANY
1454 || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
1455 tp->t_state &= ~TS_TTSTOP;
1456 tp->t_lflag &= ~FLUSHO;
1457 ttstart(tp);
1458 }
beb52b92 1459 } else {
15637ed4
RG
1460 do {
1461 u_char line_status;
1462 int recv_data;
1463
1464 line_status = (u_char) buf[CE_INPUT_OFFSET];
1465 recv_data = (u_char) *buf++;
1466 if (line_status
1467 & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
1468 if (line_status & LSR_BI)
1469 recv_data |= TTY_BI;
1470 if (line_status & LSR_FE)
1471 recv_data |= TTY_FE;
1472 if (line_status & LSR_OE)
1473 recv_data |= TTY_OE;
1474 if (line_status & LSR_PE)
1475 recv_data |= TTY_PE;
1476 }
1477 (*linesw[tp->t_line].l_rint)(recv_data, tp);
1478 } while (--incc > 0);
1479 }
1480 if (com_events == 0)
1481 break;
1482 }
1483 if (com_events >= LOTS_OF_EVENTS)
1484 goto repeat;
15637ed4
RG
1485}
1486
1487static int
1488comparam(tp, t)
1489 struct tty *tp;
1490 struct termios *t;
1491{
1492 u_int cfcr;
1493 int cflag;
1494 struct com_s *com;
1495 int divisor;
1496 int error;
1497 Port_t iobase;
1498 int s;
1499 int unit;
1500
1501 /* check requested parameters */
1502 divisor = ttspeedtab(t->c_ospeed, comspeedtab);
142126f9
AC
1503 if (t->c_ispeed == 0)
1504 t->c_ispeed = t->c_ospeed;
0d532fc2 1505 if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed)
15637ed4
RG
1506 return (EINVAL);
1507
1508 /* parameters are OK, convert them to the com struct and the device */
8a86c29c 1509 unit = DEV_TO_UNIT(tp->t_dev);
15637ed4
RG
1510 com = com_addr(unit);
1511 iobase = com->iobase;
1512 s = spltty();
142126f9
AC
1513 if (divisor == 0)
1514 commctl(com, MCR_DTR, DMBIC); /* hang up line */
1515 else
1516 commctl(com, MCR_DTR, DMBIS);
15637ed4
RG
1517 cflag = t->c_cflag;
1518 switch (cflag & CSIZE) {
1519 case CS5:
1520 cfcr = CFCR_5BITS;
1521 break;
1522 case CS6:
1523 cfcr = CFCR_6BITS;
1524 break;
1525 case CS7:
1526 cfcr = CFCR_7BITS;
1527 break;
1528 default:
1529 cfcr = CFCR_8BITS;
1530 break;
1531 }
1532 if (cflag & PARENB) {
1533 cfcr |= CFCR_PENAB;
1534 if (!(cflag & PARODD))
1535 cfcr |= CFCR_PEVEN;
1536 }
1537 if (cflag & CSTOPB)
1538 cfcr |= CFCR_STOPB;
1539
8a86c29c
AC
1540 if (com->hasfifo) {
1541 /*
1542 * Use a fifo trigger level low enough so that the input
1543 * latency from the fifo is less than about 16 msec and
1544 * the total latency is less than about 30 msec. These
1545 * latencies are reasonable for humans. Serial comms
1546 * protocols shouldn't expect anything better since modem
1547 * latencies are larger.
1548 */
1549 com->ftl = t->c_ospeed <= 4800
1550 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14;
1551 if (com->ftl > com->ftl_max)
1552 com->ftl = com->ftl_max;
1553 outb(iobase + com_fifo, FIFO_ENABLE | com->ftl);
1554 }
1555
15637ed4
RG
1556 /*
1557 * Some UARTs lock up if the divisor latch registers are selected
1558 * while the UART is doing output (they refuse to transmit anything
1559 * more until given a hard reset). Fix this by stopping filling
1560 * the device buffers and waiting for them to drain. Reading the
0a817a83 1561 * line status port outside of siointr1() might lose some receiver
15637ed4
RG
1562 * error bits, but that is acceptable here.
1563 */
1564 disable_intr();
55116fbc 1565retry:
15637ed4
RG
1566 com->state &= ~CS_TTGO;
1567 enable_intr();
1568 while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
1569 != (LSR_TSRE | LSR_TXRDY)) {
8a86c29c
AC
1570 error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH,
1571 "siotx", hz / 100);
15637ed4
RG
1572 if (error != 0 && error != EAGAIN) {
1573 if (!(tp->t_state & TS_TTSTOP)) {
1574 disable_intr();
1575 com->state |= CS_TTGO;
1576 enable_intr();
1577 }
1578 splx(s);
1579 return (error);
1580 }
1581 }
1582
1583 disable_intr(); /* very important while com_data is hidden */
55116fbc
AC
1584
1585 /*
1586 * XXX - clearing CS_TTGO is not sufficient to stop further output,
beb52b92
AC
1587 * because siopoll() calls comstart() which usually sets it again
1588 * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be
1589 * sufficient, for similar reasons.
55116fbc
AC
1590 */
1591 if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
0a817a83 1592 != (LSR_TSRE | LSR_TXRDY))
55116fbc
AC
1593 goto retry;
1594
142126f9
AC
1595 if (divisor != 0) {
1596 outb(iobase + com_cfcr, cfcr | CFCR_DLAB);
1597 outb(iobase + com_dlbl, divisor & 0xFF);
1598 outb(iobase + com_dlbh, (u_int) divisor >> 8);
1599 }
15637ed4
RG
1600 outb(iobase + com_cfcr, com->cfcr_image = cfcr);
1601 if (!(tp->t_state & TS_TTSTOP))
1602 com->state |= CS_TTGO;
1603 if (cflag & CRTS_IFLOW)
1604 com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */
1605 else
1606 com->state &= ~CS_RTS_IFLOW;
1607
1608 /*
1609 * Set up state to handle output flow control.
1610 * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
8a86c29c 1611 * Now has 10+ msec latency, while CTS flow has 50- usec latency.
15637ed4
RG
1612 */
1613 com->state &= ~CS_CTS_OFLOW;
1614 com->state |= CS_ODEVREADY;
1615 if (cflag & CCTS_OFLOW) {
1616 com->state |= CS_CTS_OFLOW;
55116fbc 1617 if (!(com->last_modem_status & MSR_CTS))
15637ed4
RG
1618 com->state &= ~CS_ODEVREADY;
1619 }
1620
beb52b92 1621 /*
0a817a83 1622 * Recover from fiddling with CS_TTGO. We used to call siointr1()
beb52b92
AC
1623 * unconditionally, but that defeated the careful discarding of
1624 * stale input in sioopen().
beb52b92
AC
1625 */
1626 if (com->state >= (CS_BUSY | CS_TTGO))
0a817a83 1627 siointr1(com);
beb52b92 1628
55116fbc 1629 enable_intr();
15637ed4
RG
1630 splx(s);
1631 return (0);
1632}
1633
4c45483e 1634static void
15637ed4
RG
1635comstart(tp)
1636 struct tty *tp;
1637{
1638 struct com_s *com;
1639 int s;
1640 int unit;
1641
8a86c29c 1642 unit = DEV_TO_UNIT(tp->t_dev);
15637ed4
RG
1643 com = com_addr(unit);
1644 s = spltty();
1645 disable_intr();
1646 if (tp->t_state & TS_TTSTOP)
1647 com->state &= ~CS_TTGO;
1648 else
1649 com->state |= CS_TTGO;
55116fbc 1650 if (tp->t_state & TS_RTS_IFLOW) {
15637ed4
RG
1651 if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
1652 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
beb52b92 1653 } else {
0a817a83
AC
1654 /*
1655 * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it
1656 * appropriately in comparam() if RTS-flow is being changed.
1657 * Check for races.
1658 */
55116fbc 1659 if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
15637ed4
RG
1660 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
1661 }
1662 enable_intr();
1663 if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
1664 goto out;
8a86c29c
AC
1665 if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel)
1666 ttwwakeup(tp);
15637ed4
RG
1667 if (com->ocount != 0) {
1668 disable_intr();
0a817a83 1669 siointr1(com);
15637ed4 1670 enable_intr();
8a86c29c 1671 } else if (RB_LEN(tp->t_out) != 0) {
15637ed4 1672 tp->t_state |= TS_BUSY;
8a86c29c 1673 com->ocount = RB_CONTIGGET(tp->t_out);
15637ed4 1674 disable_intr();
8a86c29c 1675 com->obufend = (com->optr = (u_char *)tp->t_out->rb_hd)
15637ed4
RG
1676 + com->ocount;
1677 com->state |= CS_BUSY;
0a817a83 1678 siointr1(com); /* fake interrupt to start output */
15637ed4
RG
1679 enable_intr();
1680 }
1681out:
1682 splx(s);
15637ed4
RG
1683}
1684
1685void
1686siostop(tp, rw)
1687 struct tty *tp;
1688 int rw;
1689{
1690 struct com_s *com;
1691
8a86c29c 1692 com = com_addr(DEV_TO_UNIT(tp->t_dev));
15637ed4
RG
1693 if (rw & FWRITE)
1694 comflush(com);
1695 disable_intr();
87129df8
AC
1696 if (rw & FREAD) {
1697 com_events -= (com->iptr - com->ibuf);
1698 com->iptr = com->ibuf;
1699 }
15637ed4
RG
1700 if (tp->t_state & TS_TTSTOP)
1701 com->state &= ~CS_TTGO;
1702 else
1703 com->state |= CS_TTGO;
1704 enable_intr();
1705}
1706
beb52b92
AC
1707int
1708sioselect(dev, rw, p)
1709 dev_t dev;
1710 int rw;
1711 struct proc *p;
1712{
8a86c29c
AC
1713 if (minor(dev) & CONTROL_MASK)
1714 return (ENODEV);
1715 return (ttselect(dev & ~MINOR_MAGIC_MASK, rw, p));
beb52b92
AC
1716}
1717
142126f9 1718static void
15637ed4
RG
1719commctl(com, bits, how)
1720 struct com_s *com;
1721 int bits;
1722 int how;
1723{
1724 disable_intr();
1725 switch (how) {
1726 case DMSET:
142126f9
AC
1727 outb(com->modem_ctl_port,
1728 com->mcr_image = bits | (com->mcr_image & MCR_IENABLE));
15637ed4
RG
1729 break;
1730 case DMBIS:
1731 outb(com->modem_ctl_port, com->mcr_image |= bits);
1732 break;
1733 case DMBIC:
55116fbc 1734 outb(com->modem_ctl_port, com->mcr_image &= ~bits);
15637ed4
RG
1735 break;
1736 }
1737 enable_intr();
15637ed4
RG
1738}
1739
4c45483e 1740static void
55116fbc 1741comwakeup(chan, ticks)
0a817a83
AC
1742 caddr_t chan;
1743 int ticks;
15637ed4 1744{
0a817a83 1745 int unit;
15637ed4 1746
8a86c29c 1747 timeout(comwakeup, (caddr_t)NULL, hz / 100);
0a817a83
AC
1748
1749 if (com_events != 0) {
8a86c29c
AC
1750 int s;
1751
1752 s = splsofttty();
0a817a83 1753 siopoll();
0a817a83 1754 splx(s);
0a817a83 1755 }
15637ed4
RG
1756
1757 /* recover from lost output interrupts */
1758 for (unit = 0; unit < NSIO; ++unit) {
beb52b92
AC
1759 struct com_s *com;
1760
15637ed4
RG
1761 com = com_addr(unit);
1762 if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
1763 disable_intr();
0a817a83 1764 siointr1(com);
15637ed4
RG
1765 enable_intr();
1766 }
1767 }
1768}
1769
15637ed4 1770/*
142126f9 1771 * Following are all routines needed for SIO to act as console
15637ed4
RG
1772 */
1773#include "i386/i386/cons.h"
1774
0a817a83
AC
1775struct siocnstate {
1776 u_char dlbl;
1777 u_char dlbh;
1778 u_char ier;
1779 u_char cfcr;
1780 u_char mcr;
1781};
1782
1783static Port_t siocniobase;
1784
8a86c29c
AC
1785static void siocnclose __P((struct siocnstate *sp));
1786static void siocnopen __P((struct siocnstate *sp));
1787static void siocntxwait __P((void));
1788
0a817a83
AC
1789static void
1790siocntxwait()
1791{
1792 int timo;
1793
1794 /*
1795 * Wait for any pending transmission to finish. Required to avoid
1796 * the UART lockup bug when the speed is changed, and for normal
1797 * transmits.
1798 */
1799 timo = 100000;
1800 while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
1801 != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
1802 ;
1803}
1804
1805static void
1806siocnopen(sp)
1807 struct siocnstate *sp;
1808{
1809 int divisor;
1810 Port_t iobase;
1811
1812 /*
1813 * Save all the device control registers except the fifo register
1814 * and set our default ones (cs8 -parenb speed=comdefaultrate).
1815 * We can't save the fifo register since it is read-only.
1816 */
1817 iobase = siocniobase;
1818 sp->ier = inb(iobase + com_ier);
1819 outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */
1820 siocntxwait();
1821 sp->cfcr = inb(iobase + com_cfcr);
1822 outb(iobase + com_cfcr, CFCR_DLAB);
1823 sp->dlbl = inb(iobase + com_dlbl);
1824 sp->dlbh = inb(iobase + com_dlbh);
1825 divisor = ttspeedtab(comdefaultrate, comspeedtab);
1826 outb(iobase + com_dlbl, divisor & 0xFF);
1827 outb(iobase + com_dlbh, (u_int) divisor >> 8);
1828 outb(iobase + com_cfcr, CFCR_8BITS);
1829 sp->mcr = inb(iobase + com_mcr);
1830 outb(iobase + com_mcr, MCR_DTR | MCR_RTS);
1831}
1832
1833static void
1834siocnclose(sp)
1835 struct siocnstate *sp;
1836{
1837 Port_t iobase;
1838
1839 /*
1840 * Restore the device control registers.
1841 */
1842 siocntxwait();
1843 iobase = siocniobase;
1844 outb(iobase + com_cfcr, CFCR_DLAB);
1845 outb(iobase + com_dlbl, sp->dlbl);
1846 outb(iobase + com_dlbh, sp->dlbh);
1847 outb(iobase + com_cfcr, sp->cfcr);
1848 /*
8a86c29c 1849 * XXX damp oscllations of MCR_DTR and MCR_RTS by not restoring them.
0a817a83
AC
1850 */
1851 outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
1852 outb(iobase + com_ier, sp->ier);
1853}
1854
15637ed4
RG
1855void
1856siocnprobe(cp)
1857 struct consdev *cp;
1858{
1859 int unit;
1860
1861 /* locate the major number */
0a817a83 1862 /* XXX - should be elsewhere since KGDB uses it */
15637ed4 1863 for (commajor = 0; commajor < nchrdev; commajor++)
4c45483e 1864 if (cdevsw[commajor].d_open == sioopen)
15637ed4
RG
1865 break;
1866
1867 /* XXX: ick */
8a86c29c 1868 unit = DEV_TO_UNIT(CONUNIT);
0a817a83 1869 siocniobase = CONADDR;
15637ed4
RG
1870
1871 /* make sure hardware exists? XXX */
1872
1873 /* initialize required fields */
1874 cp->cn_dev = makedev(commajor, unit);
15637ed4
RG
1875#ifdef COMCONSOLE
1876 cp->cn_pri = CN_REMOTE; /* Force a serial port console */
1877#else
1878 cp->cn_pri = CN_NORMAL;
1879#endif
1880}
1881
1882void
1883siocninit(cp)
1884 struct consdev *cp;
1885{
15637ed4 1886 /*
0a817a83
AC
1887 * XXX can delete more comconsole stuff now that i/o routines are
1888 * fairly reentrant.
15637ed4 1889 */
8a86c29c 1890 comconsole = DEV_TO_UNIT(cp->cn_dev);
15637ed4
RG
1891}
1892
1893int
1894siocngetc(dev)
1895 dev_t dev;
1896{
1897 int c;
1898 Port_t iobase;
1899 int s;
0a817a83 1900 struct siocnstate sp;
15637ed4 1901
0a817a83
AC
1902 iobase = siocniobase;
1903 s = spltty();
1904 siocnopen(&sp);
15637ed4
RG
1905 while (!(inb(iobase + com_lsr) & LSR_RXRDY))
1906 ;
1907 c = inb(iobase + com_data);
0a817a83 1908 siocnclose(&sp);
15637ed4
RG
1909 splx(s);
1910 return (c);
1911}
1912
1913void
1914siocnputc(dev, c)
1915 dev_t dev;
1916 int c;
1917{
15637ed4 1918 int s;
0a817a83 1919 struct siocnstate sp;
15637ed4 1920
0a817a83
AC
1921 s = spltty();
1922 siocnopen(&sp);
1923 siocntxwait();
1924 outb(siocniobase + com_data, c);
1925 siocnclose(&sp);
15637ed4
RG
1926 splx(s);
1927}
1928
15637ed4 1929#endif /* NSIO > 0 */