summaryrefslogtreecommitdiffstats
path: root/sys/pc98/cbus
diff options
context:
space:
mode:
authorasami <asami@FreeBSD.org>1996-06-14 10:04:54 +0000
committerasami <asami@FreeBSD.org>1996-06-14 10:04:54 +0000
commit11677d0ab4154036d365fe6697f9d6048dd6bf0e (patch)
treeb16bc29399fcb327f66d0a663ab7e5390be39106 /sys/pc98/cbus
parent27f43565aafed9c68ce08066a71a43a9c692bcb7 (diff)
downloadFreeBSD-src-11677d0ab4154036d365fe6697f9d6048dd6bf0e.zip
FreeBSD-src-11677d0ab4154036d365fe6697f9d6048dd6bf0e.tar.gz
The PC98-specific files.
Ok'd by: core Submitted by: FreeBSD(98) development team
Diffstat (limited to 'sys/pc98/cbus')
-rw-r--r--sys/pc98/cbus/30line.h125
-rw-r--r--sys/pc98/cbus/cbus.h185
-rw-r--r--sys/pc98/cbus/clock.c1129
-rw-r--r--sys/pc98/cbus/fdc.c2489
-rw-r--r--sys/pc98/cbus/fdcreg.h102
-rw-r--r--sys/pc98/cbus/pcrtc.c1129
-rw-r--r--sys/pc98/cbus/sio.c4023
7 files changed, 9182 insertions, 0 deletions
diff --git a/sys/pc98/cbus/30line.h b/sys/pc98/cbus/30line.h
new file mode 100644
index 0000000..a6b7b7c
--- /dev/null
+++ b/sys/pc98/cbus/30line.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1994, 1995, 1996. FreeBSD(98) porting team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PC98_PC98_30LINE_H__
+#define __PC98_PC98_30LINE_H__
+
+#include <pc98/pc98/module.h>
+
+#ifndef LINE30_ROW
+#define LINE30_ROW 30
+#endif
+
+#define _CR 80
+#ifndef _VS
+#define _VS 7
+#endif
+#ifndef _HS
+#define _HS 6 + 1
+#endif
+#ifndef _HFP
+#define _HFP 10 + 1
+#endif
+#ifndef _HBP
+#define _HBP 7 + 1
+#endif
+#ifndef _VFP
+#define _VFP 7
+#endif
+#ifndef _VBP
+#define _VBP 25
+#endif
+
+#define _LF LINE30_ROW*16
+
+#define _GDC_RESET 0x00
+#define _GDC_SYNC 0x0e
+#define _GDC_MASTER 0x6f
+#define _GDC_SLAVE 0x6e
+#define _GDC_START 0x0d
+#define _GDC_STOP 0x0c
+#define _GDC_SCROLL 0x70
+#define _GDC_PITCH 0x47
+
+#define GDC_CR 0
+#define GDC_VS 1
+#define GDC_HS 2
+#define GDC_HFP 3
+#define GDC_HBP 4
+#define GDC_VFP 5
+#define GDC_VBP 6
+#define GDC_LF 7
+
+
+#define _2_5MHZ 0
+#define _5MHZ 1
+
+#define _25L 0
+#define _30L 1
+
+#define T25_G400 0
+#define T30_G400 1
+#define T30_G480 2
+
+static void master_gdc_cmd(unsigned int);
+static void master_gdc_prm(unsigned int);
+static void master_gdc_word_prm(unsigned int);
+static void master_gdc_fifo_empty(void);
+static void master_gdc_wait_vsync(void);
+
+static void gdc_cmd(unsigned int);
+static void gdc_prm(unsigned int);
+static void gdc_word_prm(unsigned int);
+static void gdc_fifo_empty(void);
+static void gdc_wait_vsync(void);
+
+static int check_gdc_clock(void);
+
+static int gdc_INFO = _25L;
+static void initialize_gdc(unsigned int);
+
+static unsigned int master_param[2][8] = {
+{78, 8, 7, 9, 7, 7, 25, 400},
+{_CR-2, _VS, _HS-1, _HFP-1, _HBP-1, _VFP, _VBP, _LF}};
+
+static unsigned int slave_param[6][8] = {
+{38, 8, 3, 4, 3, 7, 25, 400}, /* normal */
+{78, 8, 7, 9, 7, 7, 25, 400},
+{_CR/2-2, _VS, (_HS)/2-1, (_HFP)/2-1, (_HBP)/2-1,
+_VFP+(_LF-400)/2+8, _VBP+(_LF-400)/2-8, 400}, /* 30 & 400 */
+{_CR-2, _VS, _HS-1, _HFP-1, _HBP-1,
+_VFP+(_LF-400)/2+8, _VBP+(_LF-400)/2-8, 400},
+{_CR/2-2, _VS, (_HS)/2-1, (_HFP)/2-1, (_HBP)/2-1,
+_VFP, _VBP, _LF}, /* 30 & 480 */
+{_CR-2, _VS, _HS-1, _HFP-1, _HBP-1, _VFP, _VBP, _LF}};
+
+static int SlavePCH[2] = {40,80};
+static int MasterPCH = 80;
+static int SlaveScrlLF[3] = {400,400,_LF};
+
+#endif
diff --git a/sys/pc98/cbus/cbus.h b/sys/pc98/cbus/cbus.h
new file mode 100644
index 0000000..806c840
--- /dev/null
+++ b/sys/pc98/cbus/cbus.h
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)isa.h 5.7 (Berkeley) 5/9/91
+ * $Id: isa.h,v 1.18 1996/01/30 22:55:57 mpp Exp $
+ */
+
+#ifndef _PC98_PC98_PC98_H_
+#define _PC98_PC98_PC98_H_
+
+/* BEWARE: Included in both assembler and C code */
+
+/*
+ * PC98 Bus conventions
+ */
+/*
+ * PC98 Bus conventions
+ * modified for PC9801 by A.Kojima F.Ukai M.Ishii
+ * Kyoto University Microcomputer Club (KMC)
+ */
+
+/*
+ * Input / Output Port Assignments
+ */
+
+#ifndef IO_BEGIN
+#ifndef PC98 /* IBM-PC */
+#define IO_ISABEGIN 0x000 /* 0x000 - Beginning of I/O Registers */
+#endif
+
+/* PC98 IO address ... very dirty (^_^; */
+
+#define IO_ICU1 0x000 /* 8259A Interrupt Controller #1 */
+#define IO_ICU2 0x008 /* 8259A Interrupt Controller #2 */
+#define IO_DMA 0x001 /* 8237A DMA Controller */
+#define IO_RTC 0x020 /* 4990A RTC */
+#define IO_DMAPG 0x021 /* DMA Page Registers */
+#define IO_COM1 0x030 /* 8251A RS232C serial I/O (int) */
+#define IO_COM2 0x0b1 /* 8251A RS232C serial I/O (ext) */
+#define IO_COM3 0x0b9 /* 8251A RS232C serial I/O (ext) */
+#define IO_SYSPORT 0x031 /* 8255A System Port */
+#define IO_LPT 0x040 /* 8255A Printer Port */
+#define IO_KBD 0x041 /* 8251A Keyboard */
+#define IO_PPI 0x035 /* Programmable Peripheral Interface */
+#define IO_NMI 0x050 /* NMI Control */
+#define IO_WAIT 0x05F /* WAIT 0.6 us */
+#define IO_GDC1 0x060 /* 7220 GDC Text Control */
+#define IO_TIMER 0x071 /* 8253C Timer */
+#define IO_SASI 0x080 /* SASI Hard Disk Controller */
+#define IO_SOUND 0x188 /* YM2203 FM sound board */
+#define IO_GDC2 0x0a0 /* 7220 GDC Graphic Control */
+#define IO_EGC 0x4a0 /* 7220 GDC Graphic Control */
+#define IO_CGROM 0x0a1 /* Character ROM */
+#define IO_SCSI 0xCC0 /* SCSI Controller */
+#define IO_FD1 0x090 /* 765A 1MB FDC */
+#define IO_FD2 0x0c8 /* 765A 640KB FDC */
+#define IO_FDPORT 0x0be /* FD I/F port (1M<->640K,EMTON) */
+#define IO_BEEPF 0x3fdb /* beep frequency */
+#define IO_MOUSE 0x7fd9 /* mouse */
+#define IO_MOUSETM 0xdfbd /* mouse timer */
+#define IO_REEST 0x0F0 /* CPU FPU reset */
+#define IO_A2OEN 0x0F2 /* A20 enable */
+#define IO_A20CT 0x0F6 /* A20 control enable/disable */
+#define IO_NPX 0x0F8 /* Numeric Coprocessor */
+#define IO_BMS 0x7fd9 /* Bus Mouse */
+#define IO_MSE 0x7fd9 /* Bus Mouse */
+#define IO_SIO1 0x0d0 /* MC16550II ext RS232C */
+#define IO_SIO2 0x8d0 /* MC16550II ext RS232C */
+
+/*#ifdef PC98NS*/
+#if 1
+/* Oct 13, ukai */
+#define IO_WD1_NEC 0x640 /* 98note IDE Hard disk controller */
+#define IO_WD1_EPSON 0x80 /* 386note Hard disk controller */
+#define IO_WD1 IO_WD1_NEC /* IDE Hard disk controller */
+#endif
+
+#ifndef PC98 /* IBM-PC */
+#define IO_ISAEND 0x3FF /* - 0x3FF End of I/O Registers */
+#endif IO_ISABEGIN
+#endif
+
+/*
+ * Input / Output Port Sizes - these are from several sources, and tend
+ * to be the larger of what was found, ie COM ports can be 4, but some
+ * boards do not fully decode the address, thus 8 ports are used.
+ */
+
+#ifndef IO_PC98SIZES
+#define IO_PC98SIZES
+
+#define IO_COMSIZE 8 /* 8250, 16X50 com controllers (4?) */
+#define IO_CGASIZE 16 /* CGA controllers */
+#define IO_DMASIZE 16 /* 8237 DMA controllers */
+#define IO_DPGSIZE 32 /* 74LS612 DMA page registers */
+#define IO_FDCSIZE 8 /* Nec765 floppy controllers */
+#define IO_WDCSIZE 8 /* WD compatible disk controllers */
+#define IO_GAMSIZE 16 /* AT compatible game controllers */
+#define IO_ICUSIZE 16 /* 8259A interrupt controllers */
+#define IO_KBDSIZE 16 /* 8042 Keyboard controllers */
+#define IO_LPTSIZE 8 /* LPT controllers, some use only 4 */
+#define IO_MDASIZE 16 /* Monochrome display controllers */
+#define IO_RTCSIZE 16 /* CMOS real time clock, NMI control */
+#define IO_TMRSIZE 16 /* 8253 programmable timers */
+#define IO_NPXSIZE 16 /* 80387/80487 NPX registers */
+#define IO_VGASIZE 16 /* VGA controllers */
+#define IO_EISASIZE 4096 /* EISA controllers */
+#define IO_PMPSIZE 2 /* 82347 power management peripheral */
+
+#endif /* IO_PC98SIZES */
+
+/*
+ * Input / Output Memory Physical Addresses
+ */
+
+#ifndef IOM_BEGIN
+#define IOM_BEGIN 0x0a0000 /* Start of I/O Memory "hole" */
+#define IOM_END 0x100000 /* End of I/O Memory "hole" */
+#define IOM_SIZE (IOM_END - IOM_BEGIN)
+#endif IOM_BEGIN
+
+/*
+ * RAM Physical Address Space (ignoring the above mentioned "hole")
+ */
+
+#ifndef RAM_BEGIN
+#define RAM_BEGIN 0x0000000 /* Start of RAM Memory */
+#ifdef EPSON_BOUNCEDMA
+#define RAM_END 0x0f00000 /* End of EPSON GR?? RAM Memory */
+#else
+#define RAM_END 0x1000000 /* End of RAM Memory */
+#endif
+#define RAM_SIZE (RAM_END - RAM_BEGIN)
+#endif RAM_BEGIN
+
+#ifndef PC98 /* IBM-PC */
+/*
+ * Oddball Physical Memory Addresses
+ */
+#ifndef COMPAQ_RAMRELOC
+#define COMPAQ_RAMRELOC 0x80c00000 /* Compaq RAM relocation/diag */
+#define COMPAQ_RAMSETUP 0x80c00002 /* Compaq RAM setup */
+#define WEITEK_FPU 0xC0000000 /* WTL 2167 */
+#define CYRIX_EMC 0xC0000000 /* Cyrix EMC */
+#endif COMPAQ_RAMRELOC
+#endif
+
+/*
+ * Obtained from NetBSD/pc98
+ */
+#define MADDRUNK -1
+
+#endif /* !_PC98_PC98_PC98_H_ */
diff --git a/sys/pc98/cbus/clock.c b/sys/pc98/cbus/clock.c
new file mode 100644
index 0000000..b2befbf
--- /dev/null
+++ b/sys/pc98/cbus/clock.c
@@ -0,0 +1,1129 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)clock.c 7.2 (Berkeley) 5/12/91
+ * $Id: clock.c,v 1.58 1996/05/01 08:39:02 bde Exp $
+ */
+
+/*
+ * inittodr, settodr and support routines written
+ * by Christoph Robitschko <chmr@edvz.tu-graz.ac.at>
+ *
+ * reintroduced and updated by Chris Stenton <chris@gnome.co.uk> 8/10/94
+ */
+
+/*
+ * modified for PC98
+ * $Id: clock.c,v 1.7 1994/03/26 22:56:13 kakefuda Exp kakefuda $
+ */
+
+/*
+ * Primitive clock interrupt routines.
+ */
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
+#include <machine/clock.h>
+#ifdef CLK_CALIBRATION_LOOP
+#include <machine/cons.h>
+#endif
+#include <machine/cpu.h>
+#include <machine/frame.h>
+
+#ifdef PC98
+#include <sys/syslog.h>
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/timerreg.h>
+#else
+#include <i386/isa/icu.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/rtc.h>
+#include <i386/isa/timerreg.h>
+#endif
+
+/*
+ * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we
+ * can use a simple formula for leap years.
+ */
+#define LEAPYEAR(y) ((u_int)(y) % 4 == 0)
+#define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31)
+
+#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
+
+/*
+ * Time in timer cycles that it takes for microtime() to disable interrupts
+ * and latch the count. microtime() currently uses "cli; outb ..." so it
+ * normally takes less than 2 timer cycles. Add a few for cache misses.
+ * Add a few more to allow for latency in bogus calls to microtime() with
+ * interrupts already disabled.
+ */
+#define TIMER0_LATCH_COUNT 20
+
+/*
+ * Minimum maximum count that we are willing to program into timer0.
+ * Must be large enough to guarantee that the timer interrupt handler
+ * returns before the next timer interrupt. Must be larger than
+ * TIMER0_LATCH_COUNT so that we don't have to worry about underflow in
+ * the calculation of timer0_overflow_threshold.
+ */
+#define TIMER0_MIN_MAX_COUNT TIMER_DIV(20000)
+
+int adjkerntz; /* local offset from GMT in seconds */
+int disable_rtc_set; /* disable resettodr() if != 0 */
+int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
+
+u_int idelayed;
+#if defined(I586_CPU) || defined(I686_CPU)
+unsigned i586_ctr_freq;
+unsigned i586_ctr_rate;
+long long i586_ctr_bias;
+long long i586_last_tick;
+unsigned long i586_avg_tick;
+#endif
+int statclock_disable;
+u_int stat_imask = SWI_CLOCK_MASK;
+int timer0_max_count;
+u_int timer0_overflow_threshold;
+u_int timer0_prescaler_count;
+
+static int beeping = 0;
+static u_int clk_imask = HWI_MASK | SWI_MASK;
+static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
+static u_int hardclock_max_count;
+/*
+ * XXX new_function and timer_func should not handle clockframes, but
+ * timer_func currently needs to hold hardclock to handle the
+ * timer0_state == 0 case. We should use register_intr()/unregister_intr()
+ * to switch between clkintr() and a slightly different timerintr().
+ * This will require locking when acquiring and releasing timer0 - the
+ * current (nonexistent) locking doesn't seem to be adequate even now.
+ */
+static void (*new_function) __P((struct clockframe *frame));
+static u_int new_rate;
+#ifndef PC98
+static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
+static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
+#endif
+#ifdef TIMER_FREQ
+static u_int timer_freq = TIMER_FREQ;
+#else
+#ifdef PC98
+#ifndef AUTO_CLOCK
+#ifndef PC98_8M
+static u_int timer_freq = 2457600;
+#else /* !PC98_8M */
+static u_int timer_freq = 1996800;
+#endif /* PC98_8M */
+#else /* AUTO_CLOCK */
+static u_int timer_freq = 2457600;
+#endif /* AUTO_CLOCK */
+#else /* IBM-PC */
+static u_int timer_freq = 1193182;
+#endif /* PC98 */
+#endif
+static char timer0_state = 0;
+#ifdef PC98
+static char timer1_state = 0;
+#endif
+static char timer2_state = 0;
+static void (*timer_func) __P((struct clockframe *frame)) = hardclock;
+int rtc_inb __P((void));
+
+#if 0
+void
+clkintr(struct clockframe frame)
+{
+ hardclock(&frame);
+ setdelayed();
+}
+#else
+static void
+clkintr(struct clockframe frame)
+{
+ timer_func(&frame);
+ switch (timer0_state) {
+ case 0:
+ setdelayed();
+ break;
+ case 1:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ hardclock(&frame);
+ setdelayed();
+ timer0_prescaler_count -= hardclock_max_count;
+ }
+ break;
+ case 2:
+ setdelayed();
+ timer0_max_count = TIMER_DIV(new_rate);
+ timer0_overflow_threshold =
+ timer0_max_count - TIMER0_LATCH_COUNT;
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ enable_intr();
+ timer0_prescaler_count = 0;
+ timer_func = new_function;
+ timer0_state = 1;
+ break;
+ case 3:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ hardclock(&frame);
+ setdelayed();
+ timer0_max_count = hardclock_max_count;
+ timer0_overflow_threshold =
+ timer0_max_count - TIMER0_LATCH_COUNT;
+ disable_intr();
+ outb(TIMER_MODE,
+ TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ enable_intr();
+ /*
+ * See microtime.s for this magic.
+ */
+#ifdef PC98
+#ifndef AUTO_CLOCK
+#ifndef PC98_8M
+ time.tv_usec += (6667 *
+ (timer0_prescaler_count - hardclock_max_count))
+ >> 14;
+#else /* PC98_8M */
+ time.tv_usec += (16411 *
+ (timer0_prescaler_count - hardclock_max_count))
+ >> 15;
+#endif /* PC98_8M */
+#else /* AUTO_CLOCK */
+ if (pc98_machine_type & M_8M) {
+ /* PC98_8M */
+ time.tv_usec += (16411 *
+ (timer0_prescaler_count -
+ hardclock_max_count)) >> 15;
+ } else {
+ time.tv_usec += (6667 *
+ (timer0_prescaler_count -
+ hardclock_max_count)) >> 14;
+ }
+#endif /* AUTO_CLOCK */
+#else /* IBM-PC */
+ time.tv_usec += (27645 *
+ (timer0_prescaler_count - hardclock_max_count))
+ >> 15;
+#endif /* PC98 */
+ if (time.tv_usec >= 1000000)
+ time.tv_usec -= 1000000;
+ timer0_prescaler_count = 0;
+ timer_func = hardclock;;
+ timer0_state = 0;
+ }
+ break;
+ }
+}
+#endif
+
+int
+acquire_timer0(int rate, void (*function) __P((struct clockframe *frame)))
+{
+ if (timer0_state || TIMER_DIV(rate) < TIMER0_MIN_MAX_COUNT ||
+ !function)
+ return -1;
+ new_function = function;
+ new_rate = rate;
+ timer0_state = 2;
+ return 0;
+}
+
+#ifdef PC98
+int
+acquire_timer1(int mode)
+{
+ if (timer1_state)
+ return -1;
+ timer1_state = 1;
+ outb(TIMER_MODE, TIMER_SEL1 | (mode &0x3f));
+ return 0;
+}
+#endif
+
+int
+acquire_timer2(int mode)
+{
+ if (timer2_state)
+ return -1;
+ timer2_state = 1;
+ outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f));
+ return 0;
+}
+
+int
+release_timer0()
+{
+ if (!timer0_state)
+ return -1;
+ timer0_state = 3;
+ return 0;
+}
+
+#ifdef PC98
+int
+release_timer1()
+{
+ if (!timer1_state)
+ return -1;
+ timer1_state = 0;
+ outb(TIMER_MODE, TIMER_SEL1|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+#endif
+
+int
+release_timer2()
+{
+ if (!timer2_state)
+ return -1;
+ timer2_state = 0;
+ outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+
+#ifndef PC98
+/*
+ * This routine receives statistical clock interrupts from the RTC.
+ * As explained above, these occur at 128 interrupts per second.
+ * When profiling, we receive interrupts at a rate of 1024 Hz.
+ *
+ * This does not actually add as much overhead as it sounds, because
+ * when the statistical clock is active, the hardclock driver no longer
+ * needs to keep (inaccurate) statistics on its own. This decouples
+ * statistics gathering from scheduling interrupts.
+ *
+ * The RTC chip requires that we read status register C (RTC_INTR)
+ * to acknowledge an interrupt, before it will generate the next one.
+ */
+static void
+rtcintr(struct clockframe frame)
+{
+ u_char stat;
+ stat = rtcin(RTC_INTR);
+ if(stat & RTCIR_PERIOD) {
+ statclock(&frame);
+ }
+}
+
+#ifdef DDB
+static void
+DDB_printrtc(void)
+{
+ printf("%02x/%02x/%02x %02x:%02x:%02x, A = %02x, B = %02x, C = %02x\n",
+ rtcin(RTC_YEAR), rtcin(RTC_MONTH), rtcin(RTC_DAY),
+ rtcin(RTC_HRS), rtcin(RTC_MIN), rtcin(RTC_SEC),
+ rtcin(RTC_STATUSA), rtcin(RTC_STATUSB), rtcin(RTC_INTR));
+}
+#endif
+#endif /* for PC98 */
+
+static int
+getit(void)
+{
+ int high, low;
+
+ disable_intr();
+ /* select timer0 and latch counter value */
+ outb(TIMER_MODE, TIMER_SEL0);
+ low = inb(TIMER_CNTR0);
+ high = inb(TIMER_CNTR0);
+ enable_intr();
+ return ((high << 8) | low);
+}
+
+/*
+ * Wait "n" microseconds.
+ * Relies on timer 1 counting down from (timer_freq / hz)
+ * Note: timer had better have been programmed before this is first used!
+ */
+void
+DELAY(int n)
+{
+ int prev_tick, tick, ticks_left, sec, usec;
+
+#ifdef DELAYDEBUG
+ int getit_calls = 1;
+ int n1;
+ static int state = 0;
+
+ if (state == 0) {
+ state = 1;
+ for (n1 = 1; n1 <= 10000000; n1 *= 10)
+ DELAY(n1);
+ state = 2;
+ }
+ if (state == 1)
+ printf("DELAY(%d)...", n);
+#endif
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted. Guess the initial overhead is 20 usec (on most systems it
+ * takes about 1.5 usec for each of the i/o's in getit(). The loop
+ * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
+ * multiplications and divisions to scale the count take a while).
+ */
+ prev_tick = getit();
+ n -= 20;
+ /*
+ * Calculate (n * (timer_freq / 1e6)) without using floating point
+ * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * and without any avoidable overflows.
+ */
+ sec = n / 1000000;
+ usec = n - sec * 1000000;
+ ticks_left = sec * timer_freq
+ + usec * (timer_freq / 1000000)
+ + usec * ((timer_freq % 1000000) / 1000) / 1000
+ + usec * (timer_freq % 1000) / 1000000;
+ if (n < 0)
+ ticks_left = 0; /* XXX timer_freq is unsigned */
+
+ while (ticks_left > 0) {
+ tick = getit();
+#ifdef DELAYDEBUG
+ ++getit_calls;
+#endif
+ if (tick > prev_tick)
+ ticks_left -= prev_tick - (tick - timer0_max_count);
+ else
+ ticks_left -= prev_tick - tick;
+ prev_tick = tick;
+ }
+#ifdef DELAYDEBUG
+ if (state == 1)
+ printf(" %d calls to getit() at %d usec each\n",
+ getit_calls, (n + 5) / getit_calls);
+#endif
+}
+
+static void
+sysbeepstop(void *chan)
+{
+#ifdef PC98 /* PC98 */
+ outb(IO_PPI, inb(IO_PPI)|0x08); /* disable counter1 output to speaker */
+ release_timer1();
+#else
+ outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */
+ release_timer2();
+#endif
+ beeping = 0;
+}
+
+int
+sysbeep(int pitch, int period)
+{
+#ifdef PC98
+ if (acquire_timer1(TIMER_SQWAVE|TIMER_16BIT))
+ return -1;
+ disable_intr();
+ outb(0x3fdb, pitch);
+ outb(0x3fdb, (pitch>>8));
+ enable_intr();
+ if (!beeping) {
+ outb(IO_PPI, (inb(IO_PPI) & 0xf7)); /* enable counter1 output to speaker */
+ beeping = period;
+ timeout(sysbeepstop, (void *)NULL, period);
+ }
+#else
+
+ if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
+ return -1;
+ disable_intr();
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ enable_intr();
+ if (!beeping) {
+ outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */
+ beeping = period;
+ timeout(sysbeepstop, (void *)NULL, period);
+ }
+#endif
+ return 0;
+}
+
+#ifndef PC98
+/*
+ * RTC support routines
+ */
+
+int
+rtcin(reg)
+ int reg;
+{
+ u_char val;
+
+ outb(IO_RTC, reg);
+ inb(0x84);
+ val = inb(IO_RTC + 1);
+ inb(0x84);
+ return (val);
+}
+
+static __inline void
+writertc(u_char reg, u_char val)
+{
+ outb(IO_RTC, reg);
+ outb(IO_RTC + 1, val);
+}
+
+static __inline int
+readrtc(int port)
+{
+ return(bcd2bin(rtcin(port)));
+}
+#endif
+
+#ifdef PC98
+unsigned int delaycount;
+#define FIRST_GUESS 0x2000
+static void findcpuspeed(void)
+{
+ int i;
+ int remainder;
+
+ /* Put counter in count down mode */
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
+ outb(TIMER_CNTR0, 0xff);
+ outb(TIMER_CNTR0, 0xff);
+ for (i = FIRST_GUESS; i; i--)
+ ;
+ remainder = getit();
+ delaycount = (FIRST_GUESS * TIMER_DIV(1000)) / (0xffff - remainder);
+}
+#endif
+
+#ifndef PC98
+static u_int
+calibrate_clocks(void)
+{
+ u_int count, prev_count, tot_count;
+ int sec, start_sec, timeout;
+
+ printf("Calibrating clock(s) relative to mc146818A clock ... ");
+ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
+ goto fail;
+ timeout = 100000000;
+
+ /* Read the mc146818A seconds counter. */
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Wait for the mC146818A seconds counter to change. */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ if (sec != start_sec)
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Start keeping track of the i8254 counter. */
+ prev_count = getit();
+ if (prev_count == 0 || prev_count > timer0_max_count)
+ goto fail;
+ tot_count = 0;
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)
+ wrmsr(0x10, 0LL); /* XXX 0x10 is the MSR for the TSC */
+#endif
+
+ /*
+ * Wait for the mc146818A seconds counter to change. Read the i8254
+ * counter for each iteration since this is convenient and only
+ * costs a few usec of inaccuracy. The timing of the final reads
+ * of the counters almost matches the timing of the initial reads,
+ * so the main cause of inaccuracy is the varying latency from
+ * inside getit() or rtcin(RTC_STATUSA) to the beginning of the
+ * rtcin(RTC_SEC) that returns a changed seconds count. The
+ * maximum inaccuracy from this cause is < 10 usec on 486's.
+ */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP))
+ sec = rtcin(RTC_SEC);
+ count = getit();
+ if (count == 0 || count > timer0_max_count)
+ goto fail;
+ if (count > prev_count)
+ tot_count += prev_count - (count - timer0_max_count);
+ else
+ tot_count += prev_count - count;
+ prev_count = count;
+ if (sec != start_sec)
+ break;
+ if (--timeout == 0)
+ goto fail;
+ }
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ /*
+ * Read the cpu cycle counter. The timing considerations are
+ * similar to those for the i8254 clock.
+ */
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686) {
+ unsigned long long i586_count;
+
+ i586_count = rdtsc();
+ i586_ctr_freq = i586_count;
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz, ", i586_ctr_freq);
+ }
+#endif
+
+ printf("i8254 clock: %u Hz\n", tot_count);
+ return (tot_count);
+
+fail:
+ printf("failed, using default i8254 clock of %u Hz\n", timer_freq);
+ return (timer_freq);
+}
+#endif /* !PC98 */
+
+static void
+set_timer_freq(u_int freq, int intr_freq)
+{
+ u_long ef;
+
+ ef = read_eflags();
+ timer_freq = freq;
+ timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
+ timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ write_eflags(ef);
+}
+
+/*
+ * Initialize 8253 timer 0 early so that it can be used in DELAY().
+ * XXX initialization of other timers is unintentionally left blank.
+ */
+void
+startrtclock()
+{
+ u_int delta, freq;
+
+#ifdef PC98
+ findcpuspeed();
+#ifndef AUTO_CLOCK
+ if (pc98_machine_type & M_8M) {
+#ifndef PC98_8M
+ log(LOG_EMERG,
+ "you must reconfig a kernel with \"PC98_8M\" option.\n");
+#endif
+ } else {
+#ifdef PC98_8M
+ log(LOG_EMERG,
+ "You must reconfig a kernel without \"PC98_8M\" option.\n");
+#endif
+ }
+#else /* AUTO_CLOCK */
+ if (pc98_machine_type & M_8M)
+ timer_freq = 1996800L; /* 1.9968 MHz */
+ else
+ timer_freq = 2457600L; /* 2.4576 MHz */
+#endif /* AUTO_CLOCK */
+#endif /* PC98 */
+
+#ifndef PC98
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+#endif
+
+#ifndef PC98
+ /*
+ * Temporarily calibrate with a high intr_freq to get a low
+ * timer0_max_count to help detect bogus i8254 counts.
+ */
+ set_timer_freq(timer_freq, 20000);
+ freq = calibrate_clocks();
+#ifdef CLK_CALIBRATION_LOOP
+ if (bootverbose) {
+ printf(
+ "Press a key on the console to abort clock calibration\n");
+ while (!cncheckc())
+ calibrate_clocks();
+ }
+#endif
+
+ /*
+ * Use the calibrated i8254 frequency if it seems reasonable.
+ * Otherwise use the default, and don't use the calibrated i586
+ * frequency.
+ */
+ delta = freq > timer_freq ? freq - timer_freq : timer_freq - freq;
+ if (delta < timer_freq / 100) {
+#ifndef CLK_USE_I8254_CALIBRATION
+ printf(
+"CLK_USE_I8254_CALIBRATION not specified - using default frequency\n");
+ freq = timer_freq;
+#endif
+ timer_freq = freq;
+ } else {
+ printf("%d Hz differs from default of %d Hz by more than 1%%\n",
+ freq, timer_freq);
+#if defined(I586_CPU) || defined(I686_CPU)
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+#endif
+ }
+#endif
+
+ set_timer_freq(timer_freq, hz);
+
+#if defined(I586_CPU) || defined(I686_CPU)
+#ifndef CLK_USE_I586_CALIBRATION
+ if (i586_ctr_rate != 0) {
+ printf(
+"CLK_USE_I586_CALIBRATION not specified - using old calibration method\n");
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+ }
+#endif
+ if (i586_ctr_rate == 0 &&
+ (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)) {
+ /*
+ * Calibration of the i586 clock relative to the mc146818A
+ * clock failed. Do a less accurate calibration relative
+ * to the i8254 clock.
+ */
+ unsigned long long i586_count;
+
+ wrmsr(0x10, 0LL); /* XXX */
+ DELAY(1000000);
+ i586_count = rdtsc();
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz\n", i586_ctr_freq);
+ }
+#endif
+}
+
+#ifdef PC98
+void
+rtc_serialcombit(int i)
+{
+ outb(IO_RTC, ((i&0x01)<<5)|0x07);
+ DELAY(1);
+ outb(IO_RTC, ((i&0x01)<<5)|0x17);
+ DELAY(1);
+ outb(IO_RTC, ((i&0x01)<<5)|0x07);
+ DELAY(1);
+}
+
+void
+rtc_serialcom(int i)
+{
+ rtc_serialcombit(i&0x01);
+ rtc_serialcombit((i&0x02)>>1);
+ rtc_serialcombit((i&0x04)>>2);
+ rtc_serialcombit((i&0x08)>>3);
+ outb(IO_RTC, 0x07);
+ DELAY(1);
+ outb(IO_RTC, 0x0f);
+ DELAY(1);
+ outb(IO_RTC, 0x07);
+ DELAY(1);
+}
+
+void
+rtc_outb(int val)
+{
+ int s;
+ int sa = 0;
+
+ for (s=0;s<8;s++) {
+ sa = ((val >> s) & 0x01) ? 0x27 : 0x07;
+ outb(IO_RTC, sa); /* set DI & CLK 0 */
+ DELAY(1);
+ outb(IO_RTC, sa | 0x10); /* CLK 1 */
+ DELAY(1);
+ }
+ outb(IO_RTC, sa & 0xef); /* CLK 0 */
+}
+
+int
+rtc_inb(void)
+{
+ int s;
+ int sa = 0;
+
+ for (s=0;s<8;s++) {
+ sa |= ((inb(0x33) & 0x01) << s);
+ outb(IO_RTC, 0x17); /* CLK 1 */
+ DELAY(1);
+ outb(IO_RTC, 0x07); /* CLK 0 */
+ DELAY(2);
+ }
+ return sa;
+}
+#endif /* PC-98 */
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+void
+inittodr(time_t base)
+{
+ unsigned long sec, days;
+ int yd;
+ int year, month;
+ int y, m, s;
+#ifdef PC98
+ int second, min, hour;
+#endif
+
+ s = splclock();
+ time.tv_sec = base;
+ time.tv_usec = 0;
+ splx(s);
+
+#ifdef PC98
+ rtc_serialcom(0x03); /* Time Read */
+ rtc_serialcom(0x01); /* Register shift command. */
+ DELAY(20);
+
+ second = bcd2bin(rtc_inb() & 0xff); /* sec */
+ min = bcd2bin(rtc_inb() & 0xff); /* min */
+ hour = bcd2bin(rtc_inb() & 0xff); /* hour */
+ days = bcd2bin(rtc_inb() & 0xff) - 1; /* date */
+
+ month = (rtc_inb() >> 4) & 0x0f; /* month */
+ for (m = 1; m < month; m++)
+ days += daysinmonth[m-1];
+ year = bcd2bin(rtc_inb() & 0xff) + 1900; /* year */
+ /* 2000 year problem */
+ if (year < 1995)
+ year += 100;
+ if (year < 1970)
+ goto wrong_time;
+ for (y = 1970; y < year; y++)
+ days += DAYSPERYEAR + LEAPYEAR(y);
+ if ((month > 2) && LEAPYEAR(year))
+ days ++;
+ sec = ((( days * 24 +
+ hour) * 60 +
+ min) * 60 +
+ second);
+ /* sec now contains the number of seconds, since Jan 1 1970,
+ in the local time zone */
+#else /* IBM-PC */
+ /* Look if we have a RTC present and the time is valid */
+ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
+ goto wrong_time;
+
+ /* wait for time update to complete */
+ /* If RTCSA_TUP is zero, we have at least 244us before next update */
+ while (rtcin(RTC_STATUSA) & RTCSA_TUP);
+
+ days = 0;
+#ifdef USE_RTC_CENTURY
+ year = readrtc(RTC_YEAR) + readrtc(RTC_CENTURY) * 100;
+#else
+ year = readrtc(RTC_YEAR) + 1900;
+ if (year < 1970)
+ year += 100;
+#endif
+ if (year < 1970)
+ goto wrong_time;
+ month = readrtc(RTC_MONTH);
+ for (m = 1; m < month; m++)
+ days += daysinmonth[m-1];
+ if ((month > 2) && LEAPYEAR(year))
+ days ++;
+ days += readrtc(RTC_DAY) - 1;
+ yd = days;
+ for (y = 1970; y < year; y++)
+ days += DAYSPERYEAR + LEAPYEAR(y);
+ sec = ((( days * 24 +
+ readrtc(RTC_HRS)) * 60 +
+ readrtc(RTC_MIN)) * 60 +
+ readrtc(RTC_SEC));
+ /* sec now contains the number of seconds, since Jan 1 1970,
+ in the local time zone */
+#endif
+
+ sec += tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
+
+ s = splclock();
+ time.tv_sec = sec;
+ splx(s);
+ return;
+
+wrong_time:
+ printf("Invalid time in real time clock.\n");
+ printf("Check and reset the date immediately!\n");
+}
+
+/*
+ * Write system time back to RTC
+ */
+void
+resettodr()
+{
+ unsigned long tm;
+ int y, m, s;
+#ifdef PC98
+ int wd;
+#endif
+
+ if (disable_rtc_set)
+ return;
+
+ s = splclock();
+ tm = time.tv_sec;
+ splx(s);
+
+#ifdef PC98
+ rtc_serialcom(0x01); /* Register shift command. */
+
+ /* Calculate local time to put in RTC */
+
+ tm -= tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
+
+ rtc_outb(bin2bcd(tm%60)); tm /= 60; /* Write back Seconds */
+ rtc_outb(bin2bcd(tm%60)); tm /= 60; /* Write back Minutes */
+ rtc_outb(bin2bcd(tm%24)); tm /= 24; /* Write back Hours */
+
+ /* We have now the days since 01-01-1970 in tm */
+ wd = (tm+4)%7;
+ for (y = 1970, m = DAYSPERYEAR + LEAPYEAR(y);
+ tm >= m;
+ y++, m = DAYSPERYEAR + LEAPYEAR(y))
+ tm -= m;
+
+ /* Now we have the years in y and the day-of-the-year in tm */
+ for (m = 0; ; m++) {
+ int ml;
+
+ ml = daysinmonth[m];
+ if (m == 1 && LEAPYEAR(y))
+ ml++;
+ if (tm < ml)
+ break;
+ tm -= ml;
+ }
+
+ m++;
+ rtc_outb(bin2bcd(tm+1)); /* Write back Day */
+ rtc_outb((m << 4) | wd); /* Write back Month & Weekday */
+ rtc_outb(bin2bcd(y%100)); /* Write back Year */
+
+ rtc_serialcom(0x02); /* Time set & Counter hold command. */
+ rtc_serialcom(0x00); /* Register hold command. */
+#else
+ /* Disable RTC updates and interrupts. */
+ writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR);
+
+ /* Calculate local time to put in RTC */
+
+ tm -= tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
+
+ writertc(RTC_SEC, bin2bcd(tm%60)); tm /= 60; /* Write back Seconds */
+ writertc(RTC_MIN, bin2bcd(tm%60)); tm /= 60; /* Write back Minutes */
+ writertc(RTC_HRS, bin2bcd(tm%24)); tm /= 24; /* Write back Hours */
+
+ /* We have now the days since 01-01-1970 in tm */
+ writertc(RTC_WDAY, (tm+4)%7); /* Write back Weekday */
+ for (y = 1970, m = DAYSPERYEAR + LEAPYEAR(y);
+ tm >= m;
+ y++, m = DAYSPERYEAR + LEAPYEAR(y))
+ tm -= m;
+
+ /* Now we have the years in y and the day-of-the-year in tm */
+ writertc(RTC_YEAR, bin2bcd(y%100)); /* Write back Year */
+#ifdef USE_RTC_CENTURY
+ writertc(RTC_CENTURY, bin2bcd(y/100)); /* ... and Century */
+#endif
+ for (m = 0; ; m++) {
+ int ml;
+
+ ml = daysinmonth[m];
+ if (m == 1 && LEAPYEAR(y))
+ ml++;
+ if (tm < ml)
+ break;
+ tm -= ml;
+ }
+
+ writertc(RTC_MONTH, bin2bcd(m + 1)); /* Write back Month */
+ writertc(RTC_DAY, bin2bcd(tm + 1)); /* Write back Month Day */
+
+ /* Reenable RTC updates and interrupts. */
+ writertc(RTC_STATUSB, rtc_statusb);
+#endif
+}
+
+/*
+ * Start both clocks running.
+ */
+void
+cpu_initclocks()
+{
+#ifndef PC98
+ int diag;
+
+ if (statclock_disable) {
+ /*
+ * The stat interrupt mask is different without the
+ * statistics clock. Also, don't set the interrupt
+ * flag which would normally cause the RTC to generate
+ * interrupts.
+ */
+ stat_imask = HWI_MASK | SWI_MASK;
+ rtc_statusb = RTCSB_24HR;
+ } else {
+ /* Setting stathz to nonzero early helps avoid races. */
+ stathz = RTC_NOPROFRATE;
+ profhz = RTC_PROFRATE;
+ }
+#endif
+
+ /* Finish initializing 8253 timer 0. */
+ register_intr(/* irq */ 0, /* XXX id */ 0, /* flags */ 0,
+ /* XXX */ (inthand2_t *)clkintr, &clk_imask,
+ /* unit */ 0);
+ INTREN(IRQ0);
+#if defined(I586_CPU) || defined(I686_CPU)
+ /*
+ * Finish setting up anti-jitter measures.
+ */
+ if (i586_ctr_rate) {
+ i586_last_tick = rdtsc();
+ i586_ctr_bias = i586_last_tick;
+ }
+#endif
+
+#ifndef PC98
+ /* Initialize RTC. */
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+
+static int
+sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ /*
+ * Use `i8254' instead of `timer' in external names because `timer'
+ * is is too generic. Should use it everywhere.
+ */
+ freq = timer_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != timer_freq) {
+ if (timer0_state != 0)
+ return (EBUSY); /* too much trouble to handle */
+ set_timer_freq(freq, hz);
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
+
+#if defined(I586_CPU) || defined(I686_CPU)
+static int
+sysctl_machdep_i586_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ if (i586_ctr_rate == 0)
+ return (EOPNOTSUPP);
+ freq = i586_ctr_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != i586_ctr_freq) {
+ i586_ctr_freq = freq;
+ i586_ctr_rate = ((unsigned long long)freq <<
+ I586_CTR_RATE_SHIFT) / 1000000;
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i586_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i586_freq, "I", "");
+#endif /* defined(I586_CPU) || defined(I686_CPU) */
+
+ /* Don't bother enabling the statistics clock. */
+ if (statclock_disable)
+ return;
+ diag = rtcin(RTC_DIAG);
+ if (diag != 0)
+ printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
+ register_intr(/* irq */ 8, /* XXX id */ 1, /* flags */ 0,
+ /* XXX */ (inthand2_t *)rtcintr, &stat_imask,
+ /* unit */ 0);
+ INTREN(IRQ8);
+ writertc(RTC_STATUSB, rtc_statusb);
+#endif
+}
+
+void
+setstatclockrate(int newhz)
+{
+#ifndef PC98
+ if (newhz == RTC_PROFRATE)
+ rtc_statusa = RTCSA_DIVIDER | RTCSA_PROF;
+ else
+ rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
+ writertc(RTC_STATUSA, rtc_statusa);
+#endif
+}
diff --git a/sys/pc98/cbus/fdc.c b/sys/pc98/cbus/fdc.c
new file mode 100644
index 0000000..55a8354
--- /dev/null
+++ b/sys/pc98/cbus/fdc.c
@@ -0,0 +1,2489 @@
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Don Ahn.
+ *
+ * Copyright (c) 1993, 1994 by
+ * jc@irbs.UUCP (John Capo)
+ * vak@zebub.msk.su (Serge Vakulenko)
+ * ache@astral.msk.su (Andrew A. Chernov)
+ *
+ * Copyright (c) 1993, 1994, 1995 by
+ * joerg_wunsch@uriah.sax.de (Joerg Wunsch)
+ * dufault@hda.com (Peter Dufault)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fd.c 7.4 (Berkeley) 5/25/91
+ * $Id: fd.c,v 1.89 1996/05/03 20:15:11 phk Exp $
+ *
+ */
+
+#include "ft.h"
+#if NFT < 1
+#undef NFDC
+#endif
+#include "fd.h"
+
+#if NFDC > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <machine/clock.h>
+#include <machine/ioctl_fd.h>
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/syslog.h>
+#include <sys/devconf.h>
+#include <sys/dkstat.h>
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/fdreg.h>
+#include <pc98/pc98/fdc.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/fdreg.h>
+#include <i386/isa/fdc.h>
+#include <i386/isa/rtc.h>
+#endif
+#include <machine/stdarg.h>
+#if NFT > 0
+#include <sys/ftape.h>
+#ifdef PC98
+#include <pc98/pc98/ftreg.h>
+#else
+#include <i386/isa/ftreg.h>
+#endif
+#endif
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif
+
+
+static int fd_goaway(struct kern_devconf *, int);
+static int fdc_goaway(struct kern_devconf *, int);
+static int fd_externalize(struct kern_devconf *, struct sysctl_req *);
+
+/*
+ * Templates for the kern_devconf structures used when we attach.
+ */
+static struct kern_devconf kdc_fd[NFD] = { {
+ 0, 0, 0, /* filled in by kern_devconf.c */
+ "fd", 0, { MDDT_DISK, 0 },
+ fd_externalize, 0, fd_goaway, DISK_EXTERNALLEN,
+ 0, /* parent */
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "floppy disk",
+ DC_CLS_DISK /* class */
+} };
+
+struct kern_devconf kdc_fdc[NFDC] = { {
+ 0, 0, 0, /* filled in by kern_devconf.c */
+#ifdef PC98
+ "fdc", 0, { MDDT_PC98, 0, "bio" },
+ pc98_generic_externalize, 0, fdc_goaway, PC98_EXTERNALLEN,
+#else
+ "fdc", 0, { MDDT_ISA, 0, "bio" },
+ isa_generic_externalize, 0, fdc_goaway, ISA_EXTERNALLEN,
+#endif
+ 0, /* parent */
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "floppy disk/tape controller",
+ DC_CLS_MISC /* class */
+} };
+
+static inline void
+fd_registerdev(int ctlr, int unit)
+{
+ if(unit != 0)
+ kdc_fd[unit] = kdc_fd[0];
+
+ kdc_fd[unit].kdc_unit = unit;
+ kdc_fd[unit].kdc_parent = &kdc_fdc[ctlr];
+ kdc_fd[unit].kdc_parentdata = 0;
+ dev_attach(&kdc_fd[unit]);
+}
+
+static inline void
+#ifdef PC98
+fdc_registerdev(struct pc98_device *dvp)
+#else
+fdc_registerdev(struct isa_device *dvp)
+#endif
+{
+ int unit = dvp->id_unit;
+
+ if(unit != 0)
+ kdc_fdc[unit] = kdc_fdc[0];
+
+ kdc_fdc[unit].kdc_unit = unit;
+#ifdef PC98
+ kdc_fdc[unit].kdc_parent = &kdc_nec0;
+#else
+ kdc_fdc[unit].kdc_parent = &kdc_isa0;
+#endif
+ kdc_fdc[unit].kdc_parentdata = dvp;
+ dev_attach(&kdc_fdc[unit]);
+}
+
+static int
+fdc_goaway(struct kern_devconf *kdc, int force)
+{
+ if(force) {
+ dev_detach(kdc);
+ return 0;
+ } else {
+ return EBUSY; /* XXX fix */
+ }
+}
+
+static int
+fd_goaway(struct kern_devconf *kdc, int force)
+{
+ dev_detach(kdc);
+ return 0;
+}
+
+#define b_cylin b_resid /* XXX now spelled b_cylinder elsewhere */
+
+/* misuse a flag to identify format operation */
+#define B_FORMAT B_XXX
+
+/*
+ * this biotab field doubles as a field for the physical unit number
+ * on the controller
+ */
+#define id_physid id_scsiid
+
+/* error returns for fd_cmd() */
+#define FD_FAILED -1
+#define FD_NOT_VALID -2
+#define FDC_ERRMAX 100 /* do not log more */
+
+#ifdef PC98
+#define NUMTYPES 5
+#define NUMDENS NUMTYPES
+#else
+#define NUMTYPES 14
+#define NUMDENS (NUMTYPES - 6)
+#endif
+
+/* These defines (-1) must match index for fd_types */
+#define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */
+#define NO_TYPE 0 /* must match NO_TYPE in ft.c */
+#ifdef PC98
+#define FDT_NONE 0 /* none present */
+#define FDT_12M 1 /* 1M/640K FDD */
+#define FDT_144M 2 /* 1.44M/1M/640K FDD */
+
+#define FD_1200 1
+#define FD_1232 2
+#define FD_720 3
+#define FD_640 4
+#define FD_1440 5
+#else
+#define FD_1720 1
+#define FD_1480 2
+#define FD_1440 3
+#define FD_1200 4
+#define FD_820 5
+#define FD_800 6
+#define FD_720 7
+#define FD_360 8
+
+#define FD_1480in5_25 9
+#define FD_1440in5_25 10
+#define FD_820in5_25 11
+#define FD_800in5_25 12
+#define FD_720in5_25 13
+#define FD_360in5_25 14
+#endif
+
+
+static struct fd_type fd_types[NUMTYPES] =
+{
+#ifdef PC98
+{ 15,2,0xFF,0x1B,80,2400,1,0,2,0x54,1 }, /* 1.2 meg HD floppy */
+{ 8,3,0xFF,0x35,77,1232,1,0,2,0x74,1 }, /* 1.2 meg HD floppy 1024/sec */
+{ 9,2,0xFF,0x20,80,1440,1,1,2,0x50,1 }, /* 720k floppy in 1.2meg drive */
+{ 8,2,0xFF,0x2A,80,1280,1,1,2,0x50,1 }, /* 640k floppy in 1.2meg drive */
+{ 18,2,0xFF,0x1B,80,2880,1,2,2,0x54,1 }, /* 1.44 meg HD 3.5in floppy */
+#else
+{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
+{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
+{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
+{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */
+{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */
+{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */
+{ 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */
+{ 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */
+
+{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
+{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
+{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */
+{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */
+{ 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */
+{ 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */
+#endif
+};
+
+#ifdef PC98
+#define DRVS_PER_CTLR 4 /* 4 floppies */
+#else
+#define DRVS_PER_CTLR 2 /* 2 floppies */
+#endif
+
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+struct fdc_data fdc_data[NFDC];
+
+/***********************************************************************\
+* Per drive structure. *
+* N per controller (DRVS_PER_CTLR) *
+\***********************************************************************/
+static struct fd_data {
+ struct fdc_data *fdc; /* pointer to controller structure */
+ int fdsu; /* this units number on this controller */
+ int type; /* Drive type (FD_1440...) */
+ struct fd_type *ft; /* pointer to the type descriptor */
+ int flags;
+#define FD_OPEN 0x01 /* it's open */
+#define FD_ACTIVE 0x02 /* it's active */
+#define FD_MOTOR 0x04 /* motor should be on */
+#define FD_MOTOR_WAIT 0x08 /* motor coming up */
+ int skip;
+ int hddrv;
+#define FD_NO_TRACK -2
+ int track; /* where we think the head is */
+ int options; /* user configurable options, see ioctl_fd.h */
+ int dkunit; /* disk stats unit number */
+#ifdef DEVFS
+ void *bdevs[1 + NUMDENS + MAXPARTITIONS];
+ void *cdevs[1 + NUMDENS + MAXPARTITIONS];
+#endif
+#ifdef PC98
+ int pc98_trans;
+#endif
+} fd_data[NFD];
+
+#ifdef EPSON_NRDISK
+typedef unsigned int nrd_t;
+
+#define P_NRD_ADDRH 0xc24
+#define P_NRD_ADDRM 0xc22
+#define P_NRD_ADDRL 0xc20
+#define P_NRD_CHECK 0xc20
+#define P_NRD_DATA 0xc26
+#define P_NRD_LED 0xc36
+#define B_NRD_CHK 0x80
+#define B_NRD_LED 0x40
+#define A_NRD_INFO 0x2
+#define A_NRD_BASE 0x400
+#define NRD_STATUS 0x0
+#define NRD_ST0_HD 0x04
+
+static fdu_t nrdu=-1;
+static int nrdsec=0;
+static nrd_t nrdblkn=0;
+static nrd_t nrdaddr=0x0;
+
+#define nrd_check_ready() ({ \
+ (epson_inb(P_NRD_CHECK) & B_NRD_CHK) ? 0 : 1; \
+ })
+#define nrd_LED_on() epson_outb(P_NRD_LED, B_NRD_LED)
+#define nrd_LED_off() epson_outb(P_NRD_LED, ~B_NRD_LED)
+#define nrd_trac() ((int)(nrd_info(nrdaddr) & 0xff))
+#define nrd_head() ((int)((nrd_info(nrdaddr) >> 8) & 0xff))
+#define nrd_sec() ((int)(nrd_info(nrdaddr + 2) & 0xff))
+#define nrd_secsize() ((int)((nrd_info(A_NRD_INFO) >> 8) & 0xff))
+#define nrd_addrset(p) nrd_addr((nrd_t)((nrd_t)p+A_NRD_BASE))
+
+static inline void
+nrd_addr(addr)
+ nrd_t addr;
+{
+ epson_outb(P_NRD_ADDRH, (u_char)((addr >> 16) & 0x1f));
+ epson_outb(P_NRD_ADDRM, (u_char)((addr >> 8) & 0xff));
+ epson_outb(P_NRD_ADDRL, (u_char)(addr & 0xff));
+}
+
+static inline u_short
+nrd_info(addr)
+ nrd_t addr;
+{
+ u_short tmp;
+
+ nrd_addr(addr);
+ outb(0x43f, 0x42);
+ tmp = (short)inw(P_NRD_DATA);
+ outb(0x43f, 0x40);
+ return ((u_short)tmp);
+}
+#endif /* EPSON_NRDISK */
+
+/***********************************************************************\
+* Throughout this file the following conventions will be used: *
+* fd is a pointer to the fd_data struct for the drive in question *
+* fdc is a pointer to the fdc_data struct for the controller *
+* fdu is the floppy drive unit number *
+* fdcu is the floppy controller unit number *
+* fdsu is the floppy drive unit number on that controller. (sub-unit) *
+\***********************************************************************/
+
+#if NFT > 0
+int ftopen(dev_t, int);
+int ftintr(ftu_t ftu);
+int ftclose(dev_t, int);
+void ftstrategy(struct buf *);
+int ftioctl(dev_t, int, caddr_t, int, struct proc *);
+int ftdump(dev_t);
+int ftsize(dev_t);
+#ifdef PC98
+int ftattach(struct pc98_device *, struct pc98_device *, int);
+#else
+int ftattach(struct isa_device *, struct isa_device *, int);
+#endif
+#endif
+
+/* autoconfig functions */
+#ifdef PC98
+static int fdprobe(struct pc98_device *);
+static int fdattach(struct pc98_device *);
+#else
+static int fdprobe(struct isa_device *);
+static int fdattach(struct isa_device *);
+#endif
+
+/* needed for ft driver, thus exported */
+int in_fdc(fdcu_t);
+int out_fdc(fdcu_t, int);
+
+/* internal functions */
+static void set_motor(fdcu_t, int, int);
+# define TURNON 1
+# define TURNOFF 0
+static timeout_t fd_turnoff;
+static timeout_t fd_motor_on;
+static void fd_turnon(fdu_t);
+static void fdc_reset(fdc_p);
+static int fd_in(fdcu_t, int *);
+static void fdstart(fdcu_t);
+static timeout_t fd_timeout;
+static timeout_t fd_pseudointr;
+static int fdstate(fdcu_t, fdc_p);
+static int retrier(fdcu_t);
+static int fdformat(dev_t, struct fd_formb *, struct proc *);
+
+
+#define DEVIDLE 0
+#define FINDWORK 1
+#define DOSEEK 2
+#define SEEKCOMPLETE 3
+#define IOCOMPLETE 4
+#define RECALCOMPLETE 5
+#define STARTRECAL 6
+#define RESETCTLR 7
+#define SEEKWAIT 8
+#define RECALWAIT 9
+#define MOTORWAIT 10
+#define IOTIMEDOUT 11
+
+#ifdef DEBUG
+char *fdstates[] =
+{
+"DEVIDLE",
+"FINDWORK",
+"DOSEEK",
+"SEEKCOMPLETE",
+"IOCOMPLETE",
+"RECALCOMPLETE",
+"STARTRECAL",
+"RESETCTLR",
+"SEEKWAIT",
+"RECALWAIT",
+"MOTORWAIT",
+"IOTIMEDOUT"
+};
+
+/* CAUTION: fd_debug causes huge amounts of logging output */
+int fd_debug = 0;
+#define TRACE0(arg) if(fd_debug) printf(arg)
+#define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2)
+#else /* DEBUG */
+#define TRACE0(arg)
+#define TRACE1(arg1, arg2)
+#endif /* DEBUG */
+
+/* autoconfig structure */
+#ifdef PC98
+struct pc98_driver fdcdriver = {
+#else
+struct isa_driver fdcdriver = {
+#endif
+ fdprobe, fdattach, "fdc",
+};
+
+static d_open_t Fdopen; /* NOTE, not fdopen */
+static d_close_t fdclose;
+static d_ioctl_t fdioctl;
+static d_strategy_t fdstrategy;
+
+#define CDEV_MAJOR 9
+#define BDEV_MAJOR 2
+extern struct cdevsw fd_cdevsw;
+static struct bdevsw fd_bdevsw =
+ { Fdopen, fdclose, fdstrategy, fdioctl, /*2*/
+ nodump, nopsize, 0, "fd", &fd_cdevsw, -1 };
+
+static struct cdevsw fd_cdevsw =
+ { Fdopen, fdclose, rawread, rawwrite, /*9*/
+ fdioctl, nostop, nullreset, nodevtotty,
+ seltrue, nommap, fdstrategy, "fd",
+ &fd_bdevsw, -1 };
+
+#ifdef PC98
+static struct pc98_device *fdcdevs[NFDC];
+#else
+static struct isa_device *fdcdevs[NFDC];
+#endif
+
+/*
+ * Provide hw.devconf information.
+ */
+static int
+fd_externalize(struct kern_devconf *kdc, struct sysctl_req *req)
+{
+ return disk_externalize(fd_data[kdc->kdc_unit].fdsu, req);
+}
+
+static int
+fdc_err(fdcu_t fdcu, const char *s)
+{
+ fdc_data[fdcu].fdc_errs++;
+ if(s) {
+ if(fdc_data[fdcu].fdc_errs < FDC_ERRMAX)
+ printf("fdc%d: %s", fdcu, s);
+ else if(fdc_data[fdcu].fdc_errs == FDC_ERRMAX)
+ printf("fdc%d: too many errors, not logging any more\n",
+ fdcu);
+ }
+
+ return FD_FAILED;
+}
+
+/*
+ * fd_cmd: Send a command to the chip. Takes a varargs with this structure:
+ * Unit number,
+ * # of output bytes, output bytes as ints ...,
+ * # of input bytes, input bytes as ints ...
+ */
+
+static int
+fd_cmd(fdcu_t fdcu, int n_out, ...)
+{
+ u_char cmd;
+ int n_in;
+ int n;
+ va_list ap;
+
+ va_start(ap, n_out);
+ cmd = (u_char)(va_arg(ap, int));
+ va_end(ap);
+ va_start(ap, n_out);
+ for (n = 0; n < n_out; n++)
+ {
+ if (out_fdc(fdcu, va_arg(ap, int)) < 0)
+ {
+ char msg[50];
+ sprintf(msg,
+ "cmd %x failed at out byte %d of %d\n",
+ cmd, n + 1, n_out);
+ return fdc_err(fdcu, msg);
+ }
+ }
+ n_in = va_arg(ap, int);
+ for (n = 0; n < n_in; n++)
+ {
+ int *ptr = va_arg(ap, int *);
+ if (fd_in(fdcu, ptr) < 0)
+ {
+ char msg[50];
+ sprintf(msg,
+ "cmd %02x failed at in byte %d of %d\n",
+ cmd, n + 1, n_in);
+ return fdc_err(fdcu, msg);
+ }
+ }
+
+ return 0;
+}
+
+static int
+fd_sense_drive_status(fdc_p fdc, int *st3p)
+{
+ int st3;
+
+ if (fd_cmd(fdc->fdcu, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3))
+ {
+ return fdc_err(fdc->fdcu, "Sense Drive Status failed\n");
+ }
+ if (st3p)
+ *st3p = st3;
+
+ return 0;
+}
+
+static int
+fd_sense_int(fdc_p fdc, int *st0p, int *cylp)
+{
+ int st0, cyl;
+
+#ifdef EPSON_NRDISK
+ if (fdc->fdu == nrdu) {
+ if (fdc->fd->track >= 0) nrdaddr = (fdc->fd->track + 1) * 8;
+ else nrdaddr = 0x0;
+ *st0p = nrd_head() ? NRD_ST0_HD : NRD_STATUS;
+ *cylp = nrd_trac();
+ }
+ else {
+#endif /* EPSON_NRDISK */
+ int ret = fd_cmd(fdc->fdcu, 1, NE7CMD_SENSEI, 1, &st0);
+
+ if (ret)
+ {
+ (void)fdc_err(fdc->fdcu,
+ "sense intr err reading stat reg 0\n");
+ return ret;
+ }
+
+ if (st0p)
+ *st0p = st0;
+
+ if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV)
+ {
+ /*
+ * There doesn't seem to have been an interrupt.
+ */
+ return FD_NOT_VALID;
+ }
+
+ if (fd_in(fdc->fdcu, &cyl) < 0)
+ {
+ return fdc_err(fdc->fdcu, "can't get cyl num\n");
+ }
+
+ if (cylp)
+ *cylp = cyl;
+
+#ifdef EPSON_NRDISK
+ }
+#endif /* EPSON_NRDISK */
+ return 0;
+}
+
+
+static int
+fd_read_status(fdc_p fdc, int fdsu)
+{
+ int i, ret;
+
+ for (i = 0; i < 7; i++)
+ {
+ /*
+ * XXX types are poorly chosen. Only bytes can by read
+ * from the hardware, but fdc_status wants u_longs and
+ * fd_in() gives ints.
+ */
+ int status;
+
+#ifdef EPSON_NRDISK
+ if (fdc->fdu == nrdu) {
+ switch (i) {
+ case 0: fdc->status[i] = nrd_head()
+ ? NRD_ST0_HD : NRD_STATUS; break;
+ case 1: fdc->status[i] = NRD_STATUS; break;
+ case 2: fdc->status[i] = NRD_STATUS; break;
+ case 3: fdc->status[i] = nrd_trac(); break;
+ case 4: fdc->status[i] = nrd_head(); break;
+ case 5: fdc->status[i] = nrdsec; break;
+ case 6: fdc->status[i] = nrd_secsize(); break;
+ }
+ ret = 0;
+ }
+ else {
+#endif /* EPSON_NRDISK */
+ ret = fd_in(fdc->fdcu, &status);
+ fdc->status[i] = status;
+ if (ret != 0)
+ break;
+#ifdef EPSON_NRDISK
+ }
+#endif /* EPSON_NRDISK */
+ }
+
+ if (ret == 0)
+ fdc->flags |= FDC_STAT_VALID;
+ else
+ fdc->flags &= ~FDC_STAT_VALID;
+
+ return ret;
+}
+
+/****************************************************************************/
+/* autoconfiguration stuff */
+/****************************************************************************/
+#ifdef PC98
+static int pc98_trans = 0; /* 0 : HD , 1 : DD , 2 : 1.44 */
+static int pc98_trans_prev = 0;
+
+static void set_density(fdcu_t, fdu_t);
+static int pc98_fd_check_ready(fdu_t);
+
+static void set_density(fdcu, fdu)
+ fdcu_t fdcu;
+ fdu_t fdu;
+{
+ /* always motor on */
+ outb(IO_FDPORT,
+ (pc98_trans != 1 ? FDP_FDDEXC : 0) | FDP_PORTEXC);
+ DELAY(100);
+ outb(fdc_data[fdcu].baseport + FDOUT, FDO_RST | FDO_DMAE);
+ /* in the case of note W, always inhibit 100ms timer */
+}
+
+static int pc98_fd_check_ready(fdu)
+ fdu_t fdu;
+{
+ fd_p fd = fd_data + fdu;
+ fdcu_t fdcu = fd->fdc->fdcu;
+ int retry = 0;
+
+#ifdef EPSON_NRDISK
+ if (fdu == nrdu) {
+ if (nrd_check_ready()) return 0;
+ else return -1;
+ }
+#endif
+ while (retry++ < 30000) {
+ set_motor(fdcu, fd->fdsu, TURNON);
+ out_fdc(fdcu, NE7CMD_SENSED); /* Sense Drive Status */
+ DELAY(100);
+ out_fdc(fdcu, fdu); /* Drive number */
+ DELAY(100);
+ if ((in_fdc(fdcu) & NE7_ST3_RD)){
+ outb(fdc_data[fdcu].baseport + FDOUT,
+ FDO_DMAE | FDO_MTON);
+ DELAY(10);
+ return 0;
+ }
+ }
+ return -1;
+}
+#endif
+
+
+/*
+ * probe for existance of controller
+ */
+static int
+#ifdef PC98
+fdprobe(struct pc98_device *dev)
+#else
+fdprobe(struct isa_device *dev)
+#endif
+{
+ fdcu_t fdcu = dev->id_unit;
+ if(fdc_data[fdcu].flags & FDC_ATTACHED)
+ {
+ printf("fdc%d: unit used multiple times\n", fdcu);
+ return 0;
+ }
+
+ fdcdevs[fdcu] = dev;
+ fdc_data[fdcu].baseport = dev->id_iobase;
+
+#ifndef DEV_LKM
+ fdc_registerdev(dev);
+#endif
+
+#ifndef PC98
+ /* First - lets reset the floppy controller */
+ outb(dev->id_iobase+FDOUT, 0);
+ DELAY(100);
+ outb(dev->id_iobase+FDOUT, FDO_FRST);
+#endif
+
+ /* see if it can handle a command */
+#ifdef PC98
+ if (fd_cmd(fdcu,
+ 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0),
+ 0))
+#else
+ if (fd_cmd(fdcu,
+ 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
+ 0))
+#endif
+ {
+ return(0);
+ }
+ kdc_fdc[fdcu].kdc_state = DC_IDLE;
+ return (IO_FDCSIZE);
+}
+
+/*
+ * wire controller into system, look for floppy units
+ */
+static int
+#ifdef PC98
+fdattach(struct pc98_device *dev)
+#else
+fdattach(struct isa_device *dev)
+#endif
+{
+ unsigned fdt;
+ fdu_t fdu;
+ fdcu_t fdcu = dev->id_unit;
+ fdc_p fdc = fdc_data + fdcu;
+ fd_p fd;
+ int fdsu, st0, st3, i, unithasfd;
+#ifdef PC98
+ struct pc98_device *fdup;
+#else
+ struct isa_device *fdup;
+#endif
+ int ic_type = 0;
+#ifdef DEVFS
+ int mynor;
+ int typemynor;
+ int typesize;
+#endif
+
+ fdc->fdcu = fdcu;
+ fdc->flags |= FDC_ATTACHED;
+#ifdef PC98
+ fdc->dmachan = 2;
+ if (fdc->dmachan != dev->id_drq) {
+ dev->id_drq = fdc->dmachan;
+ printf(" [dma is changed to #%d]", fdc->dmachan);
+ }
+ /* Acquire the DMA channel forever, The driver will do the rest */
+ pc98_dma_acquire(fdc->dmachan);
+ pc98_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
+ fdc->state = DEVIDLE;
+ fdc_reset(fdc);
+#else
+ fdc->dmachan = dev->id_drq;
+ /* Acquire the DMA channel forever, The driver will do the rest */
+ isa_dma_acquire(fdc->dmachan);
+ isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */);
+ fdc->state = DEVIDLE;
+ /* reset controller, turn motor off, clear fdout mirror reg */
+ outb(fdc->baseport + FDOUT, ((fdc->fdout = 0)));
+#endif
+ TAILQ_INIT(&fdc->head);
+
+ /* check for each floppy drive */
+#ifdef PC98
+ for (fdup = pc98_biotab_fdc; fdup->id_driver != 0; fdup++) {
+#else
+ for (fdup = isa_biotab_fdc; fdup->id_driver != 0; fdup++) {
+#endif
+ if (fdup->id_iobase != dev->id_iobase)
+ continue;
+ fdu = fdup->id_unit;
+ fd = &fd_data[fdu];
+ if (fdu >= (NFD+NFT))
+ continue;
+ fdsu = fdup->id_physid;
+ /* look up what bios thinks we have */
+ switch (fdu) {
+#ifdef PC98
+ case 0: case 1: case 2: case 3:
+ if ((PC98_SYSTEM_PARAMETER(0x5ae) >> fdu) & 0x01)
+ fdt = FDT_144M;
+#ifdef EPSON_NRDISK
+ else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fdu) & 0x01) {
+ fdt = FDT_12M;
+ switch (epson_machine_id) {
+ case 0x20: case 0x27:
+ if ((PC98_SYSTEM_PARAMETER(0x488) >> fdu) & 0x01) {
+ if (nrd_check_ready()) {
+ nrd_LED_on();
+ nrdu = fdu;
+ }
+ else fdt = FDT_NONE;
+ }
+ }
+ }
+#else /* !EPSON_NRDISK */
+ else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fdu) & 0x01) {
+ fdt = FDT_12M;
+ switch (epson_machine_id) {
+ case 0x20: case 0x27:
+ if ((PC98_SYSTEM_PARAMETER(0x488) >> fdu) & 0x01)
+ fdt = FDT_NONE;
+ }
+ }
+#endif /* EPSON_NRDISK */
+ else fdt = FDT_NONE;
+ break;
+ default:
+ fdt = FDT_NONE;
+ break;
+#else
+ case 0: fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
+ break;
+ case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
+ break;
+ default: fdt = RTCFDT_NONE;
+ break;
+#endif
+ }
+ /* is there a unit? */
+#ifdef PC98
+ if ((fdt == FDT_NONE)
+#else
+ if ((fdt == RTCFDT_NONE)
+#endif
+#if NFT > 0
+ || (fdsu >= DRVS_PER_CTLR)) {
+#else
+ ) {
+#ifdef PC98
+ fd->fdc = fdc;
+#endif
+ fd->type = NO_TYPE;
+#endif
+#if NFT > 0
+ /* If BIOS says no floppy, or > 2nd device */
+ /* Probe for and attach a floppy tape. */
+ /* Tell FT if there was already a disk */
+ /* with this unit number found. */
+
+ unithasfd = 0;
+ if (fdu < NFD && fd->type != NO_TYPE)
+ unithasfd = 1;
+ if (ftattach(dev, fdup, unithasfd))
+ continue;
+ if (fdsu < DRVS_PER_CTLR)
+ fd->type = NO_TYPE;
+#endif
+ continue;
+ }
+
+#ifdef PC98
+ kdc_fdc[fdcu].kdc_description =
+ "NEC 765 floppy disk/tape controller";
+#else
+ /* select it */
+ set_motor(fdcu, fdsu, TURNON);
+ DELAY(1000000); /* 1 sec */
+
+ if (ic_type == 0 &&
+ fd_cmd(fdcu, 1, NE7CMD_VERSION, 1, &ic_type) == 0)
+ {
+ printf("fdc%d: ", fdcu);
+ ic_type = (u_char)ic_type;
+ switch( ic_type ) {
+ case 0x80:
+ printf("NEC 765\n");
+ fdc->fdct = FDC_NE765;
+ kdc_fdc[fdcu].kdc_description =
+ "NEC 765 floppy disk/tape controller";
+ break;
+ case 0x81:
+ printf("Intel 82077\n");
+ fdc->fdct = FDC_I82077;
+ kdc_fdc[fdcu].kdc_description =
+ "Intel 82077 floppy disk/tape controller";
+ break;
+ case 0x90:
+ printf("NEC 72065B\n");
+ fdc->fdct = FDC_NE72065;
+ kdc_fdc[fdcu].kdc_description =
+ "NEC 72065B floppy disk/tape controller";
+ break;
+ default:
+ printf("unknown IC type %02x\n", ic_type);
+ fdc->fdct = FDC_UNKNOWN;
+ break;
+ }
+ }
+ if ((fd_cmd(fdcu, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) &&
+ (st3 & NE7_ST3_T0)) {
+ /* if at track 0, first seek inwards */
+ /* seek some steps: */
+ (void)fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0);
+ DELAY(300000); /* ...wait a moment... */
+ (void)fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
+ }
+
+ /* If we're at track 0 first seek inwards. */
+ if ((fd_sense_drive_status(fdc, &st3) == 0) &&
+ (st3 & NE7_ST3_T0)) {
+ /* Seek some steps... */
+ if (fd_cmd(fdcu, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
+ /* ...wait a moment... */
+ DELAY(300000);
+ /* make ctrlr happy: */
+ (void)fd_sense_int(fdc, 0, 0);
+ }
+ }
+
+ for(i = 0; i < 2; i++) {
+ /*
+ * we must recalibrate twice, just in case the
+ * heads have been beyond cylinder 76, since most
+ * FDCs still barf when attempting to recalibrate
+ * more than 77 steps
+ */
+ /* go back to 0: */
+ if (fd_cmd(fdcu, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
+ /* a second being enough for full stroke seek*/
+ DELAY(i == 0? 1000000: 300000);
+
+ /* anything responding? */
+ if (fd_sense_int(fdc, &st0, 0) == 0 &&
+ (st0 & NE7_ST0_EC) == 0)
+ break; /* already probed succesfully */
+ }
+ }
+
+ set_motor(fdcu, fdsu, TURNOFF);
+
+ if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
+ continue;
+#endif
+
+ fd->track = FD_NO_TRACK;
+ fd->fdc = fdc;
+ fd->fdsu = fdsu;
+ fd->options = 0;
+ printf("fd%d: ", fdu);
+
+ fd_registerdev(fdcu, fdu);
+ switch (fdt) {
+#ifdef PC98
+ case FDT_12M:
+#ifdef EPSON_NRDISK
+ if (fdu == nrdu) {
+ printf("EPSON RAM DRIVE\n");
+ nrd_LED_off();
+ }
+ else printf("1M/640M FDD\n");
+#else /* !EPSON_NRDISK */
+ printf("1M/640K FDD\n");
+#endif /* EPSON_NRDISK */
+ fd->type = FD_1200;
+ fd->pc98_trans = 0;
+ kdc_fd[fdu].kdc_description =
+ "1M/640K floppy disk drive";
+#ifdef DEVFS
+ sprintf(name,"rfd%d.1200",fdu);
+#endif /* DEVFS */
+ break;
+ case FDT_144M:
+ printf("1.44M FDD\n");
+ fd->type = FD_1200;
+ fd->pc98_trans = 0;
+ outb(0x4be, (fdu << 5) | 0x10);
+ kdc_fd[fdu].kdc_description =
+ "1.44MB (1440K) 3.5in floppy disk drive";
+#ifdef DEVFS
+ sprintf(name,"rfd%d.1440",fdu);
+#endif /* DEVFS */
+ break;
+#else
+ case RTCFDT_12M:
+ printf("1.2MB 5.25in\n");
+ fd->type = FD_1200;
+ kdc_fd[fdu].kdc_description =
+ "1.2MB (1200K) 5.25in floppy disk drive";
+ break;
+ case RTCFDT_144M:
+ printf("1.44MB 3.5in\n");
+ fd->type = FD_1440;
+ kdc_fd[fdu].kdc_description =
+ "1.44MB (1440K) 3.5in floppy disk drive";
+ break;
+ case RTCFDT_288M:
+ case RTCFDT_288M_1:
+ printf("2.88MB 3.5in - 1.44MB mode\n");
+ fd->type = FD_1440;
+ kdc_fd[fdu].kdc_description =
+ "2.88MB (2880K) 3.5in floppy disk drive in 1.44 mode";
+ break;
+ case RTCFDT_360K:
+ printf("360KB 5.25in\n");
+ fd->type = FD_360;
+ kdc_fd[fdu].kdc_description =
+ "360KB 5.25in floppy disk drive";
+ break;
+ case RTCFDT_720K:
+ printf("720KB 3.5in\n");
+ fd->type = FD_720;
+ kdc_fd[fdu].kdc_description =
+ "720KB 3.5in floppy disk drive";
+ break;
+#endif
+ default:
+ printf("unknown\n");
+ fd->type = NO_TYPE;
+ dev_detach(&kdc_fd[fdu]);
+ continue;
+ }
+ kdc_fd[fdu].kdc_state = DC_IDLE;
+#ifdef DEVFS
+ mynor = fdu << 6;
+ fd->bdevs[0] = devfs_add_devswf(&fd_bdevsw, mynor, DV_BLK,
+ UID_ROOT, GID_OPERATOR, 0640,
+ "fd%d", fdu);
+ fd->cdevs[0] = devfs_add_devswf(&fd_cdevsw, mynor, DV_CHR,
+ UID_ROOT, GID_OPERATOR, 0640,
+ "rfd%d", fdu);
+ for (i = 1; i < 1 + NUMDENS; i++) {
+ /*
+ * XXX this and the lookup in Fdopen() should be
+ * data driven.
+ */
+ switch (fd->type) {
+ case FD_360:
+ if (i != FD_360)
+ continue;
+ break;
+ case FD_720:
+ if (i != FD_720 && i != FD_800 && i != FD_820)
+ continue;
+ break;
+ case FD_1200:
+ if (i != FD_360 && i != FD_720 && i != FD_800
+ && i != FD_820 && i != FD_1200
+ && i != FD_1440 && i != FD_1480)
+ continue;
+ break;
+ case FD_1440:
+ if (i != FD_720 && i != FD_800 && i != FD_820
+ && i != FD_1200 && i != FD_1440
+ && i != FD_1480 && i != FD_1720)
+ continue;
+ break;
+ }
+ typemynor = mynor | i;
+ typesize = fd_types[i - 1].size / 2;
+ /*
+ * XXX all these conversions give bloated code and
+ * confusing names.
+ */
+ if (typesize == 1476)
+ typesize = 1480;
+ if (typesize == 1722)
+ typesize = 1720;
+ fd->bdevs[i] =
+ devfs_add_devswf(&fd_bdevsw, typemynor, DV_BLK,
+ UID_ROOT, GID_OPERATOR, 0640,
+ "fd%d.%d", fdu, typesize);
+ fd->cdevs[i] =
+ devfs_add_devswf(&fd_cdevsw, typemynor, DV_CHR,
+ UID_ROOT, GID_OPERATOR, 0640,
+ "rfd%d.%d", fdu, typesize);
+ }
+ for (i = 0; i < MAXPARTITIONS; i++) {
+ fd->bdevs[1 + NUMDENS + i] =
+ devfs_link(fd->bdevs[0],
+ "fd%d%c", fdu, 'a' + i);
+ fd->cdevs[1 + NUMDENS + i] =
+ devfs_link(fd->cdevs[0],
+ "rfd%d%c", fdu, 'a' + i);
+ }
+#endif /* DEVFS */
+ if (dk_ndrive < DK_NDRIVE) {
+ sprintf(dk_names[dk_ndrive], "fd%d", fdu);
+ fd->dkunit = dk_ndrive++;
+ /*
+ * XXX assume rate is FDC_500KBPS.
+ */
+ dk_wpms[dk_ndrive] = 500000 / 8 / 2;
+ } else {
+ fd->dkunit = -1;
+ }
+ }
+
+ return (1);
+}
+
+/****************************************************************************/
+/* motor control stuff */
+/* remember to not deselect the drive we're working on */
+/****************************************************************************/
+static void
+set_motor(fdcu_t fdcu, int fdsu, int turnon)
+{
+ int fdout = fdc_data[fdcu].fdout;
+ int needspecify = 0;
+
+#ifdef PC98
+ outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0)|FDP_PORTEXC);
+ DELAY(10);
+ fdout = FDO_DMAE|FDO_MTON;
+#else
+ if(turnon) {
+ fdout &= ~FDO_FDSEL;
+ fdout |= (FDO_MOEN0 << fdsu) + fdsu;
+ } else
+ fdout &= ~(FDO_MOEN0 << fdsu);
+
+ if(!turnon
+ && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0)
+ /* gonna turn off the last drive, put FDC to bed */
+ fdout &= ~ (FDO_FRST|FDO_FDMAEN);
+ else {
+ /* make sure controller is selected and specified */
+ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0)
+ needspecify = 1;
+ fdout |= (FDO_FRST|FDO_FDMAEN);
+ }
+#endif
+
+ outb(fdc_data[fdcu].baseport+FDOUT, fdout);
+ DELAY(10);
+ fdc_data[fdcu].fdout = fdout;
+#ifndef PC98
+ kdc_fdc[fdcu].kdc_state = (fdout & FDO_FRST)? DC_BUSY: DC_IDLE;
+#endif
+ TRACE1("[0x%x->FDOUT]", fdout);
+
+ if(needspecify) {
+ /*
+ * XXX
+ * special case: since we have just woken up the FDC
+ * from its sleep, we silently assume the command will
+ * be accepted, and do not test for a timeout
+ */
+#ifdef PC98
+ (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
+ NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0),
+ 0);
+#else
+ (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
+ NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
+ 0);
+#endif
+ }
+}
+
+static void
+fd_turnoff(void *arg1)
+{
+ fdu_t fdu = (fdu_t)arg1;
+ int s;
+ fd_p fd = fd_data + fdu;
+
+ TRACE1("[fd%d: turnoff]", fdu);
+
+ /*
+ * Don't turn off the motor yet if the drive is active.
+ * XXX shouldn't even schedule turnoff until drive is inactive
+ * and nothing is queued on it.
+ */
+ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fdu) {
+ timeout(fd_turnoff, arg1, 4 * hz);
+ return;
+ }
+
+ s = splbio();
+ fd->flags &= ~FD_MOTOR;
+ set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF);
+ splx(s);
+}
+
+static void
+fd_motor_on(void *arg1)
+{
+ fdu_t fdu = (fdu_t)arg1;
+ int s;
+
+ fd_p fd = fd_data + fdu;
+ s = splbio();
+ fd->flags &= ~FD_MOTOR_WAIT;
+ if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT))
+ {
+ fdintr(fd->fdc->fdcu);
+ }
+ splx(s);
+}
+
+static void
+fd_turnon(fdu_t fdu)
+{
+ fd_p fd = fd_data + fdu;
+ if(!(fd->flags & FD_MOTOR))
+ {
+ fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT);
+ set_motor(fd->fdc->fdcu, fd->fdsu, TURNON);
+ timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */
+ }
+}
+
+static void
+fdc_reset(fdc_p fdc)
+{
+ fdcu_t fdcu = fdc->fdcu;
+
+ /* Try a reset, keep motor on */
+#ifdef PC98
+ set_density(fdcu, 0);
+ if (pc98_machine_type & M_EPSON_PC98)
+ outb(fdc->baseport + FDOUT, 0xe8);
+ else
+ outb(fdc->baseport + FDOUT, 0xd8);
+ DELAY(200);
+ outb(fdc->baseport + FDOUT, 0x18);
+ DELAY(10);
+#else
+ outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
+ TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN));
+ DELAY(100);
+ /* enable FDC, but defer interrupts a moment */
+ outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN);
+ TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN);
+ DELAY(100);
+ outb(fdc->baseport + FDOUT, fdc->fdout);
+ TRACE1("[0x%x->FDOUT]", fdc->fdout);
+#endif
+
+ /* XXX after a reset, silently believe the FDC will accept commands */
+#ifdef PC98
+ (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
+ NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0),
+ 0);
+#else
+ (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
+ NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
+ 0);
+#endif
+}
+
+/****************************************************************************/
+/* fdc in/out */
+/****************************************************************************/
+int
+in_fdc(fdcu_t fdcu)
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i, j = 1000000;
+ while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
+ != (NE7_DIO|NE7_RQM) && j-- > 0) {
+ if (i == NE7_RQM)
+ return fdc_err(fdcu, "ready for output in input\n");
+ }
+ if (j <= 0)
+ return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
+#ifdef DEBUG
+ i = inb(baseport+FDDATA);
+ TRACE1("[FDDATA->0x%x]", (unsigned char)i);
+ return(i);
+#else
+ return inb(baseport+FDDATA);
+#endif
+}
+
+/*
+ * fd_in: Like in_fdc, but allows you to see if it worked.
+ */
+static int
+fd_in(fdcu_t fdcu, int *ptr)
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i, j = 1000000;
+ while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM))
+ != (NE7_DIO|NE7_RQM) && j-- > 0) {
+ DELAY(10);
+ if (i == NE7_RQM)
+ return fdc_err(fdcu, "ready for output in input\n");
+ }
+ if (j <= 0)
+ return fdc_err(fdcu, bootverbose? "input ready timeout\n": 0);
+#ifdef DEBUG
+ i = inb(baseport+FDDATA);
+ TRACE1("[FDDATA->0x%x]", (unsigned char)i);
+ *ptr = i;
+ return 0;
+#else
+ i = inb(baseport+FDDATA);
+ if (ptr)
+ *ptr = i;
+ return 0;
+#endif
+}
+
+int
+out_fdc(fdcu_t fdcu, int x)
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i;
+
+ /* Check that the direction bit is set */
+ i = 1000000;
+ while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0)
+ DELAY(10);
+ if (i <= 0) return fdc_err(fdcu, "direction bit not set\n");
+
+ /* Check that the floppy controller is ready for a command */
+ i = 1000000;
+ while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0)
+ DELAY(10);
+ if (i <= 0)
+ return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0);
+
+ /* Send the command and return */
+ outb(baseport+FDDATA, x);
+ DELAY(10);
+ TRACE1("[0x%x->FDDATA]", x);
+ return (0);
+}
+
+/****************************************************************************/
+/* fdopen/fdclose */
+/****************************************************************************/
+int
+Fdopen(dev_t dev, int flags, int mode, struct proc *p)
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ int type = FDTYPE(minor(dev));
+ fdc_p fdc;
+
+#if NFT > 0
+ /* check for a tape open */
+ if (type & F_TAPE_TYPE)
+ return(ftopen(dev, flags));
+#endif
+ /* check bounds */
+ if (fdu >= NFD)
+ return(ENXIO);
+ fdc = fd_data[fdu].fdc;
+ if ((fdc == NULL) || (fd_data[fdu].type == NO_TYPE))
+ return(ENXIO);
+ if (type > NUMDENS)
+ return(ENXIO);
+#ifdef PC98
+ if (pc98_fd_check_ready(fdu) == -1)
+ return(EIO);
+#endif
+ if (type == 0)
+ type = fd_data[fdu].type;
+#ifndef PC98
+ else {
+ if (type != fd_data[fdu].type) {
+ switch (fd_data[fdu].type) {
+ case FD_360:
+ return(ENXIO);
+ case FD_720:
+ if ( type != FD_820
+ && type != FD_800
+ )
+ return(ENXIO);
+ break;
+ case FD_1200:
+ switch (type) {
+ case FD_1480:
+ type = FD_1480in5_25;
+ break;
+ case FD_1440:
+ type = FD_1440in5_25;
+ break;
+ case FD_820:
+ type = FD_820in5_25;
+ break;
+ case FD_800:
+ type = FD_800in5_25;
+ break;
+ case FD_720:
+ type = FD_720in5_25;
+ break;
+ case FD_360:
+ type = FD_360in5_25;
+ break;
+ default:
+ return(ENXIO);
+ }
+ break;
+ case FD_1440:
+ if ( type != FD_1720
+ && type != FD_1480
+ && type != FD_1200
+ && type != FD_820
+ && type != FD_800
+ && type != FD_720
+ )
+ return(ENXIO);
+ break;
+ }
+ }
+ }
+#endif
+ fd_data[fdu].ft = fd_types + type - 1;
+ fd_data[fdu].flags |= FD_OPEN;
+ kdc_fd[fdu].kdc_state = DC_BUSY;
+
+ return 0;
+}
+
+int
+fdclose(dev_t dev, int flags, int mode, struct proc *p)
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+
+#if NFT > 0
+ int type = FDTYPE(minor(dev));
+
+ if (type & F_TAPE_TYPE)
+ return ftclose(dev, flags);
+#endif
+ fd_data[fdu].flags &= ~FD_OPEN;
+ fd_data[fdu].options &= ~FDOPT_NORETRY;
+ kdc_fd[fdu].kdc_state = DC_IDLE;
+
+ return(0);
+}
+
+
+/****************************************************************************/
+/* fdstrategy */
+/****************************************************************************/
+void
+fdstrategy(struct buf *bp)
+{
+ long nblocks, blknum;
+ int s;
+ fdcu_t fdcu;
+ fdu_t fdu;
+ fdc_p fdc;
+ fd_p fd;
+ size_t fdblk;
+
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = &fd_data[fdu];
+ fdc = fd->fdc;
+ fdcu = fdc->fdcu;
+
+#if NFT > 0
+ if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) {
+ /* ft tapes do not (yet) support strategy i/o */
+ bp->b_error = ENODEV;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+ /* check for controller already busy with tape */
+ if (fdc->flags & FDC_TAPE_BUSY) {
+ bp->b_error = EBUSY;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+#endif
+ fdblk = 128 << (fd->ft->secsize);
+ if (!(bp->b_flags & B_FORMAT)) {
+ if ((fdu >= NFD) || (bp->b_blkno < 0)) {
+ printf(
+ "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
+ fdu, (u_long)bp->b_blkno, bp->b_bcount);
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+ if ((bp->b_bcount % fdblk) != 0) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+ }
+
+ /*
+ * Set up block calculations.
+ */
+ blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk;
+ nblocks = fd->ft->size;
+#ifdef PC98
+#define B_XXX2 0x8000000
+ if (bp->b_flags & B_XXX2) {
+ blknum *= 2;
+ bp->b_blkno *= 2;
+ bp->b_flags &= ~B_XXX2;
+ }
+#endif
+ if (blknum + (bp->b_bcount / fdblk) > nblocks) {
+ if (blknum == nblocks) {
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = ENOSPC;
+ bp->b_flags |= B_ERROR;
+ }
+ goto bad;
+ }
+ bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads);
+ bp->b_pblkno = bp->b_blkno;
+ s = splbio();
+ tqdisksort(&fdc->head, bp);
+ untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */
+ fdstart(fdcu);
+ splx(s);
+ return;
+
+bad:
+ biodone(bp);
+}
+
+/***************************************************************\
+* fdstart *
+* We have just queued something.. if the controller is not busy *
+* then simulate the case where it has just finished a command *
+* So that it (the interrupt routine) looks on the queue for more*
+* work to do and picks up what we just added. *
+* If the controller is already busy, we need do nothing, as it *
+* will pick up our work when the present work completes *
+\***************************************************************/
+static void
+fdstart(fdcu_t fdcu)
+{
+ int s;
+
+ s = splbio();
+ if(fdc_data[fdcu].state == DEVIDLE)
+ {
+ fdintr(fdcu);
+ }
+ splx(s);
+}
+
+static void
+fd_timeout(void *arg1)
+{
+ fdcu_t fdcu = (fdcu_t)arg1;
+ fdu_t fdu = fdc_data[fdcu].fdu;
+ int baseport = fdc_data[fdcu].baseport;
+ struct buf *bp;
+ int s;
+
+ bp = TAILQ_FIRST(&fdc_data[fdcu].head);
+
+ /*
+ * Due to IBM's brain-dead design, the FDC has a faked ready
+ * signal, hardwired to ready == true. Thus, any command
+ * issued if there's no diskette in the drive will _never_
+ * complete, and must be aborted by resetting the FDC.
+ * Many thanks, Big Blue!
+ */
+
+ s = splbio();
+
+ TRACE1("fd%d[fd_timeout()]", fdu);
+ /* See if the controller is still busy (patiently awaiting data) */
+ if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB)
+ {
+ DELAY(5);
+ TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS));
+ /* yup, it is; kill it now */
+ fdc_reset(&fdc_data[fdcu]);
+ printf("fd%d: Operation timeout\n", fdu);
+ }
+
+ if (bp)
+ {
+ retrier(fdcu);
+ fdc_data[fdcu].status[0] = NE7_ST0_IC_RC;
+ fdc_data[fdcu].state = IOTIMEDOUT;
+ if( fdc_data[fdcu].retry < 6)
+ fdc_data[fdcu].retry = 6;
+ }
+ else
+ {
+ fdc_data[fdcu].fd = (fd_p) 0;
+ fdc_data[fdcu].fdu = -1;
+ fdc_data[fdcu].state = DEVIDLE;
+ }
+ fdintr(fdcu);
+ splx(s);
+}
+
+/* just ensure it has the right spl */
+static void
+fd_pseudointr(void *arg1)
+{
+ fdcu_t fdcu = (fdcu_t)arg1;
+ int s;
+
+ s = splbio();
+ fdintr(fdcu);
+ splx(s);
+}
+
+/***********************************************************************\
+* fdintr *
+* keep calling the state machine until it returns a 0 *
+* ALWAYS called at SPLBIO *
+\***********************************************************************/
+void
+fdintr(fdcu_t fdcu)
+{
+ fdc_p fdc = fdc_data + fdcu;
+#if NFT > 0
+ fdu_t fdu = fdc->fdu;
+
+ if (fdc->flags & FDC_TAPE_BUSY)
+ (ftintr(fdu));
+ else
+#endif
+ while(fdstate(fdcu, fdc))
+ ;
+}
+
+/***********************************************************************\
+* The controller state machine. *
+* if it returns a non zero value, it should be called again immediatly *
+\***********************************************************************/
+static int
+fdstate(fdcu_t fdcu, fdc_p fdc)
+{
+ int read, format, head, sec = 0, sectrac, st0, cyl, st3;
+ unsigned long blknum;
+ fdu_t fdu = fdc->fdu;
+ fd_p fd;
+ register struct buf *bp;
+ struct fd_formb *finfo = NULL;
+ size_t fdblk;
+
+ bp = TAILQ_FIRST(&fdc->head);
+ if(!bp) {
+ /***********************************************\
+ * nothing left for this controller to do *
+ * Force into the IDLE state, *
+ \***********************************************/
+ fdc->state = DEVIDLE;
+ if(fdc->fd)
+ {
+ printf("fd%d: unexpected valid fd pointer\n",
+ fdc->fdu);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ }
+ TRACE1("[fdc%d IDLE]", fdcu);
+ return(0);
+ }
+ fdu = FDUNIT(minor(bp->b_dev));
+ fd = fd_data + fdu;
+ fdblk = 128 << fd->ft->secsize;
+ if (fdc->fd && (fd != fdc->fd))
+ {
+ printf("fd%d: confused fd pointers\n", fdu);
+ }
+ read = bp->b_flags & B_READ;
+ format = bp->b_flags & B_FORMAT;
+ if(format)
+ finfo = (struct fd_formb *)bp->b_un.b_addr;
+ TRACE1("fd%d", fdu);
+ TRACE1("[%s]", fdstates[fdc->state]);
+ TRACE1("(0x%x)", fd->flags);
+ untimeout(fd_turnoff, (caddr_t)fdu);
+ timeout(fd_turnoff, (caddr_t)fdu, 4 * hz);
+ switch (fdc->state)
+ {
+ case DEVIDLE:
+ case FINDWORK: /* we have found new work */
+ fdc->retry = 0;
+ fd->skip = 0;
+ fdc->fd = fd;
+ fdc->fdu = fdu;
+#ifdef PC98
+ pc98_trans = fd->ft->trans;
+ if (pc98_trans_prev != pc98_trans) {
+ int i;
+ set_density(fdcu, fdu);
+ for (i = 0; i < 10; i++) {
+ outb(0x5f, 0);
+ outb(0x5f, 0);
+ }
+ pc98_trans_prev = pc98_trans;
+ }
+ if (pc98_trans != fd->pc98_trans) {
+ if (pc98_trans != 1 &&
+ (PC98_SYSTEM_PARAMETER(0x5ae) >> fdu) & 0x01) {
+ outb(0x4be, (fdu << 5) | 0x10 | (pc98_trans >> 1));
+ outb(0x5f, 0);
+ outb(0x5f, 0);
+ }
+ fd->pc98_trans = pc98_trans;
+ }
+#else
+ outb(fdc->baseport+FDCTL, fd->ft->trans);
+#endif
+ TRACE1("[0x%x->FDCTL]", fd->ft->trans);
+ /*******************************************************\
+ * If the next drive has a motor startup pending, then *
+ * it will start up in it's own good time *
+ \*******************************************************/
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ fdc->state = MOTORWAIT;
+ return(0); /* come back later */
+ }
+ /*******************************************************\
+ * Maybe if it's not starting, it SHOULD be starting *
+ \*******************************************************/
+#ifdef EPSON_NRDISK
+ if (fdu != nrdu) {
+ if (!(fd->flags & FD_MOTOR))
+ {
+ fdc->state = MOTORWAIT;
+ fd_turnon(fdu);
+ return(0);
+ }
+ else /* at least make sure we are selected */
+ {
+ set_motor(fdcu, fd->fdsu, TURNON);
+ }
+ }
+#else /* !EPSON_NRDISK */
+ if (!(fd->flags & FD_MOTOR))
+ {
+ fdc->state = MOTORWAIT;
+ fd_turnon(fdu);
+ return(0);
+ }
+ else /* at least make sure we are selected */
+ {
+ set_motor(fdcu, fd->fdsu, TURNON);
+ }
+#endif
+ fdc->state = DOSEEK;
+ break;
+ case DOSEEK:
+ if (bp->b_cylin == fd->track)
+ {
+ fdc->state = SEEKCOMPLETE;
+ break;
+ }
+#ifdef PC98
+ pc98_fd_check_ready(fdu);
+#endif
+ if (fd_cmd(fdcu, 3, NE7CMD_SEEK,
+ fd->fdsu, bp->b_cylin * fd->ft->steptrac,
+ 0))
+ {
+ /*
+ * seek command not accepted, looks like
+ * the FDC went off to the Saints...
+ */
+ fdc->retry = 6; /* try a reset */
+ return(retrier(fdcu));
+ }
+ fd->track = FD_NO_TRACK;
+ fdc->state = SEEKWAIT;
+ return(0); /* will return later */
+ case SEEKWAIT:
+ /* allow heads to settle */
+ timeout(fd_pseudointr, (caddr_t)fdcu, hz / 16);
+ fdc->state = SEEKCOMPLETE;
+ return(0); /* will return later */
+ case SEEKCOMPLETE : /* SEEK DONE, START DMA */
+ /* Make sure seek really happened*/
+ if(fd->track == FD_NO_TRACK)
+ {
+ int descyl = bp->b_cylin * fd->ft->steptrac;
+ do {
+ /*
+ * This might be a "ready changed" interrupt,
+ * which cannot really happen since the
+ * RDY pin is hardwired to + 5 volts. This
+ * generally indicates a "bouncing" intr
+ * line, so do one of the following:
+ *
+ * When running on an enhanced FDC that is
+ * known to not go stuck after responding
+ * with INVALID, fetch all interrupt states
+ * until seeing either an INVALID or a
+ * real interrupt condition.
+ *
+ * When running on a dumb old NE765, give
+ * up immediately. The controller will
+ * provide up to four dummy RC interrupt
+ * conditions right after reset (for the
+ * corresponding four drives), so this is
+ * our only chance to get notice that it
+ * was not the FDC that caused the interrupt.
+ */
+ if (fd_sense_int(fdc, &st0, &cyl)
+ == FD_NOT_VALID)
+ return 0;
+ if(fdc->fdct == FDC_NE765
+ && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
+ return 0; /* hope for a real intr */
+ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
+
+ if (0 == descyl)
+ {
+ int failed = 0;
+ /*
+ * seek to cyl 0 requested; make sure we are
+ * really there
+ */
+ if (fd_sense_drive_status(fdc, &st3))
+ failed = 1;
+#ifdef EPSON_NRDISK
+ if (fdu == nrdu) st3 = NE7_ST3_T0;
+#endif /* EPSON_NRDISK */
+ if ((st3 & NE7_ST3_T0) == 0) {
+ printf(
+ "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n",
+ fdu, st3, NE7_ST3BITS);
+ failed = 1;
+ }
+
+ if (failed)
+ {
+ if(fdc->retry < 3)
+ fdc->retry = 3;
+ return(retrier(fdcu));
+ }
+ }
+#ifdef EPSON_NRDISK
+ if (fdu == nrdu) cyl = descyl;
+#endif
+
+ if (cyl != descyl)
+ {
+ printf(
+ "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n",
+ fdu, descyl, cyl, st0);
+ return(retrier(fdcu));
+ }
+ }
+
+ fd->track = bp->b_cylin;
+ if(format)
+ fd->skip = (char *)&(finfo->fd_formb_cylno(0))
+ - (char *)finfo;
+#ifdef EPSON_NRDISK
+ if (fdu != nrdu) {
+#endif /* EPSON_NRDISK */
+#ifdef PC98
+ pc98_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
+#else
+ isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip,
+#endif
+ format ? bp->b_bcount : fdblk, fdc->dmachan);
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk
+ + fd->skip/fdblk;
+ sectrac = fd->ft->sectrac;
+ sec = blknum % (sectrac * fd->ft->heads);
+ head = sec / sectrac;
+ sec = sec % sectrac + 1;
+ fd->hddrv = ((head&1)<<2)+fdu;
+ if(format || !read)
+ {
+ /* make sure the drive is writable */
+ if(fd_sense_drive_status(fdc, &st3) != 0)
+ {
+ /* stuck controller? */
+ fdc->retry = 6; /* reset the beast */
+ return(retrier(fdcu));
+ }
+ if(st3 & NE7_ST3_WP)
+ {
+ /*
+ * XXX YES! this is ugly.
+ * in order to force the current operation
+ * to fail, we will have to fake an FDC
+ * error - all error handling is done
+ * by the retrier()
+ */
+ fdc->status[0] = NE7_ST0_IC_AT;
+ fdc->status[1] = NE7_ST1_NW;
+ fdc->status[2] = 0;
+ fdc->status[3] = fd->track;
+ fdc->status[4] = head;
+ fdc->status[5] = sec;
+ fdc->retry = 8; /* break out immediately */
+ fdc->state = IOTIMEDOUT; /* not really... */
+ return (1);
+ }
+ }
+
+ if(format)
+ {
+ /* formatting */
+ if(fd_cmd(fdcu, 6,
+ NE7CMD_FORMAT,
+ head << 2 | fdu,
+ finfo->fd_formb_secshift,
+ finfo->fd_formb_nsecs,
+ finfo->fd_formb_gaplen,
+ finfo->fd_formb_fillbyte,
+ 0))
+ {
+ /* controller fell over */
+ fdc->retry = 6;
+ return(retrier(fdcu));
+ }
+ }
+ else
+ {
+ if (fd_cmd(fdcu, 9,
+ (read ? NE7CMD_READ : NE7CMD_WRITE),
+ head << 2 | fdu, /* head & unit */
+ fd->track, /* track */
+ head,
+ sec, /* sector + 1 */
+ fd->ft->secsize, /* sector size */
+ sectrac, /* sectors/track */
+ fd->ft->gap, /* gap size */
+ fd->ft->datalen, /* data length */
+ 0))
+ {
+ /* the beast is sleeping again */
+ fdc->retry = 6;
+ return(retrier(fdcu));
+ }
+ }
+ fdc->state = IOCOMPLETE;
+ timeout(fd_timeout, (caddr_t)fdcu, hz);
+ return(0); /* will return later */
+#ifdef EPSON_NRDISK
+ }
+ else {
+ nrdblkn = (nrd_t)((unsigned long)bp->b_blkno*DEV_BSIZE/fdblk
+ + fd->skip/fdblk);
+ nrd_LED_on();
+ nrd_addrset(fdblk * nrdblkn);
+ while (!nrd_check_ready()) DELAY(1);
+ if (read) epson_insw(P_NRD_DATA,
+ bp->b_un.b_addr + fd->skip,
+ fdblk / sizeof(short));
+ else epson_outsw(P_NRD_DATA,
+ bp->b_un.b_addr + fd->skip,
+ (format ? bp->b_bcount : fdblk)
+ / sizeof(short));
+
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk
+ + fd->skip/fdblk;
+ sectrac = fd->ft->sectrac;
+ sec = blknum % (sectrac * fd->ft->heads);
+ head = sec / sectrac;
+ sec = sec % sectrac + 1;
+ fd->hddrv = ((head&1)<<2)+fdu;
+
+ if (nrdsec++ >= nrd_sec())
+ nrdaddr = (nrd_t)(fd->track * 8 + head * 4);
+ nrdsec = sec;
+ fdc->state = IOCOMPLETE;
+ }
+#endif
+ case IOCOMPLETE: /* IO DONE, post-analyze */
+#ifdef EPSON_NRDISK
+ if (fdu != nrdu) untimeout(fd_timeout, (caddr_t)fdcu);
+#else
+ untimeout(fd_timeout, (caddr_t)fdcu);
+#endif
+
+ if (fd_read_status(fdc, fd->fdsu))
+ {
+ if (fdc->retry < 6)
+ fdc->retry = 6; /* force a reset */
+ return retrier(fdcu);
+ }
+
+ fdc->state = IOTIMEDOUT;
+
+ /* FALLTHROUGH */
+
+ case IOTIMEDOUT:
+#ifdef EPSON_NRDISK
+ if (fdu != nrdu) {
+#endif /* EPSON_NRDISK */
+#ifdef PC98
+ pc98_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
+#else
+ isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip,
+#endif
+ format ? bp->b_bcount : fdblk, fdc->dmachan);
+#ifdef EPSON_NRDISK
+ }
+ else nrd_LED_off();
+#endif /* EPSON_NRDISK */
+ if (fdc->status[0] & NE7_ST0_IC)
+ {
+ if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
+ && fdc->status[1] & NE7_ST1_OR) {
+ /*
+ * DMA overrun. Someone hogged the bus
+ * and didn't release it in time for the
+ * next FDC transfer.
+ * Just restart it, don't increment retry
+ * count. (vak)
+ */
+ fdc->state = SEEKCOMPLETE;
+ return (1);
+ }
+ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV
+ && fdc->retry < 6)
+ fdc->retry = 6; /* force a reset */
+ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT
+ && fdc->status[2] & NE7_ST2_WC
+ && fdc->retry < 3)
+ fdc->retry = 3; /* force recalibrate */
+ return(retrier(fdcu));
+ }
+ /* All OK */
+ fd->skip += fdblk;
+ if (!format && fd->skip < bp->b_bcount)
+ {
+ /* set up next transfer */
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk
+ + fd->skip/fdblk;
+#ifdef EPSON_NRDISK
+ nrdblkn = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk
+ + fd->skip/fdblk;
+#endif
+ bp->b_cylin =
+ (blknum / (fd->ft->sectrac * fd->ft->heads));
+ fdc->state = DOSEEK;
+ }
+ else
+ {
+ /* ALL DONE */
+ fd->skip = 0;
+ bp->b_resid = 0;
+ TAILQ_REMOVE(&fdc->head, bp, b_act);
+ biodone(bp);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ fdc->state = FINDWORK;
+ }
+ return(1);
+ case RESETCTLR:
+ fdc_reset(fdc);
+ fdc->retry++;
+ fdc->state = STARTRECAL;
+ break;
+ case STARTRECAL:
+ /* XXX clear the fdc results from the last reset, if any. */
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ (void)fd_sense_int(fdc, &st0, &cyl);
+ }
+
+#ifdef PC98
+ pc98_fd_check_ready(fdu);
+#endif
+ if(fd_cmd(fdcu,
+ 2, NE7CMD_RECAL, fdu,
+ 0)) /* Recalibrate Function */
+ {
+ /* arrgl */
+ fdc->retry = 6;
+ return(retrier(fdcu));
+ }
+ fdc->state = RECALWAIT;
+ return(0); /* will return later */
+ case RECALWAIT:
+ /* allow heads to settle */
+ timeout(fd_pseudointr, (caddr_t)fdcu, hz / 8);
+ fdc->state = RECALCOMPLETE;
+ return(0); /* will return later */
+ case RECALCOMPLETE:
+ do {
+ /*
+ * See SEEKCOMPLETE for a comment on this:
+ */
+ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID)
+ return 0;
+ if(fdc->fdct == FDC_NE765
+ && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC)
+ return 0; /* hope for a real intr */
+ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC);
+#ifdef EPSON_NRDISK
+ if (fdu == nrdu) {
+ st0 = NE7_ST0_IC_NT;
+ cyl = 0;
+ }
+#endif
+ if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0)
+ {
+ if(fdc->retry > 3)
+ /*
+ * a recalibrate from beyond cylinder 77
+ * will "fail" due to the FDC limitations;
+ * since people used to complain much about
+ * the failure message, try not logging
+ * this one if it seems to be the first
+ * time in a line
+ */
+ printf("fd%d: recal failed ST0 %b cyl %d\n",
+ fdu, st0, NE7_ST0BITS, cyl);
+ if(fdc->retry < 3) fdc->retry = 3;
+ return(retrier(fdcu));
+ }
+ fd->track = 0;
+ /* Seek (probably) necessary */
+ fdc->state = DOSEEK;
+ return(1); /* will return immediatly */
+ case MOTORWAIT:
+ if(fd->flags & FD_MOTOR_WAIT)
+ {
+ return(0); /* time's not up yet */
+ }
+ /*
+ * since the controller was off, it has lost its
+ * idea about the current track it were; thus,
+ * recalibrate the bastard
+ */
+ fdc->state = STARTRECAL;
+ return(1); /* will return immediatly */
+ default:
+ printf("fdc%d: Unexpected FD int->", fdcu);
+ if (fd_read_status(fdc, fd->fdsu) == 0)
+ printf("FDC status :%lx %lx %lx %lx %lx %lx %lx ",
+ fdc->status[0],
+ fdc->status[1],
+ fdc->status[2],
+ fdc->status[3],
+ fdc->status[4],
+ fdc->status[5],
+ fdc->status[6] );
+ else
+ printf("No status available ");
+ if (fd_sense_int(fdc, &st0, &cyl) != 0)
+ {
+ printf("[controller is dead now]\n");
+ return(0);
+ }
+ printf("ST0 = %x, PCN = %x\n", st0, cyl);
+ return(0);
+ }
+ /*XXX confusing: some branches return immediately, others end up here*/
+ return(1); /* Come back immediatly to new state */
+}
+
+static int
+retrier(fdcu)
+ fdcu_t fdcu;
+{
+ fdc_p fdc = fdc_data + fdcu;
+ register struct buf *bp;
+
+ bp = TAILQ_FIRST(&fdc->head);
+
+ if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY)
+ goto fail;
+ switch(fdc->retry)
+ {
+ case 0: case 1: case 2:
+ fdc->state = SEEKCOMPLETE;
+ break;
+ case 3: case 4: case 5:
+ fdc->state = STARTRECAL;
+ break;
+ case 6:
+ fdc->state = RESETCTLR;
+ break;
+ case 7:
+ break;
+ default:
+ fail:
+ {
+ dev_t sav_b_dev = bp->b_dev;
+ /* Trick diskerr */
+ bp->b_dev = makedev(major(bp->b_dev),
+ (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART);
+ diskerr(bp, "fd", "hard error", LOG_PRINTF,
+ fdc->fd->skip / DEV_BSIZE,
+ (struct disklabel *)NULL);
+ bp->b_dev = sav_b_dev;
+ if (fdc->flags & FDC_STAT_VALID)
+ {
+ printf(
+ " (ST0 %b ST1 %b ST2 %b cyl %ld hd %ld sec %ld)\n",
+ fdc->status[0], NE7_ST0BITS,
+ fdc->status[1], NE7_ST1BITS,
+ fdc->status[2], NE7_ST2BITS,
+ fdc->status[3], fdc->status[4],
+ fdc->status[5]);
+ }
+ else
+ printf(" (No status)\n");
+ }
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ bp->b_resid = bp->b_bcount - fdc->fd->skip;
+ TAILQ_REMOVE(&fdc->head, bp, b_act);
+ fdc->fd->skip = 0;
+ biodone(bp);
+ fdc->state = FINDWORK;
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ /* XXX abort current command, if any. */
+ return(1);
+ }
+ fdc->retry++;
+ return(1);
+}
+
+static int
+fdformat(dev, finfo, p)
+ dev_t dev;
+ struct fd_formb *finfo;
+ struct proc *p;
+{
+ fdu_t fdu;
+ fd_p fd;
+
+ struct buf *bp;
+ int rv = 0, s;
+ size_t fdblk;
+
+ fdu = FDUNIT(minor(dev));
+ fd = &fd_data[fdu];
+ fdblk = 128 << fd->ft->secsize;
+
+ /* set up a buffer header for fdstrategy() */
+ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
+ if(bp == 0)
+ return ENOBUFS;
+ /*
+ * keep the process from being swapped
+ */
+ p->p_flag |= P_PHYSIO;
+ bzero((void *)bp, sizeof(struct buf));
+ bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
+ bp->b_proc = p;
+ bp->b_dev = dev;
+
+ /*
+ * calculate a fake blkno, so fdstrategy() would initiate a
+ * seek to the requested cylinder
+ */
+ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
+ + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
+
+ bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
+ bp->b_un.b_addr = (caddr_t)finfo;
+
+ /* now do the format */
+ fdstrategy(bp);
+
+ /* ...and wait for it to complete */
+ s = splbio();
+ while(!(bp->b_flags & B_DONE))
+ {
+ rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz);
+ if(rv == EWOULDBLOCK)
+ break;
+ }
+ splx(s);
+
+ if(rv == EWOULDBLOCK) {
+ /* timed out */
+ rv = EIO;
+ biodone(bp);
+ }
+ if(bp->b_flags & B_ERROR)
+ rv = bp->b_error;
+ /*
+ * allow the process to be swapped
+ */
+ p->p_flag &= ~P_PHYSIO;
+ free(bp, M_TEMP);
+ return rv;
+}
+
+/*
+ * TODO: don't allocate buffer on stack.
+ */
+
+int
+fdioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ fd_p fd = &fd_data[fdu];
+ size_t fdblk;
+
+ struct fd_type *fdt;
+ struct disklabel *dl;
+ char buffer[DEV_BSIZE];
+ int error = 0;
+
+#if NFT > 0
+ int type = FDTYPE(minor(dev));
+
+ /* check for a tape ioctl */
+ if (type & F_TAPE_TYPE)
+ return ftioctl(dev, cmd, addr, flag, p);
+#endif
+
+ fdblk = 128 << fd->ft->secsize;
+
+#ifdef PC98
+ pc98_fd_check_ready(fdu);
+#endif
+ switch (cmd)
+ {
+ case DIOCGDINFO:
+ bzero(buffer, sizeof (buffer));
+ dl = (struct disklabel *)buffer;
+ dl->d_secsize = fdblk;
+ fdt = fd_data[FDUNIT(minor(dev))].ft;
+ dl->d_secpercyl = fdt->size / fdt->tracks;
+ dl->d_type = DTYPE_FLOPPY;
+
+ if (readdisklabel(dkmodpart(dev, RAW_PART), fdstrategy, dl)
+ == NULL)
+ error = 0;
+ else
+ error = EINVAL;
+
+ *(struct disklabel *)addr = *dl;
+ break;
+
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ break;
+
+ case DIOCWLABEL:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ break;
+
+ case DIOCWDINFO:
+ if ((flag & FWRITE) == 0)
+ {
+ error = EBADF;
+ break;
+ }
+
+ dl = (struct disklabel *)addr;
+
+ if ((error = setdisklabel((struct disklabel *)buffer, dl,
+ (u_long)0)) != 0)
+ break;
+
+ error = writedisklabel(dev, fdstrategy,
+ (struct disklabel *)buffer);
+ break;
+
+ case FD_FORM:
+ if((flag & FWRITE) == 0)
+ error = EBADF; /* must be opened for writing */
+ else if(((struct fd_formb *)addr)->format_version !=
+ FD_FORMAT_VERSION)
+ error = EINVAL; /* wrong version of formatting prog */
+ else
+ error = fdformat(dev, (struct fd_formb *)addr, p);
+ break;
+
+ case FD_GTYPE: /* get drive type */
+ *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft;
+ break;
+
+ case FD_STYPE: /* set drive type */
+ /* this is considered harmful; only allow for superuser */
+ if(suser(p->p_ucred, &p->p_acflag) != 0)
+ return EPERM;
+ *fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr;
+ break;
+
+ case FD_GOPTS: /* get drive options */
+ *(int *)addr = fd_data[FDUNIT(minor(dev))].options;
+ break;
+
+ case FD_SOPTS: /* set drive options */
+ fd_data[FDUNIT(minor(dev))].options = *(int *)addr;
+ break;
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+
+static fd_devsw_installed = 0;
+
+static void fd_drvinit(void *notused )
+{
+ dev_t dev;
+
+ if( ! fd_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev,&fd_cdevsw, NULL);
+ dev = makedev(BDEV_MAJOR, 0);
+ bdevsw_add(&dev,&fd_bdevsw, NULL);
+ fd_devsw_installed = 1;
+ }
+}
+
+SYSINIT(fddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,fd_drvinit,NULL)
+
+#endif
+/*
+ * Hello emacs, these are the
+ * Local Variables:
+ * c-indent-level: 8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * c-brace-offset: -8
+ * c-brace-imaginary-offset: 0
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c++-hanging-braces: 1
+ * c++-access-specifier-offset: -8
+ * c++-empty-arglist-indent: 8
+ * c++-friend-offset: 0
+ * End:
+ */
diff --git a/sys/pc98/cbus/fdcreg.h b/sys/pc98/cbus/fdcreg.h
new file mode 100644
index 0000000..7b10161
--- /dev/null
+++ b/sys/pc98/cbus/fdcreg.h
@@ -0,0 +1,102 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91
+ * $Id: fdreg.h,v 1.8 1994/09/25 23:37:38 phk Exp $
+ */
+
+/*
+ * AT floppy controller registers and bitfields
+ */
+
+#ifdef PC98
+/* uses NEC765 controller */
+#include <pc98/pc98/ic/nec765.h>
+
+#define FDSTS 0 /* NEC 765 Main Status Register (R) */
+#define FDDATA 2 /* NEC 765 Data Register (R/W) */
+
+/* registers */
+#define FDOUT 4 /* Digital Output Register (W) */
+#define FDO_RST 0x80 /* FDC RESET */
+#define FDO_FRY 0x40 /* force READY */
+#define FDO_AIE 0x20 /* Attention Interrupt Enable */
+#define FDO_DD 0x20 /* FDD Mode Exchange 0:1M 1:640K */
+#define FDO_DMAE 0x10 /* enable floppy DMA */
+#define FDO_MTON 0x08 /* MOTOR ON (when EMTON=1)*/
+#define FDO_TMSK 0x04 /* TIMER MASK */
+#define FDO_TTRG 0x01 /* TIMER TRIGER */
+
+#define FDIN 4 /* Digital Input Register (R) */
+#define FDI_TYP0 0x04 /* FDD #1/#2 TYPE */
+#define FDI_TYP1 0x08 /* FDD #3/#4 TYPE */
+#define FDI_RDY 0x10 /* Ready */
+#define FDI_DMACH 0x20 /* DMA Channel */
+#define FDI_FINT0 0x40 /* Interrupt */
+#define FDI_FINT1 0x80 /* Interrupt */
+
+#define FDP_EMTON 0x04 /* enable MTON */
+#define FDP_FDDEXC 0x02 /* FDD Mode Exchange 1:1M 0:640K */
+#define FDP_PORTEXC 0x01 /* PORT Exchane 1:1M 0:640K */
+
+#else
+
+/* uses NEC765 controller */
+#include <i386/isa/ic/nec765.h>
+
+/* registers */
+#define FDOUT 2 /* Digital Output Register (W) */
+#define FDO_FDSEL 0x03 /* floppy device select */
+#define FDO_FRST 0x04 /* floppy controller reset */
+#define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */
+#define FDO_MOEN0 0x10 /* motor enable drive 0 */
+#define FDO_MOEN1 0x20 /* motor enable drive 1 */
+#define FDO_MOEN2 0x40 /* motor enable drive 2 */
+#define FDO_MOEN3 0x80 /* motor enable drive 3 */
+
+#define FDSTS 4 /* NEC 765 Main Status Register (R) */
+#define FDDATA 5 /* NEC 765 Data Register (R/W) */
+#define FDCTL 7 /* Control Register (W) */
+
+#ifndef FDC_500KBPS
+# define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */
+# define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */
+# define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */
+# define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */
+ /* for some controllers 1MPBS instead */
+#endif /* FDC_500KBPS */
+
+#define FDIN 7 /* Digital Input Register (R) */
+#define FDI_DCHG 0x80 /* diskette has been changed */
+ /* requires drive and motor being selected */
+ /* is cleared by any step pulse to drive */
+#endif
diff --git a/sys/pc98/cbus/pcrtc.c b/sys/pc98/cbus/pcrtc.c
new file mode 100644
index 0000000..b2befbf
--- /dev/null
+++ b/sys/pc98/cbus/pcrtc.c
@@ -0,0 +1,1129 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)clock.c 7.2 (Berkeley) 5/12/91
+ * $Id: clock.c,v 1.58 1996/05/01 08:39:02 bde Exp $
+ */
+
+/*
+ * inittodr, settodr and support routines written
+ * by Christoph Robitschko <chmr@edvz.tu-graz.ac.at>
+ *
+ * reintroduced and updated by Chris Stenton <chris@gnome.co.uk> 8/10/94
+ */
+
+/*
+ * modified for PC98
+ * $Id: clock.c,v 1.7 1994/03/26 22:56:13 kakefuda Exp kakefuda $
+ */
+
+/*
+ * Primitive clock interrupt routines.
+ */
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
+#include <machine/clock.h>
+#ifdef CLK_CALIBRATION_LOOP
+#include <machine/cons.h>
+#endif
+#include <machine/cpu.h>
+#include <machine/frame.h>
+
+#ifdef PC98
+#include <sys/syslog.h>
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/timerreg.h>
+#else
+#include <i386/isa/icu.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/rtc.h>
+#include <i386/isa/timerreg.h>
+#endif
+
+/*
+ * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we
+ * can use a simple formula for leap years.
+ */
+#define LEAPYEAR(y) ((u_int)(y) % 4 == 0)
+#define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31)
+
+#define TIMER_DIV(x) ((timer_freq + (x) / 2) / (x))
+
+/*
+ * Time in timer cycles that it takes for microtime() to disable interrupts
+ * and latch the count. microtime() currently uses "cli; outb ..." so it
+ * normally takes less than 2 timer cycles. Add a few for cache misses.
+ * Add a few more to allow for latency in bogus calls to microtime() with
+ * interrupts already disabled.
+ */
+#define TIMER0_LATCH_COUNT 20
+
+/*
+ * Minimum maximum count that we are willing to program into timer0.
+ * Must be large enough to guarantee that the timer interrupt handler
+ * returns before the next timer interrupt. Must be larger than
+ * TIMER0_LATCH_COUNT so that we don't have to worry about underflow in
+ * the calculation of timer0_overflow_threshold.
+ */
+#define TIMER0_MIN_MAX_COUNT TIMER_DIV(20000)
+
+int adjkerntz; /* local offset from GMT in seconds */
+int disable_rtc_set; /* disable resettodr() if != 0 */
+int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */
+
+u_int idelayed;
+#if defined(I586_CPU) || defined(I686_CPU)
+unsigned i586_ctr_freq;
+unsigned i586_ctr_rate;
+long long i586_ctr_bias;
+long long i586_last_tick;
+unsigned long i586_avg_tick;
+#endif
+int statclock_disable;
+u_int stat_imask = SWI_CLOCK_MASK;
+int timer0_max_count;
+u_int timer0_overflow_threshold;
+u_int timer0_prescaler_count;
+
+static int beeping = 0;
+static u_int clk_imask = HWI_MASK | SWI_MASK;
+static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
+static u_int hardclock_max_count;
+/*
+ * XXX new_function and timer_func should not handle clockframes, but
+ * timer_func currently needs to hold hardclock to handle the
+ * timer0_state == 0 case. We should use register_intr()/unregister_intr()
+ * to switch between clkintr() and a slightly different timerintr().
+ * This will require locking when acquiring and releasing timer0 - the
+ * current (nonexistent) locking doesn't seem to be adequate even now.
+ */
+static void (*new_function) __P((struct clockframe *frame));
+static u_int new_rate;
+#ifndef PC98
+static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
+static u_char rtc_statusb = RTCSB_24HR | RTCSB_PINTR;
+#endif
+#ifdef TIMER_FREQ
+static u_int timer_freq = TIMER_FREQ;
+#else
+#ifdef PC98
+#ifndef AUTO_CLOCK
+#ifndef PC98_8M
+static u_int timer_freq = 2457600;
+#else /* !PC98_8M */
+static u_int timer_freq = 1996800;
+#endif /* PC98_8M */
+#else /* AUTO_CLOCK */
+static u_int timer_freq = 2457600;
+#endif /* AUTO_CLOCK */
+#else /* IBM-PC */
+static u_int timer_freq = 1193182;
+#endif /* PC98 */
+#endif
+static char timer0_state = 0;
+#ifdef PC98
+static char timer1_state = 0;
+#endif
+static char timer2_state = 0;
+static void (*timer_func) __P((struct clockframe *frame)) = hardclock;
+int rtc_inb __P((void));
+
+#if 0
+void
+clkintr(struct clockframe frame)
+{
+ hardclock(&frame);
+ setdelayed();
+}
+#else
+static void
+clkintr(struct clockframe frame)
+{
+ timer_func(&frame);
+ switch (timer0_state) {
+ case 0:
+ setdelayed();
+ break;
+ case 1:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ hardclock(&frame);
+ setdelayed();
+ timer0_prescaler_count -= hardclock_max_count;
+ }
+ break;
+ case 2:
+ setdelayed();
+ timer0_max_count = TIMER_DIV(new_rate);
+ timer0_overflow_threshold =
+ timer0_max_count - TIMER0_LATCH_COUNT;
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ enable_intr();
+ timer0_prescaler_count = 0;
+ timer_func = new_function;
+ timer0_state = 1;
+ break;
+ case 3:
+ if ((timer0_prescaler_count += timer0_max_count)
+ >= hardclock_max_count) {
+ hardclock(&frame);
+ setdelayed();
+ timer0_max_count = hardclock_max_count;
+ timer0_overflow_threshold =
+ timer0_max_count - TIMER0_LATCH_COUNT;
+ disable_intr();
+ outb(TIMER_MODE,
+ TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ enable_intr();
+ /*
+ * See microtime.s for this magic.
+ */
+#ifdef PC98
+#ifndef AUTO_CLOCK
+#ifndef PC98_8M
+ time.tv_usec += (6667 *
+ (timer0_prescaler_count - hardclock_max_count))
+ >> 14;
+#else /* PC98_8M */
+ time.tv_usec += (16411 *
+ (timer0_prescaler_count - hardclock_max_count))
+ >> 15;
+#endif /* PC98_8M */
+#else /* AUTO_CLOCK */
+ if (pc98_machine_type & M_8M) {
+ /* PC98_8M */
+ time.tv_usec += (16411 *
+ (timer0_prescaler_count -
+ hardclock_max_count)) >> 15;
+ } else {
+ time.tv_usec += (6667 *
+ (timer0_prescaler_count -
+ hardclock_max_count)) >> 14;
+ }
+#endif /* AUTO_CLOCK */
+#else /* IBM-PC */
+ time.tv_usec += (27645 *
+ (timer0_prescaler_count - hardclock_max_count))
+ >> 15;
+#endif /* PC98 */
+ if (time.tv_usec >= 1000000)
+ time.tv_usec -= 1000000;
+ timer0_prescaler_count = 0;
+ timer_func = hardclock;;
+ timer0_state = 0;
+ }
+ break;
+ }
+}
+#endif
+
+int
+acquire_timer0(int rate, void (*function) __P((struct clockframe *frame)))
+{
+ if (timer0_state || TIMER_DIV(rate) < TIMER0_MIN_MAX_COUNT ||
+ !function)
+ return -1;
+ new_function = function;
+ new_rate = rate;
+ timer0_state = 2;
+ return 0;
+}
+
+#ifdef PC98
+int
+acquire_timer1(int mode)
+{
+ if (timer1_state)
+ return -1;
+ timer1_state = 1;
+ outb(TIMER_MODE, TIMER_SEL1 | (mode &0x3f));
+ return 0;
+}
+#endif
+
+int
+acquire_timer2(int mode)
+{
+ if (timer2_state)
+ return -1;
+ timer2_state = 1;
+ outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f));
+ return 0;
+}
+
+int
+release_timer0()
+{
+ if (!timer0_state)
+ return -1;
+ timer0_state = 3;
+ return 0;
+}
+
+#ifdef PC98
+int
+release_timer1()
+{
+ if (!timer1_state)
+ return -1;
+ timer1_state = 0;
+ outb(TIMER_MODE, TIMER_SEL1|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+#endif
+
+int
+release_timer2()
+{
+ if (!timer2_state)
+ return -1;
+ timer2_state = 0;
+ outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT);
+ return 0;
+}
+
+#ifndef PC98
+/*
+ * This routine receives statistical clock interrupts from the RTC.
+ * As explained above, these occur at 128 interrupts per second.
+ * When profiling, we receive interrupts at a rate of 1024 Hz.
+ *
+ * This does not actually add as much overhead as it sounds, because
+ * when the statistical clock is active, the hardclock driver no longer
+ * needs to keep (inaccurate) statistics on its own. This decouples
+ * statistics gathering from scheduling interrupts.
+ *
+ * The RTC chip requires that we read status register C (RTC_INTR)
+ * to acknowledge an interrupt, before it will generate the next one.
+ */
+static void
+rtcintr(struct clockframe frame)
+{
+ u_char stat;
+ stat = rtcin(RTC_INTR);
+ if(stat & RTCIR_PERIOD) {
+ statclock(&frame);
+ }
+}
+
+#ifdef DDB
+static void
+DDB_printrtc(void)
+{
+ printf("%02x/%02x/%02x %02x:%02x:%02x, A = %02x, B = %02x, C = %02x\n",
+ rtcin(RTC_YEAR), rtcin(RTC_MONTH), rtcin(RTC_DAY),
+ rtcin(RTC_HRS), rtcin(RTC_MIN), rtcin(RTC_SEC),
+ rtcin(RTC_STATUSA), rtcin(RTC_STATUSB), rtcin(RTC_INTR));
+}
+#endif
+#endif /* for PC98 */
+
+static int
+getit(void)
+{
+ int high, low;
+
+ disable_intr();
+ /* select timer0 and latch counter value */
+ outb(TIMER_MODE, TIMER_SEL0);
+ low = inb(TIMER_CNTR0);
+ high = inb(TIMER_CNTR0);
+ enable_intr();
+ return ((high << 8) | low);
+}
+
+/*
+ * Wait "n" microseconds.
+ * Relies on timer 1 counting down from (timer_freq / hz)
+ * Note: timer had better have been programmed before this is first used!
+ */
+void
+DELAY(int n)
+{
+ int prev_tick, tick, ticks_left, sec, usec;
+
+#ifdef DELAYDEBUG
+ int getit_calls = 1;
+ int n1;
+ static int state = 0;
+
+ if (state == 0) {
+ state = 1;
+ for (n1 = 1; n1 <= 10000000; n1 *= 10)
+ DELAY(n1);
+ state = 2;
+ }
+ if (state == 1)
+ printf("DELAY(%d)...", n);
+#endif
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted. Guess the initial overhead is 20 usec (on most systems it
+ * takes about 1.5 usec for each of the i/o's in getit(). The loop
+ * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The
+ * multiplications and divisions to scale the count take a while).
+ */
+ prev_tick = getit();
+ n -= 20;
+ /*
+ * Calculate (n * (timer_freq / 1e6)) without using floating point
+ * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point
+ * and without any avoidable overflows.
+ */
+ sec = n / 1000000;
+ usec = n - sec * 1000000;
+ ticks_left = sec * timer_freq
+ + usec * (timer_freq / 1000000)
+ + usec * ((timer_freq % 1000000) / 1000) / 1000
+ + usec * (timer_freq % 1000) / 1000000;
+ if (n < 0)
+ ticks_left = 0; /* XXX timer_freq is unsigned */
+
+ while (ticks_left > 0) {
+ tick = getit();
+#ifdef DELAYDEBUG
+ ++getit_calls;
+#endif
+ if (tick > prev_tick)
+ ticks_left -= prev_tick - (tick - timer0_max_count);
+ else
+ ticks_left -= prev_tick - tick;
+ prev_tick = tick;
+ }
+#ifdef DELAYDEBUG
+ if (state == 1)
+ printf(" %d calls to getit() at %d usec each\n",
+ getit_calls, (n + 5) / getit_calls);
+#endif
+}
+
+static void
+sysbeepstop(void *chan)
+{
+#ifdef PC98 /* PC98 */
+ outb(IO_PPI, inb(IO_PPI)|0x08); /* disable counter1 output to speaker */
+ release_timer1();
+#else
+ outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */
+ release_timer2();
+#endif
+ beeping = 0;
+}
+
+int
+sysbeep(int pitch, int period)
+{
+#ifdef PC98
+ if (acquire_timer1(TIMER_SQWAVE|TIMER_16BIT))
+ return -1;
+ disable_intr();
+ outb(0x3fdb, pitch);
+ outb(0x3fdb, (pitch>>8));
+ enable_intr();
+ if (!beeping) {
+ outb(IO_PPI, (inb(IO_PPI) & 0xf7)); /* enable counter1 output to speaker */
+ beeping = period;
+ timeout(sysbeepstop, (void *)NULL, period);
+ }
+#else
+
+ if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT))
+ return -1;
+ disable_intr();
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+ enable_intr();
+ if (!beeping) {
+ outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */
+ beeping = period;
+ timeout(sysbeepstop, (void *)NULL, period);
+ }
+#endif
+ return 0;
+}
+
+#ifndef PC98
+/*
+ * RTC support routines
+ */
+
+int
+rtcin(reg)
+ int reg;
+{
+ u_char val;
+
+ outb(IO_RTC, reg);
+ inb(0x84);
+ val = inb(IO_RTC + 1);
+ inb(0x84);
+ return (val);
+}
+
+static __inline void
+writertc(u_char reg, u_char val)
+{
+ outb(IO_RTC, reg);
+ outb(IO_RTC + 1, val);
+}
+
+static __inline int
+readrtc(int port)
+{
+ return(bcd2bin(rtcin(port)));
+}
+#endif
+
+#ifdef PC98
+unsigned int delaycount;
+#define FIRST_GUESS 0x2000
+static void findcpuspeed(void)
+{
+ int i;
+ int remainder;
+
+ /* Put counter in count down mode */
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
+ outb(TIMER_CNTR0, 0xff);
+ outb(TIMER_CNTR0, 0xff);
+ for (i = FIRST_GUESS; i; i--)
+ ;
+ remainder = getit();
+ delaycount = (FIRST_GUESS * TIMER_DIV(1000)) / (0xffff - remainder);
+}
+#endif
+
+#ifndef PC98
+static u_int
+calibrate_clocks(void)
+{
+ u_int count, prev_count, tot_count;
+ int sec, start_sec, timeout;
+
+ printf("Calibrating clock(s) relative to mc146818A clock ... ");
+ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
+ goto fail;
+ timeout = 100000000;
+
+ /* Read the mc146818A seconds counter. */
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Wait for the mC146818A seconds counter to change. */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP)) {
+ sec = rtcin(RTC_SEC);
+ if (sec != start_sec)
+ break;
+ }
+ if (--timeout == 0)
+ goto fail;
+ }
+
+ /* Start keeping track of the i8254 counter. */
+ prev_count = getit();
+ if (prev_count == 0 || prev_count > timer0_max_count)
+ goto fail;
+ tot_count = 0;
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)
+ wrmsr(0x10, 0LL); /* XXX 0x10 is the MSR for the TSC */
+#endif
+
+ /*
+ * Wait for the mc146818A seconds counter to change. Read the i8254
+ * counter for each iteration since this is convenient and only
+ * costs a few usec of inaccuracy. The timing of the final reads
+ * of the counters almost matches the timing of the initial reads,
+ * so the main cause of inaccuracy is the varying latency from
+ * inside getit() or rtcin(RTC_STATUSA) to the beginning of the
+ * rtcin(RTC_SEC) that returns a changed seconds count. The
+ * maximum inaccuracy from this cause is < 10 usec on 486's.
+ */
+ start_sec = sec;
+ for (;;) {
+ if (!(rtcin(RTC_STATUSA) & RTCSA_TUP))
+ sec = rtcin(RTC_SEC);
+ count = getit();
+ if (count == 0 || count > timer0_max_count)
+ goto fail;
+ if (count > prev_count)
+ tot_count += prev_count - (count - timer0_max_count);
+ else
+ tot_count += prev_count - count;
+ prev_count = count;
+ if (sec != start_sec)
+ break;
+ if (--timeout == 0)
+ goto fail;
+ }
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ /*
+ * Read the cpu cycle counter. The timing considerations are
+ * similar to those for the i8254 clock.
+ */
+ if (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686) {
+ unsigned long long i586_count;
+
+ i586_count = rdtsc();
+ i586_ctr_freq = i586_count;
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz, ", i586_ctr_freq);
+ }
+#endif
+
+ printf("i8254 clock: %u Hz\n", tot_count);
+ return (tot_count);
+
+fail:
+ printf("failed, using default i8254 clock of %u Hz\n", timer_freq);
+ return (timer_freq);
+}
+#endif /* !PC98 */
+
+static void
+set_timer_freq(u_int freq, int intr_freq)
+{
+ u_long ef;
+
+ ef = read_eflags();
+ timer_freq = freq;
+ timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq);
+ timer0_overflow_threshold = timer0_max_count - TIMER0_LATCH_COUNT;
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ outb(TIMER_CNTR0, timer0_max_count & 0xff);
+ outb(TIMER_CNTR0, timer0_max_count >> 8);
+ write_eflags(ef);
+}
+
+/*
+ * Initialize 8253 timer 0 early so that it can be used in DELAY().
+ * XXX initialization of other timers is unintentionally left blank.
+ */
+void
+startrtclock()
+{
+ u_int delta, freq;
+
+#ifdef PC98
+ findcpuspeed();
+#ifndef AUTO_CLOCK
+ if (pc98_machine_type & M_8M) {
+#ifndef PC98_8M
+ log(LOG_EMERG,
+ "you must reconfig a kernel with \"PC98_8M\" option.\n");
+#endif
+ } else {
+#ifdef PC98_8M
+ log(LOG_EMERG,
+ "You must reconfig a kernel without \"PC98_8M\" option.\n");
+#endif
+ }
+#else /* AUTO_CLOCK */
+ if (pc98_machine_type & M_8M)
+ timer_freq = 1996800L; /* 1.9968 MHz */
+ else
+ timer_freq = 2457600L; /* 2.4576 MHz */
+#endif /* AUTO_CLOCK */
+#endif /* PC98 */
+
+#ifndef PC98
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+#endif
+
+#ifndef PC98
+ /*
+ * Temporarily calibrate with a high intr_freq to get a low
+ * timer0_max_count to help detect bogus i8254 counts.
+ */
+ set_timer_freq(timer_freq, 20000);
+ freq = calibrate_clocks();
+#ifdef CLK_CALIBRATION_LOOP
+ if (bootverbose) {
+ printf(
+ "Press a key on the console to abort clock calibration\n");
+ while (!cncheckc())
+ calibrate_clocks();
+ }
+#endif
+
+ /*
+ * Use the calibrated i8254 frequency if it seems reasonable.
+ * Otherwise use the default, and don't use the calibrated i586
+ * frequency.
+ */
+ delta = freq > timer_freq ? freq - timer_freq : timer_freq - freq;
+ if (delta < timer_freq / 100) {
+#ifndef CLK_USE_I8254_CALIBRATION
+ printf(
+"CLK_USE_I8254_CALIBRATION not specified - using default frequency\n");
+ freq = timer_freq;
+#endif
+ timer_freq = freq;
+ } else {
+ printf("%d Hz differs from default of %d Hz by more than 1%%\n",
+ freq, timer_freq);
+#if defined(I586_CPU) || defined(I686_CPU)
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+#endif
+ }
+#endif
+
+ set_timer_freq(timer_freq, hz);
+
+#if defined(I586_CPU) || defined(I686_CPU)
+#ifndef CLK_USE_I586_CALIBRATION
+ if (i586_ctr_rate != 0) {
+ printf(
+"CLK_USE_I586_CALIBRATION not specified - using old calibration method\n");
+ i586_ctr_freq = 0;
+ i586_ctr_rate = 0;
+ }
+#endif
+ if (i586_ctr_rate == 0 &&
+ (cpu_class == CPUCLASS_586 || cpu_class == CPUCLASS_686)) {
+ /*
+ * Calibration of the i586 clock relative to the mc146818A
+ * clock failed. Do a less accurate calibration relative
+ * to the i8254 clock.
+ */
+ unsigned long long i586_count;
+
+ wrmsr(0x10, 0LL); /* XXX */
+ DELAY(1000000);
+ i586_count = rdtsc();
+ i586_ctr_rate = (i586_count << I586_CTR_RATE_SHIFT) / 1000000;
+ printf("i586 clock: %u Hz\n", i586_ctr_freq);
+ }
+#endif
+}
+
+#ifdef PC98
+void
+rtc_serialcombit(int i)
+{
+ outb(IO_RTC, ((i&0x01)<<5)|0x07);
+ DELAY(1);
+ outb(IO_RTC, ((i&0x01)<<5)|0x17);
+ DELAY(1);
+ outb(IO_RTC, ((i&0x01)<<5)|0x07);
+ DELAY(1);
+}
+
+void
+rtc_serialcom(int i)
+{
+ rtc_serialcombit(i&0x01);
+ rtc_serialcombit((i&0x02)>>1);
+ rtc_serialcombit((i&0x04)>>2);
+ rtc_serialcombit((i&0x08)>>3);
+ outb(IO_RTC, 0x07);
+ DELAY(1);
+ outb(IO_RTC, 0x0f);
+ DELAY(1);
+ outb(IO_RTC, 0x07);
+ DELAY(1);
+}
+
+void
+rtc_outb(int val)
+{
+ int s;
+ int sa = 0;
+
+ for (s=0;s<8;s++) {
+ sa = ((val >> s) & 0x01) ? 0x27 : 0x07;
+ outb(IO_RTC, sa); /* set DI & CLK 0 */
+ DELAY(1);
+ outb(IO_RTC, sa | 0x10); /* CLK 1 */
+ DELAY(1);
+ }
+ outb(IO_RTC, sa & 0xef); /* CLK 0 */
+}
+
+int
+rtc_inb(void)
+{
+ int s;
+ int sa = 0;
+
+ for (s=0;s<8;s++) {
+ sa |= ((inb(0x33) & 0x01) << s);
+ outb(IO_RTC, 0x17); /* CLK 1 */
+ DELAY(1);
+ outb(IO_RTC, 0x07); /* CLK 0 */
+ DELAY(2);
+ }
+ return sa;
+}
+#endif /* PC-98 */
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+void
+inittodr(time_t base)
+{
+ unsigned long sec, days;
+ int yd;
+ int year, month;
+ int y, m, s;
+#ifdef PC98
+ int second, min, hour;
+#endif
+
+ s = splclock();
+ time.tv_sec = base;
+ time.tv_usec = 0;
+ splx(s);
+
+#ifdef PC98
+ rtc_serialcom(0x03); /* Time Read */
+ rtc_serialcom(0x01); /* Register shift command. */
+ DELAY(20);
+
+ second = bcd2bin(rtc_inb() & 0xff); /* sec */
+ min = bcd2bin(rtc_inb() & 0xff); /* min */
+ hour = bcd2bin(rtc_inb() & 0xff); /* hour */
+ days = bcd2bin(rtc_inb() & 0xff) - 1; /* date */
+
+ month = (rtc_inb() >> 4) & 0x0f; /* month */
+ for (m = 1; m < month; m++)
+ days += daysinmonth[m-1];
+ year = bcd2bin(rtc_inb() & 0xff) + 1900; /* year */
+ /* 2000 year problem */
+ if (year < 1995)
+ year += 100;
+ if (year < 1970)
+ goto wrong_time;
+ for (y = 1970; y < year; y++)
+ days += DAYSPERYEAR + LEAPYEAR(y);
+ if ((month > 2) && LEAPYEAR(year))
+ days ++;
+ sec = ((( days * 24 +
+ hour) * 60 +
+ min) * 60 +
+ second);
+ /* sec now contains the number of seconds, since Jan 1 1970,
+ in the local time zone */
+#else /* IBM-PC */
+ /* Look if we have a RTC present and the time is valid */
+ if (!(rtcin(RTC_STATUSD) & RTCSD_PWR))
+ goto wrong_time;
+
+ /* wait for time update to complete */
+ /* If RTCSA_TUP is zero, we have at least 244us before next update */
+ while (rtcin(RTC_STATUSA) & RTCSA_TUP);
+
+ days = 0;
+#ifdef USE_RTC_CENTURY
+ year = readrtc(RTC_YEAR) + readrtc(RTC_CENTURY) * 100;
+#else
+ year = readrtc(RTC_YEAR) + 1900;
+ if (year < 1970)
+ year += 100;
+#endif
+ if (year < 1970)
+ goto wrong_time;
+ month = readrtc(RTC_MONTH);
+ for (m = 1; m < month; m++)
+ days += daysinmonth[m-1];
+ if ((month > 2) && LEAPYEAR(year))
+ days ++;
+ days += readrtc(RTC_DAY) - 1;
+ yd = days;
+ for (y = 1970; y < year; y++)
+ days += DAYSPERYEAR + LEAPYEAR(y);
+ sec = ((( days * 24 +
+ readrtc(RTC_HRS)) * 60 +
+ readrtc(RTC_MIN)) * 60 +
+ readrtc(RTC_SEC));
+ /* sec now contains the number of seconds, since Jan 1 1970,
+ in the local time zone */
+#endif
+
+ sec += tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
+
+ s = splclock();
+ time.tv_sec = sec;
+ splx(s);
+ return;
+
+wrong_time:
+ printf("Invalid time in real time clock.\n");
+ printf("Check and reset the date immediately!\n");
+}
+
+/*
+ * Write system time back to RTC
+ */
+void
+resettodr()
+{
+ unsigned long tm;
+ int y, m, s;
+#ifdef PC98
+ int wd;
+#endif
+
+ if (disable_rtc_set)
+ return;
+
+ s = splclock();
+ tm = time.tv_sec;
+ splx(s);
+
+#ifdef PC98
+ rtc_serialcom(0x01); /* Register shift command. */
+
+ /* Calculate local time to put in RTC */
+
+ tm -= tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
+
+ rtc_outb(bin2bcd(tm%60)); tm /= 60; /* Write back Seconds */
+ rtc_outb(bin2bcd(tm%60)); tm /= 60; /* Write back Minutes */
+ rtc_outb(bin2bcd(tm%24)); tm /= 24; /* Write back Hours */
+
+ /* We have now the days since 01-01-1970 in tm */
+ wd = (tm+4)%7;
+ for (y = 1970, m = DAYSPERYEAR + LEAPYEAR(y);
+ tm >= m;
+ y++, m = DAYSPERYEAR + LEAPYEAR(y))
+ tm -= m;
+
+ /* Now we have the years in y and the day-of-the-year in tm */
+ for (m = 0; ; m++) {
+ int ml;
+
+ ml = daysinmonth[m];
+ if (m == 1 && LEAPYEAR(y))
+ ml++;
+ if (tm < ml)
+ break;
+ tm -= ml;
+ }
+
+ m++;
+ rtc_outb(bin2bcd(tm+1)); /* Write back Day */
+ rtc_outb((m << 4) | wd); /* Write back Month & Weekday */
+ rtc_outb(bin2bcd(y%100)); /* Write back Year */
+
+ rtc_serialcom(0x02); /* Time set & Counter hold command. */
+ rtc_serialcom(0x00); /* Register hold command. */
+#else
+ /* Disable RTC updates and interrupts. */
+ writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR);
+
+ /* Calculate local time to put in RTC */
+
+ tm -= tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0);
+
+ writertc(RTC_SEC, bin2bcd(tm%60)); tm /= 60; /* Write back Seconds */
+ writertc(RTC_MIN, bin2bcd(tm%60)); tm /= 60; /* Write back Minutes */
+ writertc(RTC_HRS, bin2bcd(tm%24)); tm /= 24; /* Write back Hours */
+
+ /* We have now the days since 01-01-1970 in tm */
+ writertc(RTC_WDAY, (tm+4)%7); /* Write back Weekday */
+ for (y = 1970, m = DAYSPERYEAR + LEAPYEAR(y);
+ tm >= m;
+ y++, m = DAYSPERYEAR + LEAPYEAR(y))
+ tm -= m;
+
+ /* Now we have the years in y and the day-of-the-year in tm */
+ writertc(RTC_YEAR, bin2bcd(y%100)); /* Write back Year */
+#ifdef USE_RTC_CENTURY
+ writertc(RTC_CENTURY, bin2bcd(y/100)); /* ... and Century */
+#endif
+ for (m = 0; ; m++) {
+ int ml;
+
+ ml = daysinmonth[m];
+ if (m == 1 && LEAPYEAR(y))
+ ml++;
+ if (tm < ml)
+ break;
+ tm -= ml;
+ }
+
+ writertc(RTC_MONTH, bin2bcd(m + 1)); /* Write back Month */
+ writertc(RTC_DAY, bin2bcd(tm + 1)); /* Write back Month Day */
+
+ /* Reenable RTC updates and interrupts. */
+ writertc(RTC_STATUSB, rtc_statusb);
+#endif
+}
+
+/*
+ * Start both clocks running.
+ */
+void
+cpu_initclocks()
+{
+#ifndef PC98
+ int diag;
+
+ if (statclock_disable) {
+ /*
+ * The stat interrupt mask is different without the
+ * statistics clock. Also, don't set the interrupt
+ * flag which would normally cause the RTC to generate
+ * interrupts.
+ */
+ stat_imask = HWI_MASK | SWI_MASK;
+ rtc_statusb = RTCSB_24HR;
+ } else {
+ /* Setting stathz to nonzero early helps avoid races. */
+ stathz = RTC_NOPROFRATE;
+ profhz = RTC_PROFRATE;
+ }
+#endif
+
+ /* Finish initializing 8253 timer 0. */
+ register_intr(/* irq */ 0, /* XXX id */ 0, /* flags */ 0,
+ /* XXX */ (inthand2_t *)clkintr, &clk_imask,
+ /* unit */ 0);
+ INTREN(IRQ0);
+#if defined(I586_CPU) || defined(I686_CPU)
+ /*
+ * Finish setting up anti-jitter measures.
+ */
+ if (i586_ctr_rate) {
+ i586_last_tick = rdtsc();
+ i586_ctr_bias = i586_last_tick;
+ }
+#endif
+
+#ifndef PC98
+ /* Initialize RTC. */
+ writertc(RTC_STATUSA, rtc_statusa);
+ writertc(RTC_STATUSB, RTCSB_24HR);
+
+static int
+sysctl_machdep_i8254_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ /*
+ * Use `i8254' instead of `timer' in external names because `timer'
+ * is is too generic. Should use it everywhere.
+ */
+ freq = timer_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != timer_freq) {
+ if (timer0_state != 0)
+ return (EBUSY); /* too much trouble to handle */
+ set_timer_freq(freq, hz);
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i8254_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i8254_freq, "I", "");
+
+#if defined(I586_CPU) || defined(I686_CPU)
+static int
+sysctl_machdep_i586_freq SYSCTL_HANDLER_ARGS
+{
+ int error;
+ u_int freq;
+
+ if (i586_ctr_rate == 0)
+ return (EOPNOTSUPP);
+ freq = i586_ctr_freq;
+ error = sysctl_handle_opaque(oidp, &freq, sizeof freq, req);
+ if (error == 0 && freq != i586_ctr_freq) {
+ i586_ctr_freq = freq;
+ i586_ctr_rate = ((unsigned long long)freq <<
+ I586_CTR_RATE_SHIFT) / 1000000;
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, OID_AUTO, i586_freq, CTLTYPE_INT | CTLFLAG_RW,
+ 0, sizeof(u_int), sysctl_machdep_i586_freq, "I", "");
+#endif /* defined(I586_CPU) || defined(I686_CPU) */
+
+ /* Don't bother enabling the statistics clock. */
+ if (statclock_disable)
+ return;
+ diag = rtcin(RTC_DIAG);
+ if (diag != 0)
+ printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS);
+ register_intr(/* irq */ 8, /* XXX id */ 1, /* flags */ 0,
+ /* XXX */ (inthand2_t *)rtcintr, &stat_imask,
+ /* unit */ 0);
+ INTREN(IRQ8);
+ writertc(RTC_STATUSB, rtc_statusb);
+#endif
+}
+
+void
+setstatclockrate(int newhz)
+{
+#ifndef PC98
+ if (newhz == RTC_PROFRATE)
+ rtc_statusa = RTCSA_DIVIDER | RTCSA_PROF;
+ else
+ rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF;
+ writertc(RTC_STATUSA, rtc_statusa);
+#endif
+}
diff --git a/sys/pc98/cbus/sio.c b/sys/pc98/cbus/sio.c
new file mode 100644
index 0000000..b16d153
--- /dev/null
+++ b/sys/pc98/cbus/sio.c
@@ -0,0 +1,4023 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)com.c 7.5 (Berkeley) 5/16/91
+ * $Id: sio.c,v 1.142 1996/05/02 09:34:40 phk Exp $
+ */
+
+#include "opt_comconsole.h"
+#include "opt_ddb.h"
+#include "opt_sio.h"
+#include "sio.h"
+
+/*
+ * Serial driver, based on 386BSD-0.1 com driver.
+ * Mostly rewritten to use pseudo-DMA.
+ * Works for National Semiconductor NS8250-NS16550AF UARTs.
+ * COM driver, based on HP dca driver.
+ *
+ * Changes for PC-Card integration:
+ * - Added PC-Card driver table and handlers
+ */
+/*===============================================================
+ * 386BSD(98),FreeBSD-1.1x(98) com driver.
+ * -----
+ * modified for PC9801 by M.Ishii
+ * Kyoto University Microcomputer Club (KMC)
+ * Chou "TEFUTEFU" Hirotomi
+ * Kyoto Univ. the faculty of medicine
+ *===============================================================
+ * FreeBSD-2.0.1(98) sio driver.
+ * -----
+ * modified for pc98 Internal i8251 and MICRO CORE MC16550II
+ * T.Koike(hfc01340@niftyserve.or.jp)
+ * implement kernel device configuration
+ * aizu@orient.center.nitech.ac.jp
+ *
+ * Notes.
+ * -----
+ * PC98 localization based on 386BSD(98) com driver. Using its PC98 local
+ * functions.
+ * This driver is under debugging,has bugs.
+ *
+ * 1) config
+ * options COM_MULTIPORT #if using MC16550II
+ * device sio0 at nec? port 0x30 tty irq 4 vector siointr #internal
+ * device sio1 at nec? port 0xd2 tty irq 5 flags 0x101 vector siointr #mc1
+ * device sio2 at nec? port 0x8d2 tty flags 0x101 vector siointr #mc2
+ * # ~~~~~iobase ~~multi port flag
+ * # ~ master device is sio1
+ * 2) device
+ * cd /dev; MAKEDEV ttyd0 ttyd1 ..
+ * 3) /etc/rc.serial
+ * 57600bps is too fast for sio0(internal8251)
+ * my ex.
+ * #set default speed 9600
+ * modem()
+ * :
+ * stty </dev/ttyid$i crtscts 9600
+ * : # ~~~~ default speed(can change after init.)
+ * modem 0 1 2
+ * 4) COMCONSOLE
+ * not changed.
+ * 5) PC9861K,PIO9032B,B98_01
+ * not tested.
+ */
+/*
+ * modified for AIWA B98-01
+ * by T.Hatanou <hatanou@yasuda.comm.waseda.ac.jp> last update: 15 Sep.1995
+ *
+ * How to configure...
+ * # options COM_MULTIPORT # support for MICROCORE MC16550II
+ * ... comment-out this line, which will conflict with B98_01.
+ * options "B98_01" # support for AIWA B98-01
+ * device sio1 at nec? port 0x00d1 tty irq ? vector siointr
+ * device sio2 at nec? port 0x00d5 tty irq ? vector siointr
+ * ... you can leave these lines `irq ?', irq will be autodetected.
+ */
+#ifdef PC98
+#define MC16550 0
+#define COM_IF_INTERNAL 1
+#if 0
+#define COM_IF_PC9861K 2
+#define COM_IF_PIO9032B 3
+#endif
+#ifdef B98_01
+#undef COM_MULTIPORT /* COM_MULTIPORT will conflict with B98_01 */
+#define COM_IF_B98_01 4
+#endif /* B98_01 */
+#endif /* PC98 */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/reboot.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/dkstat.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+#include <sys/devconf.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif
+
+#include <machine/clock.h>
+
+#ifdef PC98
+#include <pc98/pc98/icu.h> /* XXX just to get at `imen' */
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/sioreg.h>
+#include <pc98/pc98/ic/i8251.h>
+#include <pc98/pc98/ic/ns16550.h>
+#else
+#include <i386/isa/icu.h> /* XXX just to get at `imen' */
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/sioreg.h>
+
+#ifdef COM_ESP
+#include <i386/isa/ic/esp.h>
+#endif
+#include <i386/isa/ic/ns16550.h>
+#endif
+
+#include "crd.h"
+#if NCRD > 0
+#include <pccard/card.h>
+#include <pccard/driver.h>
+#include <pccard/slot.h>
+#endif
+
+#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
+#define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE)
+#define RS_IBUFSIZE 256
+
+#define CALLOUT_MASK 0x80
+#define CONTROL_MASK 0x60
+#define CONTROL_INIT_STATE 0x20
+#define CONTROL_LOCK_STATE 0x40
+#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev)))
+#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK)
+#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK)
+
+#ifdef COM_MULTIPORT
+/* checks in flags for multiport and which is multiport "master chip"
+ * for a given card
+ */
+#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01)
+#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff)
+#define COM_NOTAST4(dev) ((dev)->id_flags & 0x04)
+#endif /* COM_MULTIPORT */
+
+#define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08)
+#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02)
+#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80)
+
+#ifndef PC98
+#define com_scr 7 /* scratch register for 16450-16550 (R/W) */
+#endif /* !PC98 */
+
+/*
+ * Input buffer watermarks.
+ * The external device is asked to stop sending when the buffer exactly reaches
+ * high water, or when the high level requests it.
+ * The high level is notified immediately (rather than at a later clock tick)
+ * when this watermark is reached.
+ * The buffer size is chosen so the watermark should almost never be reached.
+ * The low watermark is invisibly 0 since the buffer is always emptied all at
+ * once.
+ */
+#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
+
+/*
+ * com state bits.
+ * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
+ * than the other bits so that they can be tested as a group without masking
+ * off the low bits.
+ *
+ * The following com and tty flags correspond closely:
+ * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and
+ * siostop())
+ * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart())
+ * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam())
+ * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam())
+ * TS_FLUSH is not used.
+ * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON.
+ * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state).
+ */
+#define CS_BUSY 0x80 /* output in progress */
+#define CS_TTGO 0x40 /* output not stopped by XOFF */
+#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */
+#define CS_CHECKMSR 1 /* check of MSR scheduled */
+#define CS_CTS_OFLOW 2 /* use CTS output flow control */
+#define CS_DTR_OFF 0x10 /* DTR held off */
+#define CS_ODONE 4 /* output completed */
+#define CS_RTS_IFLOW 8 /* use RTS input flow control */
+
+static char const * const error_desc[] = {
+#define CE_OVERRUN 0
+ "silo overflow",
+#define CE_INTERRUPT_BUF_OVERFLOW 1
+ "interrupt-level buffer overflow",
+#define CE_TTY_BUF_OVERFLOW 2
+ "tty-level buffer overflow",
+};
+
+#define CE_NTYPES 3
+#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum])
+
+/* types. XXX - should be elsewhere */
+typedef u_int Port_t; /* hardware port */
+typedef u_char bool_t; /* boolean */
+
+/* queue of linear buffers */
+struct lbq {
+ u_char *l_head; /* next char to process */
+ u_char *l_tail; /* one past the last char to process */
+ struct lbq *l_next; /* next in queue */
+ bool_t l_queued; /* nonzero if queued */
+};
+
+/* com device structure */
+struct com_s {
+ u_char state; /* miscellaneous flag bits */
+ bool_t active_out; /* nonzero if the callout device is open */
+ u_char cfcr_image; /* copy of value written to CFCR */
+#ifdef COM_ESP
+ bool_t esp; /* is this unit a hayes esp board? */
+#endif
+ u_char fifo_image; /* copy of value written to FIFO */
+ bool_t hasfifo; /* nonzero for 16550 UARTs */
+ bool_t loses_outints; /* nonzero if device loses output interrupts */
+ u_char mcr_image; /* copy of value written to MCR */
+#ifdef COM_MULTIPORT
+ bool_t multiport; /* is this unit part of a multiport device? */
+#endif /* COM_MULTIPORT */
+ bool_t no_irq; /* nonzero if irq is not attached */
+ bool_t gone; /* hardware disappeared */
+ bool_t poll; /* nonzero if polling is required */
+ bool_t poll_output; /* nonzero if polling for output is required */
+ int unit; /* unit number */
+ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */
+ u_int tx_fifo_size;
+ u_int wopeners; /* # processes waiting for DCD in open() */
+
+ /*
+ * The high level of the driver never reads status registers directly
+ * because there would be too many side effects to handle conveniently.
+ * Instead, it reads copies of the registers stored here by the
+ * interrupt handler.
+ */
+ u_char last_modem_status; /* last MSR read by intr handler */
+ u_char prev_modem_status; /* last MSR handled by high level */
+
+ u_char hotchar; /* ldisc-specific char to be handled ASAP */
+ u_char *ibuf; /* start of input buffer */
+ u_char *ibufend; /* end of input buffer */
+ u_char *ihighwater; /* threshold in input buffer */
+ u_char *iptr; /* next free spot in input buffer */
+
+ struct lbq obufq; /* head of queue of output buffers */
+ struct lbq obufs[2]; /* output buffers */
+
+#ifdef PC98
+ Port_t cmd_port;
+ Port_t sts_port;
+ Port_t in_modem_port;
+ Port_t intr_ctrl_port;
+ int intr_enable;
+ int pc98_prev_modem_status;
+ int pc98_modem_delta;
+ int modem_car_chg_timer;
+ int pc98_prev_siocmd;
+ int pc98_prev_siomod;
+ int modem_checking;
+ int pc98_if_type;
+#endif /* PC98 */
+ Port_t data_port; /* i/o ports */
+#ifdef COM_ESP
+ Port_t esp_port;
+#endif
+ Port_t int_id_port;
+ Port_t iobase;
+ Port_t modem_ctl_port;
+ Port_t line_status_port;
+ Port_t modem_status_port;
+
+ struct tty *tp; /* cross reference */
+
+ /* Initial state. */
+ struct termios it_in; /* should be in struct tty */
+ struct termios it_out;
+
+ /* Lock state. */
+ struct termios lt_in; /* should be in struct tty */
+ struct termios lt_out;
+
+ bool_t do_timestamp;
+ struct timeval timestamp;
+
+ u_long bytes_in; /* statistics */
+ u_long bytes_out;
+ u_int delta_error_counts[CE_NTYPES];
+ u_long error_counts[CE_NTYPES];
+
+ /*
+ * Ping-pong input buffers. The extra factor of 2 in the sizes is
+ * to allow for an error byte for each input byte.
+ */
+#define CE_INPUT_OFFSET RS_IBUFSIZE
+ u_char ibuf1[2 * RS_IBUFSIZE];
+ u_char ibuf2[2 * RS_IBUFSIZE];
+
+ /*
+ * Data area for output buffers. Someday we should build the output
+ * buffer queue without copying data.
+ */
+ u_char obuf1[256];
+ u_char obuf2[256];
+#ifdef DEVFS
+ void *devfs_token_ttyd;
+ void *devfs_token_ttyl;
+ void *devfs_token_ttyi;
+ void *devfs_token_cuaa;
+ void *devfs_token_cual;
+ void *devfs_token_cuai;
+#endif
+};
+
+/*
+ * XXX public functions in drivers should be declared in headers produced
+ * by `config', not here.
+ */
+
+/* Interrupt handling entry points. */
+inthand2_t siointrts;
+void siopoll __P((void));
+
+/* Device switch entry points. */
+#define sioreset noreset
+#define siommap nommap
+#define siostrategy nostrategy
+
+#ifdef PC98
+#ifdef COM_ESP
+static int espattach __P((struct pc98_device *isdp, struct com_s *com,
+ Port_t esp_port));
+#endif
+static int sioattach __P((struct pc98_device *dev));
+#else
+#ifdef COM_ESP
+static int espattach __P((struct isa_device *isdp, struct com_s *com,
+ Port_t esp_port));
+#endif
+static int sioattach __P((struct isa_device *dev));
+#endif
+static timeout_t siodtrwakeup;
+static void comhardclose __P((struct com_s *com));
+static void siointr1 __P((struct com_s *com));
+static int commctl __P((struct com_s *com, int bits, int how));
+static int comparam __P((struct tty *tp, struct termios *t));
+#ifdef PC98
+static int sioprobe __P((struct pc98_device *dev));
+static void sioregisterdev __P((struct pc98_device *id));
+#else
+static int sioprobe __P((struct isa_device *dev));
+static void sioregisterdev __P((struct isa_device *id));
+#endif
+static void siosettimeout __P((void));
+static void comstart __P((struct tty *tp));
+static timeout_t comwakeup;
+static int tiocm_xxx2mcr __P((int tiocm_xxx));
+static void disc_optim __P((struct tty *tp, struct termios *t,
+ struct com_s *com));
+
+#ifdef DSI_SOFT_MODEM
+static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr));
+#endif /* DSI_SOFT_MODEM */
+
+static char driver_name[] = "sio";
+
+/* table and macro for fast conversion from a unit number to its com struct */
+static struct com_s *p_com_addr[NSIO];
+#define com_addr(unit) (p_com_addr[unit])
+
+static struct timeval intr_timestamp;
+
+#ifdef PC98
+struct pc98_driver siodriver = {
+#else
+struct isa_driver siodriver = {
+#endif
+ sioprobe, sioattach, driver_name
+};
+
+static d_open_t sioopen;
+static d_close_t sioclose;
+static d_read_t sioread;
+static d_write_t siowrite;
+static d_ioctl_t sioioctl;
+static d_stop_t siostop;
+static d_devtotty_t siodevtotty;
+
+#define CDEV_MAJOR 28
+static struct cdevsw sio_cdevsw = {
+ sioopen, sioclose, sioread, siowrite,
+ sioioctl, siostop, noreset, siodevtotty,
+ ttselect, nommap, NULL, driver_name,
+ NULL, -1,
+};
+
+static int comconsole = -1;
+static speed_t comdefaultrate = TTYDEF_SPEED;
+static u_int com_events; /* input chars + weighted output completions */
+static int sio_timeout;
+static int sio_timeouts_until_log;
+#if 0 /* XXX */
+static struct tty *sio_tty[NSIO];
+#else
+static struct tty sio_tty[NSIO];
+#endif
+static const int nsio_tty = NSIO;
+
+#ifdef PC98
+struct siodev {
+ short if_type;
+ short irq;
+ Port_t cmd, sts, ctrl, mod;
+ };
+static int sysclock;
+static short port_table[5][3] = {
+ {0x30, 0xb1, 0xb9},
+ {0x32, 0xb3, 0xbb},
+ {0x32, 0xb3, 0xbb},
+ {0x33, 0xb0, 0xb2},
+ {0x35, 0xb0, 0xb2}
+ };
+#define PC98SIO_data_port(ch) port_table[0][ch]
+#define PC98SIO_cmd_port(ch) port_table[1][ch]
+#define PC98SIO_sts_port(ch) port_table[2][ch]
+#define PC98SIO_in_modem_port(ch) port_table[3][ch]
+#define PC98SIO_intr_ctrl_port(ch) port_table[4][ch]
+#ifdef COM_IF_PIO9032B
+#define IO_COM_PIO9032B_2 0x0b8
+#define IO_COM_PIO9032B_3 0x0ba
+#endif /* COM_IF_PIO9032B */
+#ifdef COM_IF_B98_01
+#define IO_COM_B98_01_2 0x0d1
+#define IO_COM_B98_01_3 0x0d5
+#endif /* COM_IF_B98_01 */
+#define COM_INT_DISABLE {int previpri; previpri=spltty();
+#define COM_INT_ENABLE splx(previpri);}
+#define IEN_TxFLAG IEN_Tx
+
+#define COM_CARRIER_DETECT_EMULATE 0
+#define PC98_CHECK_MODEM_INTERVAL (hz/10)
+#define DCD_OFF_TOLERANCE 2
+#define DCD_ON_RECOGNITION 2
+#define IS_8251(type) (type != MC16550)
+#define IS_PC98IN(adr) (adr == 0x30)
+
+static void commint __P((dev_t dev));
+static void com_tiocm_set __P((struct com_s *com, int msr));
+static void com_tiocm_bis __P((struct com_s *com, int msr));
+static void com_tiocm_bic __P((struct com_s *com, int msr));
+static int com_tiocm_get __P((struct com_s *com));
+static int com_tiocm_get_delta __P((struct com_s *com));
+static void pc98_msrint_start __P((dev_t dev));
+static void com_cflag_and_speed_set __P((struct com_s *com, int cflag, int speed));
+static int pc98_ttspeedtab __P((struct com_s *com, int speed));
+static int pc98_get_modem_status __P((struct com_s *com));
+static timeout_t pc98_check_msr;
+static void pc98_set_baud_rate __P((struct com_s *com, int count));
+static void pc98_i8251_reset __P((struct com_s *com, int mode, int command));
+static void pc98_disable_i8251_interrupt __P((struct com_s *com, int mod));
+static void pc98_enable_i8251_interrupt __P((struct com_s *com, int mod));
+static int pc98_check_i8251_interrupt __P((struct com_s *com));
+static int pc98_i8251_get_cmd __P((struct com_s *com));
+static int pc98_i8251_get_mod __P((struct com_s *com));
+static void pc98_i8251_set_cmd __P((struct com_s *com, int x));
+static void pc98_i8251_or_cmd __P((struct com_s *com, int x));
+static void pc98_i8251_clear_cmd __P((struct com_s *com, int x));
+static void pc98_i8251_clear_or_cmd __P((struct com_s *com, int clr, int x));
+static int pc98_check_if_type __P((int iobase, struct siodev *iod));
+static void pc98_check_sysclock __P((void));
+static int pc98_set_ioport __P((struct com_s *com, int io_base));
+
+#define com_int_Tx_disable(com) \
+ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP)
+#define com_int_Tx_enable(com) \
+ pc98_enable_i8251_interrupt(com,IEN_TxFLAG)
+#define com_int_Rx_disable(com) \
+ pc98_disable_i8251_interrupt(com,IEN_Rx)
+#define com_int_Rx_enable(com) \
+ pc98_enable_i8251_interrupt(com,IEN_Rx)
+#define com_int_TxRx_disable(com) \
+ pc98_disable_i8251_interrupt(com,IEN_Tx|IEN_TxEMP|IEN_Rx)
+#define com_int_TxRx_enable(com) \
+ pc98_enable_i8251_interrupt(com,IEN_TxFLAG|IEN_Rx)
+#define com_send_break_on(com) \
+ pc98_i8251_or_cmd(com,CMD8251_SBRK)
+#define com_send_break_off(com) \
+ pc98_i8251_clear_cmd(com,CMD8251_SBRK)
+
+struct speedtab pc98speedtab[] = { /* internal RS232C interface */
+ 0, 0,
+ 50, 50,
+ 75, 75,
+ 150, 150,
+ 200, 200,
+ 300, 300,
+ 600, 600,
+ 1200, 1200,
+ 2400, 2400,
+ 4800, 4800,
+ 9600, 9600,
+ 19200, 19200,
+ 38400, 38400,
+ 76800, 76800,
+ 20800, 20800,
+ 41600, 41600,
+ 15600, 15600,
+ 31200, 31200,
+ 62400, 62400,
+ -1, -1
+};
+#ifdef COM_IF_PIO9032B
+struct speedtab comspeedtab_pio9032b[] = {
+ 300, 6,
+ 600, 5,
+ 1200, 4,
+ 2400, 3,
+ 4800, 2,
+ 9600, 1,
+ 19200, 0,
+ 38400, 7,
+ -1, -1
+};
+#endif
+
+#ifdef COM_IF_B98_01
+struct speedtab comspeedtab_b98_01[] = {
+ 0, 0,
+ 75, 15,
+ 150, 14,
+ 300, 13,
+ 600, 12,
+ 1200, 11,
+ 2400, 10,
+ 4800, 9,
+ 9600, 8,
+ 19200, 7,
+ 38400, 6,
+ 76800, 5,
+ 153600, 4,
+ -1, -1
+};
+#endif
+#endif /* PC98 */
+
+static struct speedtab comspeedtab[] = {
+ { 0, 0 },
+ { 50, COMBRD(50) },
+ { 75, COMBRD(75) },
+ { 110, COMBRD(110) },
+ { 134, COMBRD(134) },
+ { 150, COMBRD(150) },
+ { 200, COMBRD(200) },
+ { 300, COMBRD(300) },
+ { 600, COMBRD(600) },
+ { 1200, COMBRD(1200) },
+ { 1800, COMBRD(1800) },
+ { 2400, COMBRD(2400) },
+ { 4800, COMBRD(4800) },
+ { 9600, COMBRD(9600) },
+ { 19200, COMBRD(19200) },
+ { 38400, COMBRD(38400) },
+ { 57600, COMBRD(57600) },
+ { 115200, COMBRD(115200) },
+ { -1, -1 }
+};
+
+static struct kern_devconf kdc_sio[NSIO] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ driver_name, 0, { MDDT_PC98, 0, "tty" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ driver_name, 0, { MDDT_ISA, 0, "tty" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "Serial port",
+ DC_CLS_SERIAL /* class */
+} };
+
+#ifdef COM_ESP
+/* XXX configure this properly. */
+static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
+static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 };
+#endif
+
+#if NCRD > 0
+/*
+ * PC-Card (PCMCIA) specific code.
+ */
+static int card_intr(struct pccard_dev *); /* Interrupt handler */
+static void siounload(struct pccard_dev *); /* Disable driver */
+static void siosuspend(struct pccard_dev *); /* Suspend driver */
+static int sioinit(struct pccard_dev *, int); /* init device */
+
+static struct pccard_drv sio_info = {
+ driver_name,
+ card_intr,
+ siounload,
+ siosuspend,
+ sioinit,
+ 0, /* Attributes - presently unused */
+ &tty_imask /* Interrupt mask for device */
+ /* XXX - Should this also include net_imask? */
+};
+
+/*
+ * Called when a power down is requested. Shuts down the
+ * device and configures the device as unavailable (but
+ * still loaded...). A resume is done by calling
+ * sioinit with first=0. This is called when the user suspends
+ * the system, or the APM code suspends the system.
+ */
+static void
+siosuspend(struct pccard_dev *dp)
+{
+ printf("sio%d: suspending\n", dp->isahd.id_unit);
+}
+
+/*
+ * Initialize the device - called from Slot manager.
+ * If first is set, then check for the device's existence
+ * before initializing it. Once initialized, the device table may
+ * be set up.
+ */
+int
+sioinit(struct pccard_dev *dp, int first)
+{
+
+ /* validate unit number. */
+ if (first) {
+ if (dp->isahd.id_unit >= NSIO)
+ return(ENODEV);
+ /* Make sure it isn't already probed. */
+ if (com_addr(dp->isahd.id_unit))
+ return(EBUSY);
+ /*
+ * Probe the device. If a value is returned, the
+ * device was found at the location.
+ */
+ if (sioprobe(&dp->isahd)==0)
+ return(ENXIO);
+ if (sioattach(&dp->isahd)==0)
+ return(ENXIO);
+ }
+ /*
+ * XXX TODO:
+ * If it was initialized before, the device structure
+ * should also be initialized. We should
+ * reset (and possibly restart) the hardware, but
+ * I am not sure of the best way to do this...
+ */
+ return(0);
+}
+
+/*
+ * siounload - unload the driver and clear the table.
+ * XXX TODO:
+ * This is usually called when the card is ejected, but
+ * can be caused by a modunload of a controller driver.
+ * The idea is to reset the driver's view of the device
+ * and ensure that any driver entry points such as
+ * read and write do not hang.
+ */
+static void
+siounload(struct pccard_dev *dp)
+{
+ struct com_s *com;
+
+ com = com_addr(dp->isahd.id_unit);
+ if (!com->iobase) {
+ printf("sio%d already unloaded!\n",dp->isahd.id_unit);
+ return;
+ }
+ kdc_sio[com->unit].kdc_state = DC_UNCONFIGURED;
+ kdc_sio[com->unit].kdc_description = "Serial port";
+ if (com->tp && (com->tp->t_state & TS_ISOPEN)) {
+ com->gone = 1;
+ printf("sio%d: unload\n", dp->isahd.id_unit);
+ com->tp->t_gen++;
+ ttyclose(com->tp);
+ ttwakeup(com->tp);
+ ttwwakeup(com->tp);
+ } else {
+ com_addr(com->unit) = NULL;
+ bzero(com, sizeof *com);
+ free(com,M_TTYS);
+ printf("sio%d: unload,gone\n", dp->isahd.id_unit);
+ }
+}
+
+/*
+ * card_intr - Shared interrupt called from
+ * front end of PC-Card handler.
+ */
+static int
+card_intr(struct pccard_dev *dp)
+{
+ struct com_s *com;
+ com = com_addr(dp->isahd.id_unit);
+ if (com && !com_addr(dp->isahd.id_unit)->gone)
+ siointr1(com_addr(dp->isahd.id_unit));
+ return(1);
+}
+#endif /* NCRD > 0 */
+
+static void
+sioregisterdev(id)
+#ifdef PC98
+ struct pc98_device *id;
+#else
+ struct isa_device *id;
+#endif
+{
+ int unit;
+
+ unit = id->id_unit;
+/*
+ * If already registered, don't try to re-register.
+ */
+#ifdef PC98
+ if (kdc_sio[unit].kdc_pc98)
+#else
+ if (kdc_sio[unit].kdc_isa)
+#endif
+ return;
+ if (unit != 0)
+ kdc_sio[unit] = kdc_sio[0];
+ kdc_sio[unit].kdc_state = DC_UNCONFIGURED;
+ kdc_sio[unit].kdc_description = "Serial port";
+ kdc_sio[unit].kdc_unit = unit;
+#ifdef PC98
+ kdc_sio[unit].kdc_pc98 = id;
+#else
+ kdc_sio[unit].kdc_isa = id;
+#endif
+ dev_attach(&kdc_sio[unit]);
+}
+
+static int
+sioprobe(dev)
+#ifdef PC98
+ struct pc98_device *dev;
+#else
+ struct isa_device *dev;
+#endif
+{
+ static bool_t already_init;
+ bool_t failures[10];
+ int fn;
+#ifdef PC98
+ struct pc98_device *idev;
+#else
+ struct isa_device *idev;
+#endif
+ Port_t iobase;
+ u_char mcr_image;
+ int result;
+#ifdef PC98
+ struct pc98_device *xdev;
+ int irqout=0;
+ int ret = 0;
+ int tmp;
+ struct siodev iod;
+#else
+ struct isa_device *xdev;
+#endif
+
+ sioregisterdev(dev);
+
+ if (!already_init) {
+ /*
+ * Turn off MCR_IENABLE for all likely serial ports. An unused
+ * port with its MCR_IENABLE gate open will inhibit interrupts
+ * from any used port that shares the interrupt vector.
+ * XXX the gate enable is elsewhere for some multiports.
+ */
+#ifdef PC98
+ for (xdev = pc98_devtab_tty; xdev->id_driver != NULL; xdev++)
+#else
+ for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++)
+#endif
+ if (xdev->id_driver == &siodriver && xdev->id_enabled)
+#ifdef PC98
+ if (IS_PC98IN(xdev->id_iobase))
+ outb(xdev->id_iobase + 2, 0xf2);
+ else
+#else
+ outb(xdev->id_iobase + com_mcr, 0);
+#endif
+#if NCRD > 0
+ /*
+ * If PC-Card probe required, then register driver with
+ * slot manager.
+ */
+ pccard_add_driver(&sio_info);
+#endif
+ already_init = TRUE;
+ }
+
+#ifdef PC98
+ /*
+ * If the port is i8251 UART (internal, B98_01)
+ */
+ if(pc98_check_if_type(dev->id_iobase, &iod) == -1)
+ return 0;
+ if(IS_8251(iod.if_type)){
+ if ( iod.irq > 0 )
+ dev->id_irq = (1 << iod.irq);
+ outb(iod.cmd, 0);
+ DELAY(10);
+ outb(iod.cmd, 0);
+ DELAY(10);
+ outb(iod.cmd, 0);
+ DELAY(10);
+ outb(iod.cmd, CMD8251_RESET);
+ DELAY(1000); /* for a while...*/
+ outb(iod.cmd, 0xf2); /* MODE (dummy) */
+ DELAY(10);
+ outb(iod.cmd, 0x01); /* CMD (dummy) */
+ DELAY(1000); /* for a while...*/
+ if (( inb(iod.sts) & STS8251_TxEMP ) == 0 ) {
+ ret = 0;
+ }
+ switch (iod.if_type) {
+ case COM_IF_INTERNAL:
+ COM_INT_DISABLE
+ tmp = ( inb( iod.ctrl ) & ~(IEN_Rx|IEN_TxEMP|IEN_Tx));
+ outb( iod.ctrl, tmp|IEN_TxEMP );
+ ret = pc98_irq_pending(dev) ? 4 : 0;
+ outb( iod.ctrl, tmp );
+ COM_INT_ENABLE
+ break;
+#ifdef COM_IF_B98_01
+ case COM_IF_B98_01:
+ /* B98_01 doesn't activate TxEMP interrupt line
+ when being reset, so we can't check irq pending.*/
+ ret = 4;
+ break;
+#endif
+ }
+ if (epson_machine_id==0x20) { /* XXX */
+ ret = 4;
+ }
+ return ret;
+ }
+#endif /* PC98 */
+ /*
+ * If the device is on a multiport card and has an AST/4
+ * compatible interrupt control register, initialize this
+ * register and prepare to leave MCR_IENABLE clear in the mcr.
+ * Otherwise, prepare to set MCR_IENABLE in the mcr.
+ * Point idev to the device struct giving the correct id_irq.
+ * This is the struct for the master device if there is one.
+ */
+ idev = dev;
+ mcr_image = MCR_IENABLE;
+#ifdef COM_MULTIPORT
+ if (COM_ISMULTIPORT(dev)) {
+#ifdef PC98
+ idev = find_pc98dev(pc98_devtab_tty, &siodriver,
+#else
+ idev = find_isadev(isa_devtab_tty, &siodriver,
+#endif
+ COM_MPMASTER(dev));
+ if (idev == NULL) {
+ printf("sio%d: master device %d not configured\n",
+ dev->id_unit, COM_MPMASTER(dev));
+ return (0);
+ }
+#ifndef PC98
+ if (!COM_NOTAST4(dev)) {
+ outb(idev->id_iobase + com_scr,
+ idev->id_irq ? 0x80 : 0);
+ mcr_image = 0;
+ }
+#endif /* !PC98 */
+ }
+#endif /* COM_MULTIPORT */
+ if (idev->id_irq == 0)
+ mcr_image = 0;
+
+#ifdef PC98
+ switch(idev->id_irq){
+ case IRQ3: irqout = 4; break;
+ case IRQ5: irqout = 5; break;
+ case IRQ6: irqout = 6; break;
+ case IRQ12: irqout = 7; break;
+ default:
+ printf("sio%d: irq configuration error\n",dev->id_unit);
+ return (0);
+ }
+ outb(dev->id_iobase+0x1000, irqout);
+#endif
+ bzero(failures, sizeof failures);
+ iobase = dev->id_iobase;
+
+ /*
+ * We don't want to get actual interrupts, just masked ones.
+ * Interrupts from this line should already be masked in the ICU,
+ * but mask them in the processor as well in case there are some
+ * (misconfigured) shared interrupts.
+ */
+ disable_intr();
+/* EXTRA DELAY? */
+
+ /*
+ * XXX DELAY() reenables CPU interrupts. This is a problem for
+ * shared interrupts after the first device using one has been
+ * successfully probed - config_isadev() has enabled the interrupt
+ * in the ICU.
+ */
+#ifdef PC98
+ outb(IO_ICU1 + 2, 0xff);
+#else
+ outb(IO_ICU1 + 1, 0xff);
+#endif
+
+ /*
+ * Initialize the speed and the word size and wait long enough to
+ * drain the maximum of 16 bytes of junk in device output queues.
+ * The speed is undefined after a master reset and must be set
+ * before relying on anything related to output. There may be
+ * junk after a (very fast) soft reboot and (apparently) after
+ * master reset.
+ * XXX what about the UART bug avoided by waiting in comparam()?
+ * We don't want to to wait long enough to drain at 2 bps.
+ */
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, COMBRD(9600) & 0xff);
+ outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ DELAY((16 + 1) * 1000000 / (9600 / 10));
+
+ /*
+ * Enable the interrupt gate and disable device interupts. This
+ * should leave the device driving the interrupt line low and
+ * guarantee an edge trigger if an interrupt can be generated.
+ */
+/* EXTRA DELAY? */
+ outb(iobase + com_mcr, mcr_image);
+ outb(iobase + com_ier, 0);
+
+ /*
+ * Attempt to set loopback mode so that we can send a null byte
+ * without annoying any external device.
+ */
+/* EXTRA DELAY? */
+ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK);
+
+ /*
+ * Attempt to generate an output interrupt. On 8250's, setting
+ * IER_ETXRDY generates an interrupt independent of the current
+ * setting and independent of whether the THR is empty. On 16450's,
+ * setting IER_ETXRDY generates an interrupt independent of the
+ * current setting. On 16550A's, setting IER_ETXRDY only
+ * generates an interrupt when IER_ETXRDY is not already set.
+ */
+ outb(iobase + com_ier, IER_ETXRDY);
+
+ /*
+ * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate
+ * an interrupt. They'd better generate one for actually doing
+ * output. Loopback may be broken on the same incompatibles but
+ * it's unlikely to do more than allow the null byte out.
+ */
+ outb(iobase + com_data, 0);
+ DELAY((1 + 2) * 1000000 / (9600 / 10));
+
+ /*
+ * Turn off loopback mode so that the interrupt gate works again
+ * (MCR_IENABLE was hidden). This should leave the device driving
+ * an interrupt line high. It doesn't matter if the interrupt
+ * line oscillates while we are not looking at it, since interrupts
+ * are disabled.
+ */
+/* EXTRA DELAY? */
+ outb(iobase + com_mcr, mcr_image);
+
+ /*
+ * Check that
+ * o the CFCR, IER and MCR in UART hold the values written to them
+ * (the values happen to be all distinct - this is good for
+ * avoiding false positive tests from bus echoes).
+ * o an output interrupt is generated and its vector is correct.
+ * o the interrupt goes away when the IIR in the UART is read.
+ */
+/* EXTRA DELAY? */
+ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS;
+ failures[1] = inb(iobase + com_ier) - IER_ETXRDY;
+ failures[2] = inb(iobase + com_mcr) - mcr_image;
+ DELAY(1000); /* XXX */
+ if (idev->id_irq != 0)
+#ifdef PC98
+ failures[3] = pc98_irq_pending(idev) ? 0 : 1;
+#else
+ failures[3] = isa_irq_pending(idev) ? 0 : 1;
+#endif
+ failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY;
+ DELAY(1000); /* XXX */
+#ifdef PC98
+ if (idev->id_irq != 0)
+ failures[5] = pc98_irq_pending(idev) ? 1 : 0;
+#else
+ if (idev->id_irq != 0)
+ failures[5] = isa_irq_pending(idev) ? 1 : 0;
+#endif
+ failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
+
+ /*
+ * Turn off all device interrupts and check that they go off properly.
+ * Leave MCR_IENABLE alone. For ports without a master port, it gates
+ * the OUT2 output of the UART to
+ * the ICU input. Closing the gate would give a floating ICU input
+ * (unless there is another device driving at) and spurious interrupts.
+ * (On the system that this was first tested on, the input floats high
+ * and gives a (masked) interrupt as soon as the gate is closed.)
+ */
+ outb(iobase + com_ier, 0);
+ outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */
+ failures[7] = inb(iobase + com_ier);
+ DELAY(1000); /* XXX */
+#ifdef PC98
+ if (idev->id_irq != 0)
+ failures[8] = pc98_irq_pending(idev) ? 1 : 0;
+#else
+ if (idev->id_irq != 0)
+ failures[8] = isa_irq_pending(idev) ? 1 : 0;
+#endif
+ failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
+
+#ifdef PC98
+ outb(IO_ICU1 + 2, imen); /* XXX */
+#else
+ outb(IO_ICU1 + 1, imen); /* XXX */
+#endif
+ enable_intr();
+
+ result = IO_COMSIZE;
+ for (fn = 0; fn < sizeof failures; ++fn)
+ if (failures[fn]) {
+ outb(iobase + com_mcr, 0);
+ result = 0;
+ if (COM_VERBOSE(dev))
+ printf("sio%d: probe test %d failed\n",
+ dev->id_unit, fn);
+ }
+ return (result);
+}
+
+#ifdef COM_ESP
+static int
+espattach(isdp, com, esp_port)
+ struct isa_device *isdp;
+ struct com_s *com;
+ Port_t esp_port;
+{
+ u_char dips;
+ u_char val;
+
+ /*
+ * Check the ESP-specific I/O port to see if we're an ESP
+ * card. If not, return failure immediately.
+ */
+ if ((inb(esp_port) & 0xf3) == 0) {
+ printf(" port 0x%x is not an ESP board?\n", esp_port);
+ return (0);
+ }
+
+ /*
+ * We've got something that claims to be a Hayes ESP card.
+ * Let's hope so.
+ */
+
+ /* Get the dip-switch configuration */
+ outb(esp_port + ESP_CMD1, ESP_GETDIPS);
+ dips = inb(esp_port + ESP_STATUS1);
+
+ /*
+ * Bits 0,1 of dips say which COM port we are.
+ */
+ if (com->iobase == likely_com_ports[dips & 0x03])
+ printf(" : ESP");
+ else {
+ printf(" esp_port has com %d\n", dips & 0x03);
+ return (0);
+ }
+
+ /*
+ * Check for ESP version 2.0 or later: bits 4,5,6 = 010.
+ */
+ outb(esp_port + ESP_CMD1, ESP_GETTEST);
+ val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */
+ val = inb(esp_port + ESP_STATUS2);
+ if ((val & 0x70) < 0x20) {
+ printf("-old (%o)", val & 0x70);
+ return (0);
+ }
+
+ /*
+ * Check for ability to emulate 16550: bit 7 == 1
+ */
+ if ((dips & 0x80) == 0) {
+ printf(" slave");
+ return (0);
+ }
+
+ /*
+ * Okay, we seem to be a Hayes ESP card. Whee.
+ */
+ com->esp = TRUE;
+ com->esp_port = esp_port;
+ return (1);
+}
+#endif /* COM_ESP */
+
+static int
+sioattach(isdp)
+#ifdef PC98
+ struct pc98_device *isdp;
+#else
+ struct isa_device *isdp;
+#endif
+{
+ struct com_s *com;
+ dev_t dev;
+#ifdef COM_ESP
+ Port_t *espp;
+#endif
+ Port_t iobase;
+ int s;
+ int unit;
+
+ isdp->id_ri_flags |= RI_FAST;
+ iobase = isdp->id_iobase;
+ unit = isdp->id_unit;
+ com = malloc(sizeof *com, M_TTYS, M_NOWAIT);
+ if (com == NULL)
+ return (0);
+
+ /*
+ * sioprobe() has initialized the device registers as follows:
+ * o cfcr = CFCR_8BITS.
+ * It is most important that CFCR_DLAB is off, so that the
+ * data port is not hidden when we enable interrupts.
+ * o ier = 0.
+ * Interrupts are only enabled when the line is open.
+ * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible
+ * interrupt control register or the config specifies no irq.
+ * Keeping MCR_DTR and MCR_RTS off might stop the external
+ * device from sending before we are ready.
+ */
+ bzero(com, sizeof *com);
+ com->unit = unit;
+ com->cfcr_image = CFCR_8BITS;
+ com->dtr_wait = 3 * hz;
+ com->loses_outints = COM_LOSESOUTINTS(isdp) != 0;
+ com->no_irq = isdp->id_irq == 0;
+ com->tx_fifo_size = 1;
+ com->iptr = com->ibuf = com->ibuf1;
+ com->ibufend = com->ibuf1 + RS_IBUFSIZE;
+ com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
+ com->obufs[0].l_head = com->obuf1;
+ com->obufs[1].l_head = com->obuf2;
+
+ com->iobase = iobase;
+#ifdef PC98
+ if(pc98_set_ioport(com, iobase) == -1)
+ if((iobase & 0x0f0) == 0xd0) {
+ com->pc98_if_type = MC16550;
+ com->data_port = iobase + com_data;
+ com->int_id_port = iobase + com_iir;
+ com->modem_ctl_port = iobase + com_mcr;
+ com->mcr_image = inb(com->modem_ctl_port);
+ com->line_status_port = iobase + com_lsr;
+ com->modem_status_port = iobase + com_msr;
+ }
+#else /* not PC98 */
+ com->data_port = iobase + com_data;
+ com->int_id_port = iobase + com_iir;
+ com->modem_ctl_port = iobase + com_mcr;
+ com->mcr_image = inb(com->modem_ctl_port);
+ com->line_status_port = iobase + com_lsr;
+ com->modem_status_port = iobase + com_msr;
+#endif
+
+ /*
+ * We don't use all the flags from <sys/ttydefaults.h> since they
+ * are only relevant for logins. It's important to have echo off
+ * initially so that the line doesn't start blathering before the
+ * echo flag can be turned off.
+ */
+ com->it_in.c_iflag = 0;
+ com->it_in.c_oflag = 0;
+ com->it_in.c_cflag = TTYDEF_CFLAG;
+ com->it_in.c_lflag = 0;
+ if (unit == comconsole) {
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ DELAY(100000);
+#endif
+ com->it_in.c_iflag = TTYDEF_IFLAG;
+ com->it_in.c_oflag = TTYDEF_OFLAG;
+ com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL;
+ com->it_in.c_lflag = TTYDEF_LFLAG;
+ com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
+ }
+ termioschars(&com->it_in);
+ com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
+ com->it_out = com->it_in;
+
+ /* attempt to determine UART type */
+ printf("sio%d: type", unit);
+
+#ifdef DSI_SOFT_MODEM
+ if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) {
+ printf(" Digicom Systems, Inc. SoftModem");
+ kdc_sio[unit].kdc_description =
+ "Serial port: Digicom Systems SoftModem";
+ goto determined_type;
+ }
+#endif /* DSI_SOFT_MODEM */
+
+#ifndef PC98
+#ifdef COM_MULTIPORT
+ if (!COM_ISMULTIPORT(isdp))
+#endif
+ {
+ u_char scr;
+ u_char scr1;
+ u_char scr2;
+
+ scr = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0xa5);
+ scr1 = inb(iobase + com_scr);
+ outb(iobase + com_scr, 0x5a);
+ scr2 = inb(iobase + com_scr);
+ outb(iobase + com_scr, scr);
+ if (scr1 != 0xa5 || scr2 != 0x5a) {
+ printf(" 8250");
+ kdc_sio[unit].kdc_description =
+ "Serial port: National 8250 or compatible";
+ goto determined_type;
+ }
+ }
+#endif /* !PC98 */
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type)){
+ com_int_TxRx_disable( com );
+ com_cflag_and_speed_set( com, com->it_in.c_cflag,
+ comdefaultrate );
+ com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE );
+ com_send_break_off( com );
+ switch(com->pc98_if_type){
+ case COM_IF_INTERNAL:
+ printf(" 8251 (internal)");
+ kdc_sio[unit].kdc_description =
+ "Serial port: PC-9801 internal";
+ break;
+#ifdef COM_IF_PC9861K
+ case COM_IF_PC9861K:
+ printf(" 8251 (PC9861K)");
+ kdc_sio[unit].kdc_description =
+ "Serial port: PC-9861K";
+ break;
+#endif
+#ifdef COM_IF_PIO9032B
+ case COM_IF_PIO9032B:
+ printf(" 8251 (PIO9032B)");
+ kdc_sio[unit].kdc_description =
+ "Serial port: PIO9032B";
+ break;
+#endif
+#ifdef COM_IF_B98_01
+ case COM_IF_B98_01:
+ printf(" 8251 (B98_01)");
+ kdc_sio[unit].kdc_description =
+ "Serial port: B98_01";
+ break;
+#endif
+ }
+ } else {
+#endif /* PC98 */
+ outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH);
+ DELAY(100);
+ switch (inb(com->int_id_port) & IIR_FIFO_MASK) {
+ case FIFO_RX_LOW:
+ printf(" 16450");
+ kdc_sio[unit].kdc_description =
+ "Serial port: National 16450 or compatible";
+ break;
+ case FIFO_RX_MEDL:
+ printf(" 16450?");
+ kdc_sio[unit].kdc_description =
+ "Serial port: maybe National 16450";
+ break;
+ case FIFO_RX_MEDH:
+ printf(" 16550?");
+ kdc_sio[unit].kdc_description =
+ "Serial port: maybe National 16550";
+ break;
+ case FIFO_RX_HIGH:
+ printf(" 16550A");
+ if (COM_NOFIFO(isdp)) {
+ printf(" fifo disabled");
+ kdc_sio[unit].kdc_description =
+ "Serial port: National 16550A, FIFO disabled";
+ } else {
+ com->hasfifo = TRUE;
+ com->tx_fifo_size = 16;
+ kdc_sio[unit].kdc_description =
+ "Serial port: National 16550A or compatible";
+#ifdef COM_ESP
+ for (espp = likely_esp_ports; *espp != 0; espp++)
+ if (espattach(isdp, com, *espp)) {
+ com->tx_fifo_size = 1024;
+ kdc_sio[unit].kdc_description =
+ "Serial port: Hayes ESP";
+ break;
+ }
+#endif
+ }
+#if 0
+ /*
+ * Check for the Startech ST16C650 chip.
+ * it has a shadow register under the com_iir,
+ * which can only be accessed when cfcr == 0xff
+ */
+ {
+ u_char i, j;
+
+ i = inb(iobase + com_iir);
+ outb(iobase + com_cfcr, 0xff);
+ outb(iobase + com_iir, 0x0);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ j = inb(iobase + com_iir);
+ outb(iobase + com_iir, i);
+ if (i != j) {
+ printf(" 16550A");
+ } else {
+ com->tx_fifo_size = 32;
+ printf(" 16650");
+ kdc_sio[unit].kdc_description =
+ "Serial port: Startech 16C650 or similar";
+ }
+ if (!com->tx_fifo_size)
+ printf(" fifo disabled");
+ }
+#endif
+ break;
+ }
+#ifdef COM_ESP
+ if (com->esp) {
+ outb(iobase + com_fifo,
+ FIFO_DMA_MODE | FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST
+ | FIFO_RX_MEDH);
+
+ /* Set 16550 compatibility mode. */
+ outb(com->esp_port + ESP_CMD1, ESP_SETMODE);
+ outb(com->esp_port + ESP_CMD2,
+ ESP_MODE_SCALE | ESP_MODE_RTS | ESP_MODE_FIFO);
+
+ /* Set RTS/CTS flow control. */
+ outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE);
+ outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS);
+ outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS);
+
+ /* Set flow-control levels. */
+ outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW);
+ outb(com->esp_port + ESP_CMD2, HIBYTE(768));
+ outb(com->esp_port + ESP_CMD2, LOBYTE(768));
+ outb(com->esp_port + ESP_CMD2, HIBYTE(512));
+ outb(com->esp_port + ESP_CMD2, LOBYTE(512));
+ }
+#endif /* COM_ESP */
+ outb(iobase + com_fifo, 0);
+determined_type: ;
+
+#ifdef COM_MULTIPORT
+ if (COM_ISMULTIPORT(isdp)) {
+ com->multiport = TRUE;
+ printf(" (multiport");
+ if (unit == COM_MPMASTER(isdp))
+ printf(" master");
+ printf(")");
+#ifdef PC98
+ com->no_irq = find_pc98dev(pc98_devtab_tty, &siodriver,
+#else
+ com->no_irq = find_isadev(isa_devtab_tty, &siodriver,
+#endif
+ COM_MPMASTER(isdp))->id_irq == 0;
+ }
+#endif /* COM_MULTIPORT */
+#ifdef PC98
+ }
+#endif
+ printf("\n");
+
+ kdc_sio[unit].kdc_state = (unit == comconsole) ? DC_BUSY : DC_IDLE;
+
+ s = spltty();
+ com_addr(unit) = com;
+ splx(s);
+
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev, &sio_cdevsw, NULL);
+#ifdef DEVFS
+ /* devsw, minor, type, uid, gid, perm, fmt, ... */
+ com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw,
+ unit, DV_CHR,
+ UID_ROOT, GID_WHEEL, 0600, "ttyd%n", unit);
+ com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw,
+ unit | CONTROL_INIT_STATE, DV_CHR,
+ UID_ROOT, GID_WHEEL, 0600, "ttyid%n", unit);
+ com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw,
+ unit | CONTROL_LOCK_STATE, DV_CHR,
+ UID_ROOT, GID_WHEEL, 0600, "ttyld%n", unit);
+ com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw,
+ unit | CALLOUT_MASK, DV_CHR,
+ UID_UUCP, GID_DIALER, 0660, "cuaa%n", unit);
+ com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw,
+ unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR,
+ UID_UUCP, GID_DIALER, 0660, "cuaia%n", unit);
+ com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw,
+ unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR,
+ UID_UUCP, GID_DIALER, 0660, "cuala%n", unit);
+#endif
+ return (1);
+}
+
+static int
+sioopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct com_s *com;
+ int error;
+ Port_t iobase;
+ int mynor;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ mynor = minor(dev);
+ unit = MINOR_TO_UNIT(mynor);
+ if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL)
+ return (ENXIO);
+ if (com->gone)
+ return (ENXIO);
+ if (mynor & CONTROL_MASK)
+ return (0);
+#if 0 /* XXX */
+ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]);
+#else
+ tp = com->tp = &sio_tty[unit];
+#endif
+ s = spltty();
+ /*
+ * We jump to this label after all non-interrupted sleeps to pick
+ * up any changes of the device state.
+ */
+open_top:
+ while (com->state & CS_DTR_OFF) {
+ error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0);
+ if (com_addr(unit) == NULL)
+ return (ENXIO);
+ if (error != 0 || com->gone)
+ goto out;
+ }
+ kdc_sio[unit].kdc_state = DC_BUSY;
+ if (tp->t_state & TS_ISOPEN) {
+ /*
+ * The device is open, so everything has been initialized.
+ * Handle conflicts.
+ */
+ if (mynor & CALLOUT_MASK) {
+ if (!com->active_out) {
+ error = EBUSY;
+ goto out;
+ }
+ } else {
+ if (com->active_out) {
+ if (flag & O_NONBLOCK) {
+ error = EBUSY;
+ goto out;
+ }
+ error = tsleep(&com->active_out,
+ TTIPRI | PCATCH, "siobi", 0);
+ if (com_addr(unit) == NULL)
+ return (ENXIO);
+ if (error != 0 || com->gone)
+ goto out;
+ goto open_top;
+ }
+ }
+ if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
+ error = EBUSY;
+ goto out;
+ }
+ } else {
+ /*
+ * The device isn't open, so there are no conflicts.
+ * Initialize it. Initialization is done twice in many
+ * cases: to preempt sleeping callin opens if we are
+ * callout, and to complete a callin open after DCD rises.
+ */
+ tp->t_oproc = comstart;
+ tp->t_param = comparam;
+ tp->t_dev = dev;
+ tp->t_termios = mynor & CALLOUT_MASK
+ ? com->it_out : com->it_in;
+#ifdef PC98
+ if(!IS_8251(com->pc98_if_type))
+#endif
+ (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET);
+ com->poll = com->no_irq;
+ com->poll_output = com->loses_outints;
+ ++com->wopeners;
+ error = comparam(tp, &tp->t_termios);
+ --com->wopeners;
+ if (error != 0)
+ goto out;
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type)){
+ com_tiocm_bis(com, TIOCM_DTR|TIOCM_RTS);
+ pc98_msrint_start(dev);
+ }
+#endif
+ /*
+ * XXX we should goto open_top if comparam() slept.
+ */
+ ttsetwater(tp);
+ iobase = com->iobase;
+ if (com->hasfifo) {
+ /*
+ * (Re)enable and drain fifos.
+ *
+ * Certain SMC chips cause problems if the fifos
+ * are enabled while input is ready. Turn off the
+ * fifo if necessary to clear the input. We test
+ * the input ready bit after enabling the fifos
+ * since we've already enabled them in comparam()
+ * and to handle races between enabling and fresh
+ * input.
+ */
+ while (TRUE) {
+ outb(iobase + com_fifo,
+ FIFO_RCV_RST | FIFO_XMT_RST
+ | com->fifo_image);
+ DELAY(100);
+ if (!(inb(com->line_status_port) & LSR_RXRDY))
+ break;
+ outb(iobase + com_fifo, 0);
+ DELAY(100);
+ (void) inb(com->data_port);
+ }
+ }
+
+ disable_intr();
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type)){
+ com_tiocm_bis(com, TIOCM_LE);
+ com->pc98_prev_modem_status =
+ pc98_get_modem_status(com);
+ com_int_Rx_enable(com);
+ } else {
+#endif
+ (void) inb(com->line_status_port);
+ (void) inb(com->data_port);
+ com->prev_modem_status = com->last_modem_status
+ = inb(com->modem_status_port);
+ outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS
+ | IER_EMSC);
+#ifdef PC98
+ }
+#endif
+ enable_intr();
+ /*
+ * Handle initial DCD. Callout devices get a fake initial
+ * DCD (trapdoor DCD). If we are callout, then any sleeping
+ * callin opens get woken up and resume sleeping on "siobi"
+ * instead of "siodcd".
+ */
+ /*
+ * XXX `mynor & CALLOUT_MASK' should be
+ * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where
+ * TRAPDOOR_CARRIER is the default initial state for callout
+ * devices and SOFT_CARRIER is like CLOCAL except it hides
+ * the true carrier.
+ */
+#ifdef PC98
+ if ((IS_8251(com->pc98_if_type) &&
+ (pc98_get_modem_status(com) & TIOCM_CAR)) ||
+ (!IS_8251(com->pc98_if_type) &&
+ (com->prev_modem_status & MSR_DCD)) ||
+ mynor & CALLOUT_MASK)
+#else
+ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK)
+#endif
+ (*linesw[tp->t_line].l_modem)(tp, 1);
+ }
+ /*
+ * Wait for DCD if necessary.
+ */
+ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK)
+ && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
+ ++com->wopeners;
+ error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0);
+ if (com_addr(unit) == NULL)
+ return (ENXIO);
+ --com->wopeners;
+ if (error != 0 || com->gone)
+ goto out;
+ goto open_top;
+ }
+ error = (*linesw[tp->t_line].l_open)(dev, tp);
+ disc_optim(tp, &tp->t_termios, com);
+ if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK)
+ com->active_out = TRUE;
+ siosettimeout();
+out:
+ splx(s);
+ if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0)
+ comhardclose(com);
+ return (error);
+}
+
+static int
+sioclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag;
+ int mode;
+ struct proc *p;
+{
+ struct com_s *com;
+ int mynor;
+ int s;
+ struct tty *tp;
+
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (0);
+ com = com_addr(MINOR_TO_UNIT(mynor));
+ tp = com->tp;
+ s = spltty();
+ (*linesw[tp->t_line].l_close)(tp, flag);
+#ifdef PC98
+ com->modem_checking = 0;
+#endif
+ disc_optim(tp, &tp->t_termios, com);
+ siostop(tp, FREAD | FWRITE);
+ comhardclose(com);
+ ttyclose(tp);
+ siosettimeout();
+ splx(s);
+ if (com->gone) {
+ printf("sio%d: gone\n", com->unit);
+ s = spltty();
+ com_addr(com->unit) = 0;
+ bzero(tp,sizeof *tp);
+ bzero(com,sizeof *com);
+ free(com,M_TTYS);
+ splx(s);
+ }
+ return (0);
+}
+
+static void
+comhardclose(com)
+ struct com_s *com;
+{
+ Port_t iobase;
+ int s;
+ struct tty *tp;
+ int unit;
+
+ unit = com->unit;
+ iobase = com->iobase;
+ s = spltty();
+ com->poll = FALSE;
+ com->poll_output = FALSE;
+ com->do_timestamp = 0;
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ com_send_break_off(com);
+ else
+#endif
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+ {
+#ifdef PC98
+ int tmp;
+ if(IS_8251(com->pc98_if_type))
+ com_int_TxRx_disable(com);
+ else
+#endif
+ outb(iobase + com_ier, 0);
+ tp = com->tp;
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ tmp = pc98_get_modem_status(com) & TIOCM_CAR;
+ else
+ tmp = com->prev_modem_status & MSR_DCD;
+#endif
+ if (tp->t_cflag & HUPCL
+ /*
+ * XXX we will miss any carrier drop between here and the
+ * next open. Perhaps we should watch DCD even when the
+ * port is closed; it is not sufficient to check it at
+ * the next open because it might go up and down while
+ * we're not watching.
+ */
+ || !com->active_out
+#ifdef PC98
+ && !(tmp)
+#else
+ && !(com->prev_modem_status & MSR_DCD)
+#endif
+ && !(com->it_in.c_cflag & CLOCAL)
+ || !(tp->t_state & TS_ISOPEN)) {
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ com_tiocm_bic(com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE);
+ else
+#endif
+ (void)commctl(com, TIOCM_DTR, DMBIC);
+ if (com->dtr_wait != 0) {
+ timeout(siodtrwakeup, com, com->dtr_wait);
+ com->state |= CS_DTR_OFF;
+ }
+ }
+#ifdef PC98
+ else {
+ if(IS_8251(com->pc98_if_type))
+ com_tiocm_bic(com, TIOCM_LE );
+ }
+#endif
+ }
+ if (com->hasfifo) {
+ /*
+ * Disable fifos so that they are off after controlled
+ * reboots. Some BIOSes fail to detect 16550s when the
+ * fifos are enabled.
+ */
+ outb(iobase + com_fifo, 0);
+ }
+ com->active_out = FALSE;
+ wakeup(&com->active_out);
+ wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */
+ if (!(com->state & CS_DTR_OFF) && unit != comconsole)
+ kdc_sio[unit].kdc_state = DC_IDLE;
+ splx(s);
+}
+
+static int
+sioread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ int mynor;
+ int unit;
+ struct tty *tp;
+
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (ENODEV);
+ unit = MINOR_TO_UNIT(mynor);
+ if (com_addr(unit)->gone)
+ return (ENODEV);
+ tp = com_addr(unit)->tp;
+ return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+static int
+siowrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ int mynor;
+ struct tty *tp;
+ int unit;
+
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (ENODEV);
+
+ unit = MINOR_TO_UNIT(mynor);
+ if (com_addr(unit)->gone)
+ return (ENODEV);
+ tp = com_addr(unit)->tp;
+ /*
+ * (XXX) We disallow virtual consoles if the physical console is
+ * a serial port. This is in case there is a display attached that
+ * is not the console. In that situation we don't need/want the X
+ * server taking over the console.
+ */
+ if (constty != NULL && unit == comconsole)
+ constty = NULL;
+ return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+static void
+siodtrwakeup(chan)
+ void *chan;
+{
+ struct com_s *com;
+
+ com = (struct com_s *)chan;
+ com->state &= ~CS_DTR_OFF;
+ if (com->unit != comconsole)
+ kdc_sio[com->unit].kdc_state = DC_IDLE;
+ wakeup(&com->dtr_wait);
+}
+
+/* Interrupt routine for timekeeping purposes */
+void
+siointrts(unit)
+ int unit;
+{
+ /*
+ * XXX microtime() reenables CPU interrupts. We can't afford to
+ * be interrupted and don't want to slow down microtime(), so lock
+ * out interrupts in another way.
+ */
+#ifdef PC98
+ outb(IO_ICU1 + 2, 0xff);
+#else /* IBM-PC */
+ outb(IO_ICU1 + 1, 0xff);
+#endif /* PC98 */
+ microtime(&intr_timestamp);
+ disable_intr();
+#ifdef PC98
+ outb(IO_ICU1 + 2, imen);
+#else /* IBM_PC */
+ outb(IO_ICU1 + 1, imen);
+#endif /* PC98 */
+
+ siointr(unit);
+}
+
+void
+siointr(unit)
+ int unit;
+{
+#ifndef COM_MULTIPORT
+ siointr1(com_addr(unit));
+#else /* COM_MULTIPORT */
+ struct com_s *com;
+ bool_t possibly_more_intrs;
+
+ /*
+ * Loop until there is no activity on any port. This is necessary
+ * to get an interrupt edge more than to avoid another interrupt.
+ * If the IRQ signal is just an OR of the IRQ signals from several
+ * devices, then the edge from one may be lost because another is
+ * on.
+ */
+ do {
+ possibly_more_intrs = FALSE;
+ for (unit = 0; unit < NSIO; ++unit) {
+ com = com_addr(unit);
+#ifdef PC98
+ if (com != NULL
+ && !com->gone
+ && IS_8251(com->pc98_if_type)){
+ siointr1(com);
+ } else
+#endif /* PC98 */
+ if (com != NULL
+ && !com->gone
+ && (inb(com->int_id_port) & IIR_IMASK)
+ != IIR_NOPEND) {
+ siointr1(com);
+ possibly_more_intrs = TRUE;
+ }
+ }
+ } while (possibly_more_intrs);
+#endif /* COM_MULTIPORT */
+}
+
+static void
+siointr1(com)
+ struct com_s *com;
+{
+ u_char line_status;
+ u_char modem_status;
+ u_char *ioptr;
+ u_char recv_data;
+#ifdef PC98
+ u_char tmp=0;
+recv_data=0;
+#endif /* PC98 */
+
+ if (com->do_timestamp)
+ /* XXX a little bloat here... */
+ com->timestamp = intr_timestamp;
+ while (TRUE) {
+#ifdef PC98
+status_read:;
+ if (IS_8251(com->pc98_if_type)) {
+ tmp = inb(com->sts_port);
+more_intr:
+ line_status = 0;
+ if (tmp & STS8251_TxRDY) line_status |= LSR_TXRDY;
+ if (tmp & STS8251_RxRDY) line_status |= LSR_RXRDY;
+ if (tmp & STS8251_TxEMP) line_status |= LSR_TSRE;
+ if (tmp & STS8251_PE) line_status |= LSR_PE;
+ if (tmp & STS8251_OE) line_status |= LSR_OE;
+ if (tmp & STS8251_FE) line_status |= LSR_FE;
+ if (tmp & STS8251_BD_SD) line_status |= LSR_BI;
+ } else
+#endif /* PC98 */
+ line_status = inb(com->line_status_port);
+
+ /* input event? (check first to help avoid overruns) */
+ while (line_status & LSR_RCV_MASK) {
+ /* break/unnattached error bits or real input? */
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type)){
+ recv_data = inb(com->data_port);
+ if(tmp & 0x78){
+ pc98_i8251_or_cmd(com,CMD8251_ER);
+ recv_data = 0;
+ }
+ } else {
+#endif /* PC98 */
+ if (!(line_status & LSR_RXRDY))
+ recv_data = 0;
+ else
+ recv_data = inb(com->data_port);
+#ifdef PC98
+ }
+#endif
+ if (line_status & (LSR_PE|LSR_FE|LSR_BI)) {
+#ifdef DDB
+#ifdef BREAK_TO_DEBUGGER
+ if (line_status & LSR_BI
+ && com->unit == comconsole) {
+ Debugger("serial console break");
+ goto cont;
+ }
+#endif
+#endif
+ /*
+ Don't store PE if IGNPAR and BI if IGNBRK,
+ this hack allows "raw" tty optimization
+ works even if IGN* is set.
+ */
+ if ( com->tp == NULL
+ || !(com->tp->t_state & TS_ISOPEN)
+ || (line_status & (LSR_PE|LSR_FE))
+ && (com->tp->t_iflag & IGNPAR)
+ || (line_status & LSR_BI)
+ && (com->tp->t_iflag & IGNBRK))
+ goto cont;
+ if ( (line_status & (LSR_PE|LSR_FE))
+ && (com->tp->t_state & TS_CAN_BYPASS_L_RINT)
+ && ((line_status & LSR_FE)
+ || (line_status & LSR_PE)
+ && (com->tp->t_iflag & INPCK)))
+ recv_data = 0;
+ }
+
+ ++com->bytes_in;
+ if (com->hotchar != 0 && recv_data == com->hotchar)
+ setsofttty();
+ ioptr = com->iptr;
+ if (ioptr >= com->ibufend)
+ CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW);
+ else {
+ ++com_events;
+ schedsofttty();
+#if 0 /* for testing input latency vs efficiency */
+if (com->iptr - com->ibuf == 8)
+ setsofttty();
+#endif
+ ioptr[0] = recv_data;
+ ioptr[CE_INPUT_OFFSET] = line_status;
+ com->iptr = ++ioptr;
+ if (ioptr == com->ihighwater
+ && com->state & CS_RTS_IFLOW)
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ com_tiocm_bic(com, TIOCM_RTS);
+ else
+#endif
+ outb(com->modem_ctl_port,
+ com->mcr_image &= ~MCR_RTS);
+ if (line_status & LSR_OE)
+ CE_RECORD(com, CE_OVERRUN);
+ }
+cont:
+ /*
+ * "& 0x7F" is to avoid the gcc-1.40 generating a slow
+ * jump from the top of the loop to here
+ */
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ goto status_read;
+ else
+#endif
+ line_status = inb(com->line_status_port) & 0x7F;
+ }
+
+ /* modem status change? (always check before doing output) */
+#ifdef PC98
+ if(!IS_8251(com->pc98_if_type)){
+#endif
+ modem_status = inb(com->modem_status_port);
+ if (modem_status != com->last_modem_status) {
+ /*
+ * Schedule high level to handle DCD changes. Note
+ * that we don't use the delta bits anywhere. Some
+ * UARTs mess them up, and it's easy to remember the
+ * previous bits and calculate the delta.
+ */
+ com->last_modem_status = modem_status;
+ if (!(com->state & CS_CHECKMSR)) {
+ com_events += LOTS_OF_EVENTS;
+ com->state |= CS_CHECKMSR;
+ setsofttty();
+ }
+
+ /* handle CTS change immediately for crisp flow ctl */
+ if (com->state & CS_CTS_OFLOW) {
+ if (modem_status & MSR_CTS)
+ com->state |= CS_ODEVREADY;
+ else
+ com->state &= ~CS_ODEVREADY;
+ }
+ }
+#ifdef PC98
+ }
+#endif
+
+ /* output queued and everything ready? */
+ if (line_status & LSR_TXRDY
+ && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
+ ioptr = com->obufq.l_head;
+ if (com->tx_fifo_size > 1) {
+ u_int ocount;
+
+ ocount = com->obufq.l_tail - ioptr;
+ if (ocount > com->tx_fifo_size)
+ ocount = com->tx_fifo_size;
+ com->bytes_out += ocount;
+ do
+ outb(com->data_port, *ioptr++);
+ while (--ocount != 0);
+ } else {
+ outb(com->data_port, *ioptr++);
+ ++com->bytes_out;
+ }
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ if ( !(pc98_check_i8251_interrupt(com) & IEN_TxFLAG) )
+ com_int_Tx_enable(com);
+#endif
+ com->obufq.l_head = ioptr;
+ if (ioptr >= com->obufq.l_tail) {
+ struct lbq *qp;
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG )
+ com_int_Tx_disable(com);
+#endif
+ qp = com->obufq.l_next;
+ qp->l_queued = FALSE;
+ qp = qp->l_next;
+ if (qp != NULL) {
+ com->obufq.l_head = qp->l_head;
+ com->obufq.l_tail = qp->l_tail;
+ com->obufq.l_next = qp;
+ } else {
+ /* output just completed */
+ com->state &= ~CS_BUSY;
+ }
+ if (!(com->state & CS_ODONE)) {
+ com_events += LOTS_OF_EVENTS;
+ com->state |= CS_ODONE;
+ setsofttty(); /* handle at high level ASAP */
+ }
+ }
+ }
+#ifdef PC98
+ else if (line_status & LSR_TXRDY) {
+ if(IS_8251(com->pc98_if_type))
+ if ( pc98_check_i8251_interrupt(com) & IEN_TxFLAG )
+ com_int_Tx_disable(com);
+ }
+ if(IS_8251(com->pc98_if_type))
+ if ((tmp = inb(com->sts_port)) & STS8251_RxRDY)
+ goto more_intr;
+#endif
+
+ /* finished? */
+#ifndef COM_MULTIPORT
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ return;
+#endif
+ if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
+#endif /* COM_MULTIPORT */
+ return;
+ }
+}
+
+static int
+sioioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ struct com_s *com;
+ int error;
+ Port_t iobase;
+ int mynor;
+ int s;
+ struct tty *tp;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ int oldcmd;
+ struct termios term;
+#endif
+
+ mynor = minor(dev);
+ com = com_addr(MINOR_TO_UNIT(mynor));
+ if (com->gone)
+ return (ENODEV);
+ iobase = com->iobase;
+ if (mynor & CONTROL_MASK) {
+ struct termios *ct;
+
+ switch (mynor & CONTROL_MASK) {
+ case CONTROL_INIT_STATE:
+ ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in;
+ break;
+ case CONTROL_LOCK_STATE:
+ ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in;
+ break;
+ default:
+ return (ENODEV); /* /dev/nodev */
+ }
+ switch (cmd) {
+ case TIOCSETA:
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0)
+ return (error);
+ *ct = *(struct termios *)data;
+ return (0);
+ case TIOCGETA:
+ *(struct termios *)data = *ct;
+ return (0);
+ case TIOCGETD:
+ *(int *)data = TTYDISC;
+ return (0);
+ case TIOCGWINSZ:
+ bzero(data, sizeof(struct winsize));
+ return (0);
+#ifdef DSI_SOFT_MODEM
+ /*
+ * Download micro-code to Digicom modem.
+ */
+ case TIOCDSIMICROCODE:
+ {
+ u_long l;
+ u_char *p,*pi;
+
+ pi = (u_char*)(*(caddr_t*)data);
+ error = copyin(pi,&l,sizeof l);
+ if(error)
+ {return error;};
+ pi += sizeof l;
+
+ p = malloc(l,M_TEMP,M_NOWAIT);
+ if(!p)
+ {return ENOBUFS;}
+ error = copyin(pi,p,l);
+ if(error)
+ {free(p,M_TEMP); return error;};
+ if(error = LoadSoftModem(
+ MINOR_TO_UNIT(mynor),iobase,l,p))
+ {free(p,M_TEMP); return error;}
+ free(p,M_TEMP);
+ return(0);
+ }
+#endif /* DSI_SOFT_MODEM */
+ default:
+ return (ENOTTY);
+ }
+ }
+ tp = com->tp;
+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
+ term = tp->t_termios;
+ oldcmd = cmd;
+ error = ttsetcompat(tp, &cmd, data, &term);
+ if (error != 0)
+ return (error);
+ if (cmd != oldcmd)
+ data = (caddr_t)&term;
+#endif
+ if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) {
+ int cc;
+ struct termios *dt = (struct termios *)data;
+ struct termios *lt = mynor & CALLOUT_MASK
+ ? &com->lt_out : &com->lt_in;
+
+ dt->c_iflag = (tp->t_iflag & lt->c_iflag)
+ | (dt->c_iflag & ~lt->c_iflag);
+ dt->c_oflag = (tp->t_oflag & lt->c_oflag)
+ | (dt->c_oflag & ~lt->c_oflag);
+ dt->c_cflag = (tp->t_cflag & lt->c_cflag)
+ | (dt->c_cflag & ~lt->c_cflag);
+ dt->c_lflag = (tp->t_lflag & lt->c_lflag)
+ | (dt->c_lflag & ~lt->c_lflag);
+ for (cc = 0; cc < NCCS; ++cc)
+ if (lt->c_cc[cc] != 0)
+ dt->c_cc[cc] = tp->t_cc[cc];
+ if (lt->c_ispeed != 0)
+ dt->c_ispeed = tp->t_ispeed;
+ if (lt->c_ospeed != 0)
+ dt->c_ospeed = tp->t_ospeed;
+ }
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return (error);
+ s = spltty();
+ error = ttioctl(tp, cmd, data, flag);
+ disc_optim(tp, &tp->t_termios, com);
+ if (error >= 0) {
+ splx(s);
+ return (error);
+ }
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type)){
+ switch (cmd) {
+ case TIOCSBRK:
+ com_send_break_on( com );
+ break;
+ case TIOCCBRK:
+ com_send_break_off( com );
+ break;
+ case TIOCSDTR:
+ (void)commctl(com, TIOCM_DTR, DMBIS);
+ break;
+ case TIOCCDTR:
+ (void)commctl(com, TIOCM_DTR, DMBIC);
+ break;
+ case TIOCMSET:
+ (void)commctl(com, *(int *)data, DMSET);
+ break;
+ case TIOCMBIS:
+ (void)commctl(com, *(int *)data, DMBIS);
+ break;
+ case TIOCMBIC:
+ (void)commctl(com, *(int *)data, DMBIC);
+ break;
+ case TIOCMGET:
+ *(int *)data = commctl(com, 0, DMGET);
+ break;
+ case TIOCMSDTRWAIT:
+ /* must be root since the wait applies to following logins */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ com->dtr_wait = *(int *)data * hz / 100;
+ break;
+ case TIOCMGDTRWAIT:
+ *(int *)data = com->dtr_wait * 100 / hz;
+ break;
+ case TIOCTIMESTAMP:
+ com->do_timestamp = TRUE;
+ *(struct timeval *)data = com->timestamp;
+ break;
+ default:
+ splx(s);
+ return (ENOTTY);
+ }
+ } else {
+#endif
+ switch (cmd) {
+ case TIOCSBRK:
+ outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK);
+ break;
+ case TIOCCBRK:
+ outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
+ break;
+ case TIOCSDTR:
+ (void)commctl(com, TIOCM_DTR, DMBIS);
+ break;
+ case TIOCCDTR:
+ (void)commctl(com, TIOCM_DTR, DMBIC);
+ break;
+ case TIOCMSET:
+ (void)commctl(com, *(int *)data, DMSET);
+ break;
+ case TIOCMBIS:
+ (void)commctl(com, *(int *)data, DMBIS);
+ break;
+ case TIOCMBIC:
+ (void)commctl(com, *(int *)data, DMBIC);
+ break;
+ case TIOCMGET:
+ *(int *)data = commctl(com, 0, DMGET);
+ break;
+ case TIOCMSDTRWAIT:
+ /* must be root since the wait applies to following logins */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0) {
+ splx(s);
+ return (error);
+ }
+ com->dtr_wait = *(int *)data * hz / 100;
+ break;
+ case TIOCMGDTRWAIT:
+ *(int *)data = com->dtr_wait * 100 / hz;
+ break;
+ case TIOCTIMESTAMP:
+ com->do_timestamp = TRUE;
+ *(struct timeval *)data = com->timestamp;
+ break;
+ default:
+ splx(s);
+ return (ENOTTY);
+ }
+#ifdef PC98
+ }
+#endif
+ splx(s);
+ return (0);
+}
+
+void
+siopoll()
+{
+ int unit;
+
+ if (com_events == 0)
+ return;
+repeat:
+ for (unit = 0; unit < NSIO; ++unit) {
+ u_char *buf;
+ struct com_s *com;
+ u_char *ibuf;
+ int incc;
+ struct tty *tp;
+#ifdef PC98
+ int tmp;
+#endif
+
+ com = com_addr(unit);
+ if (com == NULL)
+ continue;
+ if (com->gone)
+ continue;
+ tp = com->tp;
+ if (tp == NULL) {
+ /*
+ * XXX forget any events related to closed devices
+ * (actually never opened devices) so that we don't
+ * loop.
+ */
+ disable_intr();
+ incc = com->iptr - com->ibuf;
+ com->iptr = com->ibuf;
+ if (com->state & CS_CHECKMSR) {
+ incc += LOTS_OF_EVENTS;
+ com->state &= ~CS_CHECKMSR;
+ }
+ com_events -= incc;
+ enable_intr();
+ if (incc != 0)
+ log(LOG_DEBUG,
+ "sio%d: %d events for device with no tp\n",
+ unit, incc);
+ continue;
+ }
+
+ /* switch the role of the low-level input buffers */
+ if (com->iptr == (ibuf = com->ibuf)) {
+ buf = NULL; /* not used, but compiler can't tell */
+ incc = 0;
+ } else {
+ buf = ibuf;
+ disable_intr();
+ incc = com->iptr - buf;
+ com_events -= incc;
+ if (ibuf == com->ibuf1)
+ ibuf = com->ibuf2;
+ else
+ ibuf = com->ibuf1;
+ com->ibufend = ibuf + RS_IBUFSIZE;
+ com->ihighwater = ibuf + RS_IHIGHWATER;
+ com->iptr = ibuf;
+
+ /*
+ * There is now room for another low-level buffer full
+ * of input, so enable RTS if it is now disabled and
+ * there is room in the high-level buffer.
+ */
+ /*
+ * XXX this used not to look at CS_RTS_IFLOW. The
+ * change is to allow full control of MCR_RTS via
+ * ioctls after turning CS_RTS_IFLOW off. Check
+ * for races. We shouldn't allow the ioctls while
+ * CS_RTS_IFLOW is on.
+ */
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ tmp = com_tiocm_get(com) & TIOCM_RTS;
+ else
+ tmp = com->mcr_image & MCR_RTS;
+#endif
+ if ((com->state & CS_RTS_IFLOW)
+#ifdef PC98
+ && !(tmp)
+#else
+ && !(com->mcr_image & MCR_RTS)
+#endif
+ && !(tp->t_state & TS_TBLOCK))
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ com_tiocm_bis(com, TIOCM_RTS);
+ else
+#endif
+ outb(com->modem_ctl_port,
+ com->mcr_image |= MCR_RTS);
+ enable_intr();
+ com->ibuf = ibuf;
+ }
+
+ if (com->state & CS_CHECKMSR) {
+ u_char delta_modem_status;
+
+#ifdef PC98
+ if(!IS_8251(com->pc98_if_type)){
+#endif
+ disable_intr();
+ delta_modem_status = com->last_modem_status
+ ^ com->prev_modem_status;
+ com->prev_modem_status = com->last_modem_status;
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~CS_CHECKMSR;
+ enable_intr();
+ if (delta_modem_status & MSR_DCD)
+ (*linesw[tp->t_line].l_modem)
+ (tp, com->prev_modem_status & MSR_DCD);
+#ifdef PC98
+ }
+#endif
+ }
+ if (com->state & CS_ODONE) {
+ disable_intr();
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~CS_ODONE;
+ if (!(com->state & CS_BUSY))
+ com->tp->t_state &= ~TS_BUSY;
+ enable_intr();
+ (*linesw[tp->t_line].l_start)(tp);
+ }
+ if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
+ continue;
+ /*
+ * Avoid the grotesquely inefficient lineswitch routine
+ * (ttyinput) in "raw" mode. It usually takes about 450
+ * instructions (that's without canonical processing or echo!).
+ * slinput is reasonably fast (usually 40 instructions plus
+ * call overhead).
+ */
+ if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
+ if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER
+ && (com->state & CS_RTS_IFLOW
+ || tp->t_iflag & IXOFF)
+ && !(tp->t_state & TS_TBLOCK))
+ ttyblock(tp);
+ tk_nin += incc;
+ tk_rawcc += incc;
+ tp->t_rawcc += incc;
+ com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+ += b_to_q((char *)buf, incc, &tp->t_rawq);
+ ttwakeup(tp);
+ if (tp->t_state & TS_TTSTOP
+ && (tp->t_iflag & IXANY
+ || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
+ tp->t_state &= ~TS_TTSTOP;
+ tp->t_lflag &= ~FLUSHO;
+ comstart(tp);
+ }
+ } else {
+ do {
+ u_char line_status;
+ int recv_data;
+
+ line_status = (u_char) buf[CE_INPUT_OFFSET];
+ recv_data = (u_char) *buf++;
+ if (line_status
+ & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
+ if (line_status & LSR_BI)
+ recv_data |= TTY_BI;
+ if (line_status & LSR_FE)
+ recv_data |= TTY_FE;
+ if (line_status & LSR_OE)
+ recv_data |= TTY_OE;
+ if (line_status & LSR_PE)
+ recv_data |= TTY_PE;
+ }
+ (*linesw[tp->t_line].l_rint)(recv_data, tp);
+ } while (--incc > 0);
+ }
+ if (com_events == 0)
+ break;
+ }
+ if (com_events >= LOTS_OF_EVENTS)
+ goto repeat;
+}
+
+static int
+comparam(tp, t)
+ struct tty *tp;
+ struct termios *t;
+{
+ u_int cfcr;
+ int cflag;
+ struct com_s *com;
+ int divisor;
+ int error;
+ Port_t iobase;
+ int s;
+ int unit;
+ int txtimeout;
+#ifdef PC98
+ Port_t tmp_port;
+ int tmp_flg;
+#endif
+
+#ifdef PC98
+ cfcr = 0;
+ unit = DEV_TO_UNIT(tp->t_dev);
+ com = com_addr(unit);
+ iobase = com->iobase;
+ if(IS_8251(com->pc98_if_type)) {
+ divisor = pc98_ttspeedtab(com, t->c_ospeed);
+ } else
+#endif
+ /* do historical conversions */
+ if (t->c_ispeed == 0)
+ t->c_ispeed = t->c_ospeed;
+
+ /* check requested parameters */
+ divisor = ttspeedtab(t->c_ospeed, comspeedtab);
+ if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed)
+ return (EINVAL);
+
+ /* parameters are OK, convert them to the com struct and the device */
+#ifndef PC98
+ unit = DEV_TO_UNIT(tp->t_dev);
+ com = com_addr(unit);
+ iobase = com->iobase;
+#endif
+ s = spltty();
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type)){
+ if(divisor == 0){
+ com_int_TxRx_disable( com );
+ com_tiocm_bic( com, TIOCM_DTR|TIOCM_RTS|TIOCM_LE );
+ }
+ } else {
+#endif
+ if (divisor == 0)
+ (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */
+ else
+ (void)commctl(com, TIOCM_DTR, DMBIS);
+#ifdef PC98
+ }
+#endif
+ cflag = t->c_cflag;
+#ifdef PC98
+ if(!IS_8251(com->pc98_if_type)){
+#endif
+ switch (cflag & CSIZE) {
+ case CS5:
+ cfcr = CFCR_5BITS;
+ break;
+ case CS6:
+ cfcr = CFCR_6BITS;
+ break;
+ case CS7:
+ cfcr = CFCR_7BITS;
+ break;
+ default:
+ cfcr = CFCR_8BITS;
+ break;
+ }
+ if (cflag & PARENB) {
+ cfcr |= CFCR_PENAB;
+ if (!(cflag & PARODD))
+ cfcr |= CFCR_PEVEN;
+ }
+ if (cflag & CSTOPB)
+ cfcr |= CFCR_STOPB;
+
+ if (com->hasfifo && divisor != 0) {
+ /*
+ * Use a fifo trigger level low enough so that the input
+ * latency from the fifo is less than about 16 msec and
+ * the total latency is less than about 30 msec. These
+ * latencies are reasonable for humans. Serial comms
+ * protocols shouldn't expect anything better since modem
+ * latencies are larger.
+ */
+ com->fifo_image = t->c_ospeed <= 4800
+ ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH;
+ outb(iobase + com_fifo, com->fifo_image);
+ }
+
+ /*
+ * Some UARTs lock up if the divisor latch registers are selected
+ * while the UART is doing output (they refuse to transmit anything
+ * more until given a hard reset). Fix this by stopping filling
+ * the device buffers and waiting for them to drain. Reading the
+ * line status port outside of siointr1() might lose some receiver
+ * error bits, but that is acceptable here.
+ */
+#ifdef PC98
+ }
+#endif
+ disable_intr();
+retry:
+ com->state &= ~CS_TTGO;
+ txtimeout = tp->t_timeout;
+ enable_intr();
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type)){
+ tmp_port = com->sts_port;
+ tmp_flg = (STS8251_TxRDY|STS8251_TxEMP);
+ } else {
+ tmp_port = com->line_status_port;
+ tmp_flg = (LSR_TSRE|LSR_TXRDY);
+ }
+ while ((inb(tmp_port) & tmp_flg) != tmp_flg) {
+#else
+ while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY)) {
+#endif
+ tp->t_state |= TS_SO_OCOMPLETE;
+ error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH,
+ "siotx", hz / 100);
+ if ( txtimeout != 0
+ && (!error || error == EAGAIN)
+ && (txtimeout -= hz / 100) <= 0
+ )
+ error = EIO;
+ if (com->gone)
+ error = ENODEV;
+ if (error != 0 && error != EAGAIN) {
+ if (!(tp->t_state & TS_TTSTOP)) {
+ disable_intr();
+ com->state |= CS_TTGO;
+ enable_intr();
+ }
+ splx(s);
+ return (error);
+ }
+ }
+
+ disable_intr(); /* very important while com_data is hidden */
+
+ /*
+ * XXX - clearing CS_TTGO is not sufficient to stop further output,
+ * because siopoll() calls comstart() which usually sets it again
+ * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be
+ * sufficient, for similar reasons.
+ */
+#ifdef PC98
+ if ((inb(tmp_port) & tmp_flg) != tmp_flg)
+#else
+ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY))
+#endif
+ goto retry;
+
+#ifdef PC98
+ if(!IS_8251(com->pc98_if_type)){
+#endif
+ if (divisor != 0) {
+ outb(iobase + com_cfcr, cfcr | CFCR_DLAB);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ }
+ outb(iobase + com_cfcr, com->cfcr_image = cfcr);
+#ifdef PC98
+ } else
+ com_cflag_and_speed_set(com, cflag, t->c_ospeed);
+#endif
+ if (!(tp->t_state & TS_TTSTOP))
+ com->state |= CS_TTGO;
+ if (cflag & CRTS_IFLOW)
+ com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */
+ else
+ com->state &= ~CS_RTS_IFLOW;
+
+ /*
+ * Set up state to handle output flow control.
+ * XXX - worth handling MDMBUF (DCD) flow control at the lowest level?
+ * Now has 10+ msec latency, while CTS flow has 50- usec latency.
+ */
+ com->state |= CS_ODEVREADY;
+ com->state &= ~CS_CTS_OFLOW;
+ if (cflag & CCTS_OFLOW) {
+ com->state |= CS_CTS_OFLOW;
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type)){
+ if (!(pc98_get_modem_status(com) & TIOCM_CTS))
+ com->state &= ~CS_ODEVREADY;
+ } else {
+#endif
+ if (!(com->last_modem_status & MSR_CTS))
+ com->state &= ~CS_ODEVREADY;
+#ifdef PC98
+ }
+#endif
+ }
+ /* XXX shouldn't call functions while intrs are disabled. */
+ disc_optim(tp, t, com);
+ /*
+ * Recover from fiddling with CS_TTGO. We used to call siointr1()
+ * unconditionally, but that defeated the careful discarding of
+ * stale input in sioopen().
+ */
+ if (com->state >= (CS_BUSY | CS_TTGO))
+ siointr1(com);
+
+ enable_intr();
+ splx(s);
+ return (0);
+}
+
+static void
+comstart(tp)
+ struct tty *tp;
+{
+ struct com_s *com;
+ int s;
+ int unit;
+#ifdef PC98
+ int tmp;
+#endif
+
+ unit = DEV_TO_UNIT(tp->t_dev);
+ com = com_addr(unit);
+ s = spltty();
+ disable_intr();
+ if (tp->t_state & TS_TTSTOP)
+ com->state &= ~CS_TTGO;
+ else
+ com->state |= CS_TTGO;
+ if (tp->t_state & TS_TBLOCK) {
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ tmp = com_tiocm_get(com) & TIOCM_RTS;
+ else
+ tmp = com->mcr_image & MCR_RTS;
+ if (tmp && (com->state & CS_RTS_IFLOW))
+#else
+ if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
+#endif
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ com_tiocm_bic(com, TIOCM_RTS);
+ else
+#endif
+ outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
+ } else {
+ /*
+ * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it
+ * appropriately in comparam() if RTS-flow is being changed.
+ * Check for races.
+ */
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ tmp = com_tiocm_get(com) & TIOCM_RTS;
+ else
+ tmp = com->mcr_image & MCR_RTS;
+ if (!(tmp) && com->iptr < com->ihighwater)
+#else
+ if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater)
+#endif
+#ifdef PC98
+ if(IS_8251(com->pc98_if_type))
+ com_tiocm_bis(com, TIOCM_RTS);
+ else
+#endif
+ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
+ }
+ enable_intr();
+ if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
+#ifdef PC98
+/* if(IS_8251(com->pc98_if_type))
+ com_int_Tx_enable(com); */
+#endif
+ splx(s);
+ return;
+ }
+ if (tp->t_outq.c_cc != 0) {
+ struct lbq *qp;
+ struct lbq *next;
+
+ if (!com->obufs[0].l_queued) {
+ com->obufs[0].l_tail
+ = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1,
+ sizeof com->obuf1);
+ com->obufs[0].l_next = NULL;
+ com->obufs[0].l_queued = TRUE;
+ disable_intr();
+ if (com->state & CS_BUSY) {
+ qp = com->obufq.l_next;
+ while ((next = qp->l_next) != NULL)
+ qp = next;
+ qp->l_next = &com->obufs[0];
+ } else {
+ com->obufq.l_head = com->obufs[0].l_head;
+ com->obufq.l_tail = com->obufs[0].l_tail;
+ com->obufq.l_next = &com->obufs[0];
+ com->state |= CS_BUSY;
+ }
+ enable_intr();
+ }
+ if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) {
+ com->obufs[1].l_tail
+ = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2,
+ sizeof com->obuf2);
+ com->obufs[1].l_next = NULL;
+ com->obufs[1].l_queued = TRUE;
+ disable_intr();
+ if (com->state & CS_BUSY) {
+ qp = com->obufq.l_next;
+ while ((next = qp->l_next) != NULL)
+ qp = next;
+ qp->l_next = &com->obufs[1];
+ } else {
+ com->obufq.l_head = com->obufs[1].l_head;
+ com->obufq.l_tail = com->obufs[1].l_tail;
+ com->obufq.l_next = &com->obufs[1];
+ com->state |= CS_BUSY;
+ }
+ enable_intr();
+ }
+ tp->t_state |= TS_BUSY;
+ }
+ disable_intr();
+ if (com->state >= (CS_BUSY | CS_TTGO))
+ siointr1(com); /* fake interrupt to start output */
+ enable_intr();
+#ifdef PC98
+/* if(IS_8251(com->pc98_if_type))
+ com_int_Tx_enable(com); */
+#endif
+ ttwwakeup(tp);
+ splx(s);
+}
+
+static void
+siostop(tp, rw)
+ struct tty *tp;
+ int rw;
+{
+ struct com_s *com;
+
+ com = com_addr(DEV_TO_UNIT(tp->t_dev));
+ if (com->gone)
+ return;
+ disable_intr();
+ if (rw & FWRITE) {
+ com->obufs[0].l_queued = FALSE;
+ com->obufs[1].l_queued = FALSE;
+ if (com->state & CS_ODONE)
+ com_events -= LOTS_OF_EVENTS;
+ com->state &= ~(CS_ODONE | CS_BUSY);
+ com->tp->t_state &= ~TS_BUSY;
+ }
+ if (rw & FREAD) {
+ com_events -= (com->iptr - com->ibuf);
+ com->iptr = com->ibuf;
+ }
+ enable_intr();
+ comstart(tp);
+
+ /* XXX should clear h/w fifos too. */
+}
+
+static struct tty *
+siodevtotty(dev)
+ dev_t dev;
+{
+ int mynor;
+ int unit;
+
+ mynor = minor(dev);
+ if (mynor & CONTROL_MASK)
+ return (NULL);
+ unit = MINOR_TO_UNIT(mynor);
+ if ((u_int) unit >= NSIO)
+ return (NULL);
+ return (&sio_tty[unit]);
+}
+
+static int
+commctl(com, bits, how)
+ struct com_s *com;
+ int bits;
+ int how;
+{
+ int mcr;
+ int msr;
+
+ if (how == DMGET) {
+ bits = TIOCM_LE; /* XXX - always enabled while open */
+ mcr = com->mcr_image;
+ if (mcr & MCR_DTR)
+ bits |= TIOCM_DTR;
+ if (mcr & MCR_RTS)
+ bits |= TIOCM_RTS;
+ msr = com->prev_modem_status;
+ if (msr & MSR_CTS)
+ bits |= TIOCM_CTS;
+ if (msr & MSR_DCD)
+ bits |= TIOCM_CD;
+ if (msr & MSR_DSR)
+ bits |= TIOCM_DSR;
+ /*
+ * XXX - MSR_RI is naturally volatile, and we make MSR_TERI
+ * more volatile by reading the modem status a lot. Perhaps
+ * we should latch both bits until the status is read here.
+ */
+ if (msr & (MSR_RI | MSR_TERI))
+ bits |= TIOCM_RI;
+ return (bits);
+ }
+ mcr = 0;
+ if (bits & TIOCM_DTR)
+ mcr |= MCR_DTR;
+ if (bits & TIOCM_RTS)
+ mcr |= MCR_RTS;
+ if (com->gone)
+ return(0);
+ disable_intr();
+ switch (how) {
+ case DMSET:
+ outb(com->modem_ctl_port,
+ com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE));
+ break;
+ case DMBIS:
+ outb(com->modem_ctl_port, com->mcr_image |= mcr);
+ break;
+ case DMBIC:
+ outb(com->modem_ctl_port, com->mcr_image &= ~mcr);
+ break;
+ }
+ enable_intr();
+ return (0);
+}
+
+static void
+siosettimeout()
+{
+ struct com_s *com;
+ bool_t someopen;
+ int unit;
+
+ /*
+ * Set our timeout period to 1 second if no polled devices are open.
+ * Otherwise set it to max(1/200, 1/hz).
+ * Enable timeouts iff some device is open.
+ */
+ untimeout(comwakeup, (void *)NULL);
+ sio_timeout = hz;
+ someopen = FALSE;
+ for (unit = 0; unit < NSIO; ++unit) {
+ com = com_addr(unit);
+ if (com != NULL && com->tp != NULL
+ && com->tp->t_state & TS_ISOPEN && !com->gone) {
+ someopen = TRUE;
+ if (com->poll || com->poll_output) {
+ sio_timeout = hz > 200 ? hz / 200 : 1;
+ break;
+ }
+ }
+ }
+ if (someopen) {
+ sio_timeouts_until_log = hz / sio_timeout;
+ timeout(comwakeup, (void *)NULL, sio_timeout);
+ } else {
+ /* Flush error messages, if any. */
+ sio_timeouts_until_log = 1;
+ comwakeup((void *)NULL);
+ untimeout(comwakeup, (void *)NULL);
+ }
+}
+
+static void
+comwakeup(chan)
+ void *chan;
+{
+ struct com_s *com;
+ int unit;
+
+ timeout(comwakeup, (void *)NULL, sio_timeout);
+
+ /*
+ * Recover from lost output interrupts.
+ * Poll any lines that don't use interrupts.
+ */
+ for (unit = 0; unit < NSIO; ++unit) {
+ com = com_addr(unit);
+ if (com != NULL && !com->gone
+ && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {
+ disable_intr();
+ siointr1(com);
+ enable_intr();
+ }
+ }
+
+ /*
+ * Check for and log errors, but not too often.
+ */
+ if (--sio_timeouts_until_log > 0)
+ return;
+ sio_timeouts_until_log = hz / sio_timeout;
+ for (unit = 0; unit < NSIO; ++unit) {
+ int errnum;
+
+ com = com_addr(unit);
+ if (com == NULL)
+ continue;
+ if (com->gone)
+ continue;
+ for (errnum = 0; errnum < CE_NTYPES; ++errnum) {
+ u_int delta;
+ u_long total;
+
+ disable_intr();
+ delta = com->delta_error_counts[errnum];
+ com->delta_error_counts[errnum] = 0;
+ enable_intr();
+ if (delta == 0)
+ continue;
+ total = com->error_counts[errnum] += delta;
+ log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n",
+ unit, delta, error_desc[errnum],
+ delta == 1 ? "" : "s", total);
+ }
+ }
+}
+
+#ifdef PC98
+/* commint is called when modem control line changes */
+static void
+commint(dev_t dev)
+{
+ register struct tty *tp;
+ int stat,delta;
+ struct com_s *com;
+ int mynor,unit;
+
+ mynor = minor(dev);
+ unit = MINOR_TO_UNIT(mynor);
+ com = com_addr(unit);
+ tp = com->tp;
+
+ stat = com_tiocm_get(com);
+ delta = com_tiocm_get_delta(com);
+
+ if (com->state & CS_CTS_OFLOW) {
+ if (stat & TIOCM_CTS)
+ com->state |= CS_ODEVREADY;
+ else
+ com->state &= ~CS_ODEVREADY;
+ }
+ if ((delta & TIOCM_CAR) && (mynor & CALLOUT_MASK) == 0) {
+ if (stat & TIOCM_CAR )
+ (void)(*linesw[tp->t_line].l_modem)(tp, 1);
+ else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
+ /* negate DTR, RTS */
+ com_tiocm_bic(com, (tp->t_cflag & HUPCL) ?
+ TIOCM_DTR|TIOCM_RTS|TIOCM_LE : TIOCM_LE );
+ /* disable IENABLE */
+ com_int_TxRx_disable( com );
+ }
+ }
+}
+#endif
+
+static void
+disc_optim(tp, t, com)
+ struct tty *tp;
+ struct termios *t;
+ struct com_s *com;
+{
+ if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
+ && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
+ && (!(t->c_iflag & PARMRK)
+ || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
+ && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
+ && linesw[tp->t_line].l_rint == ttyinput)
+ tp->t_state |= TS_CAN_BYPASS_L_RINT;
+ else
+ tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
+ /*
+ * Prepare to reduce input latency for packet
+ * discplines with a end of packet character.
+ */
+ if (tp->t_line == SLIPDISC)
+ com->hotchar = 0xc0;
+ else if (tp->t_line == PPPDISC)
+ com->hotchar = 0x7e;
+ else
+ com->hotchar = 0;
+}
+
+/*
+ * Following are all routines needed for SIO to act as console
+ */
+#include <machine/cons.h>
+
+struct siocnstate {
+ u_char dlbl;
+ u_char dlbh;
+ u_char ier;
+ u_char cfcr;
+ u_char mcr;
+};
+
+static Port_t siocniobase;
+
+static void siocnclose __P((struct siocnstate *sp));
+static void siocnopen __P((struct siocnstate *sp));
+static void siocntxwait __P((void));
+
+static void
+siocntxwait()
+{
+ int timo;
+#ifdef PC98
+ int tmp;
+#endif
+
+ /*
+ * Wait for any pending transmission to finish. Required to avoid
+ * the UART lockup bug when the speed is changed, and for normal
+ * transmits.
+ */
+ timo = 100000;
+ while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
+ != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
+ ;
+}
+
+static void
+siocnopen(sp)
+ struct siocnstate *sp;
+{
+ int divisor;
+ Port_t iobase;
+
+ /*
+ * Save all the device control registers except the fifo register
+ * and set our default ones (cs8 -parenb speed=comdefaultrate).
+ * We can't save the fifo register since it is read-only.
+ */
+ iobase = siocniobase;
+ sp->ier = inb(iobase + com_ier);
+ outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */
+ siocntxwait();
+ sp->cfcr = inb(iobase + com_cfcr);
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ sp->dlbl = inb(iobase + com_dlbl);
+ sp->dlbh = inb(iobase + com_dlbh);
+ divisor = ttspeedtab(comdefaultrate, comspeedtab);
+ outb(iobase + com_dlbl, divisor & 0xFF);
+ outb(iobase + com_dlbh, (u_int) divisor >> 8);
+ outb(iobase + com_cfcr, CFCR_8BITS);
+ sp->mcr = inb(iobase + com_mcr);
+ /*
+ * We don't want interrupts, but must be careful not to "disable"
+ * them by clearing the MCR_IENABLE bit, since that might cause
+ * an interrupt by floating the IRQ line.
+ */
+ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS);
+}
+
+static void
+siocnclose(sp)
+ struct siocnstate *sp;
+{
+ Port_t iobase;
+
+ /*
+ * Restore the device control registers.
+ */
+ siocntxwait();
+ iobase = siocniobase;
+ outb(iobase + com_cfcr, CFCR_DLAB);
+ outb(iobase + com_dlbl, sp->dlbl);
+ outb(iobase + com_dlbh, sp->dlbh);
+ outb(iobase + com_cfcr, sp->cfcr);
+ /*
+ * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them.
+ */
+ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
+ outb(iobase + com_ier, sp->ier);
+}
+
+void
+siocnprobe(cp)
+ struct consdev *cp;
+{
+ int unit;
+
+ /* XXX: ick */
+ unit = DEV_TO_UNIT(CONUNIT);
+ siocniobase = CONADDR;
+
+ /* make sure hardware exists? XXX */
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(CDEV_MAJOR, unit);
+#ifdef COMCONSOLE
+ cp->cn_pri = CN_REMOTE; /* Force a serial port console */
+#else
+ cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
+#endif
+}
+
+void
+siocninit(cp)
+ struct consdev *cp;
+{
+ comconsole = DEV_TO_UNIT(cp->cn_dev);
+}
+
+int
+siocncheckc(dev)
+ dev_t dev;
+{
+ int c;
+ Port_t iobase;
+ int s;
+ struct siocnstate sp;
+
+ iobase = siocniobase;
+ s = spltty();
+ siocnopen(&sp);
+ if (inb(iobase + com_lsr) & LSR_RXRDY)
+ c = inb(iobase + com_data);
+ else
+ c = 0;
+ siocnclose(&sp);
+ splx(s);
+ return (c);
+}
+
+
+int
+siocngetc(dev)
+ dev_t dev;
+{
+ int c;
+ Port_t iobase;
+ int s;
+ struct siocnstate sp;
+
+ iobase = siocniobase;
+ s = spltty();
+ siocnopen(&sp);
+ while (!(inb(iobase + com_lsr) & LSR_RXRDY))
+ ;
+ c = inb(iobase + com_data);
+ siocnclose(&sp);
+ splx(s);
+ return (c);
+}
+
+void
+siocnputc(dev, c)
+ dev_t dev;
+ int c;
+{
+ int s;
+ struct siocnstate sp;
+
+ s = spltty();
+ siocnopen(&sp);
+ siocntxwait();
+ outb(siocniobase + com_data, c);
+ siocnclose(&sp);
+ splx(s);
+}
+
+#ifdef DSI_SOFT_MODEM
+/*
+ * The magic code to download microcode to a "Connection 14.4+Fax"
+ * modem from Digicom Systems Inc. Very magic.
+ */
+
+#define DSI_ERROR(str) { ptr = str; goto error; }
+static int
+LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr)
+{
+ int int_c,int_k;
+ int data_0188, data_0187;
+
+ /*
+ * First see if it is a DSI SoftModem
+ */
+ if(!((inb(base_io+7) ^ inb(base_io+7) & 0x80)))
+ return ENODEV;
+
+ data_0188 = inb(base_io+4);
+ data_0187 = inb(base_io+3);
+ outb(base_io+3,0x80);
+ outb(base_io+4,0x0C);
+ outb(base_io+0,0x31);
+ outb(base_io+1,0x8C);
+ outb(base_io+7,0x10);
+ outb(base_io+7,0x19);
+
+ if(0x18 != (inb(base_io+7) & 0x1A))
+ DSI_ERROR("dsp bus not granted");
+
+ if(0x01 != (inb(base_io+7) & 0x01)) {
+ outb(base_io+7,0x18);
+ outb(base_io+7,0x19);
+ if(0x01 != (inb(base_io+7) & 0x01))
+ DSI_ERROR("program mem not granted");
+ }
+
+ int_c = 0;
+
+ while(1) {
+ if(int_c >= 7 || size <= 0x1800)
+ break;
+
+ for(int_k = 0 ; int_k < 0x800; int_k++) {
+ outb(base_io+0,*ptr++);
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+
+ size -= 0x1800;
+ int_c++;
+ }
+
+ if(size > 0x1800) {
+ outb(base_io+7,0x18);
+ outb(base_io+7,0x19);
+ if(0x00 != (inb(base_io+7) & 0x01))
+ DSI_ERROR("program data not granted");
+
+ for(int_k = 0 ; int_k < 0x800; int_k++) {
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,0);
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+
+ size -= 0x1800;
+
+ while(size > 0x1800) {
+ for(int_k = 0 ; int_k < 0xC00; int_k++) {
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+ size -= 0x1800;
+ }
+
+ if(size < 0x1800) {
+ for(int_k=0;int_k<size/2;int_k++) {
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+ }
+
+ } else if (size > 0) {
+ if(int_c == 7) {
+ outb(base_io+7,0x18);
+ outb(base_io+7,0x19);
+ if(0x00 != (inb(base_io+7) & 0x01))
+ DSI_ERROR("program data not granted");
+ for(int_k = 0 ; int_k < size/3; int_k++) {
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,0);
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+ } else {
+ for(int_k = 0 ; int_k < size/3; int_k++) {
+ outb(base_io+0,*ptr++);
+ outb(base_io+1,*ptr++);
+ outb(base_io+2,*ptr++);
+ }
+ }
+ }
+ outb(base_io+7,0x11);
+ outb(base_io+7,3);
+
+ outb(base_io+4,data_0188 & 0xfb);
+
+ outb(base_io+3,data_0187);
+
+ return 0;
+error:
+ printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",unit,ptr);
+ outb(base_io+7,0x00); \
+ outb(base_io+3,data_0187); \
+ outb(base_io+4,data_0188); \
+ return EIO;
+}
+#endif /* DSI_SOFT_MODEM */
+#ifdef PC98
+/*
+ * pc98 local function
+ */
+
+static void
+com_tiocm_set(struct com_s *com, int msr)
+{
+ int s;
+ int tmp = 0;
+ int mask = CMD8251_TxEN|CMD8251_RxEN|CMD8251_DTR|CMD8251_RTS;
+
+ s=spltty();
+ com->pc98_prev_modem_status = ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) )
+ | ( com->pc98_prev_modem_status & ~(TIOCM_LE|TIOCM_DTR|TIOCM_RTS) );
+ tmp |= (CMD8251_TxEN|CMD8251_RxEN);
+ if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR;
+ if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS;
+ pc98_i8251_clear_or_cmd( com, mask, tmp );
+ splx(s);
+}
+
+static void
+com_tiocm_bis(struct com_s *com, int msr)
+{
+ int s;
+ int tmp = 0;
+
+ s=spltty();
+ com->pc98_prev_modem_status |= ( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) );
+ tmp |= CMD8251_TxEN|CMD8251_RxEN;
+ if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR;
+ if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS;
+
+ pc98_i8251_or_cmd( com, tmp );
+ splx(s);
+}
+
+static void
+com_tiocm_bic(struct com_s *com, int msr)
+{
+ int s;
+ int tmp = msr;
+
+ s=spltty();
+ com->pc98_prev_modem_status &= ~( msr & (TIOCM_LE|TIOCM_DTR|TIOCM_RTS) );
+ if ( msr & TIOCM_DTR ) tmp |= CMD8251_DTR;
+ if ( msr & TIOCM_RTS ) tmp |= CMD8251_RTS;
+
+ pc98_i8251_clear_cmd( com, tmp );
+ splx(s);
+}
+
+static int
+com_tiocm_get(struct com_s *com)
+{
+ return( com->pc98_prev_modem_status );
+}
+
+static int
+com_tiocm_get_delta(struct com_s *com)
+{
+ int tmp;
+
+ tmp = com->pc98_modem_delta;
+ com->pc98_modem_delta = 0;
+ return( tmp );
+}
+
+/* convert to TIOCM_?? ( ioctl.h ) */
+static int
+pc98_get_modem_status(struct com_s *com)
+{
+ int stat, stat2;
+ register int msr;
+
+ int ret;
+
+ stat = inb(com->sts_port);
+ stat2 = inb(com->in_modem_port);
+ msr = com->pc98_prev_modem_status
+ & ~(TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS);
+ if ( !(stat2 & CICSCD_CD) ) msr |= TIOCM_CAR;
+ if ( !(stat2 & CICSCD_CI) ) msr |= TIOCM_RI;
+ if ( stat & STS8251_DSR ) msr |= TIOCM_DSR;
+ if ( !(stat2 & CICSCD_CS) ) msr |= TIOCM_CTS;
+#if COM_CARRIER_DETECT_EMULATE
+ if ( msr & (TIOCM_DSR|TIOCM_CTS) ) {
+ msr |= TIOCM_CAR;
+ }
+#endif
+ return(msr);
+}
+
+static void
+pc98_check_msr(void* chan)
+{
+ int msr, delta;
+ int s;
+ register struct tty *tp;
+ struct com_s *com;
+ int mynor;
+ int unit;
+ dev_t dev;
+
+ dev=(dev_t)chan;
+ mynor = minor(dev);
+ unit = MINOR_TO_UNIT(mynor);
+ com = com_addr(unit);
+ tp = com->tp;
+
+ s = spltty();
+ msr = pc98_get_modem_status(com);
+ /* make change flag */
+ delta = msr ^ com->pc98_prev_modem_status;
+ if ( delta & TIOCM_CAR ) {
+ if ( com->modem_car_chg_timer ) {
+ if ( -- com->modem_car_chg_timer )
+ msr ^= TIOCM_CAR;
+ } else {
+ if ( com->modem_car_chg_timer = ( msr & TIOCM_CAR ) ?
+ DCD_ON_RECOGNITION : DCD_OFF_TOLERANCE )
+ msr ^= TIOCM_CAR;
+ }
+ } else
+ com->modem_car_chg_timer = 0;
+ delta = ( msr ^ com->pc98_prev_modem_status ) &
+ (TIOCM_CAR|TIOCM_RI|TIOCM_DSR|TIOCM_CTS);
+ com->pc98_prev_modem_status = msr;
+ delta = ( com->pc98_modem_delta |= delta );
+ splx(s);
+ if ( com->modem_checking || (tp->t_state & (TS_ISOPEN)) ) {
+ if ( delta ) {
+ commint(dev);
+ }
+ timeout(pc98_check_msr, (caddr_t)dev,
+ PC98_CHECK_MODEM_INTERVAL);
+ } else {
+ com->modem_checking = 0;
+ }
+}
+
+static void
+pc98_msrint_start(dev_t dev)
+{
+ struct com_s *com;
+ int mynor;
+ int unit;
+ int s = spltty();
+
+ mynor = minor(dev);
+ unit = MINOR_TO_UNIT(mynor);
+ com = com_addr(unit);
+ /* modem control line check routine envoke interval is 1/10 sec */
+ if ( com->modem_checking == 0 ) {
+ com->pc98_prev_modem_status = pc98_get_modem_status(com);
+ com->pc98_modem_delta = 0;
+ timeout(pc98_check_msr, (caddr_t)dev,
+ PC98_CHECK_MODEM_INTERVAL);
+ com->modem_checking = 1;
+ }
+ splx(s);
+}
+
+static void
+pc98_disable_i8251_interrupt(struct com_s *com, int mod)
+{
+ /* disable interrupt */
+ register int tmp;
+
+ mod |= ~(IEN_Tx|IEN_TxEMP|IEN_Rx);
+ COM_INT_DISABLE
+ tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx);
+ outb( com->intr_ctrl_port, (com->intr_enable&=~mod) | tmp );
+ COM_INT_ENABLE
+}
+
+static void
+pc98_enable_i8251_interrupt(struct com_s *com, int mod)
+{
+ register int tmp;
+
+ COM_INT_DISABLE
+ tmp = inb( com->intr_ctrl_port ) & ~(IEN_Tx|IEN_TxEMP|IEN_Rx);
+ outb( com->intr_ctrl_port, (com->intr_enable|=mod) | tmp );
+ COM_INT_ENABLE
+}
+
+static int
+pc98_check_i8251_interrupt(struct com_s *com)
+{
+ return ( com->intr_enable & 0x07 );
+}
+
+static void
+pc98_i8251_clear_cmd(struct com_s *com, int x)
+{
+ int tmp;
+
+ COM_INT_DISABLE
+ tmp = com->pc98_prev_siocmd & ~(x);
+ outb(com->cmd_port, tmp);
+ com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH);
+ COM_INT_ENABLE
+}
+
+static void
+pc98_i8251_or_cmd(struct com_s *com, int x)
+{
+ int tmp;
+
+ COM_INT_DISABLE
+ tmp = com->pc98_prev_siocmd | (x);
+ outb(com->cmd_port, tmp);
+ com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH);
+ COM_INT_ENABLE
+}
+
+static void
+pc98_i8251_set_cmd(struct com_s *com, int x)
+{
+ int tmp;
+
+ COM_INT_DISABLE
+ tmp = (x);
+ outb(com->cmd_port, tmp);
+ com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH);
+ COM_INT_ENABLE
+}
+
+static void
+pc98_i8251_clear_or_cmd(struct com_s *com, int clr, int x)
+{
+ int tmp;
+ COM_INT_DISABLE
+ tmp = com->pc98_prev_siocmd & ~(clr);
+ tmp |= (x);
+ outb(com->cmd_port, tmp);
+ com->pc98_prev_siocmd = tmp & ~(CMD8251_ER|CMD8251_RESET|CMD8251_EH);
+ COM_INT_ENABLE
+}
+
+static int
+pc98_i8251_get_cmd(struct com_s *com)
+{
+ return com->pc98_prev_siocmd;
+}
+
+static int
+pc98_i8251_get_mod(struct com_s *com)
+{
+ return com->pc98_prev_siomod;
+}
+
+static void
+pc98_i8251_reset(struct com_s *com, int mode, int command)
+{
+ outb(com->cmd_port, 0); /* dummy */
+ DELAY(2);
+ outb(com->cmd_port, 0); /* dummy */
+ DELAY(2);
+ outb(com->cmd_port, 0); /* dummy */
+ DELAY(2);
+ outb(com->cmd_port, CMD8251_RESET); /* internal reset */
+ DELAY(2);
+ outb(com->cmd_port, mode ); /* mode register */
+ com->pc98_prev_siomod = mode;
+ DELAY(2);
+ pc98_i8251_set_cmd( com, (command|CMD8251_ER) );
+}
+
+static void
+pc98_check_sysclock(void)
+{
+ /* get system clock from port */
+ if ( pc98_machine_type & M_8M ) {
+ /* 8 MHz system & H98 */
+ sysclock = 8;
+ } else {
+ /* 5 MHz system */
+ sysclock = 5;
+ }
+}
+
+static void
+com_cflag_and_speed_set( struct com_s *com, int cflag, int speed)
+{
+ int cfcr=0, count;
+ int s, previnterrupt;
+
+ count = pc98_ttspeedtab( com, speed );
+ if ( count < 0 ) return;
+
+ previnterrupt = pc98_check_i8251_interrupt(com);
+ pc98_disable_i8251_interrupt( com, IEN_Tx|IEN_TxEMP|IEN_Rx );
+
+ switch ( cflag&CSIZE ) {
+ case CS5:
+ cfcr = MOD8251_5BITS; break;
+ case CS6:
+ cfcr = MOD8251_6BITS; break;
+ case CS7:
+ cfcr = MOD8251_7BITS; break;
+ case CS8:
+ cfcr = MOD8251_8BITS; break;
+ }
+ if ( cflag&PARENB ) {
+ if ( cflag&PARODD )
+ cfcr |= MOD8251_PODD;
+ else
+ cfcr |= MOD8251_PEVEN;
+ } else
+ cfcr |= MOD8251_PDISAB;
+
+ if ( cflag&CSTOPB )
+ cfcr |= MOD8251_STOP2;
+ else
+ cfcr |= MOD8251_STOP1;
+
+ if ( count & 0x10000 )
+ cfcr |= MOD8251_CLKX1;
+ else
+ cfcr |= MOD8251_CLKX16;
+
+ if (epson_machine_id != 0x20) { /* XXX */
+ {
+ int tmp;
+ while (!((tmp = inb(com->sts_port)) & STS8251_TxEMP))
+ ;
+ }
+ }
+ /* set baud rate from ospeed */
+ pc98_set_baud_rate( com, count );
+
+ if ( cfcr != pc98_i8251_get_mod(com) )
+ pc98_i8251_reset(com, cfcr, pc98_i8251_get_cmd(com) );
+
+ pc98_enable_i8251_interrupt( com, previnterrupt );
+}
+
+static int
+pc98_ttspeedtab(struct com_s *com, int speed)
+{
+ int effect_sp, count=-1, mod;
+
+ switch ( com->pc98_if_type ) {
+ case COM_IF_INTERNAL:
+ /* for *1CLK asynchronous! mode , TEFUTEFU */
+ effect_sp = ttspeedtab( speed, pc98speedtab );
+ if ( effect_sp < 0 )
+ effect_sp = ttspeedtab( (speed-1), pc98speedtab );
+ if ( effect_sp <= 0 )
+ return effect_sp;
+ mod = (sysclock == 5 ? 2457600 : 1996800);
+ if ( effect_sp == speed )
+ mod /= 16;
+ count = mod / effect_sp;
+ if ( count > 65535 )
+ return(-1);
+ if ( effect_sp >= 2400 )
+ if ( !(sysclock != 5 &&
+ (effect_sp == 19200 || effect_sp == 38400)) )
+ if ( ( mod % effect_sp ) != 0 )
+ return(-1);
+ if ( effect_sp != speed )
+ count |= 0x10000;
+ break;
+#ifdef COM_IF_PC9861K
+ case COM_IF_PC9861K:
+ effect_sp = speed;
+ count = 1;
+ break;
+#endif
+#ifdef COM_IF_PIO9032B
+ case COM_IF_PIO9032B:
+ if ( speed == 0 ) return 0;
+ count = ttspeedtab( speed, comspeedtab_pio9032b );
+ if ( count < 0 ) return count;
+ effect_sp = speed;
+ break;
+#endif
+#ifdef COM_IF_B98_01
+ case COM_IF_B98_01:
+ effect_sp=speed;
+ count = ttspeedtab( speed, comspeedtab_b98_01 );
+ if ( count <= 3 )
+ return -1; /* invalid speed/count */
+ if ( count <= 5 )
+ count |= 0x10000; /* x1 mode for 76800 and 153600 */
+ else
+ count -= 4; /* x16 mode for slower */
+ break;
+#endif
+ }
+ return count;
+}
+
+static void
+pc98_set_baud_rate( struct com_s *com, int count)
+{
+ int s;
+
+ switch ( com->pc98_if_type ) {
+ case COM_IF_INTERNAL:
+ if ( count < 0 ) {
+ printf( "[ Illegal count : %d ]", count );
+ return;
+ } else if ( count == 0)
+ return;
+ /* set i8253 */
+ s = splclock();
+ outb( 0x77, 0xb6 );
+ outb( 0x5f, 0);
+ outb( 0x75, count & 0xff );
+ outb( 0x5f, 0);
+ outb( 0x75, (count >> 8) & 0xff );
+ splx(s);
+ break;
+#if 0
+#ifdef COM_IF_PC9861K
+ case COM_IF_PC9861K:
+ break;
+ /* ext. RS232C board: speed is determined by DIP switch */
+#endif
+#endif /* 0 */
+#ifdef COM_IF_PIO9032B
+ case COM_IF_PIO9032B:
+ outb( com_addr[unit], count & 0x07 );
+ break;
+#endif
+#ifdef COM_IF_B98_01
+ case COM_IF_B98_01:
+ outb( com->iobase, count & 0x0f );
+#ifdef B98_01_OLD
+ /* some old board should be controlled in different way,
+ but this hasn't been tested yet.*/
+ outb( com->iobase+2, ( count & 0x10000 ) ? 0xf0 : 0xf2 );
+#endif
+ break;
+#endif
+ }
+}
+static int
+pc98_check_if_type( int iobase, struct siodev *iod)
+{
+ int irr = 0, tmp = 0;
+ int ret = 0;
+ static short irq_tab[2][8] = {
+ { 3, 5, 6, 9, 10, 12, 13, -1},
+ { 3, 10, 12, 13, 5, 6, 9, -1}
+ };
+ iod->irq = 0;
+ switch ( iobase & 0xff ) {
+ case IO_COM1:
+ iod->if_type = COM_IF_INTERNAL;
+ ret = 0; iod->irq = 4; break;
+#ifdef COM_IF_PC9861K
+ case IO_COM2:
+ iod->if_type = COM_IF_PC9861K;
+ ret = 1; irr = 0; tmp = 3; break;
+ case IO_COM3:
+ iod->if_type = COM_IF_PC9861K;
+ ret = 2; irr = 1; tmp = 3; break;
+#endif
+#ifdef COM_IF_PIO9032B
+ case IO_COM_PIO9032B_2:
+ iod->if_type = COM_IF_PIO9032B;
+ ret = 1; irr = 0; tmp = 7; break;
+ case IO_COM_PIO9032B_3:
+ iod->if_type = COM_IF_PIO9032B;
+ ret = 2; irr = 1; tmp = 7; break;
+#endif
+#ifdef COM_IF_B98_01
+ case IO_COM_B98_01_2:
+ iod->if_type = COM_IF_B98_01;
+ ret = 1; irr = 0; tmp = 7;
+ outb(iobase + 2, 0xf2);
+ outb(iobase, 4);
+ break;
+ case IO_COM_B98_01_3:
+ iod->if_type = COM_IF_B98_01;
+ ret = 2; irr = 1; tmp = 7;
+ outb(iobase + 2, 0xf2);
+ outb(iobase , 4);
+ break;
+#endif
+ default:
+ if((iobase & 0x0f0) == 0xd0){
+ iod->if_type = MC16550;
+ return 0;
+ }
+ return -1;
+ }
+
+ iod->cmd = ( iobase & 0xff00 )|PC98SIO_cmd_port(ret);
+ iod->sts = ( iobase & 0xff00 )|PC98SIO_sts_port(ret);
+ iod->mod = ( iobase & 0xff00 )|PC98SIO_in_modem_port(ret);
+ iod->ctrl = ( iobase & 0xff00 )|PC98SIO_intr_ctrl_port(ret);
+
+ if ( iod->irq == 0 ) {
+ tmp &= inb( iod->mod );
+ iod->irq = irq_tab[irr][tmp];
+ if ( iod->irq == -1 ) return -1;
+ }
+ return 0;
+}
+static int
+pc98_set_ioport( struct com_s *com, int io_base )
+{
+ int a, io, type;
+
+ switch ( io_base & 0xff ) {
+ case IO_COM1: a = 0; io = 0; type = COM_IF_INTERNAL;
+ pc98_check_sysclock(); break;
+#ifdef COM_IF_PC9861K
+ case IO_COM2: a = 1; io = 0; type = COM_IF_PC9861K; break;
+ case IO_COM3: a = 2; io = 0; type = COM_IF_PC9861K; break;
+#endif /* COM_IF_PC9861K */
+#ifdef COM_IF_PIO9032B
+ /* PIO9032B : I/O address is changeable */
+ case IO_COM_PIO9032B_2:
+ a = 1; io = io_base & 0xff00;
+ type = COM_IF_PIO9032B; break;
+ case IO_COM_PIO9032B_3:
+ a = 2; io = io_base & 0xff00;
+ type = COM_IF_PIO9032B; break;
+#endif /* COM_IF_PIO9032B */
+#ifdef COM_IF_B98_01
+ case IO_COM_B98_01_2:
+ a = 1; io = 0; type = COM_IF_B98_01; break;
+ case IO_COM_B98_01_3:
+ a = 2; io = 0; type = COM_IF_B98_01; break;
+#endif /* COM_IF_B98_01*/
+ default: /* i/o address not match */
+ return -1;
+ }
+
+ com->pc98_if_type = type;
+ com->data_port = io | PC98SIO_data_port(a);
+ com->cmd_port = io | PC98SIO_cmd_port(a);
+ com->sts_port = io | PC98SIO_sts_port(a);
+ com->in_modem_port = io | PC98SIO_in_modem_port(a);
+ com->intr_ctrl_port = io | PC98SIO_intr_ctrl_port(a);
+ return 0;
+}
+#endif /* PC98 defined */
OpenPOWER on IntegriCloud