summaryrefslogtreecommitdiffstats
path: root/sys/pc98
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
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')
-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
-rw-r--r--sys/pc98/conf/GENERIC164
-rw-r--r--sys/pc98/pc98/machdep.c2068
9 files changed, 11414 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 */
diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC
new file mode 100644
index 0000000..3df81d4
--- /dev/null
+++ b/sys/pc98/conf/GENERIC
@@ -0,0 +1,164 @@
+#
+# GENERIC -- Generic machine with WD/AHx/NCR/BTx family disks
+#
+# $Id: GENERIC,v 1.70 1996/05/13 04:29:13 nate Exp $
+#
+
+# GENERIC98 -- Generic PC98 machine with WD/SBIC55 disks
+
+machine "pc98"
+cpu "I386_CPU"
+cpu "I486_CPU"
+cpu "I586_CPU"
+cpu "I686_CPU"
+ident "GENERIC98"
+maxusers 10
+
+options "PC98" #PC98
+options MATH_EMULATE #Support for x87 emulation
+#options GPL_MATH_EMULATE #GPL-licensed emulator
+options INET #InterNETworking
+options FFS #Berkeley Fast Filesystem
+options NFS #Network Filesystem
+options MSDOSFS #MSDOS Filesystem
+options "CD9660" #ISO 9660 Filesystem
+options PROCFS #Process filesystem
+options "COMPAT_43" #Compatible with BSD 4.3 [KEEP THIS!]
+options SYSVSHM
+options SYSVSEM
+options SYSVMSG
+options UCONSOLE #Allow users to grab the console
+options FAILSAFE #Be conservative
+options "MAXCONS=4" #4 virtual consoles
+options BOUNCE_BUFFERS #include support for DMA bounce buffers
+options EPSON_BOUNCEDMA #use bounce buufer for 15-16M
+#options EPSON_MEMWIN #EPSON memory window support
+options "LINE30"
+options AUTO_CLOCK
+options COM_MULTIPORT
+
+#
+# non-Intel CPU support
+#
+#options "IBM_486SLC" # IBM486SLC/SLC2 support
+#options "CYRIX_486DLC" # Cyrix 486DLC/SLC/DLC2/SLC2 support
+#option "CYRIX_5X86" # Cyrix 5x86 support
+#options SUSP_HLT # CPU enters suspend mode when HALT
+#options "DISABLE_5X86_LSSER" # Load-Store reordering enable
+
+#
+# sbic55.c.new
+#
+#options SCSI_SYNC # synchronous transfer mode
+#options FORCE_BUSMASTER
+#options "HA55BS_ID=0"
+
+#
+# IBM-PC HDD support
+#options COMPAT_ATDISK
+
+#
+# FreeBSD(98)-current is a *TEST VERSION*.
+# It is highly recomended to compile with following options, and to
+# record the panic messages and the result of trace command brefore
+# reporting a problem.
+#
+# If you need more information for the kernel, KTRACE option may help
+# you.
+#
+options DDB
+options DIAGNOSTIC
+#options KTRACE
+
+
+config kernel root on wd0
+
+controller nec0
+controller pci0
+
+controller fdc0 at nec? port "IO_FD1" bio irq 11 drq 2 vector fdintr
+disk fd0 at fdc0 drive 0
+disk fd1 at fdc0 drive 1
+disk fd2 at fdc0 drive 2
+disk fd3 at fdc0 drive 3
+tape ft0 at fdc0 drive 4
+
+controller wdc0 at nec? port "IO_WD1" bio irq 9 vector wdintr
+disk wd0 at wdc0 drive 0
+#disk wd1 at wdc0 drive 1
+#disk wd2 at wdc0 drive 2
+#disk wd3 at wdc0 drive 3
+
+options ATAPI # Enable ATAPI support for IDE bus
+options ATAPI_STATIC #Don't do it as an LKM
+device wcd #IDE CD-ROM
+
+controller sbic0 at nec? port "IO_SCSI" bio irq 5 drq 3 vector sbicintr
+#controller sbic0 at nec? port "IO_SCSI" bio irq 5 drq 3 flags 0xff vector sbicintr
+controller aic0 at nec? port 0x1840 bio irq 5 vector aicintr
+controller ahc0
+
+controller scbus0
+
+device sd0
+
+device st0
+
+device cd0 #Only need one of these, the code dynamically grows
+
+device od0
+
+controller matcd0 at nec? port? bio
+
+# syscons is the default console driver, resembling an SCO console
+device sc0 at nec? port "IO_KBD" tty irq 1 vector scintr
+#options XSERVER # include code for XFree86
+
+# Mandatory, don't remove
+device npx0 at nec? port "IO_NPX" irq 8 vector npxintr
+
+#
+# Laptop support (see LINT for more options)
+#
+device apm0 at nec? disable # Advanced Power Management
+options APM_BROKEN_STATCLOCK # Workaround some buggy APM BIOS
+# PCCARD (PCMCIA) support
+#controller crd0
+#device pcic0 at crd?
+#device pcic1 at crd?
+
+device lpt0 at nec? port "IO_LPT" tty
+device mse0 at nec? port "IO_MSE" tty irq 13 vector mseintr
+
+device sio0 at nec? port "IO_COM1" tty irq 4 vector siointr
+device sio1 at nec? port 0xd2 tty irq 5 flags 0x101 vector siointr
+device sio2 at nec? port 0x8d2 tty flags 0x101 vector siointr
+
+
+device ed0 at nec? port 0x00d0 net irq 6 vector edintr
+device ed1 at nec? port 0x56d0 net irq 5 vector edintr
+device ed2 at nec? port 0x00d0 net irq 6 iomem 0xd0000 iosiz 16384 vector edintr
+device fe0 at nec? prot 0x00d0 net irq 3 vector feintr
+device zp0 at nec? port 0x0300 net irq 10 iomem 0xe0000 vector zpintr
+device ep0 at nec? port 0x00d0 net irq 6 vector epintr
+
+#controller snd0
+#device sb0 at nec? port 0x20d2 irq 10 conflicts drq 3 vector sbintr
+#device sbxvi0 at nec? drq 3
+#device sbmidi0 at nec? port 0x80d2
+#device opl0 at nec? port 0x28d2
+
+#device pcm0 at nec? port 0xa460 irq 12 vector pcmintr
+
+#device mss0 at nec? port 0xf40 irq12 drq 1 vectro adintr
+
+pseudo-device loop
+pseudo-device ether
+pseudo-device log
+pseudo-device sl 2
+# ijppp uses tun instead of ppp device
+#pseudo-device ppp 1
+pseudo-device tun 1
+pseudo-device pty 16
+# keep this if you want to be able to continue to use /stand/sysinstall
+pseudo-device gzip # Exec gzipped a.out's
diff --git a/sys/pc98/pc98/machdep.c b/sys/pc98/pc98/machdep.c
new file mode 100644
index 0000000..029bbf5
--- /dev/null
+++ b/sys/pc98/pc98/machdep.c
@@ -0,0 +1,2068 @@
+/*-
+ * Copyright (c) 1992 Terrence R. Lambert.
+ * Copyright (c) 1982, 1987, 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: @(#)machdep.c 7.4 (Berkeley) 6/3/91
+ * $Id: machdep.c,v 1.192 1996/06/08 11:03:01 bde Exp $
+ */
+
+#include "npx.h"
+#ifndef PC98
+#include "isa.h"
+#endif
+#include "opt_sysvipc.h"
+#include "opt_ddb.h"
+#include "opt_bounce.h"
+#include "opt_machdep.h"
+#include "opt_perfmon.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysproto.h>
+#include <sys/signalvar.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/reboot.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mount.h>
+#include <sys/msgbuf.h>
+#include <sys/ioctl.h>
+#include <sys/sysent.h>
+#include <sys/tty.h>
+#include <sys/sysctl.h>
+#include <sys/devconf.h>
+#include <sys/vmmeter.h>
+
+#ifdef SYSVSHM
+#include <sys/shm.h>
+#endif
+
+#ifdef SYSVMSG
+#include <sys/msg.h>
+#endif
+
+#ifdef SYSVSEM
+#include <sys/sem.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_prot.h>
+#include <vm/lock.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_map.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_extern.h>
+
+#include <sys/user.h>
+#include <sys/exec.h>
+#include <sys/vnode.h>
+
+#include <ddb/ddb.h>
+
+#include <net/netisr.h>
+
+#include <machine/cpu.h>
+#include <machine/npx.h>
+#include <machine/reg.h>
+#include <machine/psl.h>
+#include <machine/clock.h>
+#include <machine/specialreg.h>
+#include <machine/sysarch.h>
+#include <machine/cons.h>
+#include <machine/devconf.h>
+#include <machine/bootinfo.h>
+#include <machine/md_var.h>
+#ifdef PERFMON
+#include <machine/perfmon.h>
+#endif
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/rtc.h>
+#endif
+#include <machine/random.h>
+
+extern void init386 __P((int first));
+extern int ptrace_set_pc __P((struct proc *p, unsigned int addr));
+extern int ptrace_single_step __P((struct proc *p));
+extern int ptrace_write_u __P((struct proc *p, vm_offset_t off, int data));
+extern void dblfault_handler __P((void));
+
+extern void i486_bzero __P((void *, size_t));
+extern void i586_bzero __P((void *, size_t));
+extern void i686_bzero __P((void *, size_t));
+
+static void cpu_startup __P((void *));
+SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL)
+
+static void identifycpu(void);
+
+char machine[] = "i386";
+SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD, machine, 0, "");
+
+static char cpu_model[128];
+SYSCTL_STRING(_hw, HW_MODEL, model, CTLFLAG_RD, cpu_model, 0, "");
+
+struct kern_devconf kdc_cpu0 = {
+ 0, 0, 0, /* filled in by dev_attach */
+ "cpu", 0, { MDDT_CPU },
+ 0, 0, 0, CPU_EXTERNALLEN,
+ 0, /* CPU has no parent */
+ 0, /* no parentdata */
+ DC_BUSY, /* the CPU is always busy */
+ cpu_model, /* no sense in duplication */
+ DC_CLS_CPU /* class */
+};
+
+#ifndef PANIC_REBOOT_WAIT_TIME
+#define PANIC_REBOOT_WAIT_TIME 15 /* default to 15 seconds */
+#endif
+
+#ifdef BOUNCE_BUFFERS
+extern char *bouncememory;
+extern int maxbkva;
+#ifdef BOUNCEPAGES
+int bouncepages = BOUNCEPAGES;
+#else
+int bouncepages = 0;
+#endif
+#endif /* BOUNCE_BUFFERS */
+
+extern int freebufspace;
+int msgbufmapped = 0; /* set when safe to use msgbuf */
+int _udatasel, _ucodesel;
+u_int atdevbase;
+
+
+int physmem = 0;
+int cold = 1;
+
+static int
+sysctl_hw_physmem SYSCTL_HANDLER_ARGS
+{
+ int error = sysctl_handle_int(oidp, 0, ctob(physmem), req);
+ return (error);
+}
+
+SYSCTL_PROC(_hw, HW_PHYSMEM, physmem, CTLTYPE_INT|CTLFLAG_RD,
+ 0, 0, sysctl_hw_physmem, "I", "");
+
+static int
+sysctl_hw_usermem SYSCTL_HANDLER_ARGS
+{
+ int error = sysctl_handle_int(oidp, 0,
+ ctob(physmem - cnt.v_wire_count), req);
+ return (error);
+}
+
+SYSCTL_PROC(_hw, HW_USERMEM, usermem, CTLTYPE_INT|CTLFLAG_RD,
+ 0, 0, sysctl_hw_usermem, "I", "");
+
+int boothowto = 0, bootverbose = 0, Maxmem = 0;
+static int badpages = 0;
+#ifdef PC98
+int Maxmem_under16M = 0;
+extern pt_entry_t *panic_kwin_pte;
+extern caddr_t panic_kwin;
+#endif
+long dumplo;
+extern int bootdev;
+
+vm_offset_t phys_avail[10];
+
+/* must be 2 less so 0 0 can signal end of chunks */
+#define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2)
+
+int cpu_class = CPUCLASS_386; /* smallest common denominator */
+
+static void dumpsys __P((void));
+static void setup_netisrs __P((struct linker_set *)); /* XXX declare elsewhere */
+
+static vm_offset_t buffer_sva, buffer_eva;
+vm_offset_t clean_sva, clean_eva;
+static vm_offset_t pager_sva, pager_eva;
+extern struct linker_set netisr_set;
+
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+
+static void
+cpu_startup(dummy)
+ void *dummy;
+{
+ register unsigned i;
+ register caddr_t v;
+ vm_offset_t maxaddr;
+ vm_size_t size = 0;
+ int firstaddr;
+ vm_offset_t minaddr;
+
+ if (boothowto & RB_VERBOSE)
+ bootverbose++;
+
+ /*
+ * Initialize error message buffer (at end of core).
+ */
+
+ /* avail_end was pre-decremented in init386() to compensate */
+ for (i = 0; i < btoc(sizeof (struct msgbuf)); i++)
+ pmap_enter(pmap_kernel(), (vm_offset_t)msgbufp,
+ avail_end + i * PAGE_SIZE,
+ VM_PROT_ALL, TRUE);
+ msgbufmapped = 1;
+
+ /*
+ * Good {morning,afternoon,evening,night}.
+ */
+ printf(version);
+ cpu_class = i386_cpus[cpu].cpu_class;
+ startrtclock();
+ identifycpu();
+ printf("real memory = %d (%dK bytes)\n", ptoa(Maxmem), ptoa(Maxmem) / 1024);
+ /*
+ * Display any holes after the first chunk of extended memory.
+ */
+ if (badpages != 0) {
+ int indx = 1;
+
+ /*
+ * XXX skip reporting ISA hole & unmanaged kernel memory
+ */
+ if (phys_avail[0] == PAGE_SIZE)
+ indx += 2;
+
+ printf("Physical memory hole(s):\n");
+ for (; phys_avail[indx + 1] != 0; indx += 2) {
+ int size = phys_avail[indx + 1] - phys_avail[indx];
+
+ printf("0x%08lx - 0x%08lx, %d bytes (%d pages)\n", phys_avail[indx],
+ phys_avail[indx + 1] - 1, size, size / PAGE_SIZE);
+ }
+ }
+
+ /*
+ * Quickly wire in netisrs.
+ */
+ setup_netisrs(&netisr_set);
+
+/*
+#ifdef ISDN
+ DONET(isdnintr, NETISR_ISDN);
+#endif
+*/
+
+ /*
+ * Allocate space for system data structures.
+ * The first available kernel virtual address is in "v".
+ * As pages of kernel virtual memory are allocated, "v" is incremented.
+ * As pages of memory are allocated and cleared,
+ * "firstaddr" is incremented.
+ * An index into the kernel page table corresponding to the
+ * virtual memory address maintained in "v" is kept in "mapaddr".
+ */
+
+ /*
+ * Make two passes. The first pass calculates how much memory is
+ * needed and allocates it. The second pass assigns virtual
+ * addresses to the various data structures.
+ */
+ firstaddr = 0;
+again:
+ v = (caddr_t)firstaddr;
+
+#define valloc(name, type, num) \
+ (name) = (type *)v; v = (caddr_t)((name)+(num))
+#define valloclim(name, type, num, lim) \
+ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num)))
+ valloc(callout, struct callout, ncallout);
+#ifdef SYSVSHM
+ valloc(shmsegs, struct shmid_ds, shminfo.shmmni);
+#endif
+#ifdef SYSVSEM
+ valloc(sema, struct semid_ds, seminfo.semmni);
+ valloc(sem, struct sem, seminfo.semmns);
+ /* This is pretty disgusting! */
+ valloc(semu, int, (seminfo.semmnu * seminfo.semusz) / sizeof(int));
+#endif
+#ifdef SYSVMSG
+ valloc(msgpool, char, msginfo.msgmax);
+ valloc(msgmaps, struct msgmap, msginfo.msgseg);
+ valloc(msghdrs, struct msg, msginfo.msgtql);
+ valloc(msqids, struct msqid_ds, msginfo.msgmni);
+#endif
+
+ if (nbuf == 0) {
+ nbuf = 30;
+ if( physmem > 1024)
+ nbuf += min((physmem - 1024) / 12, 1024);
+ }
+ nswbuf = min(nbuf, 128);
+
+ valloc(swbuf, struct buf, nswbuf);
+ valloc(buf, struct buf, nbuf);
+
+#ifdef BOUNCE_BUFFERS
+ /*
+ * If there is more than 16MB of memory, allocate some bounce buffers
+ */
+ if (Maxmem > 4096) {
+ if (bouncepages == 0) {
+ bouncepages = 64;
+ bouncepages += ((Maxmem - 4096) / 2048) * 32;
+ }
+ v = (caddr_t)((vm_offset_t)round_page(v));
+ valloc(bouncememory, char, bouncepages * PAGE_SIZE);
+ }
+#endif
+
+ /*
+ * End of first pass, size has been calculated so allocate memory
+ */
+ if (firstaddr == 0) {
+ size = (vm_size_t)(v - firstaddr);
+ firstaddr = (int)kmem_alloc(kernel_map, round_page(size));
+ if (firstaddr == 0)
+ panic("startup: no room for tables");
+ goto again;
+ }
+
+ /*
+ * End of second pass, addresses have been assigned
+ */
+ if ((vm_size_t)(v - firstaddr) != size)
+ panic("startup: table size inconsistency");
+
+#ifdef BOUNCE_BUFFERS
+ clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva,
+ (nbuf*MAXBSIZE) + (nswbuf*MAXPHYS) +
+ maxbkva + pager_map_size, TRUE);
+ io_map = kmem_suballoc(clean_map, &minaddr, &maxaddr, maxbkva, FALSE);
+#else
+ clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva,
+ (nbuf*MAXBSIZE) + (nswbuf*MAXPHYS) + pager_map_size, TRUE);
+#endif
+ buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva,
+ (nbuf*MAXBSIZE), TRUE);
+ pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva,
+ (nswbuf*MAXPHYS) + pager_map_size, TRUE);
+ exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr,
+ (16*ARG_MAX), TRUE);
+ exech_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr,
+ (32*ARG_MAX), TRUE);
+ u_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr,
+ (maxproc*UPAGES*PAGE_SIZE), FALSE);
+
+ /*
+ * Finally, allocate mbuf pool. Since mclrefcnt is an off-size
+ * we use the more space efficient malloc in place of kmem_alloc.
+ */
+ mclrefcnt = (char *)malloc(nmbclusters+PAGE_SIZE/MCLBYTES,
+ M_MBUF, M_NOWAIT);
+ bzero(mclrefcnt, nmbclusters+PAGE_SIZE/MCLBYTES);
+ mcl_map = kmem_suballoc(kmem_map, (vm_offset_t *)&mbutl, &maxaddr,
+ nmbclusters * MCLBYTES, FALSE);
+ {
+ vm_size_t mb_map_size;
+ mb_map_size = nmbufs * MSIZE;
+ mb_map = kmem_suballoc(kmem_map, &minaddr, &maxaddr,
+ round_page(mb_map_size), FALSE);
+ }
+
+ /*
+ * Initialize callouts
+ */
+ callfree = callout;
+ for (i = 1; i < ncallout; i++)
+ callout[i-1].c_next = &callout[i];
+
+ if (boothowto & RB_CONFIG) {
+ userconfig();
+ cninit(); /* the preferred console may have changed */
+ }
+
+#ifdef BOUNCE_BUFFERS
+ /*
+ * init bounce buffers
+ */
+ vm_bounce_init();
+#endif
+
+ printf("avail memory = %d (%dK bytes)\n", ptoa(cnt.v_free_count),
+ ptoa(cnt.v_free_count) / 1024);
+
+ /*
+ * Set up buffers, so they can be used to read disk labels.
+ */
+ bufinit();
+ vm_pager_bufferinit();
+
+ /*
+ * In verbose mode, print out the BIOS's idea of the disk geometries.
+ */
+ if (bootverbose) {
+ printf("BIOS Geometries:\n");
+ for (i = 0; i < N_BIOS_GEOM; i++) {
+ unsigned long bios_geom;
+ int max_cylinder, max_head, max_sector;
+
+ bios_geom = bootinfo.bi_bios_geom[i];
+
+ /*
+ * XXX the bootstrap punts a 1200K floppy geometry
+ * when the get-disk-geometry interrupt fails. Skip
+ * drives that have this geometry.
+ */
+ if (bios_geom == 0x4f010f)
+ continue;
+
+ printf(" %x:%08lx ", i, bios_geom);
+ max_cylinder = bios_geom >> 16;
+ max_head = (bios_geom >> 8) & 0xff;
+ max_sector = bios_geom & 0xff;
+ printf(
+ "0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n",
+ max_cylinder, max_cylinder + 1,
+ max_head, max_head + 1,
+ max_sector, max_sector);
+ }
+ printf(" %d accounted for\n", bootinfo.bi_n_bios_used);
+ }
+}
+
+int
+register_netisr(num, handler)
+ int num;
+ netisr_t *handler;
+{
+
+ if (num < 0 || num >= (sizeof(netisrs)/sizeof(*netisrs)) ) {
+ printf("register_netisr: bad isr number: %d\n", num);
+ return (EINVAL);
+ }
+ netisrs[num] = handler;
+ return (0);
+}
+
+static void
+setup_netisrs(ls)
+ struct linker_set *ls;
+{
+ int i;
+ const struct netisrtab *nit;
+
+ for(i = 0; ls->ls_items[i]; i++) {
+ nit = (const struct netisrtab *)ls->ls_items[i];
+ register_netisr(nit->nit_num, nit->nit_isr);
+ }
+}
+
+static struct cpu_nameclass i386_cpus[] = {
+ { "Intel 80286", CPUCLASS_286 }, /* CPU_286 */
+ { "i386SX", CPUCLASS_386 }, /* CPU_386SX */
+ { "i386DX", CPUCLASS_386 }, /* CPU_386 */
+ { "i486SX", CPUCLASS_486 }, /* CPU_486SX */
+ { "i486DX", CPUCLASS_486 }, /* CPU_486 */
+ { "Pentium", CPUCLASS_586 }, /* CPU_586 */
+ { "Cy486DLC", CPUCLASS_486 }, /* CPU_486DLC */
+ { "Pentium Pro", CPUCLASS_686 }, /* CPU_686 */
+};
+
+static void
+identifycpu()
+{
+ printf("CPU: ");
+ strncpy(cpu_model, i386_cpus[cpu].cpu_name, sizeof cpu_model);
+
+#if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU)
+ if (!strcmp(cpu_vendor,"GenuineIntel")) {
+ if ((cpu_id & 0xf00) > 3) {
+ cpu_model[0] = '\0';
+
+ switch (cpu_id & 0x3000) {
+ case 0x1000:
+ strcpy(cpu_model, "Overdrive ");
+ break;
+ case 0x2000:
+ strcpy(cpu_model, "Dual ");
+ break;
+ }
+
+ switch (cpu_id & 0xf00) {
+ case 0x400:
+ strcat(cpu_model, "i486 ");
+ break;
+ case 0x500:
+ strcat(cpu_model, "Pentium"); /* nb no space */
+ break;
+ case 0x600:
+ strcat(cpu_model, "Pentium Pro");
+ break;
+ default:
+ strcat(cpu_model, "unknown");
+ break;
+ }
+
+ switch (cpu_id & 0xff0) {
+ case 0x400:
+ strcat(cpu_model, "DX"); break;
+ case 0x410:
+ strcat(cpu_model, "DX"); break;
+ case 0x420:
+ strcat(cpu_model, "SX"); break;
+ case 0x430:
+ strcat(cpu_model, "DX2"); break;
+ case 0x440:
+ strcat(cpu_model, "SL"); break;
+ case 0x450:
+ strcat(cpu_model, "SX2"); break;
+ case 0x470:
+ strcat(cpu_model, "DX2 Write-Back Enhanced");
+ break;
+ case 0x480:
+ strcat(cpu_model, "DX4"); break;
+ break;
+ }
+ }
+ }
+#endif
+ printf("%s (", cpu_model);
+ switch(cpu_class) {
+ case CPUCLASS_286:
+ printf("286");
+ break;
+#if defined(I386_CPU)
+ case CPUCLASS_386:
+ printf("386");
+ break;
+#endif
+#if defined(I486_CPU)
+ case CPUCLASS_486:
+ printf("486");
+ bzero = i486_bzero;
+ break;
+#endif
+#if defined(I586_CPU)
+ case CPUCLASS_586:
+ printf("%d.%02d-MHz ",
+ ((100 * i586_ctr_rate) >> I586_CTR_RATE_SHIFT) / 100,
+ ((100 * i586_ctr_rate) >> I586_CTR_RATE_SHIFT) % 100);
+ printf("586");
+ break;
+#endif
+#if defined(I686_CPU)
+ case CPUCLASS_686:
+ printf("%d.%02d-MHz ",
+ ((100 * i586_ctr_rate) >> I586_CTR_RATE_SHIFT) / 100,
+ ((100 * i586_ctr_rate) >> I586_CTR_RATE_SHIFT) % 100);
+ printf("686");
+ break;
+#endif
+ default:
+ printf("unknown"); /* will panic below... */
+ }
+ printf("-class CPU)\n");
+#if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU)
+ if(*cpu_vendor)
+ printf(" Origin = \"%s\"",cpu_vendor);
+ if(cpu_id)
+ printf(" Id = 0x%lx",cpu_id);
+
+ if (!strcmp(cpu_vendor, "GenuineIntel")) {
+ printf(" Stepping=%ld", cpu_id & 0xf);
+ if (cpu_high > 0) {
+ printf("\n Features=0x%b", cpu_feature,
+ "\020"
+ "\001FPU"
+ "\002VME"
+ "\003DE"
+ "\004PSE"
+ "\005TSC"
+ "\006MSR"
+ "\007PAE"
+ "\010MCE"
+ "\011CX8"
+ "\012APIC"
+ "\013<b10>"
+ "\014<b11>"
+ "\015MTRR"
+ "\016PGE"
+ "\017MCA"
+ "\020CMOV"
+ );
+ }
+ }
+ /* Avoid ugly blank lines: only print newline when we have to. */
+ if (*cpu_vendor || cpu_id)
+ printf("\n");
+#endif
+ /*
+ * Now that we have told the user what they have,
+ * let them know if that machine type isn't configured.
+ */
+ switch (cpu_class) {
+ case CPUCLASS_286: /* a 286 should not make it this far, anyway */
+#if !defined(I386_CPU) && !defined(I486_CPU) && !defined(I586_CPU) && !defined(I686_CPU)
+#error This kernel is not configured for one of the supported CPUs
+#endif
+#if !defined(I386_CPU)
+ case CPUCLASS_386:
+#endif
+#if !defined(I486_CPU)
+ case CPUCLASS_486:
+#endif
+#if !defined(I586_CPU)
+ case CPUCLASS_586:
+#endif
+#if !defined(I686_CPU)
+ case CPUCLASS_686:
+#endif
+ panic("CPU class not configured");
+ default:
+ break;
+ }
+#ifdef PERFMON
+ perfmon_init();
+#endif
+ dev_attach(&kdc_cpu0);
+}
+
+/*
+ * Send an interrupt to process.
+ *
+ * Stack is set up to allow sigcode stored
+ * at top to call routine, followed by kcall
+ * to sigreturn routine below. After sigreturn
+ * resets the signal mask, the stack, and the
+ * frame pointer, it returns to the user
+ * specified pc, psl.
+ */
+void
+sendsig(catcher, sig, mask, code)
+ sig_t catcher;
+ int sig, mask;
+ u_long code;
+{
+ register struct proc *p = curproc;
+ register int *regs;
+ register struct sigframe *fp;
+ struct sigframe sf;
+ struct sigacts *psp = p->p_sigacts;
+ int oonstack;
+
+ regs = p->p_md.md_regs;
+ oonstack = psp->ps_sigstk.ss_flags & SS_ONSTACK;
+ /*
+ * Allocate and validate space for the signal handler context.
+ */
+ if ((psp->ps_flags & SAS_ALTSTACK) && !oonstack &&
+ (psp->ps_sigonstack & sigmask(sig))) {
+ fp = (struct sigframe *)(psp->ps_sigstk.ss_sp +
+ psp->ps_sigstk.ss_size - sizeof(struct sigframe));
+ psp->ps_sigstk.ss_flags |= SS_ONSTACK;
+ } else {
+ fp = (struct sigframe *)regs[tESP] - 1;
+ }
+
+ /*
+ * grow() will return FALSE if the fp will not fit inside the stack
+ * and the stack can not be grown. useracc will return FALSE
+ * if access is denied.
+ */
+ if ((grow(p, (int)fp) == FALSE) ||
+ (useracc((caddr_t)fp, sizeof (struct sigframe), B_WRITE) == FALSE)) {
+ /*
+ * Process has trashed its stack; give it an illegal
+ * instruction to halt it in its tracks.
+ */
+ SIGACTION(p, SIGILL) = SIG_DFL;
+ sig = sigmask(SIGILL);
+ p->p_sigignore &= ~sig;
+ p->p_sigcatch &= ~sig;
+ p->p_sigmask &= ~sig;
+ psignal(p, SIGILL);
+ return;
+ }
+
+ /*
+ * Build the argument list for the signal handler.
+ */
+ if (p->p_sysent->sv_sigtbl) {
+ if (sig < p->p_sysent->sv_sigsize)
+ sig = p->p_sysent->sv_sigtbl[sig];
+ else
+ sig = p->p_sysent->sv_sigsize + 1;
+ }
+ sf.sf_signum = sig;
+ sf.sf_code = code;
+ sf.sf_scp = &fp->sf_sc;
+ sf.sf_addr = (char *) regs[tERR];
+ sf.sf_handler = catcher;
+
+ /* save scratch registers */
+ sf.sf_sc.sc_eax = regs[tEAX];
+ sf.sf_sc.sc_ebx = regs[tEBX];
+ sf.sf_sc.sc_ecx = regs[tECX];
+ sf.sf_sc.sc_edx = regs[tEDX];
+ sf.sf_sc.sc_esi = regs[tESI];
+ sf.sf_sc.sc_edi = regs[tEDI];
+ sf.sf_sc.sc_cs = regs[tCS];
+ sf.sf_sc.sc_ds = regs[tDS];
+ sf.sf_sc.sc_ss = regs[tSS];
+ sf.sf_sc.sc_es = regs[tES];
+ sf.sf_sc.sc_isp = regs[tISP];
+
+ /*
+ * Build the signal context to be used by sigreturn.
+ */
+ sf.sf_sc.sc_onstack = oonstack;
+ sf.sf_sc.sc_mask = mask;
+ sf.sf_sc.sc_sp = regs[tESP];
+ sf.sf_sc.sc_fp = regs[tEBP];
+ sf.sf_sc.sc_pc = regs[tEIP];
+ sf.sf_sc.sc_ps = regs[tEFLAGS];
+
+ /*
+ * Copy the sigframe out to the user's stack.
+ */
+ if (copyout(&sf, fp, sizeof(struct sigframe)) != 0) {
+ /*
+ * Something is wrong with the stack pointer.
+ * ...Kill the process.
+ */
+ sigexit(p, SIGILL);
+ };
+
+ regs[tESP] = (int)fp;
+ regs[tEIP] = (int)(((char *)PS_STRINGS) - *(p->p_sysent->sv_szsigcode));
+ regs[tEFLAGS] &= ~PSL_VM;
+ regs[tCS] = _ucodesel;
+ regs[tDS] = _udatasel;
+ regs[tES] = _udatasel;
+ regs[tSS] = _udatasel;
+}
+
+/*
+ * System call to cleanup state after a signal
+ * has been taken. Reset signal mask and
+ * stack state from context left by sendsig (above).
+ * Return to previous pc and psl as specified by
+ * context left by sendsig. Check carefully to
+ * make sure that the user has not modified the
+ * state to gain improper privileges.
+ */
+int
+sigreturn(p, uap, retval)
+ struct proc *p;
+ struct sigreturn_args /* {
+ struct sigcontext *sigcntxp;
+ } */ *uap;
+ int *retval;
+{
+ register struct sigcontext *scp;
+ register struct sigframe *fp;
+ register int *regs = p->p_md.md_regs;
+ int eflags;
+
+ /*
+ * (XXX old comment) regs[tESP] points to the return address.
+ * The user scp pointer is above that.
+ * The return address is faked in the signal trampoline code
+ * for consistency.
+ */
+ scp = uap->sigcntxp;
+ fp = (struct sigframe *)
+ ((caddr_t)scp - offsetof(struct sigframe, sf_sc));
+
+ if (useracc((caddr_t)fp, sizeof (*fp), 0) == 0)
+ return(EINVAL);
+
+ /*
+ * Don't allow users to change privileged or reserved flags.
+ */
+#define EFLAGS_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0)
+ eflags = scp->sc_ps;
+ /*
+ * XXX do allow users to change the privileged flag PSL_RF. The
+ * cpu sets PSL_RF in tf_eflags for faults. Debuggers should
+ * sometimes set it there too. tf_eflags is kept in the signal
+ * context during signal handling and there is no other place
+ * to remember it, so the PSL_RF bit may be corrupted by the
+ * signal handler without us knowing. Corruption of the PSL_RF
+ * bit at worst causes one more or one less debugger trap, so
+ * allowing it is fairly harmless.
+ */
+ if (!EFLAGS_SECURE(eflags & ~PSL_RF, regs[tEFLAGS] & ~PSL_RF)) {
+#ifdef DEBUG
+ printf("sigreturn: eflags = 0x%x\n", eflags);
+#endif
+ return(EINVAL);
+ }
+
+ /*
+ * Don't allow users to load a valid privileged %cs. Let the
+ * hardware check for invalid selectors, excess privilege in
+ * other selectors, invalid %eip's and invalid %esp's.
+ */
+#define CS_SECURE(cs) (ISPL(cs) == SEL_UPL)
+ if (!CS_SECURE(scp->sc_cs)) {
+#ifdef DEBUG
+ printf("sigreturn: cs = 0x%x\n", scp->sc_cs);
+#endif
+ trapsignal(p, SIGBUS, T_PROTFLT);
+ return(EINVAL);
+ }
+
+ /* restore scratch registers */
+ regs[tEAX] = scp->sc_eax;
+ regs[tEBX] = scp->sc_ebx;
+ regs[tECX] = scp->sc_ecx;
+ regs[tEDX] = scp->sc_edx;
+ regs[tESI] = scp->sc_esi;
+ regs[tEDI] = scp->sc_edi;
+ regs[tCS] = scp->sc_cs;
+ regs[tDS] = scp->sc_ds;
+ regs[tES] = scp->sc_es;
+ regs[tSS] = scp->sc_ss;
+ regs[tISP] = scp->sc_isp;
+
+ if (useracc((caddr_t)scp, sizeof (*scp), 0) == 0)
+ return(EINVAL);
+
+ if (scp->sc_onstack & 01)
+ p->p_sigacts->ps_sigstk.ss_flags |= SS_ONSTACK;
+ else
+ p->p_sigacts->ps_sigstk.ss_flags &= ~SS_ONSTACK;
+ p->p_sigmask = scp->sc_mask &~
+ (sigmask(SIGKILL)|sigmask(SIGCONT)|sigmask(SIGSTOP));
+ regs[tEBP] = scp->sc_fp;
+ regs[tESP] = scp->sc_sp;
+ regs[tEIP] = scp->sc_pc;
+ regs[tEFLAGS] = eflags;
+ return(EJUSTRETURN);
+}
+
+#ifdef PC98
+/*
+ * disable screen saver
+ */
+extern int scrn_blanked;
+extern void (*current_saver)(int blank);
+
+static void pc98_disable_screen_saver(void)
+{
+ if (scrn_blanked)
+ (*current_saver)(FALSE);
+}
+
+
+/*
+ * change ralay on video card
+ */
+static void pc98_change_relay(void)
+{
+ /* mode register 2 */
+ outb(0x6a, 0x07); /* enable to change FF */
+ outb(0x6a, 0x8e);
+ outb(0x6a, 0x06); /* disable to change FF */
+ outb(0x7c, 0);
+ outb(0x68, 0x0f); /* display */
+
+ /* PWLB */
+ *(int *)panic_kwin_pte = (0xf0c00000 & PG_FRAME) | PG_V | PG_RW ;
+ pmap_update();
+ *(long *)panic_kwin = 0;
+ /* PowerWindow(C-Bus) */
+ outb(0x0dc | 0x600, 0); /* XXX */
+ /* PCHKB & PCSKB4 */
+ outb(0x6e68, 0);
+ /* PCSKB */
+ outb(0x6ee8, 0);
+ /* NEC-S3, Cirrus (local bus) */
+ outb(0xfaa, 3);
+ outb(0xfab, 1);
+ outb(0xfaa, 6);
+ outb(0xfab, 0xff);
+ outb(0xfaa, 7);
+ outb(0xfab, 0);
+ /* NEC-S3 (C-bus) */
+ outb(0xfa2, 3);
+ outb(0xfa3, 0);
+ /* GA-NB */
+ outb(0x40e1, 0xc2);
+ /* WAB-S & WAP */
+ outb(0x40e1, 0xfa);
+
+ /* stop G-GDC */
+ outb(0xa2, 0x0c);
+ /* XXX start T-GDC (which is true?)*/
+ outb(0x62, 0x69);
+ outb(0x62, 0x0d);
+}
+#endif
+
+
+static int waittime = -1;
+struct pcb dumppcb;
+
+__dead void
+boot(howto)
+ int howto;
+{
+ if (!cold && (howto & RB_NOSYNC) == 0 && waittime < 0) {
+ register struct buf *bp;
+ int iter, nbusy;
+
+#ifdef PC98
+ pc98_change_relay();
+ pc98_disable_screen_saver();
+#endif
+ waittime = 0;
+ printf("\nsyncing disks... ");
+
+ sync(&proc0, NULL, NULL);
+
+ for (iter = 0; iter < 20; iter++) {
+ nbusy = 0;
+ for (bp = &buf[nbuf]; --bp >= buf; ) {
+ if ((bp->b_flags & (B_BUSY | B_INVAL)) == B_BUSY) {
+ nbusy++;
+ }
+ }
+ if (nbusy == 0)
+ break;
+ printf("%d ", nbusy);
+ DELAY(40000 * iter);
+ }
+ if (nbusy) {
+ /*
+ * Failed to sync all blocks. Indicate this and don't
+ * unmount filesystems (thus forcing an fsck on reboot).
+ */
+ printf("giving up\n");
+#ifdef SHOW_BUSYBUFS
+ nbusy = 0;
+ for (bp = &buf[nbuf]; --bp >= buf; ) {
+ if ((bp->b_flags & (B_BUSY | B_INVAL)) == B_BUSY) {
+ nbusy++;
+ printf("%d: dev:%08x, flags:%08x, blkno:%d, lblkno:%d\n", nbusy, bp->b_dev, bp->b_flags, bp->b_blkno, bp->b_lblkno);
+ }
+ }
+ DELAY(5000000); /* 5 seconds */
+#endif
+ } else {
+ printf("done\n");
+ /*
+ * Unmount filesystems
+ */
+ if (panicstr == 0)
+ vfs_unmountall();
+ }
+ DELAY(100000); /* wait for console output to finish */
+ dev_shutdownall(FALSE);
+ }
+ splhigh();
+ if (howto & RB_HALT) {
+ printf("\n");
+ printf("The operating system has halted.\n");
+ printf("Please press any key to reboot.\n\n");
+ cngetc();
+ } else {
+ if (howto & RB_DUMP) {
+ if (!cold) {
+ savectx(&dumppcb);
+ dumppcb.pcb_cr3 = rcr3();
+ dumpsys();
+ }
+
+ if (PANIC_REBOOT_WAIT_TIME != 0) {
+ if (PANIC_REBOOT_WAIT_TIME != -1) {
+ int loop;
+ printf("Automatic reboot in %d seconds - press a key on the console to abort\n",
+ PANIC_REBOOT_WAIT_TIME);
+ for (loop = PANIC_REBOOT_WAIT_TIME * 10; loop > 0; --loop) {
+ DELAY(1000 * 100); /* 1/10th second */
+ if (cncheckc()) /* Did user type a key? */
+ break;
+ }
+ if (!loop)
+ goto die;
+ }
+ } else { /* zero time specified - reboot NOW */
+ goto die;
+ }
+ printf("--> Press a key on the console to reboot <--\n");
+ cngetc();
+ }
+ }
+die:
+ printf("Rebooting...\n");
+ DELAY(1000000); /* wait 1 sec for printf's to complete and be read */
+ cpu_reset();
+ for(;;) ;
+ /* NOTREACHED */
+}
+
+/*
+ * Magic number for savecore
+ *
+ * exported (symorder) and used at least by savecore(8)
+ *
+ */
+u_long dumpmag = 0x8fca0101UL;
+
+static int dumpsize = 0; /* also for savecore */
+
+static int dodump = 1;
+SYSCTL_INT(_machdep, OID_AUTO, do_dump, CTLFLAG_RW, &dodump, 0, "");
+
+/*
+ * Doadump comes here after turning off memory management and
+ * getting on the dump stack, either when called above, or by
+ * the auto-restart code.
+ */
+static void
+dumpsys()
+{
+
+ if (!dodump)
+ return;
+ if (dumpdev == NODEV)
+ return;
+ if ((minor(dumpdev)&07) != 1)
+ return;
+ if (!(bdevsw[major(dumpdev)]))
+ return;
+ if (!(bdevsw[major(dumpdev)]->d_dump))
+ return;
+ dumpsize = Maxmem;
+ printf("\ndumping to dev %lx, offset %ld\n", dumpdev, dumplo);
+ printf("dump ");
+ switch ((*bdevsw[major(dumpdev)]->d_dump)(dumpdev)) {
+
+ case ENXIO:
+ printf("device bad\n");
+ break;
+
+ case EFAULT:
+ printf("device not ready\n");
+ break;
+
+ case EINVAL:
+ printf("area improper\n");
+ break;
+
+ case EIO:
+ printf("i/o error\n");
+ break;
+
+ case EINTR:
+ printf("aborted from console\n");
+ break;
+
+ default:
+ printf("succeeded\n");
+ break;
+ }
+}
+
+/*
+ * Clear registers on exec
+ */
+void
+setregs(p, entry, stack)
+ struct proc *p;
+ u_long entry;
+ u_long stack;
+{
+ int *regs = p->p_md.md_regs;
+
+#ifdef USER_LDT
+ struct pcb *pcb = &p->p_addr->u_pcb;
+
+ /* was i386_user_cleanup() in NetBSD */
+ if (pcb->pcb_ldt) {
+ if (pcb == curpcb)
+ lldt(GSEL(GUSERLDT_SEL, SEL_KPL));
+ kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt,
+ pcb->pcb_ldt_len * sizeof(union descriptor));
+ pcb->pcb_ldt_len = (int)pcb->pcb_ldt = 0;
+ }
+#endif
+
+ bzero(regs, sizeof(struct trapframe));
+ regs[tEIP] = entry;
+ regs[tESP] = stack;
+ regs[tEFLAGS] = PSL_USER | (regs[tEFLAGS] & PSL_T);
+ regs[tSS] = _udatasel;
+ regs[tDS] = _udatasel;
+ regs[tES] = _udatasel;
+ regs[tCS] = _ucodesel;
+
+ p->p_addr->u_pcb.pcb_flags = 0; /* no fp at all */
+ load_cr0(rcr0() | CR0_TS); /* start emulating */
+#if NNPX > 0
+ npxinit(__INITIAL_NPXCW__);
+#endif /* NNPX > 0 */
+}
+
+static int
+sysctl_machdep_adjkerntz SYSCTL_HANDLER_ARGS
+{
+ int error;
+ error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2,
+ req);
+ if (!error && req->newptr)
+ resettodr();
+ return (error);
+}
+
+SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW,
+ &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", "");
+
+SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set,
+ CTLFLAG_RW, &disable_rtc_set, 0, "");
+
+SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo,
+ CTLFLAG_RD, &bootinfo, bootinfo, "");
+
+SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock,
+ CTLFLAG_RW, &wall_cmos_clock, 0, "");
+
+/*
+ * Initialize 386 and configure to run kernel
+ */
+
+/*
+ * Initialize segments & interrupt table
+ */
+
+int currentldt;
+int _default_ldt;
+union descriptor gdt[NGDT]; /* global descriptor table */
+struct gate_descriptor idt[NIDT]; /* interrupt descriptor table */
+union descriptor ldt[NLDT]; /* local descriptor table */
+
+static struct i386tss dblfault_tss;
+static char dblfault_stack[PAGE_SIZE];
+
+extern struct user *proc0paddr;
+
+/* software prototypes -- in more palatable form */
+struct soft_segment_descriptor gdt_segs[] = {
+/* GNULL_SEL 0 Null Descriptor */
+{ 0x0, /* segment base address */
+ 0x0, /* length */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+/* GCODE_SEL 1 Code Descriptor for kernel */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMERA, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+/* GDATA_SEL 2 Data Descriptor for kernel */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMRWA, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+/* GLDT_SEL 3 LDT Descriptor */
+{ (int) ldt, /* segment base address */
+ sizeof(ldt)-1, /* length - all address space */
+ SDT_SYSLDT, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+/* GTGATE_SEL 4 Null Descriptor - Placeholder */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+/* GPANIC_SEL 5 Panic Tss Descriptor */
+{ (int) &dblfault_tss, /* segment base address */
+ sizeof(struct i386tss)-1,/* length - all address space */
+ SDT_SYS386TSS, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+/* GPROC0_SEL 6 Proc 0 Tss Descriptor */
+{ (int) kstack, /* segment base address */
+ sizeof(struct i386tss)-1,/* length - all address space */
+ SDT_SYS386TSS, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+/* GUSERLDT_SEL 7 User LDT Descriptor per process */
+{ (int) ldt, /* segment base address */
+ (512 * sizeof(union descriptor)-1), /* length */
+ SDT_SYSLDT, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* unused - default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+/* GAPMCODE32_SEL 8 APM BIOS 32-bit interface (32bit Code) */
+{ 0, /* segment base address (overwritten by APM) */
+ 0xfffff, /* length */
+ SDT_MEMERA, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+/* GAPMCODE16_SEL 9 APM BIOS 32-bit interface (16bit Code) */
+{ 0, /* segment base address (overwritten by APM) */
+ 0xfffff, /* length */
+ SDT_MEMERA, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+/* GAPMDATA_SEL 10 APM BIOS 32-bit interface (Data) */
+{ 0, /* segment base address (overwritten by APM) */
+ 0xfffff, /* length */
+ SDT_MEMRWA, /* segment type */
+ 0, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+};
+
+static struct soft_segment_descriptor ldt_segs[] = {
+ /* Null Descriptor - overwritten by call gate */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Null Descriptor - overwritten by call gate */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Null Descriptor - overwritten by call gate */
+{ 0x0, /* segment base address */
+ 0x0, /* length - all address space */
+ 0, /* segment type */
+ 0, /* segment descriptor priority level */
+ 0, /* segment descriptor present */
+ 0, 0,
+ 0, /* default 32 vs 16 bit size */
+ 0 /* limit granularity (byte/page units)*/ },
+ /* Code Descriptor for user */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMERA, /* segment type */
+ SEL_UPL, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+ /* Data Descriptor for user */
+{ 0x0, /* segment base address */
+ 0xfffff, /* length - all address space */
+ SDT_MEMRWA, /* segment type */
+ SEL_UPL, /* segment descriptor priority level */
+ 1, /* segment descriptor present */
+ 0, 0,
+ 1, /* default 32 vs 16 bit size */
+ 1 /* limit granularity (byte/page units)*/ },
+};
+
+void
+setidt(idx, func, typ, dpl, selec)
+ int idx;
+ inthand_t *func;
+ int typ;
+ int dpl;
+ int selec;
+{
+ struct gate_descriptor *ip = idt + idx;
+
+ ip->gd_looffset = (int)func;
+ ip->gd_selector = selec;
+ ip->gd_stkcpy = 0;
+ ip->gd_xx = 0;
+ ip->gd_type = typ;
+ ip->gd_dpl = dpl;
+ ip->gd_p = 1;
+ ip->gd_hioffset = ((int)func)>>16 ;
+}
+
+#define IDTVEC(name) __CONCAT(X,name)
+
+extern inthand_t
+ IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl),
+ IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm),
+ IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot),
+ IDTVEC(page), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align),
+ IDTVEC(syscall), IDTVEC(int0x80_syscall);
+
+void
+sdtossd(sd, ssd)
+ struct segment_descriptor *sd;
+ struct soft_segment_descriptor *ssd;
+{
+ ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase;
+ ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit;
+ ssd->ssd_type = sd->sd_type;
+ ssd->ssd_dpl = sd->sd_dpl;
+ ssd->ssd_p = sd->sd_p;
+ ssd->ssd_def32 = sd->sd_def32;
+ ssd->ssd_gran = sd->sd_gran;
+}
+
+void
+init386(first)
+ int first;
+{
+ int x;
+ unsigned biosbasemem, biosextmem;
+ struct gate_descriptor *gdp;
+ int gsel_tss;
+ /* table descriptors - used to load tables by microp */
+ struct region_descriptor r_gdt, r_idt;
+ int pagesinbase, pagesinext;
+ int target_page, pa_indx;
+
+ proc0.p_addr = proc0paddr;
+
+ atdevbase = ISA_HOLE_START + KERNBASE;
+
+ /*
+ * Initialize the console before we print anything out.
+ */
+ cninit();
+
+ /*
+ * make gdt memory segments, the code segment goes up to end of the
+ * page with etext in it, the data segment goes to the end of
+ * the address space
+ */
+ /*
+ * XXX text protection is temporarily (?) disabled. The limit was
+ * i386_btop(round_page(etext)) - 1.
+ */
+ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(0) - 1;
+ gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1;
+ for (x = 0; x < NGDT; x++)
+ ssdtosd(&gdt_segs[x], &gdt[x].sd);
+
+ /* make ldt memory segments */
+ /*
+ * The data segment limit must not cover the user area because we
+ * don't want the user area to be writable in copyout() etc. (page
+ * level protection is lost in kernel mode on 386's). Also, we
+ * don't want the user area to be writable directly (page level
+ * protection of the user area is not available on 486's with
+ * CR0_WP set, because there is no user-read/kernel-write mode).
+ *
+ * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it
+ * should be spelled ...MAX_USER...
+ */
+#define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS
+ /*
+ * The code segment limit has to cover the user area until we move
+ * the signal trampoline out of the user area. This is safe because
+ * the code segment cannot be written to directly.
+ */
+#define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * PAGE_SIZE)
+ ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1;
+ ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1;
+ /* Note. eventually want private ldts per process */
+ for (x = 0; x < NLDT; x++)
+ ssdtosd(&ldt_segs[x], &ldt[x].sd);
+
+ /* exceptions */
+ for (x = 0; x < NIDT; x++)
+ setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(8, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL));
+ setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+#ifdef CYRIX_486DLC
+ setidt(14, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+#else
+ setidt(14, &IDTVEC(page), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+#endif
+ setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(17, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(0x80, &IDTVEC(int0x80_syscall),
+ SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL));
+
+#ifdef PC98
+#include "nec.h"
+#include "epson.h"
+#if NNEC > 0 || NEPSON > 0
+ pc98_defaultirq();
+#endif
+#else /* IBM-PC */
+#include "isa.h"
+#if NISA >0
+ isa_defaultirq();
+#endif
+#endif
+ rand_initialize();
+
+ r_gdt.rd_limit = sizeof(gdt) - 1;
+ r_gdt.rd_base = (int) gdt;
+ lgdt(&r_gdt);
+
+ r_idt.rd_limit = sizeof(idt) - 1;
+ r_idt.rd_base = (int) idt;
+ lidt(&r_idt);
+
+ _default_ldt = GSEL(GLDT_SEL, SEL_KPL);
+ lldt(_default_ldt);
+ currentldt = _default_ldt;
+
+#ifdef DDB
+ kdb_init();
+ if (boothowto & RB_KDB)
+ Debugger("Boot flags requested debugger");
+#endif
+
+#ifdef PC98
+#ifdef EPSON_MEMWIN
+ if (pc98_machine_type & M_EPSON_PC98) {
+ if (Maxmem > 3840) {
+ if (Maxmem == Maxmem_under16M) {
+ Maxmem = 3840;
+ Maxmem_under16M = 3840;
+ } else if (Maxmem_under16M > 3840) {
+ Maxmem_under16M = 3840;
+ }
+ }
+
+ /* Disable 15MB-16MB caching */
+ switch (epson_machine_id) {
+ case 0x34: /* PC486HX */
+ case 0x35: /* PC486HG */
+ case 0x3B: /* PC486HA */
+ /* Cache control start */
+ outb(0x43f, 0x42);
+ outw(0xc40, 0x0033);
+
+ /* Disable 0xF00000-0xFFFFFF */
+ outb(0xc48, 0x49); outb(0xc4c, 0x00);
+ outb(0xc48, 0x48); outb(0xc4c, 0xf0);
+ outb(0xc48, 0x4d); outb(0xc4c, 0x00);
+ outb(0xc48, 0x4c); outb(0xc4c, 0xff);
+ outb(0xc48, 0x4f); outb(0xc4c, 0x00);
+
+ /* Cache control end */
+ outb(0x43f, 0x40);
+ break;
+
+ case 0x2B: /* PC486GR/GF */
+ case 0x30: /* PC486P */
+ case 0x31: /* PC486GRSuper */
+ case 0x32: /* PC486GR+ */
+ case 0x37: /* PC486SE */
+ case 0x38: /* PC486SR */
+ /* Disable 0xF00000-0xFFFFFF */
+ outb(0x43f, 0x42);
+ outb(0x467, 0xe0);
+ outb(0x567, 0xd8);
+
+ outb(0x43f, 0x40);
+ outb(0x467, 0xe0);
+ outb(0x567, 0xe0);
+ break;
+ }
+
+ /* Disable 15MB-16MB RAM and enable memory window */
+ outb(0x43b, inb(0x43b) & 0xfd); /* clear bit1 */
+ }
+#endif
+ biosbasemem = 640; /* 640KB */
+ biosextmem = (Maxmem * PAGE_SIZE - 0x100000)/1024; /* extent memory */
+#else /* IBM-PC */
+ /* Use BIOS values stored in RTC CMOS RAM, since probing
+ * breaks certain 386 AT relics.
+ */
+ biosbasemem = rtcin(RTC_BASELO)+ (rtcin(RTC_BASEHI)<<8);
+ biosextmem = rtcin(RTC_EXTLO)+ (rtcin(RTC_EXTHI)<<8);
+
+ /*
+ * Print a warning if the official BIOS interface disagrees
+ * with the hackish interface used above. Eventually only
+ * the official interface should be used.
+ */
+ if (bootinfo.bi_memsizes_valid) {
+ if (bootinfo.bi_basemem != biosbasemem)
+ printf("BIOS basemem (%ldK) != RTC basemem (%dK)\n",
+ bootinfo.bi_basemem, biosbasemem);
+ if (bootinfo.bi_extmem != biosextmem)
+ printf("BIOS extmem (%ldK) != RTC extmem (%dK)\n",
+ bootinfo.bi_extmem, biosextmem);
+ }
+#endif
+
+ /*
+ * If BIOS tells us that it has more than 640k in the basemem,
+ * don't believe it - set it to 640k.
+ */
+ if (biosbasemem > 640)
+ biosbasemem = 640;
+
+ /*
+ * Some 386 machines might give us a bogus number for extended
+ * mem. If this happens, stop now.
+ */
+#ifndef PC98
+#ifndef LARGEMEM
+ if (biosextmem > 65536) {
+ panic("extended memory beyond limit of 64MB");
+ /* NOTREACHED */
+ }
+#endif
+#endif
+
+ pagesinbase = biosbasemem * 1024 / PAGE_SIZE;
+ pagesinext = biosextmem * 1024 / PAGE_SIZE;
+
+ /*
+ * Special hack for chipsets that still remap the 384k hole when
+ * there's 16MB of memory - this really confuses people that
+ * are trying to use bus mastering ISA controllers with the
+ * "16MB limit"; they only have 16MB, but the remapping puts
+ * them beyond the limit.
+ */
+#ifndef PC98
+ /*
+ * If extended memory is between 15-16MB (16-17MB phys address range),
+ * chop it to 15MB.
+ */
+ if ((pagesinext > 3840) && (pagesinext < 4096))
+ pagesinext = 3840;
+#endif
+
+ /*
+ * Maxmem isn't the "maximum memory", it's one larger than the
+ * highest page of of the physical address space. It
+ */
+ Maxmem = pagesinext + 0x100000/PAGE_SIZE;
+
+#ifdef MAXMEM
+ Maxmem = MAXMEM/4;
+#endif
+
+ /* call pmap initialization to make new kernel address space */
+ pmap_bootstrap (first, 0);
+
+ /*
+ * Size up each available chunk of physical memory.
+ */
+
+ /*
+ * We currently don't bother testing base memory.
+ * XXX ...but we probably should.
+ */
+ pa_indx = 0;
+ badpages = 0;
+ if (pagesinbase > 1) {
+ phys_avail[pa_indx++] = PAGE_SIZE; /* skip first page of memory */
+ phys_avail[pa_indx] = ptoa(pagesinbase);/* memory up to the ISA hole */
+ physmem = pagesinbase - 1;
+ } else {
+ /* point at first chunk end */
+ pa_indx++;
+ }
+
+#ifdef PC98
+ /*
+ * Certain 'CPU accelerator' supports over 16MB memory on
+ * the machines whose BIOS doesn't store true size.
+ * To support this, we don't trust BIOS values if Maxmem < 4096.
+ */
+ if (Maxmem < 4096) {
+ for (target_page = ptoa(4096); /* 16MB */
+ target_page < ptoa(32768); /* 128MB */
+ target_page += 256 * PAGE_SIZE /* 1MB step */) {
+ int tmp, page_bad = FALSE, OrigMaxmem = Maxmem;
+
+ *(int *)CMAP1 = PG_V | PG_RW | PG_N | target_page;
+ pmap_update();
+
+ tmp = *(int *)CADDR1;
+ /*
+ * Test for alternating 1's and 0's
+ */
+ *(volatile int *)CADDR1 = 0xaaaaaaaa;
+ if (*(volatile int *)CADDR1 != 0xaaaaaaaa) {
+ page_bad = TRUE;
+ }
+ /*
+ * Test for alternating 0's and 1's
+ */
+ *(volatile int *)CADDR1 = 0x55555555;
+ if (*(volatile int *)CADDR1 != 0x55555555) {
+ page_bad = TRUE;
+ }
+ /*
+ * Test for all 1's
+ */
+ *(volatile int *)CADDR1 = 0xffffffff;
+ if (*(volatile int *)CADDR1 != 0xffffffff) {
+ page_bad = TRUE;
+ }
+ /*
+ * Test for all 0's
+ */
+ *(volatile int *)CADDR1 = 0x0;
+ if (*(volatile int *)CADDR1 != 0x0) {
+ /*
+ * test of page failed
+ */
+ page_bad = TRUE;
+ }
+ /*
+ * Restore original value.
+ */
+ *(int *)CADDR1 = tmp;
+ if (page_bad == TRUE) {
+ if (target_page > ptoa(4096))
+ Maxmem = atop(target_page);
+ else
+ Maxmem = OrigMaxmem;
+
+ break;
+ }
+ }
+ *(int *)CMAP1 = 0;
+ pmap_update();
+
+ /* XXX */
+ if (Maxmem > 3840) {
+ Maxmem_under16M = 3840;
+ if (Maxmem < 4096) {
+ Maxmem = 3840;
+ }
+ }
+ }
+#endif
+
+ for (target_page = avail_start; target_page < ptoa(Maxmem); target_page += PAGE_SIZE) {
+ int tmp, page_bad = FALSE;
+
+#ifdef PC98
+ /* skip system area */
+ if (target_page>=ptoa(Maxmem_under16M) &&
+ target_page < ptoa(4096))
+ page_bad = TRUE;
+#endif
+ /*
+ * map page into kernel: valid, read/write, non-cacheable
+ */
+ *(int *)CMAP1 = PG_V | PG_RW | PG_N | target_page;
+ pmap_update();
+
+ tmp = *(int *)CADDR1;
+ /*
+ * Test for alternating 1's and 0's
+ */
+ *(volatile int *)CADDR1 = 0xaaaaaaaa;
+ if (*(volatile int *)CADDR1 != 0xaaaaaaaa) {
+ page_bad = TRUE;
+ }
+ /*
+ * Test for alternating 0's and 1's
+ */
+ *(volatile int *)CADDR1 = 0x55555555;
+ if (*(volatile int *)CADDR1 != 0x55555555) {
+ page_bad = TRUE;
+ }
+ /*
+ * Test for all 1's
+ */
+ *(volatile int *)CADDR1 = 0xffffffff;
+ if (*(volatile int *)CADDR1 != 0xffffffff) {
+ page_bad = TRUE;
+ }
+ /*
+ * Test for all 0's
+ */
+ *(volatile int *)CADDR1 = 0x0;
+ if (*(volatile int *)CADDR1 != 0x0) {
+ /*
+ * test of page failed
+ */
+ page_bad = TRUE;
+ }
+ /*
+ * Restore original value.
+ */
+ *(int *)CADDR1 = tmp;
+
+ /*
+ * Adjust array of valid/good pages.
+ */
+ if (page_bad == FALSE) {
+ /*
+ * If this good page is a continuation of the
+ * previous set of good pages, then just increase
+ * the end pointer. Otherwise start a new chunk.
+ * Note that "end" points one higher than end,
+ * making the range >= start and < end.
+ */
+ if (phys_avail[pa_indx] == target_page) {
+ phys_avail[pa_indx] += PAGE_SIZE;
+ } else {
+ pa_indx++;
+ if (pa_indx == PHYS_AVAIL_ARRAY_END) {
+ printf("Too many holes in the physical address space, giving up\n");
+ pa_indx--;
+ break;
+ }
+ phys_avail[pa_indx++] = target_page; /* start */
+ phys_avail[pa_indx] = target_page + PAGE_SIZE; /* end */
+ }
+ physmem++;
+ } else {
+ badpages++;
+ page_bad = FALSE;
+ }
+ }
+
+ *(int *)CMAP1 = 0;
+ pmap_update();
+
+ /*
+ * XXX
+ * The last chunk must contain at least one page plus the message
+ * buffer to avoid complicating other code (message buffer address
+ * calculation, etc.).
+ */
+ while (phys_avail[pa_indx - 1] + PAGE_SIZE +
+ round_page(sizeof(struct msgbuf)) >= phys_avail[pa_indx]) {
+ physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]);
+ phys_avail[pa_indx--] = 0;
+ phys_avail[pa_indx--] = 0;
+ }
+
+ Maxmem = atop(phys_avail[pa_indx]);
+
+ /* Trim off space for the message buffer. */
+ phys_avail[pa_indx] -= round_page(sizeof(struct msgbuf));
+
+ avail_end = phys_avail[pa_indx];
+
+ /* now running on new page tables, configured,and u/iom is accessible */
+
+ /* make a initial tss so microp can get interrupt stack on syscall! */
+ proc0.p_addr->u_pcb.pcb_tss.tss_esp0 = (int) kstack + UPAGES*PAGE_SIZE;
+ proc0.p_addr->u_pcb.pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ;
+ gsel_tss = GSEL(GPROC0_SEL, SEL_KPL);
+
+ dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 =
+ dblfault_tss.tss_esp2 = (int) &dblfault_stack[sizeof(dblfault_stack)];
+ dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 =
+ dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL);
+ dblfault_tss.tss_cr3 = IdlePTD;
+ dblfault_tss.tss_eip = (int) dblfault_handler;
+ dblfault_tss.tss_eflags = PSL_KERNEL;
+ dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_fs = dblfault_tss.tss_gs =
+ GSEL(GDATA_SEL, SEL_KPL);
+ dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL);
+ dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL);
+
+ ((struct i386tss *)gdt_segs[GPROC0_SEL].ssd_base)->tss_ioopt =
+ (sizeof(struct i386tss))<<16;
+
+ ltr(gsel_tss);
+
+ /* make a call gate to reenter kernel with */
+ gdp = &ldt[LSYS5CALLS_SEL].gd;
+
+ x = (int) &IDTVEC(syscall);
+ gdp->gd_looffset = x++;
+ gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL);
+ gdp->gd_stkcpy = 1;
+ gdp->gd_type = SDT_SYS386CGT;
+ gdp->gd_dpl = SEL_UPL;
+ gdp->gd_p = 1;
+ gdp->gd_hioffset = ((int) &IDTVEC(syscall)) >>16;
+
+ /* transfer to user mode */
+
+ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL);
+ _udatasel = LSEL(LUDATA_SEL, SEL_UPL);
+
+ /* setup proc 0's pcb */
+ proc0.p_addr->u_pcb.pcb_flags = 0;
+ proc0.p_addr->u_pcb.pcb_cr3 = IdlePTD;
+}
+
+/*
+ * The registers are in the frame; the frame is in the user area of
+ * the process in question; when the process is active, the registers
+ * are in "the kernel stack"; when it's not, they're still there, but
+ * things get flipped around. So, since p->p_md.md_regs is the whole address
+ * of the register set, take its offset from the kernel stack, and
+ * index into the user block. Don't you just *love* virtual memory?
+ * (I'm starting to think seymour is right...)
+ */
+#define TF_REGP(p) ((struct trapframe *) \
+ ((char *)(p)->p_addr \
+ + ((char *)(p)->p_md.md_regs - kstack)))
+
+int
+ptrace_set_pc(p, addr)
+ struct proc *p;
+ unsigned int addr;
+{
+ TF_REGP(p)->tf_eip = addr;
+ return (0);
+}
+
+int
+ptrace_single_step(p)
+ struct proc *p;
+{
+ TF_REGP(p)->tf_eflags |= PSL_T;
+ return (0);
+}
+
+int ptrace_write_u(p, off, data)
+ struct proc *p;
+ vm_offset_t off;
+ int data;
+{
+ struct trapframe frame_copy;
+ vm_offset_t min;
+ struct trapframe *tp;
+
+ /*
+ * Privileged kernel state is scattered all over the user area.
+ * Only allow write access to parts of regs and to fpregs.
+ */
+ min = (char *)p->p_md.md_regs - kstack;
+ if (off >= min && off <= min + sizeof(struct trapframe) - sizeof(int)) {
+ tp = TF_REGP(p);
+ frame_copy = *tp;
+ *(int *)((char *)&frame_copy + (off - min)) = data;
+ if (!EFLAGS_SECURE(frame_copy.tf_eflags, tp->tf_eflags) ||
+ !CS_SECURE(frame_copy.tf_cs))
+ return (EINVAL);
+ *(int*)((char *)p->p_addr + off) = data;
+ return (0);
+ }
+ min = offsetof(struct user, u_pcb) + offsetof(struct pcb, pcb_savefpu);
+ if (off >= min && off <= min + sizeof(struct save87) - sizeof(int)) {
+ *(int*)((char *)p->p_addr + off) = data;
+ return (0);
+ }
+ return (EFAULT);
+}
+
+int
+fill_regs(p, regs)
+ struct proc *p;
+ struct reg *regs;
+{
+ struct trapframe *tp;
+
+ tp = TF_REGP(p);
+ regs->r_es = tp->tf_es;
+ regs->r_ds = tp->tf_ds;
+ regs->r_edi = tp->tf_edi;
+ regs->r_esi = tp->tf_esi;
+ regs->r_ebp = tp->tf_ebp;
+ regs->r_ebx = tp->tf_ebx;
+ regs->r_edx = tp->tf_edx;
+ regs->r_ecx = tp->tf_ecx;
+ regs->r_eax = tp->tf_eax;
+ regs->r_eip = tp->tf_eip;
+ regs->r_cs = tp->tf_cs;
+ regs->r_eflags = tp->tf_eflags;
+ regs->r_esp = tp->tf_esp;
+ regs->r_ss = tp->tf_ss;
+ return (0);
+}
+
+int
+set_regs(p, regs)
+ struct proc *p;
+ struct reg *regs;
+{
+ struct trapframe *tp;
+
+ tp = TF_REGP(p);
+ if (!EFLAGS_SECURE(regs->r_eflags, tp->tf_eflags) ||
+ !CS_SECURE(regs->r_cs))
+ return (EINVAL);
+ tp->tf_es = regs->r_es;
+ tp->tf_ds = regs->r_ds;
+ tp->tf_edi = regs->r_edi;
+ tp->tf_esi = regs->r_esi;
+ tp->tf_ebp = regs->r_ebp;
+ tp->tf_ebx = regs->r_ebx;
+ tp->tf_edx = regs->r_edx;
+ tp->tf_ecx = regs->r_ecx;
+ tp->tf_eax = regs->r_eax;
+ tp->tf_eip = regs->r_eip;
+ tp->tf_cs = regs->r_cs;
+ tp->tf_eflags = regs->r_eflags;
+ tp->tf_esp = regs->r_esp;
+ tp->tf_ss = regs->r_ss;
+ return (0);
+}
+
+#ifndef DDB
+void
+Debugger(const char *msg)
+{
+ printf("Debugger(\"%s\") called.\n", msg);
+}
+#endif /* no DDB */
+
+#include <sys/disklabel.h>
+#define b_cylin b_resid
+/*
+ * Determine the size of the transfer, and make sure it is
+ * within the boundaries of the partition. Adjust transfer
+ * if needed, and signal errors or early completion.
+ */
+int
+bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel)
+{
+ struct partition *p = lp->d_partitions + dkpart(bp->b_dev);
+ int labelsect = lp->d_partitions[0].p_offset;
+ int maxsz = p->p_size,
+ sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
+
+ /* overwriting disk label ? */
+ /* XXX should also protect bootstrap in first 8K */
+ if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect &&
+#if LABELSECTOR != 0
+ bp->b_blkno + p->p_offset + sz > LABELSECTOR + labelsect &&
+#endif
+ (bp->b_flags & B_READ) == 0 && wlabel == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+
+#if defined(DOSBBSECTOR) && defined(notyet)
+ /* overwriting master boot record? */
+ if (bp->b_blkno + p->p_offset <= DOSBBSECTOR &&
+ (bp->b_flags & B_READ) == 0 && wlabel == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+#endif
+
+ /* beyond partition? */
+ if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
+ /* if exactly at end of disk, return an EOF */
+ if (bp->b_blkno == maxsz) {
+ bp->b_resid = bp->b_bcount;
+ return(0);
+ }
+ /* or truncate if part of it fits */
+ sz = maxsz - bp->b_blkno;
+ if (sz <= 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+ bp->b_bcount = sz << DEV_BSHIFT;
+ }
+
+ /* calculate cylinder for disksort to order transfers with */
+ bp->b_pblkno = bp->b_blkno + p->p_offset;
+ bp->b_cylin = bp->b_pblkno / lp->d_secpercyl;
+ return(1);
+
+bad:
+ bp->b_flags |= B_ERROR;
+ return(-1);
+}
+
+int
+disk_externalize(int drive, struct sysctl_req *req)
+{
+ return SYSCTL_OUT(req, &drive, sizeof drive);
+}
OpenPOWER on IntegriCloud