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
commitdaf17bd7d3b941efbb36e2a61c81ed4cf58da10c (patch)
treefc02c196bd82ae8ebbe0dd76568f79165238d10d /sys/pc98
parent11677d0ab4154036d365fe6697f9d6048dd6bf0e (diff)
parent5c74bc7da33aff64a83802f2e8636b2720e9e1c2 (diff)
downloadFreeBSD-src-daf17bd7d3b941efbb36e2a61c81ed4cf58da10c.zip
FreeBSD-src-daf17bd7d3b941efbb36e2a61c81ed4cf58da10c.tar.gz
This commit was generated by cvs2svn to compensate for changes in r16359,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'sys/pc98')
-rw-r--r--sys/pc98/apm/apm.c929
-rw-r--r--sys/pc98/boot/Makefile5
-rw-r--r--sys/pc98/boot/biosboot/Makefile92
-rw-r--r--sys/pc98/boot/biosboot/README.386BSD151
-rw-r--r--sys/pc98/boot/biosboot/README.MACH210
-rw-r--r--sys/pc98/boot/biosboot/README.serial164
-rw-r--r--sys/pc98/boot/biosboot/README.serial.9852
-rw-r--r--sys/pc98/boot/biosboot/asm.S278
-rw-r--r--sys/pc98/boot/biosboot/asm.h144
-rw-r--r--sys/pc98/boot/biosboot/bios.S313
-rw-r--r--sys/pc98/boot/biosboot/boot.c397
-rw-r--r--sys/pc98/boot/biosboot/boot.h101
-rw-r--r--sys/pc98/boot/biosboot/boot2.S175
-rw-r--r--sys/pc98/boot/biosboot/disk.c322
-rw-r--r--sys/pc98/boot/biosboot/io.c398
-rw-r--r--sys/pc98/boot/biosboot/probe_keyboard.c40
-rw-r--r--sys/pc98/boot/biosboot/serial.S219
-rw-r--r--sys/pc98/boot/biosboot/start.S354
-rw-r--r--sys/pc98/boot/biosboot/sys.c297
-rw-r--r--sys/pc98/boot/biosboot/table.c151
-rw-r--r--sys/pc98/boot/kzipboot/Makefile30
-rw-r--r--sys/pc98/boot/kzipboot/README49
-rw-r--r--sys/pc98/boot/kzipboot/boot.c194
-rw-r--r--sys/pc98/boot/kzipboot/gzip.h78
-rw-r--r--sys/pc98/boot/kzipboot/head.S11
-rw-r--r--sys/pc98/boot/kzipboot/malloc.c58
-rw-r--r--sys/pc98/boot/kzipboot/misc.c207
-rw-r--r--sys/pc98/boot/kzipboot/tail.S12
-rw-r--r--sys/pc98/boot/kzipboot/unzip.c155
-rw-r--r--sys/pc98/boot/netboot/3c509.c155
-rw-r--r--sys/pc98/boot/netboot/3c509.h388
-rw-r--r--sys/pc98/boot/netboot/Makefile96
-rw-r--r--sys/pc98/boot/netboot/bootmenu.c346
-rw-r--r--sys/pc98/boot/netboot/ether.c572
-rw-r--r--sys/pc98/boot/netboot/ether.h267
-rw-r--r--sys/pc98/boot/netboot/if_epreg.h388
-rw-r--r--sys/pc98/boot/netboot/main.c657
-rw-r--r--sys/pc98/boot/netboot/makerom.c53
-rw-r--r--sys/pc98/boot/netboot/misc.c290
-rw-r--r--sys/pc98/boot/netboot/netboot.doc42
-rw-r--r--sys/pc98/boot/netboot/netboot.h247
-rw-r--r--sys/pc98/boot/netboot/ns8390.c651
-rw-r--r--sys/pc98/boot/netboot/ns8390.h252
-rw-r--r--sys/pc98/boot/netboot/rpc.c187
-rw-r--r--sys/pc98/boot/netboot/start2.S349
-rw-r--r--sys/pc98/conf/GENERIC98164
-rw-r--r--sys/pc98/conf/Makefile.pc98196
-rw-r--r--sys/pc98/conf/devices.pc9817
-rw-r--r--sys/pc98/conf/files.pc98277
-rw-r--r--sys/pc98/conf/majors.pc98115
-rw-r--r--sys/pc98/conf/options.pc9825
-rw-r--r--sys/pc98/i386/autoconf.c408
-rw-r--r--sys/pc98/i386/exception.s277
-rw-r--r--sys/pc98/i386/locore.s1079
-rw-r--r--sys/pc98/i386/machdep.c2068
-rw-r--r--sys/pc98/i386/microtime.s262
-rw-r--r--sys/pc98/i386/pmap.c2556
-rw-r--r--sys/pc98/i386/support.s1008
-rw-r--r--sys/pc98/i386/trap.c1006
-rw-r--r--sys/pc98/i386/userconfig.c2922
-rw-r--r--sys/pc98/i386/vm_machdep.c897
-rw-r--r--sys/pc98/pc98/30line.h125
-rw-r--r--sys/pc98/pc98/aic6360.c2518
-rw-r--r--sys/pc98/pc98/atapi.c1074
-rw-r--r--sys/pc98/pc98/atcompat_diskslice.c474
-rw-r--r--sys/pc98/pc98/clock.c1129
-rw-r--r--sys/pc98/pc98/diskslice_machdep.c652
-rw-r--r--sys/pc98/pc98/fd.c2489
-rw-r--r--sys/pc98/pc98/fd.c.new2680
-rw-r--r--sys/pc98/pc98/fdc.h84
-rw-r--r--sys/pc98/pc98/fdreg.h102
-rw-r--r--sys/pc98/pc98/ft.c2695
-rw-r--r--sys/pc98/pc98/ftreg.h90
-rw-r--r--sys/pc98/pc98/ic/i82365.h190
-rw-r--r--sys/pc98/pc98/ic/i8237.h11
-rw-r--r--sys/pc98/pc98/ic/i8251.h79
-rw-r--r--sys/pc98/pc98/ic/mb86960.h372
-rw-r--r--sys/pc98/pc98/ic/nec765.h141
-rw-r--r--sys/pc98/pc98/ic/ns16550.h68
-rw-r--r--sys/pc98/pc98/ic/wd33c93.h127
-rw-r--r--sys/pc98/pc98/icu.h125
-rw-r--r--sys/pc98/pc98/icu.s357
-rw-r--r--sys/pc98/pc98/if_ed.c3442
-rw-r--r--sys/pc98/pc98/if_edreg.h1162
-rw-r--r--sys/pc98/pc98/if_ep.c1502
-rw-r--r--sys/pc98/pc98/if_epreg.h461
-rw-r--r--sys/pc98/pc98/if_fe.c3391
-rw-r--r--sys/pc98/pc98/if_fereg.h149
-rw-r--r--sys/pc98/pc98/if_zp.c1198
-rw-r--r--sys/pc98/pc98/if_zpreg.h295
-rw-r--r--sys/pc98/pc98/kbd.h15
-rw-r--r--sys/pc98/pc98/kbdtables.h1039
-rw-r--r--sys/pc98/pc98/lpt.c1488
-rw-r--r--sys/pc98/pc98/lptreg.h59
-rw-r--r--sys/pc98/pc98/matcd/TODO42
-rw-r--r--sys/pc98/pc98/matcd/audio.c514
-rw-r--r--sys/pc98/pc98/matcd/creative.h142
-rw-r--r--sys/pc98/pc98/matcd/matcd.c2748
-rw-r--r--sys/pc98/pc98/matcd/matcddrv.h198
-rw-r--r--sys/pc98/pc98/matcd/options.h283
-rw-r--r--sys/pc98/pc98/mk30line61
-rw-r--r--sys/pc98/pc98/module.h7
-rw-r--r--sys/pc98/pc98/mse.c821
-rw-r--r--sys/pc98/pc98/npx.c679
-rw-r--r--sys/pc98/pc98/pc98.c1228
-rw-r--r--sys/pc98/pc98/pc98.h185
-rw-r--r--sys/pc98/pc98/pc98_device.h248
-rw-r--r--sys/pc98/pc98/pcaudio.c589
-rw-r--r--sys/pc98/pc98/pcibus.c533
-rw-r--r--sys/pc98/pc98/pcic.h181
-rw-r--r--sys/pc98/pc98/pcicx.c241
-rw-r--r--sys/pc98/pc98/prof_machdep.c163
-rw-r--r--sys/pc98/pc98/random_machdep.c488
-rw-r--r--sys/pc98/pc98/sbic55.c956
-rw-r--r--sys/pc98/pc98/sbic55.c.new1783
-rw-r--r--sys/pc98/pc98/sbicreg.h429
-rw-r--r--sys/pc98/pc98/sbicvar.h157
-rw-r--r--sys/pc98/pc98/scd.c1601
-rw-r--r--sys/pc98/pc98/scsireg.h312
-rw-r--r--sys/pc98/pc98/sio.c4023
-rw-r--r--sys/pc98/pc98/sioreg.h125
-rw-r--r--sys/pc98/pc98/sound/CHANGELOG122
-rw-r--r--sys/pc98/pc98/sound/COPYING25
-rw-r--r--sys/pc98/pc98/sound/README86
-rw-r--r--sys/pc98/pc98/sound/Readme.aedsp166
-rw-r--r--sys/pc98/pc98/sound/Readme.modules87
-rw-r--r--sys/pc98/pc98/sound/Readme.v30142
-rw-r--r--sys/pc98/pc98/sound/ad1848.c1525
-rw-r--r--sys/pc98/pc98/sound/ad1848_mixer.h130
-rw-r--r--sys/pc98/pc98/sound/adlib_card.c51
-rw-r--r--sys/pc98/pc98/sound/aedsp16.c838
-rw-r--r--sys/pc98/pc98/sound/audio.c583
-rw-r--r--sys/pc98/pc98/sound/coproc.h12
-rw-r--r--sys/pc98/pc98/sound/dev_table.c182
-rw-r--r--sys/pc98/pc98/sound/dev_table.h429
-rw-r--r--sys/pc98/pc98/sound/dmabuf.c1130
-rw-r--r--sys/pc98/pc98/sound/finetune.h49
-rw-r--r--sys/pc98/pc98/sound/gus_card.c197
-rw-r--r--sys/pc98/pc98/sound/gus_hw.h50
-rw-r--r--sys/pc98/pc98/sound/gus_linearvol.h18
-rw-r--r--sys/pc98/pc98/sound/gus_midi.c312
-rw-r--r--sys/pc98/pc98/sound/gus_vol.c150
-rw-r--r--sys/pc98/pc98/sound/gus_wave.c3445
-rw-r--r--sys/pc98/pc98/sound/hex2hex.h97
-rw-r--r--sys/pc98/pc98/sound/ics2101.c264
-rw-r--r--sys/pc98/pc98/sound/local.h128
-rw-r--r--sys/pc98/pc98/sound/mad16.h91
-rw-r--r--sys/pc98/pc98/sound/midi_ctrl.h22
-rw-r--r--sys/pc98/pc98/sound/midi_synth.c644
-rw-r--r--sys/pc98/pc98/sound/midi_synth.h48
-rw-r--r--sys/pc98/pc98/sound/midibuf.c470
-rw-r--r--sys/pc98/pc98/sound/mpu401.c1777
-rw-r--r--sys/pc98/pc98/sound/opl3.c1292
-rw-r--r--sys/pc98/pc98/sound/opl3.h261
-rw-r--r--sys/pc98/pc98/sound/os.h361
-rw-r--r--sys/pc98/pc98/sound/pas.h250
-rw-r--r--sys/pc98/pc98/sound/pas2_card.c420
-rw-r--r--sys/pc98/pc98/sound/pas2_midi.c341
-rw-r--r--sys/pc98/pc98/sound/pas2_mixer.c340
-rw-r--r--sys/pc98/pc98/sound/pas2_pcm.c457
-rw-r--r--sys/pc98/pc98/sound/patmgr.c268
-rw-r--r--sys/pc98/pc98/sound/pcm86.c2189
-rw-r--r--sys/pc98/pc98/sound/sb.h43
-rw-r--r--sys/pc98/pc98/sound/sb16_dsp.c589
-rw-r--r--sys/pc98/pc98/sound/sb16_midi.c311
-rw-r--r--sys/pc98/pc98/sound/sb_card.c61
-rw-r--r--sys/pc98/pc98/sound/sb_dsp.c1252
-rw-r--r--sys/pc98/pc98/sound/sb_midi.c253
-rw-r--r--sys/pc98/pc98/sound/sb_mixer.c587
-rw-r--r--sys/pc98/pc98/sound/sb_mixer.h256
-rw-r--r--sys/pc98/pc98/sound/sequencer.c1988
-rw-r--r--sys/pc98/pc98/sound/sound.doc120
-rw-r--r--sys/pc98/pc98/sound/sound_calls.h272
-rw-r--r--sys/pc98/pc98/sound/sound_config.h375
-rw-r--r--sys/pc98/pc98/sound/sound_switch.c544
-rw-r--r--sys/pc98/pc98/sound/sound_timer.c406
-rw-r--r--sys/pc98/pc98/sound/soundcard.c621
-rw-r--r--sys/pc98/pc98/sound/soundvers.h1
-rw-r--r--sys/pc98/pc98/sound/sscape.c1120
-rw-r--r--sys/pc98/pc98/sound/sys_timer.c304
-rw-r--r--sys/pc98/pc98/sound/trix.c323
-rw-r--r--sys/pc98/pc98/sound/tuning.h26
-rw-r--r--sys/pc98/pc98/sound/uart6850.c327
-rw-r--r--sys/pc98/pc98/sound/ulaw.h69
-rw-r--r--sys/pc98/pc98/spkr.c662
-rw-r--r--sys/pc98/pc98/syscons.c4394
-rw-r--r--sys/pc98/pc98/syscons.h265
-rw-r--r--sys/pc98/pc98/timerreg.h93
-rw-r--r--sys/pc98/pc98/vector.s306
-rw-r--r--sys/pc98/pc98/wcd.c1241
-rw-r--r--sys/pc98/pc98/wd.c2477
-rw-r--r--sys/pc98/pc98/wdreg.h222
192 files changed, 111120 insertions, 0 deletions
diff --git a/sys/pc98/apm/apm.c b/sys/pc98/apm/apm.c
new file mode 100644
index 0000000..d140eb0
--- /dev/null
+++ b/sys/pc98/apm/apm.c
@@ -0,0 +1,929 @@
+/*
+ * APM (Advanced Power Management) BIOS Device Driver
+ *
+ * Copyright (c) 1994 UKAI, Fumitoshi.
+ * Copyright (c) 1994-1995 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp>
+ * Copyright (c) 1996 Nate Williams <nate@FreeBSD.org>
+ *
+ * This software may be used, modified, copied, and distributed, in
+ * both source and binary form provided that the above copyright and
+ * these terms are retained. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with its
+ * use.
+ *
+ * Sep, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
+ *
+ * $Id: apm.c,v 1.43 1996/06/04 17:50:28 nate Exp $
+ */
+
+#include "apm.h"
+
+#if NAPM > 1
+#error only one APM device may be configured
+#endif
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /*DEVFS*/
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#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"
+#endif
+#include <machine/apm_bios.h>
+#include <machine/segments.h>
+#include <machine/clock.h>
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#include <sys/syslog.h>
+#include <sys/devconf.h>
+#include "apm_setup.h"
+
+static int apm_display_off __P((void));
+static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx));
+static void apm_resume __P((void));
+
+/* static data */
+struct apm_softc {
+ int initialized, active;
+ int always_halt_cpu, slow_idle_cpu;
+ int disabled, disengaged;
+ u_int minorversion, majorversion;
+ u_int cs32_base, cs16_base, ds_base;
+ u_int cs_limit, ds_limit;
+ u_int cs_entry;
+ u_int intversion;
+ struct apmhook sc_suspend;
+ struct apmhook sc_resume;
+#ifdef DEVFS
+ void *sc_devfs_token;
+#endif
+};
+
+static struct kern_devconf kdc_apm = {
+ 0, 0, 0, /* filled in by dev_attach */
+ "apm", 0, { MDDT_ISA, 0 },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "Advanced Power Management BIOS",
+ DC_CLS_MISC /* class */
+};
+
+
+static struct apm_softc apm_softc;
+static struct apmhook *hook[NAPM_HOOK]; /* XXX */
+
+#define is_enabled(foo) ((foo) ? "enabled" : "disabled")
+
+/* Map version number to integer (keeps ordering of version numbers) */
+#define INTVERSION(major, minor) ((major)*100 + (minor))
+
+static timeout_t apm_timeout;
+static d_open_t apmopen;
+static d_close_t apmclose;
+static d_ioctl_t apmioctl;
+
+#define CDEV_MAJOR 39
+static struct cdevsw apm_cdevsw =
+ { apmopen, apmclose, noread, nowrite, /*39*/
+ apmioctl, nostop, nullreset, nodevtotty,/* APM */
+ seltrue, nommap, NULL , "apm" ,NULL, -1};
+
+static void
+apm_registerdev(struct isa_device *id)
+{
+ if (kdc_apm.kdc_isa)
+ return;
+ kdc_apm.kdc_state = DC_UNCONFIGURED;
+ kdc_apm.kdc_unit = 0;
+ kdc_apm.kdc_isa = id;
+ dev_attach(&kdc_apm);
+}
+
+/* setup APM GDT discriptors */
+static void
+setup_apm_gdt(u_int code32_base, u_int code16_base, u_int data_base, u_int code_limit, u_int data_limit)
+{
+ /* setup 32bit code segment */
+ gdt_segs[GAPMCODE32_SEL].ssd_base = code32_base;
+ gdt_segs[GAPMCODE32_SEL].ssd_limit = code_limit;
+
+ /* setup 16bit code segment */
+ gdt_segs[GAPMCODE16_SEL].ssd_base = code16_base;
+ gdt_segs[GAPMCODE16_SEL].ssd_limit = code_limit;
+
+ /* setup data segment */
+ gdt_segs[GAPMDATA_SEL ].ssd_base = data_base;
+ gdt_segs[GAPMDATA_SEL ].ssd_limit = data_limit;
+
+ /* reflect these changes on physical GDT */
+ ssdtosd(gdt_segs + GAPMCODE32_SEL, &gdt[GAPMCODE32_SEL].sd);
+ ssdtosd(gdt_segs + GAPMCODE16_SEL, &gdt[GAPMCODE16_SEL].sd);
+ ssdtosd(gdt_segs + GAPMDATA_SEL , &gdt[GAPMDATA_SEL ].sd);
+}
+
+/* 48bit far pointer */
+static struct addr48 {
+ u_long offset;
+ u_short segment;
+} apm_addr;
+
+static int apm_errno;
+
+inline
+int
+apm_int(u_long *eax, u_long *ebx, u_long *ecx)
+{
+ u_long cf;
+ __asm __volatile("
+ pushfl
+ cli
+ lcall _apm_addr
+ movl $0, %3
+ jnc 1f
+ incl %3
+ 1:
+ popfl
+ "
+ : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=D" (cf)
+ : "0" (*eax), "1" (*ebx), "2" (*ecx)
+ : "dx", "si", "memory"
+ );
+ apm_errno = ((*eax) >> 8) & 0xff;
+ return cf;
+}
+
+
+/* enable/disable power management */
+static int
+apm_enable_disable_pm(struct apm_softc *sc, int enable)
+{
+ u_long eax, ebx, ecx;
+
+ eax = (APM_BIOS << 8) | APM_ENABLEDISABLEPM;
+
+ if (sc->intversion >= INTVERSION(1, 1)) {
+ ebx = PMDV_ALLDEV;
+ } else {
+ ebx = 0xffff; /* APM version 1.0 only */
+ }
+ ecx = enable;
+ return apm_int(&eax, &ebx, &ecx);
+}
+
+/* Tell APM-BIOS that WE will do 1.1 and see what they say... */
+static void
+apm_driver_version(void)
+{
+ u_long eax, ebx, ecx;
+
+ eax = (APM_BIOS << 8) | APM_DRVVERSION;
+ ebx = 0x0;
+ ecx = 0x0101;
+ if(!apm_int(&eax, &ebx, &ecx))
+ apm_version = eax & 0xffff;
+}
+
+/* engage/disengage power management (APM 1.1 or later) */
+static int
+apm_engage_disengage_pm(struct apm_softc *sc, int engage)
+{
+ u_long eax, ebx, ecx;
+
+ eax = (APM_BIOS << 8) | APM_ENGAGEDISENGAGEPM;
+ ebx = PMDV_ALLDEV;
+ ecx = engage;
+ return(apm_int(&eax, &ebx, &ecx));
+}
+
+/* get PM event */
+static u_int
+apm_getevent(struct apm_softc *sc)
+{
+ u_long eax, ebx, ecx;
+
+ eax = (APM_BIOS << 8) | APM_GETPMEVENT;
+
+ ebx = 0;
+ ecx = 0;
+ if (apm_int(&eax, &ebx, &ecx))
+ return PMEV_NOEVENT;
+
+ return ebx & 0xffff;
+}
+
+/* suspend entire system */
+static int
+apm_suspend_system(struct apm_softc *sc)
+{
+ u_long eax, ebx, ecx;
+
+ eax = (APM_BIOS << 8) | APM_SETPWSTATE;
+ ebx = PMDV_ALLDEV;
+ ecx = PMST_SUSPEND;
+
+ if (apm_int(&eax, &ebx, &ecx)) {
+ printf("Entire system suspend failure: errcode = %ld\n",
+ 0xff & (eax >> 8));
+ return 1;
+ }
+ return 0;
+}
+
+/* Display control */
+/*
+ * Experimental implementation: My laptop machine can't handle this function
+ * If your laptop can control the display via APM, please inform me.
+ * HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp>
+ */
+static int
+apm_display_off(void)
+{
+ u_long eax, ebx, ecx;
+
+ eax = (APM_BIOS << 8) | APM_SETPWSTATE;
+ ebx = PMDV_2NDSTORAGE0;
+ ecx = PMST_STANDBY;
+ if (apm_int(&eax, &ebx, &ecx)) {
+ printf("Display off failure: errcode = %ld\n",
+ 0xff & (eax >> 8));
+ return 1;
+ }
+
+ return 0;
+}
+
+/* APM Battery low handler */
+static void
+apm_battery_low(struct apm_softc *sc)
+{
+ printf("\007\007 * * * BATTERY IS LOW * * * \007\007");
+}
+
+/* APM hook manager */
+static struct apmhook *
+apm_add_hook(struct apmhook **list, struct apmhook *ah)
+{
+ int s;
+ struct apmhook *p, *prev;
+
+#ifdef APM_DEBUG
+ printf("Add hook \"%s\"\n", ah->ah_name);
+#endif
+
+ s = splhigh();
+ if (ah == NULL) {
+ panic("illegal apm_hook!");
+ }
+ prev = NULL;
+ for (p = *list; p != NULL; prev = p, p = p->ah_next) {
+ if (p->ah_order > ah->ah_order) {
+ break;
+ }
+ }
+
+ if (prev == NULL) {
+ ah->ah_next = *list;
+ *list = ah;
+ } else {
+ ah->ah_next = prev->ah_next;
+ prev->ah_next = ah;
+ }
+ splx(s);
+ return ah;
+}
+
+static void
+apm_del_hook(struct apmhook **list, struct apmhook *ah)
+{
+ int s;
+ struct apmhook *p, *prev;
+
+ s = splhigh();
+ prev = NULL;
+ for (p = *list; p != NULL; prev = p, p = p->ah_next) {
+ if (p == ah) {
+ goto deleteit;
+ }
+ }
+ panic("Tried to delete unregistered apm_hook.");
+ goto nosuchnode;
+deleteit:
+ if (prev != NULL) {
+ prev->ah_next = p->ah_next;
+ } else {
+ *list = p->ah_next;
+ }
+nosuchnode:
+ splx(s);
+}
+
+
+/* APM driver calls some functions automatically */
+static void
+apm_execute_hook(struct apmhook *list)
+{
+ struct apmhook *p;
+
+ for (p = list; p != NULL; p = p->ah_next) {
+#ifdef APM_DEBUG
+ printf("Execute APM hook \"%s.\"\n", p->ah_name);
+#endif
+ if ((*(p->ah_fun))(p->ah_arg)) {
+ printf("Warning: APM hook \"%s\" failed", p->ah_name);
+ }
+ }
+}
+
+
+/* establish an apm hook */
+struct apmhook *
+apm_hook_establish(int apmh, struct apmhook *ah)
+{
+ if (apmh < 0 || apmh >= NAPM_HOOK)
+ return NULL;
+
+ return apm_add_hook(&hook[apmh], ah);
+}
+
+/* disestablish an apm hook */
+void
+apm_hook_disestablish(int apmh, struct apmhook *ah)
+{
+ if (apmh < 0 || apmh >= NAPM_HOOK)
+ return;
+
+ apm_del_hook(&hook[apmh], ah);
+}
+
+
+static struct timeval suspend_time;
+static struct timeval diff_time;
+
+static int
+apm_default_resume(void *arg)
+{
+ int pl;
+ u_int second, minute, hour;
+ struct timeval resume_time, tmp_time;
+
+ /* modified for adjkerntz */
+ pl = splsoftclock();
+ inittodr(0); /* adjust time to RTC */
+ microtime(&resume_time);
+ tmp_time = time; /* because 'time' is volatile */
+ timevaladd(&tmp_time, &diff_time);
+ time = tmp_time;
+ splx(pl);
+ second = resume_time.tv_sec - suspend_time.tv_sec;
+ hour = second / 3600;
+ second %= 3600;
+ minute = second / 60;
+ second %= 60;
+ log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n",
+ hour, minute, second);
+ return 0;
+}
+
+static int
+apm_default_suspend(void *arg)
+{
+ int pl;
+
+ pl = splsoftclock();
+ microtime(&diff_time);
+ inittodr(0);
+ microtime(&suspend_time);
+ timevalsub(&diff_time, &suspend_time);
+ splx(pl);
+ return 0;
+}
+
+static void apm_processevent(struct apm_softc *);
+
+/*
+ * Public interface to the suspend/resume:
+ *
+ * Execute suspend and resume hook before and after sleep, respectively.
+ *
+ */
+
+void
+apm_suspend(void)
+{
+ struct apm_softc *sc = &apm_softc;
+
+ if (!sc)
+ return;
+
+ if (sc->initialized) {
+ apm_execute_hook(hook[APM_HOOK_SUSPEND]);
+ apm_suspend_system(sc);
+ apm_processevent(sc);
+ }
+}
+
+void
+apm_resume(void)
+{
+ struct apm_softc *sc = &apm_softc;
+
+ if (!sc)
+ return;
+
+ if (sc->initialized) {
+ apm_execute_hook(hook[APM_HOOK_RESUME]);
+ }
+}
+
+
+/* get APM information */
+static int
+apm_get_info(struct apm_softc *sc, apm_info_t aip)
+{
+ u_long eax, ebx, ecx;
+
+ eax = (APM_BIOS << 8) | APM_GETPWSTATUS;
+ ebx = PMDV_ALLDEV;
+ ecx = 0;
+
+ if (apm_int(&eax, &ebx, &ecx))
+ return 1;
+
+ aip->ai_acline = (ebx >> 8) & 0xff;
+ aip->ai_batt_stat = ebx & 0xff;
+ aip->ai_batt_life = ecx & 0xff;
+ aip->ai_major = (u_int)sc->majorversion;
+ aip->ai_minor = (u_int)sc->minorversion;
+ aip->ai_status = (u_int)sc->active;
+
+ return 0;
+}
+
+
+/* inform APM BIOS that CPU is idle */
+void
+apm_cpu_idle(void)
+{
+ struct apm_softc *sc = &apm_softc;
+
+ if (sc->active) {
+ u_long eax, ebx, ecx;
+
+ eax = (APM_BIOS <<8) | APM_CPUIDLE;
+ ecx = ebx = 0;
+ apm_int(&eax, &ebx, &ecx);
+ }
+ /*
+ * Some APM implementation halts CPU in BIOS, whenever
+ * "CPU-idle" function are invoked, but swtch() of
+ * FreeBSD halts CPU, therefore, CPU is halted twice
+ * in the sched loop. It makes the interrupt latency
+ * terribly long and be able to cause a serious problem
+ * in interrupt processing. We prevent it by removing
+ * "hlt" operation from swtch() and managed it under
+ * APM driver.
+ */
+ if (!sc->active || sc->always_halt_cpu) {
+ __asm("hlt"); /* wait for interrupt */
+ }
+}
+
+/* inform APM BIOS that CPU is busy */
+void
+apm_cpu_busy(void)
+{
+ struct apm_softc *sc = &apm_softc;
+
+ /*
+ * The APM specification says this is only necessary if your BIOS
+ * slows down the processor in the idle task, otherwise it's not
+ * necessary.
+ */
+ if (sc->slow_idle_cpu && sc->active) {
+ u_long eax, ebx, ecx;
+
+ eax = (APM_BIOS <<8) | APM_CPUBUSY;
+ ecx = ebx = 0;
+ apm_int(&eax, &ebx, &ecx);
+ }
+}
+
+
+/*
+ * APM timeout routine:
+ *
+ * This routine is automatically called by timer once per second.
+ */
+
+static void
+apm_timeout(void *arg)
+{
+ struct apm_softc *sc = arg;
+
+ apm_processevent(sc);
+ if (sc->active == 1) {
+ timeout(apm_timeout, (void *)sc, hz - 1 ); /* More than 1 Hz */
+ }
+}
+
+/* enable APM BIOS */
+static void
+apm_event_enable(struct apm_softc *sc)
+{
+#ifdef APM_DEBUG
+ printf("called apm_event_enable()\n");
+#endif
+ if (sc->initialized) {
+ sc->active = 1;
+ apm_timeout(sc);
+ }
+}
+
+/* disable APM BIOS */
+static void
+apm_event_disable(struct apm_softc *sc)
+{
+#ifdef APM_DEBUG
+ printf("called apm_event_disable()\n");
+#endif
+ if (sc->initialized) {
+ untimeout(apm_timeout, NULL);
+ sc->active = 0;
+ }
+}
+
+/* halt CPU in scheduling loop */
+static void
+apm_halt_cpu(struct apm_softc *sc)
+{
+ if (sc->initialized) {
+ sc->always_halt_cpu = 1;
+ }
+}
+
+/* don't halt CPU in scheduling loop */
+static void
+apm_not_halt_cpu(struct apm_softc *sc)
+{
+ if (sc->initialized) {
+ sc->always_halt_cpu = 0;
+ }
+}
+
+/* device driver definitions */
+#ifdef PC98
+static int apmprobe (struct pc98_device *);
+static int apmattach(struct pc98_device *);
+struct pc98_driver apmdriver = {
+#else
+static int apmprobe (struct isa_device *);
+static int apmattach(struct isa_device *);
+struct isa_driver apmdriver = {
+#endif
+ apmprobe, apmattach, "apm" };
+
+/*
+ * probe APM (dummy):
+ *
+ * APM probing routine is placed on locore.s and apm_init.S because
+ * this process forces the CPU to turn to real mode or V86 mode.
+ * Current version uses real mode, but on future version, we want
+ * to use V86 mode in APM initialization.
+ */
+
+static int
+#ifdef PC98
+apmprobe(struct pc98_device *dvp)
+#else
+apmprobe(struct isa_device *dvp)
+#endif
+{
+ if ( dvp->id_unit > 0 ) {
+ printf("apm: Only one APM driver supported.\n");
+ return 0;
+ }
+ apm_registerdev(dvp);
+ switch (apm_version) {
+ case APMINI_CANTFIND:
+ /* silent */
+ return 0;
+ case APMINI_NOT32BIT:
+ printf("apm: 32bit connection is not supported.\n");
+ return 0;
+ case APMINI_CONNECTERR:
+ printf("apm: 32-bit connection error.\n");
+ return 0;
+ }
+#ifdef APM_BROKEN_STATCLOCK
+ statclock_disable = 1;
+#endif
+
+ return -1;
+}
+
+
+/* Process APM event */
+static void
+apm_processevent(struct apm_softc *sc)
+{
+ int apm_event;
+
+#ifdef APM_DEBUG
+# define OPMEV_DEBUGMESSAGE(symbol) case symbol: \
+ printf("Received APM Event: " #symbol "\n");
+#else
+# define OPMEV_DEBUGMESSAGE(symbol) case symbol:
+#endif
+ do {
+ apm_event = apm_getevent(sc);
+ switch (apm_event) {
+ OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ);
+ apm_suspend();
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ);
+ apm_suspend();
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_USERSUSPENDREQ);
+ apm_suspend();
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_CRITSUSPEND);
+ apm_suspend();
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_NORMRESUME);
+ apm_resume();
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_CRITRESUME);
+ apm_resume();
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME);
+ apm_resume();
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW);
+ apm_battery_low(sc);
+ apm_suspend();
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE);
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_UPDATETIME);
+ inittodr(0); /* adjust time to RTC */
+ break;
+ OPMEV_DEBUGMESSAGE(PMEV_NOEVENT);
+ break;
+ default:
+ printf("Unknown Original APM Event 0x%x\n", apm_event);
+ break;
+ }
+ } while (apm_event != PMEV_NOEVENT);
+}
+
+/*
+ * Attach APM:
+ *
+ * Initialize APM driver (APM BIOS itself has been initialized in locore.s)
+ */
+
+static int
+#ifdef PC98
+apmattach(struct pc98_device *dvp)
+#else
+apmattach(struct isa_device *dvp)
+#endif
+{
+#define APM_KERNBASE KERNBASE
+ struct apm_softc *sc = &apm_softc;
+#ifdef APM_DSVALUE_BUG
+ caddr_t apm_bios_work;
+
+ apm_bios_work = (caddr_t)malloc(apm_ds_limit, M_DEVBUF, M_NOWAIT);
+ bcopy((caddr_t)((apm_ds_base << 4) + APM_KERNBASE), apm_bios_work,
+ apm_ds_limit);
+#endif /* APM_DSVALUE_BUG */
+
+ sc->initialized = 0;
+
+ /* Must be externally enabled */
+ sc->active = 0;
+
+ /* setup APM parameters */
+ sc->cs16_base = (apm_cs32_base << 4) + APM_KERNBASE;
+ sc->cs32_base = (apm_cs16_base << 4) + APM_KERNBASE;
+ sc->ds_base = (apm_ds_base << 4) + APM_KERNBASE;
+ sc->cs_limit = apm_cs_limit;
+ sc->ds_limit = apm_ds_limit;
+ sc->cs_entry = apm_cs_entry;
+
+#ifdef APM_DSVALUE_BUG
+ sc->ds_base = (u_int)apm_bios_work;
+#endif /* APM_DSVALUE_BUG */
+
+ /* Always call HLT in idle loop */
+ sc->always_halt_cpu = 1;
+
+ sc->slow_idle_cpu = ((apm_flags & APM_CPUIDLE_SLOW) != 0);
+ sc->disabled = ((apm_flags & APM_DISABLED) != 0);
+ sc->disengaged = ((apm_flags & APM_DISENGAGED) != 0);
+
+ /* print bootstrap messages */
+#ifdef APM_DEBUG
+ printf("apm: APM BIOS version %04x\n", apm_version);
+ printf("apm: Code32 0x%08x, Code16 0x%08x, Data 0x%08x\n",
+ sc->cs32_base, sc->cs16_base, sc->ds_base);
+ printf("apm: Code entry 0x%08x, Idling CPU %s, Management %s\n",
+ sc->cs_entry, is_enabled(sc->slow_idle_cpu),
+ is_enabled(!sc->disabled));
+ printf("apm: CS_limit=%x, DS_limit=%x\n", sc->cs_limit, sc->ds_limit);
+#endif /* APM_DEBUG */
+
+#ifdef APM_DEBUG
+ /* Workaround for some buggy APM BIOS implementations */
+ sc->cs_limit = 0xffff;
+ sc->ds_limit = 0xffff;
+#endif
+
+ /* setup GDT */
+ setup_apm_gdt(sc->cs32_base, sc->cs16_base, sc->ds_base,
+ sc->cs_limit, sc->ds_limit);
+
+ /* setup entry point 48bit pointer */
+ apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL);
+ apm_addr.offset = sc->cs_entry;
+
+#ifdef FORCE_APM10
+ apm_version = 0x100;
+ sc->majorversion = 1;
+ sc->minorversion = 0;
+ sc->intversion = INTVERSION(sc->majorversion, sc->minorversion);
+ printf("apm: running in APM 1.0 compatible mode\n");
+ kcd_apm.kdc_description =
+ "Advanced Power Management BIOS (1.0 compatability mode)",
+#else
+ /* Try to kick bios into 1.1 or greater mode */
+ apm_driver_version();
+ sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 +
+ ((apm_version & 0x000f) >> 0);
+ sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 +
+ ((apm_version & 0x0f00) >> 8);
+
+ sc->intversion = INTVERSION(sc->majorversion, sc->minorversion);
+
+ if (sc->intversion >= INTVERSION(1, 1)) {
+#ifdef APM_DEBUG
+ printf("apm: Engaged control %s\n", is_enabled(!sc->disengaged));
+#endif
+ }
+
+ printf("apm: found APM BIOS version %d.%d\n",
+ sc->majorversion, sc->minorversion);
+#endif /* FORCE_APM10 */
+
+#ifdef APM_DEBUG
+ printf("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu));
+#endif
+
+ /* enable power management */
+ if (sc->disabled) {
+ if (apm_enable_disable_pm(sc, 1)) {
+#ifdef APM_DEBUG
+ printf("apm: *Warning* enable function failed! [%x]\n",
+ apm_errno);
+#endif
+ }
+ }
+
+ /* engage power managment (APM 1.1 or later) */
+ if (sc->intversion >= INTVERSION(1, 1) && sc->disengaged) {
+ if (apm_engage_disengage_pm(sc, 1)) {
+#ifdef APM_DEBUG
+ printf("apm: *Warning* engage function failed err=[%x]",
+ apm_errno);
+ printf(" (Docked or using external power?).\n");
+#endif
+ }
+ }
+
+ /* default suspend hook */
+ sc->sc_suspend.ah_fun = apm_default_suspend;
+ sc->sc_suspend.ah_arg = sc;
+ sc->sc_suspend.ah_name = "default suspend";
+ sc->sc_suspend.ah_order = APM_MAX_ORDER;
+
+ /* default resume hook */
+ sc->sc_resume.ah_fun = apm_default_resume;
+ sc->sc_resume.ah_arg = sc;
+ sc->sc_resume.ah_name = "default resume";
+ sc->sc_resume.ah_order = APM_MIN_ORDER;
+
+ apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend);
+ apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume);
+
+ apm_event_enable(sc);
+ kdc_apm.kdc_state = DC_IDLE;
+
+ sc->initialized = 1;
+
+#ifdef DEVFS
+ sc->sc_devfs_token =
+ devfs_add_devswf(&apm_cdevsw, 0, DV_CHR, 0, 0, 0600, "apm");
+#endif
+ return 0;
+}
+
+static int
+apmopen(dev_t dev, int flag, int fmt, struct proc *p)
+{
+ struct apm_softc *sc = &apm_softc;
+
+ if (minor(dev) != 0 || !sc->initialized)
+ return (ENXIO);
+
+ return 0;
+}
+
+static int
+apmclose(dev_t dev, int flag, int fmt, struct proc *p)
+{
+ return 0;
+}
+
+static int
+apmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p)
+{
+ struct apm_softc *sc = &apm_softc;
+ int error = 0;
+
+ if (minor(dev) != 0 || !sc->initialized)
+ return (ENXIO);
+#ifdef APM_DEBUG
+ printf("APM ioctl: cmd = 0x%x\n", cmd);
+#endif
+ switch (cmd) {
+ case APMIO_SUSPEND:
+ if ( sc->active) {
+ apm_suspend();
+ } else {
+ error = EINVAL;
+ }
+ break;
+ case APMIO_GETINFO:
+ if (apm_get_info(sc, (apm_info_t)addr)) {
+ error = ENXIO;
+ }
+ break;
+ case APMIO_ENABLE:
+ kdc_apm.kdc_state = DC_BUSY;
+ apm_event_enable(sc);
+ break;
+ case APMIO_DISABLE:
+ kdc_apm.kdc_state = DC_IDLE;
+ apm_event_disable(sc);
+ break;
+ case APMIO_HALTCPU:
+ apm_halt_cpu(sc);
+ break;
+ case APMIO_NOTHALTCPU:
+ apm_not_halt_cpu(sc);
+ break;
+ case APMIO_DISPLAYOFF:
+ if (apm_display_off()) {
+ error = ENXIO;
+ }
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+
+static apm_devsw_installed = 0;
+
+static void
+apm_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! apm_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR,0);
+ cdevsw_add(&dev,&apm_cdevsw,NULL);
+ apm_devsw_installed = 1;
+ }
+}
+
+SYSINIT(apmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,apm_drvinit,NULL)
diff --git a/sys/pc98/boot/Makefile b/sys/pc98/boot/Makefile
new file mode 100644
index 0000000..c1899cc
--- /dev/null
+++ b/sys/pc98/boot/Makefile
@@ -0,0 +1,5 @@
+# $Id: Makefile,v 1.25 1995/04/15 08:24:33 phk Exp $
+
+SUBDIR= biosboot netboot
+
+.include <bsd.subdir.mk>
diff --git a/sys/pc98/boot/biosboot/Makefile b/sys/pc98/boot/biosboot/Makefile
new file mode 100644
index 0000000..fb432ad
--- /dev/null
+++ b/sys/pc98/boot/biosboot/Makefile
@@ -0,0 +1,92 @@
+# $Id: Makefile,v 1.40 1996/05/11 04:27:23 bde Exp $
+#
+
+PROG= boot
+# Order is very important on the SRCS line for this prog
+SRCS= start.S table.c boot2.S boot.c asm.S bios.S serial.S
+SRCS+= probe_keyboard.c io.c disk.c sys.c
+
+BINDIR= /usr/mdec
+BINMODE= 444
+CFLAGS= -O2 \
+ -DPC98 -DBOOTWAIT=${BOOTWAIT} -DTIMEOUT=${TIMEOUT}
+CFLAGS+= -DBOOTSEG=${BOOTSEG} -DBOOTSTACK=${BOOTSTACK}
+CFLAGS+= -DCOMCONSOLE=0x30 -DCOMCONSOLE_CLK=16 -DCOMCONSOLE_MODE=0x0c
+
+# Probe the keyboard and use the serial console if the keyboard isn't found.
+#CFLAGS+= -DPROBE_KEYBOARD
+
+# Force use of the serial console (after probing the keyboard if
+# PROBE_KEYBOARD is defined).
+#CFLAGS+= -DFORCE_COMCONSOLE
+
+# Bias the conversion from the BIOS drive number to the FreeBSD unit number
+# for hard disks. This may be useful for people booting in a mixed IDE/SCSI
+# environment (set BOOT_HD_BIAS to the number of IDE drives).
+#CFLAGS+= -DBOOT_HD_BIAS=1
+#
+# Details: this only applies if BOOT_HD_BIAS > 0. If the BIOS drive number
+# for the boot drive is >= BOOT_HD_BIAS, then the boot drive is assumed to
+# be SCSI and have unit number (BIOS_drive_number - BOOT_HD_BIAS). E.g.,
+# BOOT_HD_BIAS=1 makes BIOS drive 1 correspond to 1:sd(0,a) instead of
+# 1:wd(1,a). If `sd' is given explicitly, then the drive is assumed to be
+# SCSI and have BIOS drive number (sd_unit_number + BOOT_HD_BIAS). E.g.,
+# BOOT_HD_BIAS=1 makes sd(0,a) correspond to 1:sd(0,a) instead of 0:sd(0,a).
+
+CLEANFILES+= boot.nohdr boot.strip boot1 boot2 sizetest
+DPADD= ${LIBC}
+LDFLAGS+= -N -T 0 -nostdlib
+LDADD= -lc
+#LINKS= ${BINDIR}/sdboot ${BINDIR}/wdboot\
+# ${BINDIR}/sdboot ${BINDIR}/fdboot\
+# ${BINDIR}/bootsd ${BINDIR}/bootwd\
+# ${BINDIR}/bootsd ${BINDIR}/bootfd
+NOSHARED= YES
+NOMAN=
+STRIP=
+
+# tunable timeout parameter, waiting for keypress, calibrated in ms
+BOOTWAIT?= 5000
+# tunable timeout during string input, calibrated in ms
+#TIMEOUT?= 30000
+
+# Location that boot2 is loaded at
+BOOTSEG= 0x9000
+
+# Offset in BOOTSEG for the top of the stack, keep this 16 byte aligned
+BOOTSTACK= 0xFFF0
+
+boot.strip: boot
+ cp -p boot boot.strip
+ strip boot.strip
+ size boot.strip
+
+boot.nohdr: boot.strip
+ dd if=boot.strip of=boot.nohdr ibs=32 skip=1 obs=1024b
+ ls -l boot.nohdr
+
+boot1: boot.nohdr
+ dd if=boot.nohdr of=boot1 bs=512 count=1
+
+boot2: boot.nohdr
+ dd if=boot.nohdr of=boot2 bs=512 skip=1
+ @dd if=boot2 skip=14 of=sizetest 2> /dev/null
+ @if [ -s sizetest ] ; then \
+ echo "*** Boot2 is too BIG ***" ; exit 2 ; \
+ fi
+
+all: boot1 boot2
+
+install:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE}\
+ boot1 ${DESTDIR}${BINDIR}/boot1
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE}\
+ boot2 ${DESTDIR}${BINDIR}/boot2
+ for i in sd fd wd od vn ; do \
+ ( cd ${DESTDIR}${BINDIR} ; \
+ rm -f boot$${i} $${i}boot ; \
+ ln -s boot1 $${i}boot ; \
+ ln -s boot2 boot$${i} ; ) \
+ done
+
+.include <bsd.prog.mk>
diff --git a/sys/pc98/boot/biosboot/README.386BSD b/sys/pc98/boot/biosboot/README.386BSD
new file mode 100644
index 0000000..0dc3cbc
--- /dev/null
+++ b/sys/pc98/boot/biosboot/README.386BSD
@@ -0,0 +1,151 @@
+This Boot code is different from the original boot code that came with
+386BSD in that it uses the BIOS to load the kernel and to provide all i/o
+services. The advantage ofthis is that the same boot code exactly, can run
+on any device that is supported by the BIOS. (That's most of them)
+This is important for the 'generic scsi' project because it means we can
+write drivers for new scsi adapters without having to develop an new
+set of boot blocks for each.
+
+At this point you should read the first part of README.MACH... come back here
+when you have done that:
+
+In normal operation, when co-existing with other operating systems, the
+following operations occur:
+
+1/ the BIOS loads the first block of the disk (called the Master Boot Record
+or MBR) and if it has the correct magic numbers, jumps into it:
+
+2/ The MBR code, looks at the Partition table that is embedded within it,
+to determine which is the partition to boot from. If you install the
+boot manager when FreeBSD is first installed, it will also give you a nice
+menu for switching between operating systems.
+
+3/ The MBR will load the first record of the selected partition and
+if it has (the same) magic numbers, jumps into it. In 386bsd this is the
+first stage boot, (or boot1) it is represented in /usr/mdec by
+wdboot, asboot and sdboot. If the disk has been set up without DOS partitioning
+then this block will be at block zero, and will have been loaded directly by
+the BIOS.
+
+4/ Boot1 will look at block0 (which might be itself if there are no DOS
+partitions) and will find the 386bsd partition, and using the information
+regarding the start position of that partition, will load the next 13 sectors
+or so, to around 90000 (640k - 64k). and will jump into it at the appropriate
+entry point. Since boot1 and boot2 were compiled together as one file
+and then split later, boot1 knows the exact position within boot2 of the
+entry point.
+
+Boot 1 also contains a compiled in DOS partition table
+(in case it is at block 0), which contains a 386bsd partition starting
+at 0. This ensures that the same code can work whether or not
+boot1 is at block 0.
+
+5/ Boot2 asks the user for a boot device, partition and filename, and then
+loads the MBR of the selected device. This may or may not be the device
+which was originally used to boot the first MBR. The partition table
+of the new MBR is searched for a 386bsd partition, and if one is found,
+that is then in turn searched for the disklabel. This could all be on the
+second disk at this point, if the user selected it.
+
+6/On finding the disklabel, boot2 can find the correct unix partition
+within the 386bsd partition, and using cutdown filesystem code,
+look for the file to boot (e.g. 386bsd).
+
+7/ Boot2 loads this file starting at the location specified by the a.out header,
+(see later) and leaps into it at the location specified in he header.
+
+if the file does not exist or cannot be loaded, boot2 goes back to step 5.
+
+386bsd is now running and will hopefully start vm etc. and get to multi-user
+mode.
+
+##########################################################################
+During all these steps, all i/o is performed using the BIOS. This has a number
+of side effects:
+
+1/ Since BIOS disk calls are specified in terms of cylinder,head and sector,
+and the BIOS read the disk information from either the CMOS or from some other
+location which is un-available to us, we must use the cyl,head,sec information
+that is given in the MBR, rather than the start address in the MBR, because
+we cannot guarentee that we can corectly calculate C,H,S from the start address.
+
+Therefore, the C,H,S information in the MBR must be as correct for this boot
+to work as it would be for DOS to boot. For example, adaptec BIOS routines
+assume a layout of 64 heads and 32 sectors giving 1MB per ficticious cylinder.
+You must use these figures to calculate the correct values. Luckily, the DOS
+fdisk program will do all this for you if you tell it to give you a DOS
+partition, and you can change it to a 386BSD partition later. If you use
+no DOS partitioning, then the compiled in table in Boot1 will do just fine.
+
+If you want to do it by hand remember that BIOS counts sectors starting at 1.
+(cylinders and heads start at 0 (??))
+
+2/ you cannot overwrite the bottom 4k of ram until you have finished ALL
+bios calls, as BIOS uses this area as scratch memory.
+
+3/ Since BIOS runs in REAL mode, and Boot2 runs in protected mode,
+Boot 2 switches back to real mode just before each BIOS call and then
+back to protected mode on each return. Touch this at your peril.!
+
+#########################################################################
+In answering the prompt from Boot2:
+you can,
+1/ leave it alone.. it will boot the indicated file from the first
+partition of the first drive seen by the BIOS (C:)
+
+2/ enter only "-s" to boot the default to single user mode
+
+3/ enter only a filename (optionally with -s) to boot that kernel,
+
+4/ enter a whole line of the form shown in the prompt. This allows you to
+boot some other partition, possibly on the second drive, as root.
+
+
+##########################################################################
+In the case you have several drives the same type (all scsi or all IDE/ESDI),
+ wd(0,a)xxx
+will boot xxx from drive 0, a partition.
+ wd(1,a)xxx
+will boot xxx from drive 1, a partition.
+
+similarly for sd and for higher drive numbers (if the BIOS supports them).
+
+if you have one or more wd drives and one or more scsi drives, then you
+MUST specify the BIOS drive number for booting the scsi drives:
+ 2:sd(0,a)xxx
+will boot xxx from scsi drive 0, a partition, provided `2' is the correct
+BIOS drive number for sd0.
+
+otherwise the following will happen:
+
+with wd0 and sd0, you specify sd1 or wd1 to indicate the 2nd drive.
+it boots the kernel correctly, then tells the kernel to use sd1 as root.
+you however may not have an sd1, and problems arise.
+
+Whether sd or wd is specified to the kernel is read from the disklabel,
+so ensure that all SCSI disks have type SCSI in their disklabel or the
+boot code will assume they are ESDI or IDE. (Remember, because it is
+working through the BIOS it has ho idea what kind of disk it is.
+
+##########################################################################
+Installing:
+The makefile supplied has a target install which will create the
+files wdboot,bootwd ,sdboot and bootsd in /usr/mdec.
+BEWARE these will overwrite the existing wdboot and bootwd. (so back
+them up)
+
+there are also targets wd and sd which wil only do one of them
+
+The commented out targets wd0 and sd0 are examples of how to
+load the new bootblocks, however,make sure you change the
+device type and label to suit your drive if you uncomment them.
+(see 'man disklabel')
+
+If you already have made partitions using the old bootblocks
+these should install painlessly.
+
+Before you do this ensure you have a booting floppy with correct
+disktab and bootblock files on it so that if it doesn't work, you can
+re-disklabel from the floppy.
+
+$Id: README.386BSD,v 1.4 1996/04/07 14:27:57 bde Exp $
diff --git a/sys/pc98/boot/biosboot/README.MACH b/sys/pc98/boot/biosboot/README.MACH
new file mode 100644
index 0000000..9de1081
--- /dev/null
+++ b/sys/pc98/boot/biosboot/README.MACH
@@ -0,0 +1,210 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:33:55 rpd
+ * $Id: README.MACH,v 1.3 1993/10/16 19:11:26 rgrimes Exp $
+ */
+
+********NOTE: This is not all relevant to the 386BSD version**********
+
+ AT386 Protected Mode Bootstrap Loader
+ =====================================
+
+1. Overview of Startup
+ -------------------
+
+ After the system is rebooted, the BIOS bootstrap routine reads Sector
+ 1, Track 0 into memory at location 0000:7C00H. If location 0000:7DFEH
+ (last two bytes of that sector) contains the value AA55H, the BIOS
+ bootstrap routine will transfer control to 0000:7C00H. Otherwise, the
+ boot code in that sector is bad and the boot routine stops.
+
+ For DOS compatibility reason, one extra stage of boot is required if
+ the boot device is a hard disk. The first sector of the hard disk will
+ contain the MOS-DOS boot code and a boot record partition table.
+ When this sector is loaded into 0000:7C00H, it will relocate itself
+ to somewhere else and then load the first sector of the active
+ partition into 0000:7C00H. Both UNIX and DOS use the command "fdisk"
+[ 386bsd does not have an 'fdisk' (yet) ]
+ to install this first sector into the hard disk and to manipulate
+ the hard disk partitions.
+
+
+
+2. The First Stage Bootstrap Loader
+ --------------------------------
+
+ After startup, the first stage boot is loaded at 0000:7C00H. This
+ first stage boot will load itself and the second stage boot into
+ memory at location 0000:1000H. For floppy disks, the first cylinder
+ is reserved as the boot cylinder, and the boot code (first and second)
+ will be loaded from there. Currently, only double sided, high density
+ (15 sectors per track) floppies are supported. For hard disks, the
+ first 29 sectors of the active partition is reserved for boot code
+ which will be loaded by the first stage boot. All the disk types
+ recognized by BIOS are supported by this bootstrap loader.
+[for 386bsd we load the second stage booter to 9000:0]
+
+
+
+3. The Second Stage Bootstrap Loader
+ --------------------------------
+
+ After the boot code is loaded, the control is passed to the second
+ stage bootstrap loader "boot2()". In order to be able to load the
+ big kernel image (bigger than 512K or 640K, depends on the memory
+ configuration), the second stage boot loader will run on the protected
+ mode. This bootstarp loader does not have any stand alone device
+ drivers, all the I/O's are through the BIOS calls. Since the first
+ stage boot code will no longer be used at this moment, the memory
+ location of the first stage boot code (0000:1000H to 0000:1200H) will
+ be used as an internal buffer for BIOS calls. Immediately after this
+ internal buffer is the GDT table for the second stage boot loader.
+ Since this boot loader needs to switch back and forth between protected
+ and real mode in order to use BIOS calls, the limit of the boot code
+ and boot data segments must not be greater than 64K.
+
+ The boot loader loads the kernel image at memory location above 1 MB
+ to skip the memory hole between 521K/640K and 1MB. After the kernel
+ is loaded, the boot loader stores the information in the stack and
+ then passes control to kernel. Currently, the three information passed
+ fromm the boot loader to the kernel are type of the boot device, size
+ of the base memory and size of the extended memory.
+
+[ 386bsd receives: howto, bootdev]
+
+[ 386bsd is loaded where-ever the "MByte" bits of the load address specify,
+so if you link it for FE100000 it will load to 1MB, but if you link
+it for FE000000 it will load ad 0MB]
+
+[for machines with only 512KB normal ram the kernel will need to be linked
+for 1MB and the bootblocks modified to run below 512KB. (8000:0)]
+
+
+4. The UNIX Startup
+ ----------------
+
+ Since the boot loader loads the kernel image at memory location above
+ 1MB, the kernel has to start as protected mode. In addition, the
+ link editor description file (vuifile) has to indicate that
+ the text and data segments start above 1MB. Also, the boot loader
+ passes the infomation to the kernel through the stack.
+
+[MOST of what is mentionned below is NOT relevant to 386bsd]
+
+5. Disk Layout and Bad Block Handling
+ ---------------------------------
+
+ The System V/386 Release 3.2 (AT) disk layout will be used as the disk
+ layout for the MACH System on the AT platform.
+
+ This disk layout is as follows:
+
+ * Reserve the first sector of cylinder 0 for the DOS boot record which
+ contains the master boot code (446 bytes) and the partition table.
+ (Refer to DOS Technical Reference Manual page 9-6 to 9-10).
+
+ * Reserve the first 29 sectors of the UNIX partition for the first
+ and the second stage bootstrap.
+
+ * Reserve the 30th sector of the UNIX partition for the pdinfo and
+ the vtoc tables.
+
+ * Reserve the 31st to the 34th sectors of the UNIX partition for the
+ bad track and the bad block mapping tables.
+
+ * Reserve up to 253 consecutive tracks when required, beginning with
+ the 35th sector of the UNIX partition, for alternate tracks.
+
+ * Reserve up to 253 consecutive blocks, beginning with the first
+ sector after the alternate tracks area, for alternate blocks.
+
+ SEC
+ 1
+ ----------------------------------------------------
+ | X | | CYL 0, TRK 0
+ ---------------- .......... --------------------
+ | .......... |
+ ---------------- .......... --------------------
+ | .......... |
+ ===============================================================
+ ^ | BOOTSTRAP | CYL N, TRK M
+ | ----------------------------------------------------
+ | | |30 |31 |32 |33 |34 |
+ ---------------------------------------------------- ---
+ U | .......... | ^
+ N ---------------- .......... --------------------- |
+ I | .......... | Alternate Tracks
+ X ---------------- .......... --------------------- |
+ | .......... | V
+ P ---------------------------------------------------- ---
+ A | .......... | ^
+ R ---------------- .......... --------------------- |
+ T | .......... | Alternate Blocks
+ I ---------------- .......... -------------------- |
+ T | .......... | V
+ I ---------------------------------------------------- ---
+ O | Unix root partition starts from here |
+ N ---------------- -----------------
+ | |
+ ----------------------------------------------------
+ | |
+ ----------------------------------------------------
+ | |
+ | ---------------------------------------------------
+ | | |
+ | ----------------------------------------------------
+ V | |
+ ===============================================================
+ | ........ |
+ --------------- ........ --------------
+ | ........ |
+ ----------------------------------------------------
+
+
+ The bad block handling mechanism is as follows:
+
+ * Use the alternate track in the alternate tracks area if the
+ track containing the target sector is bad.
+
+ * Use the alternate block in the alternate blocks area if the
+ target sector is bad.
+
+
+
+
+6. How to make:
+ -----------
+
+ Since the kernel image is loaded above 1 MB, the kernel must start
+ as protected mode. This means that this bootstrap loader will work
+ only when the corresponding changes on the kernel startup code are done.
+
+ The make command to generate this bootstrap loader is:
+
+ make -f boot.mk fdboot (floppy boot loader)
+ make -f boot.mk hdboot (wini boot loader)
+[to make 386bsd bootblocks "make sd wd" (warning: they will be installed
+in /dev/mdec.. take backups)]
diff --git a/sys/pc98/boot/biosboot/README.serial b/sys/pc98/boot/biosboot/README.serial
new file mode 100644
index 0000000..8ace505
--- /dev/null
+++ b/sys/pc98/boot/biosboot/README.serial
@@ -0,0 +1,164 @@
+
+ SERIAL CONSOLE USAGE NOTES
+ Written by
+ Bill Paul <wpaul@ctr.columbia.edu>
+
+The FreeBSD boot block can now be used to boot FreeBSD on a system with
+only a dumb terminal on a serial port (COM1) as a console. This feature
+is provided for the benefit of people who wish to install FreeBSD on
+dedicated file/compute/terminal server machines that have no keyboard
+(or monitor) attached, just as is possible with Sun workstations and
+servers. People who don't need this extra functionality shouldn't notice
+the changes at all (unless I've screwed something up horribly).
+
+Note that 'options COMCONSOLE' can still be used to force the kernel to
+boot in 'serial console' mode regardless of what boot options you use.
+
+To boot FreeBSD in serial console mode, you must do the following:
+
+- UNPLUG YOUR KEYBOARD. Most PC systems probe for the keyboard during the
+ Power-On Self-Test (POST) and will generate an error if the keyboard
+ isn't detected. Additionally, many machines will pause the boot process
+ and wait for you to reattach the keyboard and press a key before
+ proceeding any further. If your computer complains about the lack of a
+ keyboard but boots anyway, then you don't have to do anything special.
+ (One machine with a PHOENIX BIOS that I have here merely says 'Keyboard
+ failed' then continues to boot normally.) If your machine complains
+ loudly about the lack of a keyboard and won't continue to boot until you
+ plug it back in, you'll have to go into your CMOS configuration menu and
+ change the 'Keyboard' setting to 'Not installed' in order to bypass the
+ keyboard probe.
+
+ NOTE #1:
+ Setting the keyboard to 'Not installed' in the CMOS configuration
+ does *NOT* mean that you won't be able to use your keyboard. All this
+ does is tell the BIOS not to probe for a keyboard at power-on so that
+ it won't bitch and moan if the keyboard isn't plugged in. You can leave the
+ keyboard plugged in even with this flag set to 'Not installed' and the
+ keyboard will still work. I repeat: changing the CMOS 'keyboard' setting
+ to 'Not installed' only disables the BIOS's keyboard probe; it does
+ *NOT* actually disable the keyboard.
+
+ NOTE #2:
+ If your system has a PS/2 mouse, chances are very good that you will
+ need to unplug your mouse as well as your keyboard. This is because
+ PS/2 mice share some hardware with the keyboard, and leaving the mouse
+ plugged in can fool the keyboard probe into thinking the keyboard is
+ still there. I have access to a Gateway 2000 Pentium 90Mhz system with
+ an AMI BIOS that behaves this way. In general this is not a problem
+ since the mouse isn't much good without the keyboard anyway.
+
+- PLUG A DUMB TERMINAL INTO COM1. If you don't have a dumb terminal, you
+ can use an old PC/XT with a modem program, or the serial port on
+ another UNIX box. If you don't have a COM1, get one. At this time,
+ there is no way to select a port other than COM1 without recompiling
+ both the kernel and the boot blocks. If you're already using COM1 for
+ another device, you'll have to temporarily remove that device and
+ install a new boot block and kernel once you get FreeBSD up and running.
+ (It is assumed that COM1 will be available on a file/compute/terminal
+ server anyway; if you really need COM1 for something else (and you can't
+ switch that something else to COM2), then you probably shouldn't even
+ be bothering with all this in the first place.)
+
+ NOTE #1:
+ The serial port settings are hardcoded to 9600 baud, 8 bits, no parity,
+ 1 stop bit.
+
+ NOTE #2:
+ In addition to a serial cable, you will need a null modem adapter
+ in order to connect the terminal to the PC's serial port. If you don't
+ have one, go to Radio Shack and buy one: they're cheap.
+
+ NOTE #3:
+ If you wish to drop into the kernel debugger from the serial console
+ (useful for remote diagnostics, but also dangerous if you generate a
+ spurious BREAK on the serial port!) then you should compile your kernel
+ with the following options:
+
+ options BREAK_TO_DEBUGGER
+ options DDB
+
+- BOOT THE MACHINE. The boot block will probe for a keyboard on your
+ system. If it fails to find one, you'll see a prompt appear on the
+ terminal that looks something like this:
+
+ No keyboard found.
+
+ >> FreeBSD BOOT @ 0x10000: 640/7168 k of memory
+ Usage: [[[0:][fd](0,a)]/kernel][-abcCdhrsv]
+ Use 1:sd(0,a)kernel to boot sd0 if it is BIOS drive 1
+ Use ? for file list or press Enter for defaults
+ Boot:
+
+ This is identical to the prompt that normally appears on the VGA console,
+ except for the 'No keyboard found' message that indicates a keyboard
+ couldn't be detected. (If a keyboard is detected, the boot prompt will
+ appear on the VGA display as usual.)
+
+ From here you can boot the system (or let it autoboot by itself) just
+ like you can from the VGA console and the kernel will automatically
+ use COM1 as the console device. No recompilation or 'options COMCONSOLE'
+ is required. This is done by passing a special flag to the kernel in
+ the 'boothowto' word. (The curious can refer to <sys/reboot.h> and the
+ sio driver sources for details.)
+
+- You will notice that there's a new boot flag: -h. You can use this to
+ force the kernel to switch console devices. For instance, if you boot
+ from the VGA console, you can use -h to force the kernel to use the
+ serial port as its console device. Alternatively, if you boot from
+ the serial port, you can use the -h to force the kernel to use the VGA
+ display as the console instead. (Can you say 'toggle' boys and girls?
+ I knew you could. :)
+
+
+Should you wish to force booting off a serial console no matter if
+there's a keyboard connected or not, you can also uncomment the line
+with the ``FORCE_COMCONSOLE'' definition in the Makefile. Remake and
+reinstall your bootblocks, and finally relabel your disk (disklabel -B)
+to pick up those boot blocks.
+
+
+CAVEATS:
+
+- The idea here is to allow people to set up dedicated servers that require
+ no graphics hardware or attached keyboards. Unfortunately, while (most?)
+ every system will let you boot without a keyboard, there are quite a few
+ that will not let you boot without a graphics adapter. Machines with
+ AMI BIOSes can be configured to boot with no graphics adapter installed
+ simply by changing the 'graphics adapter' setting in the CMOS configuration
+ to 'Not installed.' However, many machines do not support this option
+ and will refuse to boot if you have no display hardware in the system. With
+ these machines, you'll have to leave some kind of graphics card plugged in,
+ (even if it's just a junky mono board) although you won't have to attach
+ a monitor into it. You might also try installing an AMI BIOS. :)
+
+- Using a port other than COM1 as the console requires some recompiling.
+ Again, it's usually assumed that COM1 will be available for use as a
+ console device on a dedicated file/compute/terminal server, so hopefully
+ you'll never need to do this. But if you feel you must change the console
+ to a different port, here's how:
+
+ o Get the kerndist kernel source package.
+ o Edit /sys/i386/boot/biosboot/Makefile and set COMCONSOLE to the
+ address of the port you want to use (0x3F8, 0x2F8, 0x3E8 or
+ 0x2E8). Only COM1 through COM4 can be used; multiport serial
+ cards will not work. No interrupt setting is needed.
+ o Create a custom kernel configuration file and add the following
+ lines:
+
+ options "CONADDR=0x3F8"
+ options "CONUNIT=0"
+
+ Set CONADDR to the same address that you selected for COMCONSOLE
+ in the bootbios Makefile. Set CONUNIT to the unit number of the
+ serial port that this address corresponds to (0 = sio0, 1 = sio1,
+ etc). This implies that the serial port you want to use must be
+ configured into the kernel in the normal way first. I'm not
+ going to list all the possible combinations here; just use your
+ head and you should be okay.
+ o Recompile both the boot blocks and the kernel.
+ o Install the boot blocks with the disklabel command and boot
+ from the new kernel.
+
+
+$Id: README.serial,v 1.5 1996/04/13 11:57:18 jkh Exp $
diff --git a/sys/pc98/boot/biosboot/README.serial.98 b/sys/pc98/boot/biosboot/README.serial.98
new file mode 100644
index 0000000..48d3614
--- /dev/null
+++ b/sys/pc98/boot/biosboot/README.serial.98
@@ -0,0 +1,52 @@
+ README.srieal.98
+ %7%j%"%k%3%s%=!<%k$K$D$$$F
+ 2CF#>fE5 (kato@eclogite.eps.nagoya-u.ac.jp)
+ KATO Takenori
+
+FreeBSD(98)$N%3%s%=!<%k$H$7$F!"FbB"RS-232C%]!<%H$K@\B3$5$l$?%@%`C<Kv$r
+;HMQ$9$k$3$H$,$G$-$^$9!#$3$l$K$h$j!"FreeBSD(98)$r%5!<%P$H$7$F;HMQ$9$k
+;~$K!"%-!<%\!<%I$d%b%K%?$r@\B3$7$J$/$F$b$+$^$o$J$/$J$j$^$9!#
+
+%7%j%"%k%3%s%=!<%k$r;HMQ$9$k:]$O!"$3$N%I%-%e%a%s%H$*$h$S!"IBM-PCMQ$N%+!<
+%M%k%=!<%9$K4^$^$l$k!"/usr/src/sys/i386/boot/biosboot/READEME.serial$r
+NI$/FI$s$G2<$5$$!#
+
+FreeBSD(98)$G%7%j%"%k%3%s%=!<%k$r;HMQ$9$k:]$O!"0J2<$N<j=g$r<B9T$7$F2<
+$5$$!#
+
+1: %-!<%\!<%I$r@Z$jN%$9
+ %-!<%\!<%I$r@\B3$;$:$K!"K\BN$r5/F0$5$;$?>l9g!"%7%9%F%`NN0h$N%-!<
+ %\!<%I%?%$%W$,!"5l<0%-!<%\!<%I(CAPS$,5!3#<0$N%-!<%\!<%I)$HF1$8
+ $K$J$j$^$9!#FreeBSD(98)$N%V!<%H%3!<%I$O!"$3$l$r8!=P$7$F<+F0E*
+ $K%7%j%"%k%3%s%=!<%k%b!<%I$K0\$j$^$9!#
+
+2: C<Kv$r@\B3$9$k
+ FbB"RS-232C%3%M%/%?$K%@%`C<Kv$r@\B3$7$F2<$5$$!#%@%`C<Kv$,L5$$
+ >l9g$O!"E,Ev$J%Q%=%3%s$GDL?.%=%U%H%&%'%"$r<B9T$5$;$?$b$N$d!"DL
+ ?.5!G=$D$-$N%o!<%W%m$J$I$r;HMQ$7$F2<$5$$!#
+
+ FreeBSD(98)$N%V!<%H%3!<%I$G$O!"RS-232C%]!<%H$r9600%\!<!"8%S%C
+ %H!"%Q%j%F%#L5$7$K@_Dj$5$l$F$$$^$9!#
+
+3: K\BN$r5/F0$9$k
+ %V!<%H%V%m%C%/$O!"5/F0;~$K%-!<%\!<%I$N@\B3$r8!::$7$^$9!#$b$7!"
+ %-!<%\!<%I$,@\B3$5$l$F$$$J$1$l$P!"C<Kv$K0J2<$N%a%C%;!<%8$,I=<(
+ $5$l$^$9!#
+
+ No keyboard found.
+
+ >> FreeBSD BOOT @0x90000 640/25600 k of memory
+ Use hd(1,a)/kernel to boot sd0 when wd0 is also installed.
+ Usage: [[[fd(0,a)]/kernel][-Dabcdhrsv]]
+ Use ? for file list or press Enter for defaults
+
+ Boot:
+
+ $3$l$O!"IaCJ8+$F$$$k%a%C%;!<%8$H$[$H$s$IJQ$o$j$^$;$s$,!"$O$8$a
+ $K!"`No keyboard found'$HI=<($5$l$F$$$^$9!#$3$3$G!"%@%`C<Kv$+
+ $i!"%3%s%=!<%k>e$G5/F0$7$F$$$k;~$HF1$8$h$&$K%+!<%M%k$rN)$A>e$2
+ $k$3$H$,$G$-$^$9!#5/F08e$N%a%C%;!<%8$O%@%`C<Kv$KI=<($5$l$^$9!#
+
+ $b$7!"%7%j%"%k%3%s%=!<%k$+$iDL>o$N%3%s%=!<%k$K@ZBX$($k$K$O!"
+ `-h'%*%W%7%g%s$r$D$1$F5/F0$7$F2<$5$$!#
+
diff --git a/sys/pc98/boot/biosboot/asm.S b/sys/pc98/boot/biosboot/asm.S
new file mode 100644
index 0000000..e689817
--- /dev/null
+++ b/sys/pc98/boot/biosboot/asm.S
@@ -0,0 +1,278 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:34:13 rpd
+ * $Id: asm.S,v 1.9 1996/03/08 07:27:52 bde Exp $
+ */
+
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+ .file "asm.s"
+
+#include "asm.h"
+
+
+CR0_PE_ON = 0x1
+CR0_PE_OFF = 0xfffffffe
+
+ .text
+
+/*
+ *
+ * real_to_prot()
+ * transfer from real mode to protected mode.
+ */
+
+ENTRY(real_to_prot)
+ /* guarantee that interrupt is disabled when in prot mode */
+ cli
+
+ /* load the gdtr */
+ addr32
+ data32
+ lgdt EXT(Gdtr)
+
+ /* set the PE bit of CR0 */
+ mov %cr0, %eax
+
+ data32
+ or $CR0_PE_ON, %eax
+ mov %eax, %cr0
+
+ /*
+ * make intrasegment jump to flush the processor pipeline and
+ * reload CS register
+ */
+ data32
+ ljmp $0x18, $xprot
+xprot:
+
+ /*
+ * we are in USE32 mode now
+ * set up the protected mode segment registers : DS, SS, ES, FS
+ */
+ movw $0x20, %ax /* data segment */
+ mov %ax, %ds /* gas would waste a prefix byte for movw */
+ mov %ax, %ss
+ mov %ax, %es
+ movw $0x10, %ax /* flat segment */
+ mov %ax, %fs
+
+#ifdef BDE_DEBUGGER
+ /* load idtr so we can debug */
+ lidt EXT(Idtr_prot)
+#endif
+
+ ret
+
+/*
+ *
+ * prot_to_real()
+ * transfer from protected mode to real mode
+ *
+ */
+
+ENTRY(prot_to_real)
+
+ /* Prepare %ax while we're still in a mode that gas understands. */
+ movw $0x30, %ax
+
+ /* Change to use16 mode. */
+ ljmp $0x28, $x16
+x16:
+
+ mov %ax, %ds
+ mov %ax, %ss
+ mov %ax, %es
+ mov %ax, %fs
+
+ /* clear the PE bit of CR0 */
+ mov %cr0, %eax
+ data32
+ and $CR0_PE_OFF, %eax
+ mov %eax, %cr0
+
+ /*
+ * make intersegment jmp to flush the processor pipeline
+ * and reload CS register
+ */
+ data32
+ ljmp $BOOTSEG, $xreal
+xreal:
+
+ /*
+ * we are in real mode now
+ * set up the real mode segment registers : DS, SS, ES, FS
+ */
+ mov %cs, %ax
+ mov %ax, %ds
+ mov %ax, %ss
+ mov %ax, %es
+ mov %ax, %fs
+
+#ifdef BDE_DEBUGGER
+ /* load idtr so we can debug */
+ addr32
+ data32
+ lidt EXT(Idtr_real)
+#endif
+
+ data32
+ ret
+
+/*
+ * startprog(phyaddr)
+ * start the program on protected mode where phyaddr is the entry point
+ *
+ * XXX This whole mess should go away and we should run the boot code in
+ * flat 32 bit mode with it linked -T BOOTSEG. See the netboot code for
+ * how this is done.
+ */
+
+ENTRY(startprog)
+ push %ebp
+ mov %esp, %ebp
+ movl %esp, %eax /* Use eax as the old stack pointer */
+
+ /* convert the current stack to a 32 bit flat model */
+ movw $0x10, %bx
+ mov %bx, %ss
+ addl $(BOOTSEG<<4),%esp
+
+ /* copy the arguments from the old stack to the new stack */
+ pushl 0x14(%eax) /* &bootinfo */
+ pushl $0 /* was &nfsdiskless */
+ pushl $0 /* was esym */
+ pushl $0 /* was cyloffset */
+ pushl 0x10(%eax) /* bootdev */
+ pushl 0x0C(%eax) /* howto */
+ movl $(ourreturn),%ebx
+ addl $(BOOTSEG<<4),%ebx /* Fix it up for flat segments */
+ pushl %ebx /* our return address */
+
+ /* push on our entry address */
+ pushl $0x08 /* segment selector */
+ pushl 0x08(%eax) /* kernel entry address */
+
+ /* convert over the other data segs */
+ movw $0x10, %bx
+ mov %bx, %ds
+ mov %bx, %es
+
+ /* convert the PC (and code seg) */
+ lret
+ourreturn:
+ /* For now there is not much we can do, just lock in a loop */
+ jmp ourreturn
+
+/*
+ *
+ * pbzero( dst, cnt)
+ * where src is a virtual address and dst is a physical address
+ */
+
+ENTRY(pbzero)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %esi
+ push %edi
+ push %ecx
+
+ cld
+
+ /* set %es to point at the flat segment */
+ movw $0x10, %ax
+ mov %ax, %es
+
+ mov 0x8(%ebp), %edi /* destination */
+ mov 0xc(%ebp), %ecx /* count */
+ xorl %eax, %eax /* value 0 */
+
+ rep
+ stosb
+
+ pop %ecx
+ pop %edi
+ pop %esi
+ pop %es
+ pop %ebp
+
+ ret
+/*
+ * pcpy(src, dst, cnt)
+ * where src is a virtual address and dst is a physical address
+ */
+
+ENTRY(pcpy)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %esi
+ push %edi
+ push %ecx
+
+ cld
+
+ /* set %es to point at the flat segment */
+ movw $0x10, %ax
+ mov %ax, %es
+
+ mov 0x8(%ebp), %esi /* source */
+ mov 0xc(%ebp), %edi /* destination */
+ mov 0x10(%ebp), %ecx /* count */
+
+ rep
+ movsb
+
+ pop %ecx
+ pop %edi
+ pop %esi
+ pop %es
+ pop %ebp
+
+ ret
diff --git a/sys/pc98/boot/biosboot/asm.h b/sys/pc98/boot/biosboot/asm.h
new file mode 100644
index 0000000..3044b64
--- /dev/null
+++ b/sys/pc98/boot/biosboot/asm.h
@@ -0,0 +1,144 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.7 92/02/29 15:33:41 rpd
+ * $Id: asm.h,v 1.4 1995/05/30 07:58:25 rgrimes Exp $
+ */
+
+#define S_ARG0 4(%esp)
+#define S_ARG1 8(%esp)
+#define S_ARG2 12(%esp)
+#define S_ARG3 16(%esp)
+
+#define FRAME pushl %ebp; movl %esp, %ebp
+#define EMARF leave
+
+#define B_ARG0 8(%ebp)
+#define B_ARG1 12(%ebp)
+#define B_ARG2 16(%ebp)
+#define B_ARG3 20(%ebp)
+
+#ifdef wheeze
+
+#define ALIGN 4
+#define EXT(x) x
+#define LEXT(x) x:
+#define LCL(x) ./**/x
+
+#define LB(x,n) ./**/x
+#define LBb(x,n) ./**/x
+#define LBf(x,n) ./**/x
+
+#define SVC lcall $7,$0
+
+#define String .string
+#define Value .value
+#define Times(a,b) [a\*b]
+#define Divide(a,b) [a\\b]
+
+#define INB inb (%dx)
+#define OUTB outb (%dx)
+#define INL inl (%dx)
+#define OUTL outl (%dx)
+
+#else wheeze
+#define ALIGN
+#define LCL(x) x
+
+#define LB(x,n) n
+#ifdef __STDC__
+#define EXT(x) _ ## x
+#define LEXT(x) _ ## x ## :
+
+#define LBb(x,n) n ## b
+#define LBf(x,n) n ## f
+#else __STDC__
+#define EXT(x) _/**/x
+#define LEXT(x) _/**/x/**/:
+#define LBb(x,n) n/**/b
+#define LBf(x,n) n/**/f
+#endif __STDC__
+#define SVC .byte 0x9a; .long 0; .word 0x7
+
+#define String .ascii
+#define Value .word
+#define Times(a,b) (a*b)
+#define Divide(a,b) (a/b)
+
+#define INB inb %dx, %al
+#define OUTB outb %al, %dx
+#define INL inl %dx, %eax
+#define OUTL outl %eax, %dx
+
+#endif wheeze
+
+#define addr32 .byte 0x67
+#define data32 .byte 0x66
+
+#ifdef GPROF
+#ifdef __STDC__
+
+#define MCOUNT .data; LB(x, 9); .long 0; .text; lea LBb(x, 9),%edx; call mcount
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x) ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y) ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+#define ASENTRY(x) .globl x; .align ALIGN; x ## : ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+
+#else __STDC__
+
+#define MCOUNT .data; LB(x, 9): .long 0; .text; lea LBb(x, 9),%edx; call mcount
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x) ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y)
+#define ASENTRY(x) .globl x; .align ALIGN; x: ; \
+ pushl %ebp; movl %esp, %ebp; MCOUNT; popl %ebp;
+
+#endif __STDC__
+#else GPROF
+#ifdef __STDC__
+
+#define MCOUNT
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x)
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y)
+#define ASENTRY(x) .globl x; .align ALIGN; x ## :
+
+#else __STDC__
+
+#define MCOUNT
+#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x)
+#define ENTRY2(x,y) .globl EXT(x); .globl EXT(y); \
+ .align ALIGN; LEXT(x) LEXT(y)
+#define ASENTRY(x) .globl x; .align ALIGN; x:
+
+#endif __STDC__
+#endif GPROF
+
+#define Entry(x) .globl EXT(x); .align ALIGN; LEXT(x)
+#define DATA(x) .globl EXT(x); .align ALIGN; LEXT(x)
diff --git a/sys/pc98/boot/biosboot/bios.S b/sys/pc98/boot/biosboot/bios.S
new file mode 100644
index 0000000..996ae12
--- /dev/null
+++ b/sys/pc98/boot/biosboot/bios.S
@@ -0,0 +1,313 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:34:26 rpd
+ * $Id: bios.S,v 1.5 1995/09/03 05:36:13 julian Exp $
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+/*
+ * Ported to PC-9801 by Yoshio Kimura
+ */
+
+ .file "bios.s"
+
+#include "asm.h"
+ .text
+
+/*
+ * biosread(dev, cyl, head, sec, nsec, offset)
+ * Read "nsec" sectors from disk to offset "offset" in boot segment
+ * BIOS call "INT 0x1B Function 0xn6" to read sectors from disk into memory
+ * Call with %ah = 0xd6(for floppy disk) or 0x06(for hard disk)
+ * %al = DA/UA
+ * %bx = data length
+ * %ch = sector size(for floppy) or cylinder(for hard)
+ * %cl = cylinder
+ * %dh = head
+ * %dl = sector
+ * %es:%bp = segment:offset of buffer
+ * Return:
+ * %al = 0x0 on success; err code on failure
+ */
+
+ENTRY(biosread)
+ push %ebp
+ mov %esp, %ebp
+
+ push %ebx
+ push %ecx
+ push %edx
+ push %es
+
+ movb 0x14(%ebp), %dl /* sector */
+ movb 0x10(%ebp), %dh /* head */
+ movw 0x0c(%ebp), %cx /* cylinder */
+ movb 0x08(%ebp), %al /* DA/UA */
+ movb $0x06, %ah
+ andb $0xf0, %al
+ cmpb $0x90, %al
+ jnz 1f
+ incb %dl
+ movb $0x02, %ch
+ movb $0xd6, %ah
+1:
+ movb 0x08(%ebp), %al
+ movl %eax, %ebx
+
+ /* prot_to_real will set %es to BOOTSEG */
+ call EXT(prot_to_real) /* enter real mode */
+ mov %ebx, %eax
+ xor %ebx, %ebx
+ addr32
+ movb 0x18(%ebp), %bl /* number of sectors */
+ data32
+ shl $9, %ebx
+ data32
+ push %ebx
+ addr32
+ data32
+ mov 0x1c(%ebp), %ebx
+ data32
+ mov %ebx, %ebp
+ data32
+ pop %ebx
+
+ int $0x1b
+ jc 1f
+ xor %eax, %eax
+1:
+ /* save return value (actually movw %ax, %bx) */
+ mov %eax, %ebx
+
+ data32
+ call EXT(real_to_prot) /* back to protected mode */
+
+ xor %eax, %eax
+ movb %bh, %al /* return value in %ax */
+
+ pop %es
+ pop %edx
+ pop %ecx
+ pop %ebx
+ pop %ebp
+
+ ret
+
+
+/*
+ * getc()
+ * BIOS call "INT 18H Function 00H" to read character from keyboard
+ * Call with %ah = 0x0
+ * Return: %ah = keyboard scan code
+ * %al = ASCII character
+ */
+
+ENTRY(getc)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx /* save %ebx */
+
+ call EXT(prot_to_real)
+
+ movb $0x0, %ah
+ int $0x18
+
+ movb %al, %bl /* real_to_prot uses %eax */
+
+ data32
+ call EXT(real_to_prot)
+
+ xor %eax, %eax
+ movb %bl, %al
+
+ pop %ebx
+ pop %ebp
+ ret
+/*
+ * ischar()
+ * if there is a character pending, return it; otherwise return 0
+ * BIOS call "INT 18H Function 01H" to check whether a character is pending
+ * Call with %ah = 0x1
+ * Return:
+ * If key waiting to be input:
+ * %ah = keyboard scan code
+ * %al = ASCII character
+ * %bh = 1
+ * else
+ * %bh = 0
+ */
+ENTRY(ischar)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+
+ call EXT(prot_to_real) /* enter real mode */
+
+ xor %ebx, %ebx
+ movb $0x1, %ah
+ int $0x18
+ andb %bh, %bh
+ data32
+ jz nochar
+ movb %al, %bl
+
+nochar:
+ data32
+ call EXT(real_to_prot)
+
+ xor %eax, %eax
+ movb %bl, %al
+
+ pop %ebx
+ pop %ebp
+ ret
+
+/*
+ *
+ * get_diskinfo(): return a word that represents the
+ * max number of sectors and heads and drives for this device
+ *
+ */
+
+ENTRY(get_diskinfo)
+ push %ebp
+ mov %esp, %ebp
+ push %es
+ push %ebx
+ push %ecx
+ push %edx
+
+ movb 0x8(%ebp), %dl /* diskinfo(drive #) */
+ call EXT(prot_to_real) /* enter real mode */
+
+ movb %dl, %al /* ask for disk info */
+ andb $0xf0, %al
+ cmpb $0x90, %al
+ jz fdd
+
+ movb %dl, %al
+ movb $0x84, %ah
+
+ int $0x1b
+
+ jnc ok
+ /*
+ * Urk. Call failed. It is not supported for floppies by old BIOS's.
+ * Guess it's a 15-sector floppy.
+ */
+fdd:
+ subb %ah, %ah /* %ax = 0 */
+ movb %al, %al
+ movb %ah, %bh /* %bh = 0 */
+ movb $2, %bl /* %bl bits 0-3 = drive type,
+ bit 2 = 1.2M */
+ movb $79, %ch /* max track */
+ movb $1, %cl /* # floppy drives installed */
+ movb $2, %dh /* max head */
+ movb $15, %dl /* max sector */
+ /* es:di = parameter table */
+ /* carry = 0 */
+ok:
+
+ data32
+ call EXT(real_to_prot) /* back to protected mode */
+
+ /*
+ * form a longword representing all this gunk:
+ * 16 bit cylinder
+ * 8 bit head
+ * 8 bit sector
+ */
+ mov %ecx, %eax
+ sall $16,%eax /* << 16 */
+ movb %dh, %ah /* max head */
+ movb %dl, %al /* max sector (and # sectors) */
+
+ pop %edx
+ pop %ecx
+ pop %ebx
+ pop %es
+ pop %ebp
+ ret
+
+/*
+ *
+ * memsize(i) : return the memory size in KB. i == 0 for conventional memory,
+ * i == 1 for extended memory
+ * Both have the return value in AX.
+ *
+ */
+
+ENTRY(memsize)
+ push %ebp
+ mov %esp, %ebp
+ push %ebx
+
+ mov 8(%ebp), %ebx
+
+ xor %eax, %eax
+ cmpb $0x01, %bl
+ jnz memcnv
+memext:
+ movb 0x11401, %al
+ shll $7, %eax
+ xorl %ebx, %ebx
+ movw 0x11594, %bx
+ shll $10, %ebx
+ addl %ebx, %eax
+ jmp xdone
+
+memcnv:
+ movb 0x11501, %al
+ andb $0x07, %al
+ incl %eax
+ shll $7, %eax
+
+xdone:
+ pop %ebx
+ pop %ebp
+ ret
diff --git a/sys/pc98/boot/biosboot/boot.c b/sys/pc98/boot/biosboot/boot.c
new file mode 100644
index 0000000..284f036
--- /dev/null
+++ b/sys/pc98/boot/biosboot/boot.c
@@ -0,0 +1,397 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, [92/04/03 16:51:14 rvb]
+ * $Id: boot.c,v 1.50 1996/05/11 04:27:24 bde Exp $
+ */
+
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <sys/param.h>
+#include "boot.h"
+#include <a.out.h>
+#include <sys/reboot.h>
+#include <machine/bootinfo.h>
+
+#define ouraddr (BOOTSEG << 4) /* XXX */
+
+#define NAMEBUF_LEN (8*1024)
+
+char namebuf[NAMEBUF_LEN];
+struct exec head;
+struct bootinfo bootinfo;
+int loadflags;
+
+static void getbootdev(char *ptr, int *howto);
+static void loadprog(void);
+
+/* NORETURN */
+void
+boot(int drive)
+{
+ int ret;
+
+#ifdef PROBE_KEYBOARD
+ if (probe_keyboard()) {
+ init_serial();
+ loadflags |= RB_SERIAL;
+ printf("\nNo keyboard found.");
+ }
+#endif
+
+#ifdef FORCE_COMCONSOLE
+ init_serial();
+ loadflags |= RB_SERIAL;
+ printf("\nSerial console forced.");
+#endif
+
+ /* Pick up the story from the Bios on geometry of disks */
+
+#ifdef PC98
+ for(ret = 0; ret < 2; ret ++) {
+ if (*(unsigned char*)0x1155d & (1 << ret)) {
+ bootinfo.bi_bios_geom[ret] = get_diskinfo(ret + 0x80);
+ }
+#else /* IBM-PC */
+ for(ret = 0; ret < N_BIOS_GEOM; ret ++)
+ bootinfo.bi_bios_geom[ret] = get_diskinfo(ret + 0x80);
+#endif /* PC98 */
+ }
+
+ bootinfo.bi_basemem = memsize(0);
+ bootinfo.bi_extmem = memsize(1);
+ bootinfo.bi_memsizes_valid = 1;
+
+ gateA20();
+
+#ifdef PC98
+ /* set machine type to PC98_SYSTEM_PARAMETER */
+ machine_check();
+#endif /* PC98 */
+
+ /*
+ * The default boot device is the first partition in the
+ * compatibility slice on the boot drive.
+ */
+ dosdev = drive;
+#ifdef PC98
+ maj = (drive&0x70) >> 3; /* a good first bet */
+ unit = drive & 0x0f;
+#else /* IBM-PC */
+ maj = 2;
+ unit = drive & 0x7f;
+#ifdef dontneed
+ slice = 0;
+ part = 0;
+#endif
+ if (drive & 0x80) {
+ /* Hard drive. Adjust. */
+ maj = 0;
+#if BOOT_HD_BIAS > 0
+ if (unit >= BOOT_HD_BIAS) {
+ /*
+ * The drive is probably a SCSI drive with a unit
+ * number BOOT_HD_BIAS less than the BIOS drive
+ * number.
+ */
+ maj = 4;
+ unit -= BOOT_HD_BIAS;
+ }
+#endif
+ }
+#endif /* PC98 */
+
+loadstart:
+ /* print this all each time.. (saves space to do so) */
+ /* If we have looped, use the previous entries as defaults */
+ printf("\n>> FreeBSD BOOT @ 0x%x: %d/%d k of memory\n"
+ "Usage: [[[%d:][%s](%d,a)]%s][-abcCdhrsv]\n"
+ "Use 1:sd(0,a)kernel to boot sd0 if it is BIOS drive 1\n"
+ "Use ? for file list or press Enter for defaults\n\nBoot: ",
+ ouraddr, bootinfo.bi_basemem, bootinfo.bi_extmem,
+#ifdef PC98
+ dosdev & 0x0f, devs[maj], unit, name);
+#else
+ dosdev & 0x7f, devs[maj], unit, name);
+#endif
+
+ name = dflname; /* re-initialize in case of loop */
+ loadflags &= RB_SERIAL; /* clear all, but leave serial console */
+ getbootdev(namebuf, &loadflags);
+ ret = openrd();
+ if (ret != 0) {
+ if (ret > 0)
+ printf("Can't find %s\n", name);
+ goto loadstart;
+ }
+/* if (inode.i_mode&IEXEC)
+ loadflags |= RB_KDB;
+*/
+ loadprog();
+ goto loadstart;
+}
+
+static void
+loadprog(void)
+{
+ long int startaddr;
+ long int addr; /* physical address.. not directly useable */
+ long int bootdev;
+ int i;
+ unsigned pad;
+
+ read((void *)&head, sizeof(head));
+ if ( N_BADMAG(head)) {
+ printf("Invalid format!\n");
+ return;
+ }
+
+ poff = N_TXTOFF(head);
+ /*if(poff==0)
+ poff = 32;*/
+
+ /*
+ * We assume that the entry address is the same as the lowest text
+ * address and that the kernel startup code handles relocation by
+ * this address rounded down to a multiple of 16M.
+ */
+ startaddr = head.a_entry & 0x00FFFFFF;
+ addr = startaddr;
+ printf("Booting %d:%s(%d,%c)%s @ 0x%x\n"
+#ifdef PC98
+ , dosdev & 0x0f
+#else
+ , dosdev & 0x7f
+#endif
+ , devs[maj]
+ , unit
+ , 'a'+part
+ , name
+ , addr);
+ if(addr < 0x00100000)
+ {
+ /*
+ * Bail out, instead of risking to damage the BIOS
+ * variables, the loader, or the adapter memory area.
+ * We don't support loading below 1 MB any more.
+ */
+ printf("Start address too low\n");
+ return;
+ }
+ printf("text=0x%x ", head.a_text);
+ /********************************************************/
+ /* LOAD THE TEXT SEGMENT */
+ /********************************************************/
+ xread((void *)addr, head.a_text);
+ addr += head.a_text;
+
+ /********************************************************/
+ /* Load the Initialised data after the text */
+ /********************************************************/
+ while (addr & PAGE_MASK)
+ *(char *)addr++ = 0;
+
+ printf("data=0x%x ", head.a_data);
+ xread((void *)addr, head.a_data);
+ addr += head.a_data;
+
+ /********************************************************/
+ /* Skip over the uninitialised data */
+ /* (but clear it) */
+ /********************************************************/
+ printf("bss=0x%x ", head.a_bss);
+
+/*
+ * XXX however, we should be checking that we don't load ... into
+ * nonexistent memory. A full symbol table is unlikely to fit on 4MB
+ * machines.
+ */
+ pbzero((void *)addr,head.a_bss);
+ addr += head.a_bss;
+
+ /* Pad to a page boundary. */
+ pad = (unsigned)addr & PAGE_MASK;
+ if (pad != 0) {
+ pad = PAGE_SIZE - pad;
+ addr += pad;
+ }
+ bootinfo.bi_symtab = addr;
+
+ /********************************************************/
+ /* Copy the symbol table size */
+ /********************************************************/
+ pcpy(&head.a_syms, (void *)addr, sizeof(head.a_syms));
+ addr += sizeof(head.a_syms);
+
+ /********************************************************/
+ /* Load the symbol table */
+ /********************************************************/
+ printf("symbols=[+0x%x+0x%x+0x%x", pad, sizeof(head.a_syms),
+ head.a_syms);
+ xread((void *)addr, head.a_syms);
+ addr += head.a_syms;
+
+ /********************************************************/
+ /* Load the string table size */
+ /********************************************************/
+ read((void *)&i, sizeof(int));
+ pcpy(&i, (void *)addr, sizeof(int));
+ i -= sizeof(int);
+ addr += sizeof(int);
+
+ /********************************************************/
+ /* Load the string table */
+ /********************************************************/
+ printf("+0x%x+0x%x]\n", sizeof(int), i);
+ xread((void *)addr, i);
+ addr += i;
+
+ bootinfo.bi_esymtab = addr;
+
+#ifdef notyet
+#ifdef PC98
+ /*
+ * MO boot support by KATO Takenori (Nov 27, 1995)
+ *
+ * Major device number should be cahnged into 20 (od) from
+ * 4 (sd) when you boot from MO.
+ */
+ if (maj == 4) {
+ /* SCSI device*/
+ if (((*(unsigned char*)0x11482) & (1 << unit)) == 0) {
+ /*
+ * XXX
+ * Boot device is not HDD
+ */
+ int scsi_id;
+ unit = 0;
+ /*
+ * XXX
+ * If you want to boot from MO, its ID should be below
+ * than that of other SCSI devices except for HDD becaus
+ * they seem to be a MO in following code.
+ */
+ for (scsi_id = 0; scsi_id < unit; scsi_id++)
+ if ((*(unsigned char*)0x11482) & (1 << scsi_id) == 0)
+ unit++;
+ }
+ maj = 20; /* od */
+ }
+#endif
+#endif
+
+ /*
+ * For backwards compatibility, use the previously-unused adaptor
+ * and controller bitfields to hold the slice number.
+ */
+ bootdev = MAKEBOOTDEV(maj, (slice >> 4), slice & 0xf, unit, part);
+
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_kernelname = name + ouraddr;
+ bootinfo.bi_nfs_diskless = NULL;
+ bootinfo.bi_size = sizeof(bootinfo);
+ printf("total=0x%x entry point=0x%x\n", (int)addr, (int)startaddr);
+ startprog((int)startaddr, loadflags | RB_BOOTINFO, bootdev,
+ (int)&bootinfo + ouraddr);
+}
+
+void
+getbootdev(char *ptr, int *howto)
+{
+ char c;
+
+ /*
+ * Be paranoid and make doubly sure that the input buffer is empty.
+ */
+ if (*howto & RB_SERIAL)
+ init_serial();
+
+ if (!gets(ptr)) {
+ putchar('\n');
+ return;
+ }
+ while ((c = *ptr) != '\0') {
+nextarg:
+ while (c == ' ')
+ c = *++ptr;
+ if (c == '-')
+ while ((c = *++ptr) != '\0') {
+ if (c == ' ')
+ goto nextarg;
+ if (c == 'C')
+ *howto |= RB_CDROM;
+ if (c == 'a')
+ *howto |= RB_ASKNAME;
+ if (c == 'b')
+ *howto |= RB_HALT;
+ if (c == 'c')
+ *howto |= RB_CONFIG;
+ if (c == 'd')
+ *howto |= RB_KDB;
+ if (c == 'h') {
+ *howto ^= RB_SERIAL;
+ if (*howto & RB_SERIAL)
+ init_serial();
+ }
+ if (c == 'r')
+ *howto |= RB_DFLTROOT;
+ if (c == 's')
+ *howto |= RB_SINGLE;
+ if (c == 'v')
+ *howto |= RB_VERBOSE;
+ }
+ if (c == '\0')
+ return;
+ name = ptr;
+ while (*++ptr != '\0') {
+ if (*ptr == ' ') {
+ *ptr++ = '\0';
+ break;
+ }
+ }
+ }
+}
diff --git a/sys/pc98/boot/biosboot/boot.h b/sys/pc98/boot/biosboot/boot.h
new file mode 100644
index 0000000..a9cdb48
--- /dev/null
+++ b/sys/pc98/boot/biosboot/boot.h
@@ -0,0 +1,101 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:03 rpd
+ * $Id: boot.h,v 1.11 1995/06/25 14:02:52 joerg Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/inode.h>
+
+extern char *devs[], *iodest;
+extern char *name, dflname[];
+extern struct fs *fs;
+extern struct inode inode;
+extern int dosdev, unit, slice, part, maj, boff, poff, bnum, cnt;
+extern unsigned long tw_chars;
+extern int loadflags;
+extern struct disklabel disklabel;
+
+/* asm.S */
+#if ASM_ONLY
+void real_to_prot(void);
+void prot_to_real(void);
+#endif
+void startprog(unsigned int physaddr, int howto, int bootdev,
+ /* XXX struct bootinfo * */ unsigned int bootinfo);
+void pbzero(void *dst, size_t count);
+void pcpy(const void *src, void *dst, size_t count);
+
+/* bios.S */
+int biosread(int dev, int cyl, int head, int sec, int nsec, void *offset);
+void putc(int c);
+int getc(void);
+int ischar(void);
+int get_diskinfo(int drive);
+int memsize(int extended);
+
+/* boot.c */
+void boot(int drive);
+
+/* boot2.S */
+void boot2(void);
+
+/* disk.c */
+int devopen(void);
+void devread(void);
+void Bread(int dosdev, int sector);
+int badsect(int dosdev, int sector);
+
+/* io.c */
+void gateA20(void);
+void printf(const char *format, ...);
+void putchar(int c);
+int getchar(int in_buf);
+void delay1ms(void);
+int gets(char *buf);
+int strcmp(const char *s1, const char *s2);
+void bcopy(const char *from, char *to, int len);
+void twiddle(void);
+
+/* probe_keyboard.c */
+int probe_keyboard(void);
+
+/* serial.S */
+void serial_putc(int ch);
+int serial_getc(void);
+int serial_ischar(void);
+void init_serial(void);
+
+/* sys.c */
+void xread(char *addr, int size);
+void read(char *buffer, int count);
+int find(char *path);
+int block_map(int file_block);
+int openrd(void);
diff --git a/sys/pc98/boot/biosboot/boot2.S b/sys/pc98/boot/biosboot/boot2.S
new file mode 100644
index 0000000..d994d41
--- /dev/null
+++ b/sys/pc98/boot/biosboot/boot2.S
@@ -0,0 +1,175 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:26 rpd
+ * boot2.S,v 1.6 1995/01/25 21:37:40 bde Exp
+ */
+/*
+ * Ported to PC-9801 by Yoshio Kimura
+ */
+
+#include "asm.h"
+
+/* Conventional GDT indexes. */
+#define BOOT_CS_INDEX 3
+#define BOOT_CS16_INDEX 5
+#define BOOT_DS_INDEX 4
+
+#ifdef BDE_DEBUGGER
+#define DB_CS_INDEX 14
+#define DB_CS16_INDEX 15
+#define DB_DS_INDEX 16
+#define GDT_INDEX 17
+#endif
+
+/* Vector numbers. */
+#define BREAKPOINT_VECTOR 3
+#define DEBUG_VECTOR 1
+
+/*
+ * boot2() -- second stage boot
+ */
+
+ENTRY(boot2)
+ data32
+ subl %eax, %eax
+ mov %cs, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ data32
+ shll $4, %eax
+
+ /* fix up GDT entries for bootstrap */
+#define FIXUP(gdt_index) \
+ addr32; \
+ movl %eax, EXT(Gdt)+(8*gdt_index)+2; /* actually movw %ax */ \
+ addr32; \
+ movb %bl, EXT(Gdt)+(8*gdt_index)+4
+
+ data32
+ shld $16, %eax, %ebx
+ FIXUP(BOOT_CS_INDEX)
+ FIXUP(BOOT_CS16_INDEX)
+ FIXUP(BOOT_DS_INDEX)
+
+ /* fix up GDT pointer */
+ data32
+ movl %eax, %ecx
+ data32
+ addl $ EXT(Gdt), %eax
+ addr32
+ data32
+ movl %eax, EXT(Gdtr)+2
+
+#ifdef BDE_DEBUGGER
+ /* fix up GDT entry for GDT */
+ data32
+ shld $16, %eax, %ebx
+ FIXUP(GDT_INDEX)
+
+ /* fix up IDT pointer */
+ data32
+ addl $ EXT(Idt), %ecx
+ addr32
+ data32
+ movl %ecx, EXT(Idtr_prot)+2
+
+ /* %es = vector table segment for a while */
+ push %es
+ data32
+ subl %eax, %eax
+ mov %ax, %es
+
+ /* fix up GDT entries for bdb */
+ data32
+ movl $4*DEBUG_VECTOR, %esi
+ addr32
+ movl %es: 2(%esi), %eax /* actually movw to %ax */
+ data32
+ shll $4, %eax
+ data32
+ shld $16, %eax, %ebx
+ FIXUP(DB_CS_INDEX)
+ FIXUP(DB_CS16_INDEX)
+ FIXUP(DB_DS_INDEX)
+
+ /* Fetch entry points of bdb's protected mode trap handlers. These
+ * are stored at 2 before the corresponding entry points for real mode.
+ */
+ data32
+ subl %ebx, %ebx
+ addr32
+ movl %es: (%esi), %ebx /* actually movw to %bx */
+ data32
+ subl %ecx, %ecx
+ addr32
+ movl %es: 4*(BREAKPOINT_VECTOR-DEBUG_VECTOR)(%esi), %ecx
+ /* actually movw to %cx */
+
+ /* %es = bdb segment for a while */
+ data32
+ shrl $4, %eax
+ mov %ax, %es
+
+ /* fix up IDT entries for bdb */
+ data32
+ subl $2, %ebx /* calculate EA to check it */
+ jb 1f /* give up if it would trap */
+ addr32
+ movl %es: (%ebx), %eax /* actually movw to %ax */
+ addr32
+ movl %eax, EXT(Idt)+8*DEBUG_VECTOR /* actually movw %ax */
+1:
+ data32
+ subl $2, %ecx
+ jb 1f
+ addr32
+ movl %es: (%ecx), %eax /* actually movw to %ax */
+ addr32
+ movl %eax, EXT(Idt)+8*BREAKPOINT_VECTOR /* actually movw %ax */
+1:
+
+ /* finished with groping in real mode segments */
+ pop %es
+#endif /* BDE_DEBUGGER */
+
+ /* change to protected mode */
+ data32
+ call EXT(real_to_prot)
+
+ /* clear the bss */
+ movl $ EXT(edata), %edi /* no EXT(_edata) - krufty ld */
+ movl $ EXT(end), %ecx /* or EXT(_end) */
+ subl %edi, %ecx
+ subb %al, %al
+ rep
+ stosb
+
+ movzbl %dl, %edx /* discard head (%dh) and random high bits */
+ pushl %edx
+ call EXT(boot)
+oops:
+ hlt
+ jmp oops
diff --git a/sys/pc98/boot/biosboot/disk.c b/sys/pc98/boot/biosboot/disk.c
new file mode 100644
index 0000000..6c696b3
--- /dev/null
+++ b/sys/pc98/boot/biosboot/disk.c
@@ -0,0 +1,322 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:49 rpd
+ * $Id: disk.c,v 1.16 1995/09/16 13:03:59 bde Exp $
+ */
+
+/*
+ * Ported to PC-9801 by Yoshio Kimura
+ */
+
+/*
+ * 93/10/08 bde
+ * If there is no 386BSD partition, initialize the label sector with
+ * LABELSECTOR instead of with garbage.
+ *
+ * 93/08/22 bde
+ * Fixed reading of bad sector table. It is at the end of the 'c'
+ * partition, which is not always at the end of the disk.
+ */
+
+#include "boot.h"
+#ifdef DO_BAD144
+#include <sys/dkbad.h>
+#endif DO_BAD144
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+
+#define BIOS_DEV_FLOPPY 0x0
+#define BIOS_DEV_WIN 0x80
+
+#define BPS 512
+#define SPT(di) ((di)&0xff)
+#define HEADS(di) (((di)>>8)&0xff)
+
+#ifdef DO_BAD144
+struct dkbad dkb;
+int do_bad144;
+int bsize;
+#endif DO_BAD144
+
+int spt, spc;
+
+char *iodest;
+struct fs *fs;
+struct inode inode;
+int dosdev, unit, slice, part, maj, boff, poff, bnum, cnt;
+
+/*#define EMBEDDED_DISKLABEL 1*/
+
+#define I_ADDR ((void *) 0) /* XXX where all reads go */
+
+/* Read ahead buffer large enough for one track on a 1440K floppy. For
+ * reading from floppies, the bootstrap has to be loaded on a 64K boundary
+ * to ensure that this buffer doesn't cross a 64K DMA boundary.
+ */
+#define RA_SECTORS 18
+static char ra_buf[RA_SECTORS * BPS];
+static int ra_dev;
+static int ra_end;
+static int ra_first;
+
+
+int
+devopen(void)
+{
+ struct dos_partition *dptr;
+ struct disklabel *dl;
+ int dosdev = inode.i_dev;
+ int i, sector = 0, di;
+#if 0 /* Save space, already have hard error for cyl > 1023 in Bread */
+ u_long bend;
+#endif
+
+ di = get_diskinfo(dosdev);
+ spc = (spt = SPT(di)) * HEADS(di);
+ if ((dosdev & 0xf0) == 0x90)
+ {
+ boff = 0;
+ part = (spt == 15 ? 0 : 1);
+ }
+ else
+ {
+#ifdef EMBEDDED_DISKLABEL
+ dl = &disklabel;
+#else EMBEDDED_DISKLABEL
+#ifdef PC98
+ Bread(dosdev, 1);
+ dptr = (struct dos_partition *)0;
+ slice = WHOLE_DISK_SLICE;
+ for (i = 0; i < NDOSPART; i++, dptr++)
+ if (dptr->dp_mid == DOSPTYP_386BSD) {
+ slice = BASE_SLICE + i;
+ sector = dptr->dp_scyl * spc;
+ break;
+ }
+ Bread(dosdev, sector + LABELSECTOR);
+ dl=((struct disklabel *)0);
+ disklabel = *dl; /* structure copy (maybe useful later)*/
+#else
+ Bread(dosdev, 0);
+ dptr = (struct dos_partition *)(((char *)0)+DOSPARTOFF);
+ slice = WHOLE_DISK_SLICE;
+ for (i = 0; i < NDOSPART; i++, dptr++)
+ if (dptr->dp_typ == DOSPTYP_386BSD) {
+ slice = BASE_SLICE + i;
+ sector = dptr->dp_start;
+ break;
+ }
+ Bread(dosdev, sector + LABELSECTOR);
+ dl=((struct disklabel *)0);
+ disklabel = *dl; /* structure copy (maybe useful later)*/
+#endif /* PC98 */
+#endif EMBEDDED_DISKLABEL
+ if (dl->d_magic != DISKMAGIC) {
+ printf("bad disklabel");
+ return 1;
+ }
+ if( (maj == 4) || (maj == 0) || (maj == 1))
+ {
+ if (dl->d_type == DTYPE_SCSI)
+ {
+ maj = 4; /* use scsi as boot dev */
+ }
+ else
+ {
+ maj = 0; /* must be ESDI/IDE */
+ }
+ }
+ /* This little trick is for OnTrack DiskManager disks */
+ boff = dl->d_partitions[part].p_offset -
+ dl->d_partitions[2].p_offset + sector;
+
+#ifndef PC98
+ /* This is a good idea for all disks */
+ bsize = dl->d_partitions[part].p_size;
+#if 0 /* Save space, already have hard error for cyl > 1023 in Bread */
+ bend = boff + bsize - 1 ;
+ if (bend / spc >= 1024) {
+ printf("boot partition end >= cyl 1024, BIOS can't load kernel stored beyond this limit\n");
+#endif
+#endif
+
+#ifdef DO_BAD144
+ do_bad144 = 0;
+ if (dl->d_flags & D_BADSECT) {
+ /* this disk uses bad144 */
+ int i;
+ int dkbbnum;
+ struct dkbad *dkbptr;
+
+ /* find the first readable bad sector table */
+ /* some of this code is copied from ufs/ufs_disksubr.c */
+ /* including the bugs :-( */
+ /* read a bad sector table */
+
+#define BAD144_PART 2 /* XXX scattered magic numbers */
+#define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */
+ if (dl->d_partitions[BSD_PART].p_offset != 0)
+ dkbbnum = dl->d_partitions[BAD144_PART].p_offset
+ + dl->d_partitions[BAD144_PART].p_size;
+ else
+ dkbbnum = dl->d_secperunit;
+ dkbbnum -= dl->d_nsectors;
+
+ if (dl->d_secsize > DEV_BSIZE)
+ dkbbnum *= dl->d_secsize / DEV_BSIZE;
+ else
+ dkbbnum /= DEV_BSIZE / dl->d_secsize;
+ i = 0;
+ do_bad144 = 0;
+ do {
+ /* XXX: what if the "DOS sector" < 512 bytes ??? */
+ Bread(dosdev, dkbbnum + i);
+ dkbptr = (struct dkbad *) 0;
+/* XXX why is this not in <sys/dkbad.h> ??? */
+#define DKBAD_MAGIC 0x4321
+ if (dkbptr->bt_mbz == 0 &&
+ dkbptr->bt_flag == DKBAD_MAGIC) {
+ dkb = *dkbptr; /* structure copy */
+ do_bad144 = 1;
+ break;
+ }
+ i += 2;
+ } while (i < 10 && i < dl->d_nsectors);
+ if (!do_bad144)
+ printf("Bad bad sector table\n");
+ else
+ printf("Using bad sector table at %d\n", dkbbnum+i);
+ }
+#endif DO_BAD144
+ }
+ return 0;
+}
+
+void
+devread(void)
+{
+ int offset, sector = bnum;
+ int dosdev = inode.i_dev;
+ for (offset = 0; offset < cnt; offset += BPS)
+ {
+ Bread(dosdev, badsect(dosdev, sector++));
+ bcopy(0, iodest+offset, BPS);
+ }
+}
+
+void
+Bread(int dosdev, int sector)
+{
+ if (dosdev != ra_dev || sector < ra_first || sector >= ra_end)
+ {
+ int cyl, head, sec, nsec;
+
+ cyl = sector/spc;
+#ifndef PC98
+ if (cyl > 1023) {
+ printf("Error: C:%d > 1023 (BIOS limit)\n", cyl);
+ for(;;); /* loop forever */
+ }
+#endif
+ head = (sector % spc) / spt;
+ sec = sector % spt;
+ nsec = spt - sec;
+ if (nsec > RA_SECTORS)
+ nsec = RA_SECTORS;
+ twiddle();
+ if (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0)
+ {
+ nsec = 1;
+ twiddle();
+ while (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0) {
+ printf("Error: C:%d H:%d S:%d\n", cyl, head, sec);
+ twiddle();
+ }
+ }
+ ra_dev = dosdev;
+ ra_first = sector;
+ ra_end = sector + nsec;
+ }
+ bcopy(ra_buf + (sector - ra_first) * BPS, I_ADDR, BPS);
+}
+
+int
+badsect(int dosdev, int sector)
+{
+ int i;
+#ifdef DO_BAD144
+ if (do_bad144) {
+ u_short cyl;
+ u_short head;
+ u_short sec;
+ int newsec;
+ struct disklabel *dl = &disklabel;
+
+ /* XXX */
+ /* from wd.c */
+ /* bt_cyl = cylinder number in sorted order */
+ /* bt_trksec is actually (head << 8) + sec */
+
+ /* only remap sectors in the partition */
+ if (sector < boff || sector >= boff + bsize) {
+ goto no_remap;
+ }
+
+ cyl = (sector-boff) / dl->d_secpercyl;
+ head = ((sector-boff) % dl->d_secpercyl) / dl->d_nsectors;
+ sec = (sector-boff) % dl->d_nsectors;
+ sec = (head<<8) + sec;
+
+ /* now, look in the table for a possible bad sector */
+ for (i=0; i<126; i++) {
+ if (dkb.bt_bad[i].bt_cyl == cyl) {
+ /* found same cylinder */
+ if (dkb.bt_bad[i].bt_trksec == sec) {
+ /* FOUND! */
+ break;
+ }
+ } else if (dkb.bt_bad[i].bt_cyl > cyl) {
+ i = 126;
+ break;
+ }
+ }
+ if (i == 126) {
+ /* didn't find bad sector */
+ goto no_remap;
+ }
+ /* otherwise find replacement sector */
+ if (dl->d_partitions[BSD_PART].p_offset != 0)
+ newsec = dl->d_partitions[BAD144_PART].p_offset
+ + dl->d_partitions[BAD144_PART].p_size;
+ else
+ newsec = dl->d_secperunit;
+ newsec -= dl->d_nsectors + i + 1;
+ return newsec;
+ }
+#endif DO_BAD144
+ no_remap:
+ return sector;
+}
diff --git a/sys/pc98/boot/biosboot/io.c b/sys/pc98/boot/biosboot/io.c
new file mode 100644
index 0000000..b11fc7c
--- /dev/null
+++ b/sys/pc98/boot/biosboot/io.c
@@ -0,0 +1,398 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:35:57 rpd
+ * $Id: io.c,v 1.19 1996/04/30 23:43:25 bde Exp $
+ */
+
+#include "boot.h"
+#include <machine/cpufunc.h>
+#include <sys/reboot.h>
+#ifdef PC98
+#include "../../pc98/pc98_device.h"
+#endif
+
+
+/*
+ * Gate A20 for high memory
+ */
+void
+gateA20(void)
+{
+ outb(0xf2, 0x00);
+ outb(0xf6, 0x02);
+}
+
+/* printf - only handles %d as decimal, %c as char, %s as string */
+
+void
+printf(const char *format, ...)
+{
+ int *dataptr = (int *)&format;
+ char c;
+
+ dataptr++;
+ while ((c = *format++))
+ if (c != '%')
+ putchar(c);
+ else
+ switch (c = *format++) {
+ case 'd': {
+ int num = *dataptr++;
+ char buf[10], *ptr = buf;
+ if (num<0) {
+ num = -num;
+ putchar('-');
+ }
+ do
+ *ptr++ = '0'+num%10;
+ while (num /= 10);
+ do
+ putchar(*--ptr);
+ while (ptr != buf);
+ break;
+ }
+ case 'x': {
+ unsigned int num = *dataptr++, dig;
+ char buf[8], *ptr = buf;
+ do
+ *ptr++ = (dig=(num&0xf)) > 9?
+ 'a' + dig - 10 :
+ '0' + dig;
+ while (num >>= 4);
+ do
+ putchar(*--ptr);
+ while (ptr != buf);
+ break;
+ }
+ case 'c': putchar((*dataptr++)&0xff); break;
+ case 's': {
+ char *ptr = (char *)*dataptr++;
+ while ((c = *ptr++))
+ putchar(c);
+ break;
+ }
+ }
+}
+
+void
+putchar(int c)
+{
+ if (c == '\n') {
+ if (loadflags & RB_SERIAL)
+ serial_putc('\r');
+ else
+ putc('\r');
+ }
+ if (loadflags & RB_SERIAL)
+ serial_putc(c);
+ else
+ putc(c);
+}
+
+int
+getchar(int in_buf)
+{
+ int c;
+
+loop:
+ if ((c = ((loadflags & RB_SERIAL) ? serial_getc() : getc())) == '\r')
+ c = '\n';
+ if (c == '\b') {
+ if (in_buf != 0) {
+ putchar('\b');
+ putchar(' ');
+ } else {
+ goto loop;
+ }
+ }
+ putchar(c);
+ return(c);
+}
+
+#ifdef PROBE_KEYBOARD
+/*
+ * This routine uses an inb to an unused port, the time to execute that
+ * inb is approximately 1.25uS. This value is pretty constant across
+ * all CPU's and all buses, with the exception of some PCI implentations
+ * that do not forward this I/O adress to the ISA bus as they know it
+ * is not a valid ISA bus address, those machines execute this inb in
+ * 60 nS :-(.
+ *
+ * XXX this should be converted to use bios_tick.
+ */
+void
+delay1ms(void)
+{
+#ifdef PC98
+ int i = 800;
+ while (--i >= 0)
+ (void)outb(0x5f,0); /* about 600ns */
+#else
+ int i = 800;
+ while (--i >= 0)
+ (void)inb(0x84);
+#endif
+}
+#endif /* PROBE_KEYBOARD */
+
+static __inline int
+isch(void)
+{
+ int isc;
+
+ /*
+ * Checking the keyboard has the side effect of enabling clock
+ * interrupts so that bios_tick works. Check the keyboard to
+ * get this side effect even if we only want the serial status.
+ */
+ isc = ischar();
+
+ if (!(loadflags & RB_SERIAL))
+ return (isc);
+ return (serial_ischar());
+
+}
+
+#ifdef PC98
+static __inline unsigned
+#else
+static __inline unsigned
+#endif
+pword(unsigned physaddr)
+{
+#ifdef PC98
+ static int counter = 0;
+ int i;
+
+ for (i = 0; i < 512; i++)
+ (void)outb(0x5f, 0);
+
+ return (counter++);
+#else
+ unsigned result;
+
+ /*
+ * Give the fs prefix separately because gas omits it for
+ * "movl %fs:0x46c, %eax".
+ */
+ __asm __volatile("fs; movl %1, %0" : "=r" (result)
+ : "m" (*(unsigned *)physaddr));
+ return (result);
+#endif
+}
+
+int
+gets(char *buf)
+{
+#define bios_tick pword(0x46c)
+#ifdef PC98
+#define BIOS_TICK_MS 1
+#else
+#define BIOS_TICK_MS 55
+#endif
+ unsigned initial_bios_tick;
+ char *ptr=buf;
+
+#if BOOTWAIT
+ for (initial_bios_tick = bios_tick;
+ bios_tick - initial_bios_tick < BOOTWAIT / BIOS_TICK_MS;)
+#endif
+ if (isch())
+ for (;;) {
+ switch(*ptr = getchar(ptr - buf) & 0xff) {
+ case '\n':
+ case '\r':
+ *ptr = '\0';
+ return 1;
+ case '\b':
+ if (ptr > buf) ptr--;
+ continue;
+ default:
+ ptr++;
+ }
+#if TIMEOUT + 0
+#if !BOOTWAIT
+#error "TIMEOUT without BOOTWAIT"
+#endif
+ for (initial_bios_tick = bios_tick;;) {
+ if (isch())
+ break;
+ if (bios_tick - initial_bios_tick >=
+ TIMEOUT / BIOS_TICK_MS)
+ return 0;
+ }
+#endif
+ }
+ return 0;
+}
+
+int
+strcmp(const char *s1, const char *s2)
+{
+ while (*s1 == *s2) {
+ if (!*s1++)
+ return 0;
+ s2++;
+ }
+ return 1;
+}
+
+void
+bcopy(const char *from, char *to, int len)
+{
+ while (len-- > 0)
+ *to++ = *from++;
+}
+
+/* To quote Ken: "You are not expected to understand this." :) */
+
+void
+twiddle(void)
+{
+ putchar((char)tw_chars);
+ tw_chars = (tw_chars >> 8) | ((tw_chars & (unsigned long)0xFF) << 24);
+ putchar('\b');
+}
+
+static unsigned short *Crtat = (unsigned short *)0;
+static int row;
+static int col;
+
+void putc(int c)
+{
+ static unsigned short *crtat;
+ unsigned char sys_type;
+ unsigned short *cp;
+ int i, pos;
+
+ if (Crtat == 0) {
+ sys_type = *(unsigned char *)0x11501;
+ if (sys_type & 0x08) {
+ Crtat = (unsigned short *)0x50000;
+ crtat = Crtat;
+ row = 31;
+ col = 80;
+ } else {
+ Crtat = (unsigned short *)0x10000;
+ crtat = Crtat;
+ row = 25;
+ col = 80;
+ }
+ }
+
+ switch(c) {
+ case '\t':
+ do {
+ putc(' ');
+ } while ((int)crtat % 16);
+ break;
+ case '\b':
+ crtat--;
+ break;
+ case '\r':
+ crtat -= (crtat - Crtat) % col;
+ break;
+ case '\n':
+ crtat += col;
+ break;
+ default:
+ *crtat = (c == 0x5c ? 0xfc : c);
+ *(crtat++ + 0x1000) = 0xe1;
+ break;
+ }
+
+ if (crtat >= Crtat + col * row) {
+ for (i = 1; i < row; i++)
+ bcopy((void*)(Crtat+col*i), (void*)(Crtat+col*(i-1)), col*2);
+ for (i = 0, cp = Crtat + col * (row - 1); i < col*2; i++) {
+ *cp++ = ' ';
+ }
+ crtat -= col;
+ }
+ pos = crtat - Crtat;
+ while((inb(0x60) & 0x04) == 0) {}
+ outb(0x62, 0x49);
+ outb(0x60, pos & 0xff);
+ outb(0x60, pos >> 8);
+}
+
+void machine_check(void)
+{
+ int ret;
+ int i;
+ int data = 0;
+ u_char epson_machine_id = *(unsigned char *)(0x11624);
+
+ /* PC98_SYSTEM_PARAMETER(0x501) */
+ ret = ((*(unsigned char*)0x11501) & 0x08) ? M_HIGHRESO : M_NORMAL;
+
+ /* wait V-SYNC */
+ while (inb(0x60) & 0x20) {}
+ while (!(inb(0x60) & 0x20)) {}
+
+ /* ANK 'A' font */
+ outb(0xa1, 0x00);
+ outb(0xa3, 0x41);
+
+ /* M_NORMAL, use CG window (all NEC OK) */
+ /* sum */
+ for (i = 0; i < 4; i++) {
+ data += *((unsigned long*)0x14000 + i);/* 0xa4000 */
+ }
+ if (data == 0x6efc58fc) { /* DA data */
+ ret |= M_NEC_PC98;
+ } else {
+ ret |= M_EPSON_PC98;
+ }
+ ret |= (inb(0x42) & 0x20) ? M_8M : 0;
+
+ /* PC98_SYSTEM_PARAMETER(0x400) */
+ if ((*(unsigned char*)0x11400) & 0x80) {
+ ret |= M_NOTE;
+ }
+ if (ret & M_NEC_PC98) {
+ /* PC98_SYSTEM_PARAMETER(0x458) */
+ if ((*(unsigned char*)0x11458) & 0x80) {
+ ret |= M_H98;
+ } else {
+ ret |= M_NOT_H98;
+ }
+ } else {
+ ret |= M_NOT_H98;
+ switch (epson_machine_id) {
+ case 0x20: /* note A */
+ case 0x22: /* note W */
+ case 0x27: /* note AE */
+ case 0x2a: /* note WR */
+ /*case 0x2: /* note AR */
+ ret |= M_NOTE;
+ break;
+ default:
+ break;
+ }
+ }
+ (*(unsigned long *)(0x11620)) = ret;
+}
diff --git a/sys/pc98/boot/biosboot/probe_keyboard.c b/sys/pc98/boot/biosboot/probe_keyboard.c
new file mode 100644
index 0000000..3ad12a8
--- /dev/null
+++ b/sys/pc98/boot/biosboot/probe_keyboard.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) KATO Takenori, 1994-1995. 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.
+ */
+
+#ifdef PROBE_KEYBOARD
+
+int probe_keyboard(void)
+{
+ /*
+ * New type (RA and later) keyboard only!
+ */
+ if (*(unsigned char*)0x11481 & 0x48)
+ return 0;
+ return 1; /* keyboard not found */
+}
+
+#endif /* PROBE_KEYBOARD */
diff --git a/sys/pc98/boot/biosboot/serial.S b/sys/pc98/boot/biosboot/serial.S
new file mode 100644
index 0000000..d1091cd
--- /dev/null
+++ b/sys/pc98/boot/biosboot/serial.S
@@ -0,0 +1,219 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:34:26 rpd
+ * $Id: serial.S,v 1.3 1995/04/21 16:30:18 bde Exp $
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * Serial bootblock interface routines
+ * Copyright (c) 1994, J"org Wunsch
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+/*
+ * modified for PC-98 by KATO T. of Nagoya University
+ */
+
+ .file "serial.s"
+
+#include "asm.h"
+ .text
+
+/*
+ * The serial port interface routines implement a simple polled i/o
+ * interface to a standard serial port. Due to the space restrictions
+ * for the boot blocks, no BIOS support is used (since BIOS requires
+ * expensive real/protected mode switches), instead the rudimentary
+ * BIOS support is duplicated here.
+ *
+ * The base address for the i/o port is passed from the Makefile in
+ * the COMCONSOLE preprocessor macro. Console parameters are currently
+ * hard-coded to 9600 Bd, 8 bit. This can be changed in the
+ * init_serial() function.
+ */
+
+/*
+ * void serial_putc(char ch)
+ * send ch to serial port
+ *
+ */
+
+ENTRY(serial_putc)
+ push %ebp
+ mov %esp, %ebp
+ push %edx
+
+ mov $COMCONSOLE + 2, %edx # line status reg
+1: inb %dx, %al
+ test $0x01, %al
+ jz 1b # TX buffer not empty
+
+ movb 0x8(%ebp), %al
+
+ sub $2, %edx # TX output reg
+ outb %al, %dx # send this one
+
+ pop %edx
+ pop %ebp
+ ret
+
+/*
+ * int serial_getc(void)
+ * read a character from serial port
+ */
+
+ENTRY(serial_getc)
+ push %ebp
+ mov %esp, %ebp
+ push %edx
+
+ mov $COMCONSOLE + 2, %edx # line status reg
+1:
+ inb %dx, %al
+ testb $0x02, %al
+ jz 1b # no RX char available
+
+ xor %eax, %eax
+ sub $2, %edx # RX buffer reg
+ inb %dx, %al # fetch (first) character
+
+ cmp $0x7F, %eax # make DEL...
+ jne 2f
+ mov $0x08, %eax # look like BS
+2:
+ pop %edx
+ pop %ebp
+ ret
+
+/*
+ * int serial_ischar(void)
+ * if there is a character pending, return true; otherwise return 0
+ */
+ENTRY(serial_ischar)
+ push %ebp
+ mov %esp, %ebp
+ push %edx
+
+ xorl %eax, %eax
+ mov $COMCONSOLE + 2, %edx # line status reg
+ inb %dx, %al
+ andb $0x02, %al # RX char available?
+
+ pop %edx
+ pop %ebp
+ ret
+
+/*
+ * void init_serial(void)
+ * initialize the serial console port to 9600 Bd, 8 bpc
+ */
+ENTRY(init_serial)
+ push %ebp
+ mov %esp, %ebp
+ push %edx
+
+ /* set 8253 */
+ movb 0xb6, %al
+ outb %al, $0x77
+ movl $COMCONSOLE_CLK, %eax
+ outb %al, $0x75
+ inb $0x5f, %al
+ movb %ah, %al
+ outb %al, $0x75
+
+ /* inhibit com int */
+ inb $0x35, %al
+ andb $0xf8, %al
+ movb %al, %ah
+ inb $0x5f, %al
+ movb %ah, %al
+ outb %al, $0x35
+
+ inb $0x02, %al
+ orb $0x10, %al
+ outb %al, $0x02
+
+ /* dummy command */
+ xor %al,%al
+ mov $COMCONSOLE + 2, %edx
+ outb %al, %dx
+ inb $0x5f, %al
+ xor %al,%al
+ outb %al, %dx
+ inb $0x5f, %al
+ xor %al,%al
+ outb %al, %dx
+ inb $0x5f, %al
+
+ /* RESET 8251 */
+ movb $0x40, %al
+ outb %al, %dx
+
+ movb $COMCONSOLE_MODE , %al
+ andb $0xfc, %al
+ orb $0x02, %al /* factor = 1/16 */
+ outb %al, %dx
+ inb $0x5f, %al
+
+ /* start RS-232C */
+ movb $0x37, %al
+ outb %al, %dx
+
+ pop %edx
+ pop %ebp
+ ret
diff --git a/sys/pc98/boot/biosboot/start.S b/sys/pc98/boot/biosboot/start.S
new file mode 100644
index 0000000..ab159d7
--- /dev/null
+++ b/sys/pc98/boot/biosboot/start.S
@@ -0,0 +1,354 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:36:29 rpd
+ * $Id: start.S,v 1.6 1995/09/16 13:51:20 bde Exp $
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+/*
+ * Ported to PC-9801 by Yoshio Kimura
+ */
+
+#include "asm.h"
+
+ .file "start.S"
+
+SIGNATURE= 0xaa55
+LOADSZ= 8192 /* size of unix boot */
+
+ .text
+ .globl start
+
+ENTRY(boot1)
+ jmp start
+
+boot_cyl:
+ .word 0
+ String "IPL1 "
+
+start:
+ /* set up %ds */
+ xor %ax, %ax
+ mov %ax, %ds
+
+ /* set up %ss and %esp */
+ data32
+ mov $BOOTSEG, %eax
+ mov %ax, %ss
+ data32
+ mov $BOOTSTACK, %esp
+
+ /* set up %es, (where we will load boot2 to) */
+ mov %ax, %es
+
+ push %es
+ push %cx
+ push %dx
+
+ data32
+ mov $0xa000, %eax
+ mov %ax, %es
+
+ addr32
+ movb 0x501, %al
+ testb $0x08, %al
+ jnz hireso
+normal:
+ /* set up graphic screen */
+ movb $0x42, %ah
+ movb $0xc0, %ch
+ int $0x18
+ movb $0x40, %ah
+ int $0x18
+
+ data32
+ mov $0x0a00, %eax /* 80 x 25 mode */
+ jmp 1f
+hireso:
+ movb $0x08, %al /* set up RAM window */
+ outb %al, $0x91
+ movb $0x0a, %al
+ outb %al, $0x93
+ data32
+ mov $0x0a10, %ax /* 80 x 31 mode */
+1:
+ int $0x18
+ movb $0x0c, %ah /* text on */
+ int $0x18
+
+ /* cursor home and on */
+ xor %edx, %edx
+ movb $0x13, %ah
+ int $0x18
+ movb $0x11, %ah
+ int $0x18
+
+ /* highreso no supported */
+ addr32
+ movb 0x501, %al
+ testb $0x08, %al
+ jz nothireso
+
+ data32
+ mov $ehireso, %esi
+ data32
+ call message
+ hlt
+
+nothireso:
+ /* keyboad reset */
+ movb $0x03, %ah
+ int $0x18
+
+ /* transfer PC-9801 system common area to 0xa1000 */
+ data32
+ mov $0x0000, %esi
+ data32
+ mov $0x1000, %edi
+ data32
+ mov $0x0630, %ecx
+ cld
+ rep
+ movsb
+
+ /* transfer EPSON machine type to 0xa1200 */
+ push %ds
+ data32
+ mov $0xfd00, %eax
+ mov %ax, %ds
+ addr32
+ data32
+ mov 0x804, %eax
+ data32
+ and $0x00ffffff, %eax
+ addr32
+ data32
+ .byte 0x26
+ mov %eax, %es: (0x1624)
+
+ pop %ds
+ pop %dx
+ pop %cx
+ pop %es
+
+ /* bootstrap passes */
+ mov %cs, %bx
+ data32
+ cmp $0x1fc0, %ebx
+ jnz hd
+ data32
+ mov %ebp, %ecx
+ data32
+ mov %ebp, %edx
+ addr32
+ movb 0x584, %al
+ andb $0xf0, %al
+ cmpb $0x90, %al
+ jnz hd
+fd:
+ data32
+ mov $0x0200, %ecx
+ data32
+ mov $0x0001, %edx
+ movb $0xd6, %ah
+ jmp load
+hd:
+ data32
+ and %ecx, %ecx
+ jnz 1f
+ addr32
+ data32
+ mov %cs: (boot_cyl), %ecx
+1:
+ movb $0x06, %ah
+
+/*
+ * BIOS call "INT 0x1B Function 0xn6" to read sectors from disk into memory
+ * Call with %ah = 0xd6(for floppy disk) or 0x06(for hard disk)
+ * %al = DA/UA
+ * %bx = data length
+ * %ch = sector size(for floppy) or cylinder(for hard)
+ * %cl = cylinder
+ * %dh = head
+ * %dl = sector
+ * %es:%bp = segment:offset of buffer
+ * Return:
+ * %ah = 0x0 on success; err code on failure
+ */
+
+load:
+ data32
+ mov $LOADSZ, %ebx
+ addr32
+ movb 0x584, %al
+ xor %ebp, %ebp /* %bp = 0, put it at 0 in the BOOTSEG */
+ int $0x1b
+ jc read_error
+
+ /*
+ * ljmp to the second stage boot loader (boot2).
+ * After ljmp, %cs is BOOTSEG and boot1 (512 bytes) will be used
+ * as an internal buffer "intbuf".
+ */
+
+ data32
+ ljmp $BOOTSEG, $ EXT(boot2)
+
+/*
+ * read_error
+ */
+read_error:
+ data32
+ mov $eread, %esi
+err_stop:
+ data32
+ call message
+ data32
+ jmp stop
+
+/*
+ * message: write the error message in %ds:%esi to console
+ */
+message:
+
+ data32
+ push %eax
+ data32
+ push %ebx
+ push %ds
+ push %es
+ data32
+ mov $0xe000, %eax
+ mov %ax, %es
+ addr32
+ mov 0x501, %al
+ testb $0x08, %al
+ jnz 1f
+ data32
+ mov $0xa000, %eax
+ mov %ax, %es
+1:
+ mov %cs, %ax
+ mov %ax, %ds
+ addr32
+ data32
+ mov vram, %edi
+ data32
+ mov $0x00e1, %ebx
+ cld
+
+nextb:
+ lodsb /* load a byte into %al */
+ cmpb $0x0, %al
+ je done
+ cmpb $0x0d, %al
+ je cr_code
+ cmpb $0x0a, %al
+ je lf_code
+ addr32
+ movb %al, (%edi)
+ addr32
+ movb %bl, 0x2000(%edi)
+ data32
+ inc %edi
+ data32
+ inc %edi
+ jmp nextb
+cr_code:
+ data32
+ add $80, %edi
+ jmp nextb
+lf_code:
+ data32
+ mov %edi, %eax
+ data32
+ mov $80, %edx
+ data32
+ div %ebx
+ data32
+ sub %ebx, %edi
+ jmp nextb
+done:
+ addr32
+ data32
+ mov %edi, vram
+ pop %es
+ pop %ds
+ data32
+ pop %ebx
+ data32
+ pop %eax
+ data32
+ ret
+
+stop: hlt
+ data32
+ jmp stop /* halt doesnt actually halt forever */
+
+vram:
+ .long 0
+
+/* error messages */
+
+#ifdef DEBUG
+one: String "1\r\n\0"
+two: String "2\r\n\0"
+three: String "3\r\n\0"
+four: String "4\r\n\0"
+five: String "5\r\n\0"
+six: String "6\r\n\0"
+seven: String "7\r\n\0"
+#endif DEBUG
+eread: String "Read error\r\n\0"
+enoboot: String "No bootable partition\r\n\0"
+endofcode:
+ehireso: String "Highreso not supported\r\n\0"
+/* the last 2 bytes in the sector 0 contain the signature */
+ . = EXT(boot1) + 0x1fe
+ .value SIGNATURE
+ENTRY(disklabel)
+ . = EXT(boot1) + 0x400
diff --git a/sys/pc98/boot/biosboot/sys.c b/sys/pc98/boot/biosboot/sys.c
new file mode 100644
index 0000000..dcf96ba
--- /dev/null
+++ b/sys/pc98/boot/biosboot/sys.c
@@ -0,0 +1,297 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:36:34 rpd
+ * $Id: sys.c,v 1.11 1996/04/07 14:28:03 bde Exp $
+ */
+
+/*
+ * Ported to PC-9801 by Yoshio Kimura
+ */
+
+#include "boot.h"
+#include <sys/dir.h>
+#include <sys/reboot.h>
+
+#ifdef 0
+/* #define BUFSIZE 4096 */
+#define BUFSIZE MAXBSIZE
+
+char buf[BUFSIZE], fsbuf[SBSIZE], iobuf[MAXBSIZE];
+#endif
+
+static char biosdrivedigit;
+
+#define BUFSIZE 8192
+#define MAPBUFSIZE BUFSIZE
+char buf[BUFSIZE], fsbuf[BUFSIZE], iobuf[BUFSIZE];
+
+char mapbuf[MAPBUFSIZE];
+int mapblock;
+
+void
+xread(char *addr, int size)
+{
+ int count = BUFSIZE;
+ while (size > 0) {
+ if (BUFSIZE > size)
+ count = size;
+ read(buf, count);
+ pcpy(buf, addr, count);
+ size -= count;
+ addr += count;
+ }
+}
+
+void
+read(char *buffer, int count)
+{
+ int logno, off, size;
+ int cnt2, bnum2;
+
+ while (count) {
+ off = blkoff(fs, poff);
+ logno = lblkno(fs, poff);
+ cnt2 = size = blksize(fs, &inode, logno);
+ bnum2 = fsbtodb(fs, block_map(logno)) + boff;
+ cnt = cnt2;
+ bnum = bnum2;
+ if ( (!off) && (size <= count))
+ {
+ iodest = buffer;
+ devread();
+ }
+ else
+ {
+ iodest = iobuf;
+ size -= off;
+ if (size > count)
+ size = count;
+ devread();
+ bcopy(iodest+off,buffer,size);
+ }
+ buffer += size;
+ count -= size;
+ poff += size;
+ }
+}
+
+int
+find(char *path)
+{
+ char *rest, ch;
+ int block, off, loc, ino = ROOTINO;
+ struct direct *dp;
+ int list_only = 0;
+
+ if (strcmp("?", path) == 0)
+ list_only = 1;
+loop: iodest = iobuf;
+ cnt = fs->fs_bsize;
+ bnum = fsbtodb(fs,ino_to_fsba(fs,ino)) + boff;
+ devread();
+ bcopy((void *)&((struct dinode *)iodest)[ino % fs->fs_inopb],
+ (void *)&inode.i_din,
+ sizeof (struct dinode));
+ if (!*path)
+ return 1;
+ while (*path == '/')
+ path++;
+ if (!inode.i_size || ((inode.i_mode&IFMT) != IFDIR))
+ return 0;
+ for (rest = path; (ch = *rest) && ch != '/'; rest++) ;
+ *rest = 0;
+ loc = 0;
+ do {
+ if (loc >= inode.i_size) {
+ if (list_only) {
+ printf("\n");
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ if (!(off = blkoff(fs, loc))) {
+ block = lblkno(fs, loc);
+ cnt = blksize(fs, &inode, block);
+ bnum = fsbtodb(fs, block_map(block)) + boff;
+ iodest = iobuf;
+ devread();
+ }
+ dp = (struct direct *)(iodest + off);
+ loc += dp->d_reclen;
+ if (dp->d_ino && list_only)
+ printf("%s ", dp->d_name);
+ } while (!dp->d_ino || strcmp(path, dp->d_name));
+ ino = dp->d_ino;
+ *(path = rest) = ch;
+ goto loop;
+}
+
+
+int
+block_map(int file_block)
+{
+ if (file_block < NDADDR)
+ return(inode.i_db[file_block]);
+ if ((bnum=fsbtodb(fs, inode.i_ib[0])+boff) != mapblock) {
+ iodest = mapbuf;
+ cnt = fs->fs_bsize;
+ devread();
+ mapblock = bnum;
+ }
+ return (((int *)mapbuf)[(file_block - NDADDR) % NINDIR(fs)]);
+}
+
+
+int
+openrd(void)
+{
+ char **devp, *cp = name;
+ int biosdrive, ret;
+
+#ifdef PC98
+ int i;
+ unsigned char disk_equips;
+ int sdunit = 0;
+#endif
+ /*******************************************************\
+ * If bracket given look for preceding device name *
+ \*******************************************************/
+ while (*cp && *cp!='(')
+ cp++;
+ if (!*cp)
+ {
+ cp = name;
+ }
+ else
+ {
+ /*
+ * Look for a BIOS drive number (a leading digit followed
+ * by a colon).
+ */
+ if (*(name + 1) == ':' && *name >= '0' && *name <= '9') {
+ biosdrivedigit = *name;
+ name += 2;
+ } else
+ biosdrivedigit = '\0';
+
+ if (cp++ != name)
+ {
+ for (devp = devs; *devp; devp++)
+ if (name[0] == (*devp)[0] &&
+ name[1] == (*devp)[1])
+ break;
+ if (!*devp)
+ {
+ printf("Unknown device\n");
+ return 1;
+ }
+ maj = devp-devs;
+ }
+ /*******************************************************\
+ * Look inside brackets for unit number, and partition *
+ \*******************************************************/
+ /*
+ * Allow any valid digit as the unit number, as the BIOS
+ * will complain if the unit number is out of range.
+ * Restricting the range here prevents the possibilty of using
+ * BIOSes that support more than 2 units.
+ * XXX Bad values may cause strange errors, need to check if
+ * what happens when a value out of range is supplied.
+ */
+ if (*cp >= '0' && *cp <= '9')
+ unit = *cp++ - '0';
+ if (!*cp || (*cp == ',' && !*++cp))
+ return 1;
+ if (*cp >= 'a' && *cp <= 'p')
+ part = *cp++ - 'a';
+ while (*cp && *cp++!=')') ;
+ if (!*cp)
+ return 1;
+ }
+ if (biosdrivedigit != '\0')
+ biosdrive = biosdrivedigit - '0';
+ else {
+ biosdrive = unit;
+#if BOOT_HD_BIAS > 0
+ /* XXX */
+ if (maj == 4)
+ biosdrive += BOOT_HD_BIAS;
+#endif
+ }
+ switch(maj)
+ {
+ case 4: /* sd */
+#ifdef PC98
+ dosdev = unit | 0xa0;
+ disk_equips = *(unsigned char *)0x11482;
+ for (i = 0; i < unit; i++)
+ sdunit += ((disk_equips >> i) & 0x01);
+ unit = sdunit;
+#else /* IBM-PC */
+ dosdev = biosdrive | 0x80;
+#endif
+ break;
+ case 0:
+ case 2:
+#ifdef PC98
+ dosdev = (maj << 3) | unit | 0x80;
+#else
+ dosdev = biosdrive;
+#endif
+ break;
+ case 3:
+ printf("Unknown device\n");
+ return 1;
+ }
+ printf("dosdev = %x, biosdrive = %d, unit = %d, maj = %d\n",
+ dosdev, biosdrive, unit, maj);
+ inode.i_dev = dosdev;
+
+ /***********************************************\
+ * Now we know the disk unit and part, *
+ * Load disk info, (open the device) *
+ \***********************************************/
+ if (devopen())
+ return 1;
+
+ /***********************************************\
+ * Load Filesystem info (mount the device) *
+ \***********************************************/
+ iodest = (char *)(fs = (struct fs *)fsbuf);
+ cnt = SBSIZE;
+ bnum = SBLOCK + boff;
+ devread();
+ /***********************************************\
+ * Find the actual FILE on the mounted device *
+ \***********************************************/
+ ret = find(cp);
+ if (ret <= 0)
+ return (ret == 0) ? 1 : -1;
+ poff = 0;
+ name = cp;
+ return 0;
+}
diff --git a/sys/pc98/boot/biosboot/table.c b/sys/pc98/boot/biosboot/table.c
new file mode 100644
index 0000000..3bc501a
--- /dev/null
+++ b/sys/pc98/boot/biosboot/table.c
@@ -0,0 +1,151 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ * from: Mach, Revision 2.2 92/04/04 11:36:43 rpd
+ * $Id: table.c,v 1.10 1996/04/07 14:28:05 bde Exp $
+ */
+
+/*
+ Copyright 1988, 1989, 1990, 1991, 1992
+ by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "boot.h"
+
+/* Segment Descriptor
+ *
+ * 31 24 19 16 7 0
+ * ------------------------------------------------------------
+ * | | |B| |A| | | |1|0|E|W|A| |
+ * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL| TYPE | BASE 23:16 |
+ * | | |D| |L| 19..16| | |1|1|C|R|A| |
+ * ------------------------------------------------------------
+ * | | |
+ * | BASE 15..0 | LIMIT 15..0 |
+ * | | |
+ * ------------------------------------------------------------
+ */
+
+struct seg_desc {
+ unsigned short limit_15_0;
+ unsigned short base_15_0;
+ unsigned char base_23_16;
+ unsigned char p_dpl_type;
+ unsigned char g_b_a_limit;
+ unsigned char base_31_24;
+ };
+
+#define RUN 0 /* not really 0, but filled in at boot time */
+
+struct seg_desc Gdt[] = {
+ {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* 0x0 : null */
+ {0xFFFF, 0x0, 0x0, 0x9F, 0xCF, 0x0}, /* 0x08 : kernel code */
+ /* 0x9E? */
+ {0xFFFF, 0x0, 0x0, 0x93, 0xCF, 0x0}, /* 0x10 : kernel data */
+ /* 0x92? */
+ {0xFFFF, RUN, RUN, 0x9E, 0x40, 0x0}, /* 0x18 : boot code */
+#ifdef PC98
+ /*
+ * The limit of boot data should be more than 0x6FFFF to save
+ * BIOS parameter and EPSON machine ID into 2'nd T-VRAM, because
+ * base addres is 0x90000.
+ */
+ {0xFFFF, RUN, RUN, 0x92, 0x46, 0x0}, /* 0x20 : boot data */
+#else
+ {0xFFFF, RUN, RUN, 0x92, 0x40, 0x0}, /* 0x20 : boot data */
+#endif
+ {0xFFFF, RUN, RUN, 0x9E, 0x0, 0x0}, /* 0x28 : boot code, 16 bits */
+ {0xFFFF, 0x0, 0x0, 0x92, 0x0, 0x0}, /* 0x30 : boot data, 16 bits */
+#ifdef BDE_DEBUGGER
+ /* More for bdb. */
+ {}, /* BIOS_TMP_INDEX = 7 : null */
+ {}, /* TSS_INDEX = 8 : null */
+ {0xFFFF, 0x0, 0x0, 0xB2, 0x40, 0x0}, /* DS_286_INDEX = 9 */
+ {0xFFFF, 0x0, 0x0, 0xB2, 0x40, 0x0}, /* ES_286_INDEX = 10 */
+ {}, /* Unused = 11 : null */
+ {0x7FFF, 0x8000, 0xB, 0xB2, 0x40, 0x0}, /* COLOR_INDEX = 12 */
+ {0x7FFF, 0x0, 0xB, 0xB2, 0x40, 0x0}, /* MONO_INDEX = 13 */
+ {0xFFFF, RUN, RUN, 0x9A, 0x40, 0x0}, /* DB_CS_INDEX = 14 */
+ {0xFFFF, RUN, RUN, 0x9A, 0x0, 0x0}, /* DB_CS16_INDEX = 15 */
+ {0xFFFF, RUN, RUN, 0x92, 0x40, 0x0}, /* DB_DS_INDEX = 16 */
+ {8*18-1, RUN, RUN, 0x92, 0x40, 0x0}, /* GDT_INDEX = 17 */
+#endif /* BDE_DEBUGGER */
+};
+
+#ifdef BDE_DEBUGGER
+struct idt_desc {
+ unsigned short entry_15_0;
+ unsigned short selector;
+ unsigned char padding;
+ unsigned char p_dpl_type;
+ unsigned short entry_31_16;
+};
+
+struct idt_desc Idt[] = {
+ {}, /* Null (int 0) */
+ {RUN, 0x70, 0, 0x8E, 0}, /* DEBUG_VECTOR = 1 */
+ {}, /* Null (int 2) */
+ {RUN, 0x70, 0, 0xEE, 0}, /* BREAKPOINT_VECTOR = 3 */
+};
+#endif /* BDE_DEBUGGER */
+
+struct pseudo_desc {
+ unsigned short limit;
+ unsigned short base_low;
+ unsigned short base_high;
+ };
+
+struct pseudo_desc Gdtr = { sizeof Gdt - 1, RUN, RUN };
+#ifdef BDE_DEBUGGER
+struct pseudo_desc Idtr_prot = { sizeof Idt - 1, RUN, RUN };
+struct pseudo_desc Idtr_real = { 0x400 - 1, 0x0, 0x0 };
+#endif
+
+/*
+ * All initialized data is defined in one file to reduce space wastage from
+ * fragmentation.
+ */
+char *devs[] = { "wd", "dk", "fd", "wt", "sd", 0 };
+char dflname[] = "/kernel";
+char *name = dflname;
+unsigned long tw_chars = 0x5C2D2F7C; /* "\-/|" */
diff --git a/sys/pc98/boot/kzipboot/Makefile b/sys/pc98/boot/kzipboot/Makefile
new file mode 100644
index 0000000..47df4f1
--- /dev/null
+++ b/sys/pc98/boot/kzipboot/Makefile
@@ -0,0 +1,30 @@
+# $Id: Makefile,v 1.4 1995/10/06 02:57:22 peter Exp $
+
+PROG= kztail.o kzhead.o
+BINMODE = 444 # target is a relocatable object
+SRCS= tail.S head.S boot.c unzip.c misc.c malloc.c inflate.c
+OBJS_KZHEAD= head.o
+OBJS_KZTAIL= tail.o boot.o unzip.o misc.o malloc.o inflate.o
+BINDIR= /usr/lib
+.PATH: ${.CURDIR}/../../../kern
+NOMAN= toobad
+
+# Where to load the kernel
+KADDR = 0x100000
+
+# What segment our code lives in
+CSEG = 0x8
+
+STRIP= # very important!! don't let kz*.o be stripped
+
+CFLAGS+= -DKADDR=$(KADDR) -DCSEG=$(CSEG)
+CFLAGS+= -DKZIP -DCOMCONSOLE=0x30
+CFLAGS+= -DPC98
+
+kztail.o: ${OBJS_KZTAIL}
+ $(LD) -r -x -o kztail.o $(OBJS_KZTAIL)
+
+kzhead.o: ${OBJS_KZHEAD}
+ $(LD) -r -x -o kzhead.o $(OBJS_KZHEAD)
+
+.include <bsd.prog.mk>
diff --git a/sys/pc98/boot/kzipboot/README b/sys/pc98/boot/kzipboot/README
new file mode 100644
index 0000000..2845cbe
--- /dev/null
+++ b/sys/pc98/boot/kzipboot/README
@@ -0,0 +1,49 @@
+/* Beware: mostly obsolete info */
+
+This is the first (alpha) release of kernel packer/unpacker
+for FreeBSD. It is based on xBoot from Linux, but
+hardly rewritten.
+
+It assumes that:
+1) The kernel should be loaded at 0x100000 phys address.
+2) The CS selector is equal to 8, which is OK for all
+ current secondary boot programs.
+
+Run "make install" to install it. It will place
+"kzip" shell script into /usr/sbin, and several files
+into /usr/libexec/kzip directory.
+
+Then try to zip your kernel, for example:
+
+ % kzip /kernel
+ System size is 462848
+ Compressed size 247027
+
+It will create file /kernel.kz:
+
+ % ls -l /kernel /kernel.kz
+ -rwxr-xr-x 1 root 497297 Oct 8 12:41 /386bsd
+ -rwxrwxr-x 1 root 262144 Oct 8 13:37 /386bsd.kz
+
+Then rename /kernel.kz to /kernel and reboot.
+
+ % mv /kernel /o3kernel
+ % mv /kernel.kz /kernel
+ % sync
+ % reboot
+
+During booting, you will see the message:
+
+ Uncompressing kernel...done
+ Booting the kernel
+
+The packed kernel should load and run.
+
+The main problem with packed kernel is the lack of symbol table,
+so all commands that require it, will not run.
+Among them: ps, savecore, *stat, etc.
+
+Packed kernels are good for install and fixit floppies.
+
+Serge Vakulenko, <vak@zebub.msk.su>
+Opdated for FreeBSD 2.1 by Gary Jennejohn 12FEB95
diff --git a/sys/pc98/boot/kzipboot/boot.c b/sys/pc98/boot/kzipboot/boot.c
new file mode 100644
index 0000000..7a0e6bc
--- /dev/null
+++ b/sys/pc98/boot/kzipboot/boot.c
@@ -0,0 +1,194 @@
+/*
+ * FreeBSD kernel unpacker.
+ * 1993 by Serge Vakulenko
+ * modified for FreeBSD 2.1 by Gary Jennejohn - 12FEB95
+ */
+
+/*
+ * FreeBSD(98) port
+ * 1995 by KATO T. of Nagoya University
+ */
+
+#include <machine/cpufunc.h> /* for inb/outb */
+#include <sys/reboot.h> /* for RB_SERIAL */
+
+short *videomem;
+int curs;
+int cols;
+int lines;
+unsigned int port;
+
+#ifdef PC98
+unsigned char bios[0x200];
+#else
+unsigned char bios[0x100];
+#endif
+
+extern int end, edata;
+void *storage;
+void *inbuf;
+void *outbuf;
+void *window;
+
+void decompress_kernel (void *dest);
+
+int memcmp (const void *arg1, const void *arg2, unsigned len)
+{
+ unsigned char *a = (unsigned char*) arg1;
+ unsigned char *b = (unsigned char*) arg2;
+
+ for (; len-- > 0; ++a, ++b)
+ if (*a < *b)
+ return (-1);
+ else if (*a > *b)
+ return (1);
+ return (0);
+}
+
+void *memcpy (void *to, const void *from, unsigned len)
+{
+ char *f = (char*) from;
+ char *t = (char*) to;
+
+ while (len-- > 0)
+ *t++ = *f++;
+ return (to);
+}
+
+void serial_putchar (unsigned char c)
+{
+ unsigned char stat;
+
+ if (c == '\n')
+ serial_putchar('\r');
+#ifdef PC98
+ do {
+ stat = inb (COMCONSOLE+2);
+ } while (!(stat & 0x01));
+#else
+ do {
+ stat = inb (COMCONSOLE+5);
+ } while (!(stat & 0x20));
+#endif
+
+ outb (COMCONSOLE, c);
+}
+
+void putchar (unsigned char c)
+{
+ switch (c) {
+ case '\n': curs = (curs + cols) / cols * cols; break;
+#ifdef PC98
+ default: videomem[curs++] = (c == 0x5c ? 0xfc : c); break;
+#else
+ default: videomem[curs++] = 0x0700 | c; break;
+#endif
+ }
+ while (curs >= cols*lines) {
+ int col;
+
+ memcpy (videomem, videomem+cols, (lines-1) * cols * 2);
+ for (col = 0; col < cols; col++)
+#ifdef PC98
+ videomem[(lines - 1) * cols + col] = 0x20;
+#else
+ videomem[(lines - 1) * cols + col] = 0x720;
+#endif
+ curs -= cols;
+ }
+ /* set cursor position */
+#ifdef PC98
+ while ((inb(0x60) & 0x04) == 0) {}
+ outb(0x62, 0x49);
+ outb(0x60, (curs + 1) & 0xff);
+ outb(0x60, (curs + 1) >> 8);
+#else
+ outb (port, 0x0e); outb (port+1, curs>>8);
+ outb (port, 0x0f); outb (port+1, curs);
+#endif
+}
+
+int use_serial;
+
+void putstr (char *s)
+{
+ while (*s) {
+ if (use_serial)
+ serial_putchar (*s++);
+ else
+ putchar (*s++);
+ }
+}
+
+void error (char *s)
+{
+ putstr ("\n\n");
+ putstr (s);
+ putstr ("\n\n -- System halted");
+ while (1); /* Halt */
+}
+
+void boot (int howto)
+{
+ int l, c, *p;
+#ifdef PC98
+ unsigned short gdc_curaddr;
+ int i;
+#endif
+ /* clear bss */
+ for (p = &edata; p < &end; ++p)
+ *p = 0;
+
+ inbuf = (void *)0x20000;
+ outbuf = (void *)0x30000;
+ window = (void *)0x40000;
+ storage = (void *)0x50000;
+
+ if (!(use_serial = (howto & RB_SERIAL))) {
+#ifdef PC98
+ videomem = (void*)0xa0000;
+ cols = 80;
+ lines = 25;
+
+ /* Is FIFO buffer empty ? */
+ while ((inb(0x60) & 0x04) == 0) {}
+
+ outb(0x62, 0xe0); /* CSRR command */
+ /* T-GDC busy ? */
+ while ((inb(0x60) & 0x01) == 0) {}
+ /* read cursor address */
+ gdc_curaddr = inb(0x62);
+ gdc_curaddr = (inb(0x62) << 8);
+ /* ignore rest of data */
+ for (i = 0; i < 3; i++) {
+ (void)inb(0x62);
+ }
+
+ l = gdc_curaddr / 80 + 1;
+ c = 0;
+#else
+ /* Test for monochrome video adapter */
+ if ((*((unsigned char*) 0x410) & 0x30) == 0x30)
+ videomem = (void*) 0xb0000; /* monochrome */
+ else
+ videomem = (void*) 0xb8000; /* color */
+
+ port = *(unsigned short*) 0x463;
+ cols = *(unsigned short*) 0x44a;
+ lines = 1 + *(unsigned char*) 0x484;
+ c = *(unsigned char*) 0x450;
+ l = *(unsigned char*) 0x451;
+#endif
+
+ if (lines < 25)
+ lines = 25;
+ curs = l*cols + c;
+ if (curs > lines*cols)
+ curs = (lines-1) * cols;
+ }
+
+ putstr ("Uncompressing kernel...");
+ decompress_kernel ((void*) KADDR);
+ putstr ("done\n");
+ putstr ("Booting the kernel\n");
+}
diff --git a/sys/pc98/boot/kzipboot/gzip.h b/sys/pc98/boot/kzipboot/gzip.h
new file mode 100644
index 0000000..e578532
--- /dev/null
+++ b/sys/pc98/boot/kzipboot/gzip.h
@@ -0,0 +1,78 @@
+/*
+ * gzip.h -- common declarations for all gzip modules
+ * Copyright (C) 1992-1993 Jean-loup Gailly.
+ * Adapted for FreeBSD boot unpacker by Serge Vakulenko.
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+typedef unsigned char uchar;
+typedef unsigned long ulong;
+
+#define NULL 0
+
+#define STORED 0 /* Compression methods */
+#define COMPRESSED 1
+#define PACKED 2
+#define DEFLATED 8 /* methods 3 to 7 reserved */
+
+#define INBUFSIZ 0x8000 /* input buffer size */
+
+#define OUTBUFSIZ 16384 /* output buffer size */
+#define OUTBUF_EXTRA 2048 /* required by unlzw() */
+
+#define GZIP_MAGIC "\037\213" /* gzip files, 1F 8B */
+#define OLD_GZIP_MAGIC "\037\236" /* gzip 0.5 = freeze 1.x */
+#define PKZIP_MAGIC "PK\003\004" /* pkzip files */
+#define PACK_MAGIC "\037\036" /* packed files */
+#define LZW_MAGIC "\037\235" /* lzw files, 1F 9D */
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* file probably ascii text */
+#define CONTINUATION 0x02 /* cont. of multi-part gzip file */
+#define EXTRA_FIELD 0x04 /* extra field present */
+#define ORIG_NAME 0x08 /* original file name present */
+#define COMMENT 0x10 /* file comment present */
+#define ENCRYPTED 0x20 /* file is encrypted */
+#define RESERVED 0xC0 /* reserved */
+
+/* window size--must be a power of two, and */
+/* at least 32K for zip's deflate method */
+#define WSIZE 0x8000
+
+extern int method; /* compression method */
+
+extern uchar *inbuf; /* input buffer */
+extern uchar *outbuf; /* output buffer */
+extern uchar *window; /* Sliding window and suffix table (unlzw) */
+
+extern unsigned insize; /* valid bytes in inbuf */
+extern unsigned inptr; /* index of next byte to be processed in inbuf */
+extern unsigned outcnt; /* bytes in output buffer */
+
+extern int pkzip; /* set for a pkzip file */
+extern int extended; /* set if extended local header */
+extern ulong crc; /* shift register contents */
+extern ulong output_ptr; /* total output bytes */
+
+extern void unzip (void);
+extern void check_zipfile (void);
+extern void updcrc (uchar *s, unsigned n);
+extern void clear_bufs (void);
+extern void fill_inbuf (void);
+extern void flush_window (void);
+extern void error (char *m);
+
+static inline uchar get_byte ()
+{
+ if (inptr >= insize)
+ fill_inbuf ();
+ return (inbuf[inptr++]);
+}
+
+static inline void put_char (uchar c)
+{
+ window[outcnt++] = c;
+ if (outcnt == WSIZE)
+ flush_window();
+}
diff --git a/sys/pc98/boot/kzipboot/head.S b/sys/pc98/boot/kzipboot/head.S
new file mode 100644
index 0000000..914e5e2
--- /dev/null
+++ b/sys/pc98/boot/kzipboot/head.S
@@ -0,0 +1,11 @@
+/*
+ * First module in a kzipped kernel.
+ * This needs to be at the beginning so that the boot loader calls it.
+ * It may be overwritten by uncompressing the kernel, so it transfers
+ * control to a higher address that won't be overwritten.
+ *
+ * Copyright (C) Serge Vakulenko
+ */
+ .text
+head:
+ jmp tail
diff --git a/sys/pc98/boot/kzipboot/malloc.c b/sys/pc98/boot/kzipboot/malloc.c
new file mode 100644
index 0000000..cf51be7
--- /dev/null
+++ b/sys/pc98/boot/kzipboot/malloc.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1983, 1993
+ * 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.
+ */
+
+#include <sys/types.h>
+
+extern unsigned char *storage;
+
+void *
+malloc(nbytes, junk1, junk2) /* junk? not used */
+ size_t nbytes;
+ int junk1, junk2;
+{
+ unsigned char *p = storage;
+ storage += nbytes;
+ if (storage >= (unsigned char *) 0xa0000) {
+ putstr("warning: malloc wrapped\n");
+ p = (unsigned char *) 0x50000;
+ storage = p + nbytes;
+ }
+ return p;
+}
+
+void
+free(cp, junk) /* junk not used */
+ void *cp;
+ int junk;
+{
+}
diff --git a/sys/pc98/boot/kzipboot/misc.c b/sys/pc98/boot/kzipboot/misc.c
new file mode 100644
index 0000000..a971aee
--- /dev/null
+++ b/sys/pc98/boot/kzipboot/misc.c
@@ -0,0 +1,207 @@
+/*
+ * misc.c
+ *
+ * This is a collection of several routines from gzip-1.0.3
+ * adapted for Linux.
+ *
+ * Ported to 386bsd by Serge Vakulenko
+ */
+
+#include "gzip.h"
+
+unsigned outcnt;
+unsigned insize;
+unsigned inptr;
+
+extern const char input_data[];
+extern const int input_len;
+
+int input_ptr;
+
+int method;
+
+char *output_data;
+ulong output_ptr;
+
+void makecrc (void);
+void putstr (char *c);
+void *memcpy (void *to, const void *from, unsigned len);
+int memcmp (const void *arg1, const void *arg2, unsigned len);
+
+ulong crc; /* shift register contents */
+ulong crc_32_tab[256]; /* crc table, defined below */
+
+/*
+ * Run a set of bytes through the crc shift register. If s is a NULL
+ * pointer, then initialize the crc shift register contents instead.
+ * Return the current crc in either case.
+ */
+void updcrc(s, n)
+uchar *s; /* pointer to bytes to pump through */
+unsigned n; /* number of bytes in s[] */
+{
+ while (n--)
+ crc = crc_32_tab[(uchar)crc ^ (*s++)] ^ (crc >> 8);
+}
+
+/*
+ * Clear input and output buffers
+ */
+void clear_bufs()
+{
+ outcnt = 0;
+ insize = inptr = 0;
+}
+
+/*
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
+ */
+void fill_inbuf ()
+{
+ int len, i;
+
+ /* Read as much as possible */
+ insize = 0;
+ do {
+ len = INBUFSIZ - insize;
+ if (len > input_len - input_ptr + 1)
+ len = input_len-input_ptr+1;
+ if (len <= 0)
+ break;
+ for (i=0; i<len; i++)
+ inbuf[insize+i] = input_data[input_ptr+i];
+ insize += len;
+ input_ptr += len;
+ } while (insize < INBUFSIZ);
+ if (insize == 0)
+ error("unable to fill buffer");
+ inptr = 0;
+}
+
+/*
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+void flush_window()
+{
+ if (outcnt == 0) return;
+ updcrc(window, outcnt);
+
+ memcpy(&output_data[output_ptr], (char *)window, outcnt);
+
+ output_ptr += outcnt;
+ outcnt = 0;
+}
+
+/*
+ * Code to compute the CRC-32 table. Borrowed from
+ * gzip-1.0.3/makecrc.c.
+ * Not copyrighted 1990 Mark Adler
+ */
+void makecrc(void)
+{
+ ulong c; /* crc shift register */
+ ulong e; /* polynomial exclusive-or pattern */
+ int i; /* counter for all possible eight bit values */
+ int k; /* byte being shifted into crc apparatus */
+
+ /* terms of polynomial defining this crc (except x^32): */
+ static const uchar poly[] = { 0,1,2,4,5,7,8,10,11,12,16,22,23,26, };
+
+ /* Make exclusive-or pattern from polynomial */
+ e = 0;
+ for (i = 0; i < sizeof(poly)/sizeof(*poly); i++)
+ e |= 1L << (31 - poly[i]);
+
+ crc_32_tab[0] = 0;
+
+ for (i = 1; i < 256; i++) {
+ c = 0;
+ for (k = i | 256; k != 1; k >>= 1) {
+ c = c & 1 ? (c >> 1) ^ e : c >> 1;
+ if (k & 1)
+ c ^= e;
+ }
+ crc_32_tab[i] = c;
+ }
+}
+
+/*
+ * Check the magic number of the input file and update ofname if an
+ * original name was given and to_stdout is not set.
+ * Set inptr to the offset of the next byte to be processed.
+ */
+static void get_method()
+{
+ uchar flags;
+ char magic[2]; /* magic header */
+
+ magic[0] = get_byte();
+ magic[1] = get_byte();
+
+ method = -1; /* unknown yet */
+ extended = pkzip = 0;
+ /* assume multiple members in gzip file except for record oriented I/O */
+
+ if (memcmp(magic, GZIP_MAGIC, 2) == 0
+ || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) {
+ method = get_byte();
+ flags = get_byte();
+ if (flags & ENCRYPTED)
+ error("Input is encrypted");
+ if (flags & CONTINUATION)
+ error("Multi part input");
+ if (flags & RESERVED)
+ error("Input has invalid flags");
+
+ (void) get_byte(); /* Get timestamp */
+ (void) get_byte();
+ (void) get_byte();
+ (void) get_byte();
+
+ (void) get_byte(); /* Ignore extra flags for the moment */
+ (void) get_byte(); /* Ignore OS type for the moment */
+
+ if (flags & EXTRA_FIELD) {
+ unsigned len = get_byte();
+ len |= get_byte() << 8;
+ while (len--)
+ (void) get_byte();
+ }
+
+ /* Discard file comment if any */
+ if (flags & COMMENT)
+ while (get_byte())
+ continue;
+
+ } else if (memcmp(magic, PKZIP_MAGIC, 2) == 0 && inptr == 2
+ && memcmp(inbuf, PKZIP_MAGIC, 4) == 0) {
+ /*
+ * To simplify the code, we support a zip file when alone only.
+ * We are thus guaranteed that the entire local header fits in inbuf.
+ */
+ inptr = 0;
+ check_zipfile();
+
+ } else if (memcmp(magic, PACK_MAGIC, 2) == 0)
+ error("packed input");
+ else if (memcmp(magic, LZW_MAGIC, 2) == 0)
+ error("compressed input");
+ if (method == -1)
+ error("Corrupted input");
+}
+
+void
+decompress_kernel (void *dest)
+{
+ output_data = dest;
+ output_ptr = 0;
+
+ input_ptr = 0;
+
+ clear_bufs ();
+ makecrc ();
+ get_method ();
+ unzip ();
+}
diff --git a/sys/pc98/boot/kzipboot/tail.S b/sys/pc98/boot/kzipboot/tail.S
new file mode 100644
index 0000000..5dcee9c
--- /dev/null
+++ b/sys/pc98/boot/kzipboot/tail.S
@@ -0,0 +1,12 @@
+/*
+ * Boot unpacker startup routine.
+ * Copyright (C) Serge Vakulenko
+ */
+ .text
+ .globl tail
+tail:
+ cli # disable interrupts
+ pushl 4(%esp) # pass howto arg
+ call _boot # unpack the kernel image
+ popl %eax # discard howto arg
+ ljmp $CSEG, $KADDR # jump to unpacked kernel
diff --git a/sys/pc98/boot/kzipboot/unzip.c b/sys/pc98/boot/kzipboot/unzip.c
new file mode 100644
index 0000000..93a2c8b
--- /dev/null
+++ b/sys/pc98/boot/kzipboot/unzip.c
@@ -0,0 +1,155 @@
+/*
+ * unzip.c -- decompress files in gzip or pkzip format.
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ *
+ * Adapted for Linux booting by Hannu Savolainen 1993
+ * Adapted for FreeBSD booting by Serge Vakulenko
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ *
+ * The code in this file is derived from the file funzip.c written
+ * and put in the public domain by Mark Adler.
+ */
+
+/*
+ * This version can extract files in gzip or pkzip format.
+ * For the latter, only the first entry is extracted, and it has to be
+ * either deflated or stored.
+ */
+
+#include "gzip.h"
+
+#include <sys/types.h>
+#include <sys/inflate.h>
+
+/* PKZIP header definitions */
+#define LOCSIG 0x04034b50L /* four-byte lead-in (lsb first) */
+#define LOCFLG 6 /* offset of bit flag */
+#define CRPFLG 1 /* bit for encrypted entry */
+#define EXTFLG 8 /* bit for extended local header */
+#define LOCHOW 8 /* offset of compression method */
+#define LOCTIM 10 /* file mod time (for decryption) */
+#define LOCCRC 14 /* offset of crc */
+#define LOCSIZ 18 /* offset of compressed size */
+#define LOCLEN 22 /* offset of uncompressed length */
+#define LOCFIL 26 /* offset of file name field length */
+#define LOCEXT 28 /* offset of extra field length */
+#define LOCHDR 30 /* size of local header, including sig */
+#define EXTHDR 16 /* size of extended local header, inc sig */
+
+int pkzip; /* set for a pkzip file */
+int extended; /* set if extended local header */
+
+/* Macros for getting two-byte and four-byte header values */
+#define SH(p) ((ushort)(uchar)((p)[0]) | ((ushort)(uchar)((p)[1]) << 8))
+#define LG(p) ((ulong)(SH(p)) | ((ulong)(SH((p)+2)) << 16))
+
+/*
+ * Check zip file and advance inptr to the start of the compressed data.
+ * Get ofname from the local header if necessary.
+ */
+void check_zipfile()
+{
+ uchar *h = inbuf + inptr; /* first local header */
+
+ /* Check validity of local header, and skip name and extra fields */
+ inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT);
+
+ if (inptr > insize || LG(h) != LOCSIG)
+ error("input not a zip");
+
+ method = h[LOCHOW];
+ if (method != STORED && method != DEFLATED)
+ error("first entry not deflated or stored--can't extract");
+
+ /* If entry encrypted, decrypt and validate encryption header */
+ if (h[LOCFLG] & CRPFLG)
+ error("encrypted file");
+
+ /* Save flags for unzip() */
+ extended = (h[LOCFLG] & EXTFLG) != 0;
+ pkzip = 1;
+}
+
+int
+Flush (void *nu, u_char *buf, u_long cnt)
+{
+ outcnt = cnt;
+ flush_window();
+ return 0;
+}
+
+int
+NextByte (void *nu)
+{
+ return ((int) get_byte ());
+}
+
+struct inflate infl; /* put it into the BSS */
+
+/*
+ * Unzip in to out. This routine works on both gzip and pkzip files.
+ *
+ * IN assertions: the buffer inbuf contains already the beginning of
+ * the compressed data, from offsets inptr to insize-1 included.
+ * The magic header has already been checked. The output buffer is cleared.
+ */
+
+void unzip()
+{
+ ulong orig_crc = 0; /* original crc */
+ ulong orig_len = 0; /* original uncompressed length */
+ uchar buf[EXTHDR]; /* extended local header */
+ int n, res;
+
+ crc = 0xffffffffL; /* initialize crc */
+
+ if (pkzip && !extended) { /* crc and length at the end otherwise */
+ orig_crc = LG(inbuf + LOCCRC);
+ orig_len = LG(inbuf + LOCLEN);
+ }
+
+ if (method != DEFLATED)
+ error("internal error, invalid method");
+ infl.gz_input = NextByte;
+ infl.gz_output = Flush;
+ infl.gz_slide = window;
+ res = inflate (&infl);
+ if (res == 3)
+ error("out of memory");
+ else if (res != 0)
+ error("invalid compressed format");
+
+ /* Get the crc and original length */
+ if (!pkzip) {
+ /* crc32 (see algorithm.doc)
+ * uncompressed input size modulo 2^32
+ */
+ for (n = 0; n < 8; n++)
+ buf[n] = get_byte(); /* may cause an error if EOF */
+ orig_crc = LG(buf);
+ orig_len = LG(buf+4);
+
+ } else if (extended) { /* If extended header, check it */
+ /* signature - 4bytes: 0x50 0x4b 0x07 0x08
+ * CRC-32 value
+ * compressed size 4-bytes
+ * uncompressed size 4-bytes
+ */
+ for (n = 0; n < EXTHDR; n++)
+ buf[n] = get_byte(); /* may cause an error if EOF */
+ orig_crc = LG(buf+4);
+ orig_len = LG(buf+12);
+ }
+
+ /* Validate decompression */
+ if (orig_crc != (crc ^ 0xffffffffL))
+ error("crc error");
+ if (orig_len != output_ptr)
+ error("length error");
+
+ /* Check if there are more entries in a pkzip file */
+ if (pkzip && inptr+4 < insize && LG(inbuf+inptr) == LOCSIG)
+ error("zip file has more than one entry");
+}
diff --git a/sys/pc98/boot/netboot/3c509.c b/sys/pc98/boot/netboot/3c509.c
new file mode 100644
index 0000000..93a2c8b
--- /dev/null
+++ b/sys/pc98/boot/netboot/3c509.c
@@ -0,0 +1,155 @@
+/*
+ * unzip.c -- decompress files in gzip or pkzip format.
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ *
+ * Adapted for Linux booting by Hannu Savolainen 1993
+ * Adapted for FreeBSD booting by Serge Vakulenko
+ *
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ *
+ * The code in this file is derived from the file funzip.c written
+ * and put in the public domain by Mark Adler.
+ */
+
+/*
+ * This version can extract files in gzip or pkzip format.
+ * For the latter, only the first entry is extracted, and it has to be
+ * either deflated or stored.
+ */
+
+#include "gzip.h"
+
+#include <sys/types.h>
+#include <sys/inflate.h>
+
+/* PKZIP header definitions */
+#define LOCSIG 0x04034b50L /* four-byte lead-in (lsb first) */
+#define LOCFLG 6 /* offset of bit flag */
+#define CRPFLG 1 /* bit for encrypted entry */
+#define EXTFLG 8 /* bit for extended local header */
+#define LOCHOW 8 /* offset of compression method */
+#define LOCTIM 10 /* file mod time (for decryption) */
+#define LOCCRC 14 /* offset of crc */
+#define LOCSIZ 18 /* offset of compressed size */
+#define LOCLEN 22 /* offset of uncompressed length */
+#define LOCFIL 26 /* offset of file name field length */
+#define LOCEXT 28 /* offset of extra field length */
+#define LOCHDR 30 /* size of local header, including sig */
+#define EXTHDR 16 /* size of extended local header, inc sig */
+
+int pkzip; /* set for a pkzip file */
+int extended; /* set if extended local header */
+
+/* Macros for getting two-byte and four-byte header values */
+#define SH(p) ((ushort)(uchar)((p)[0]) | ((ushort)(uchar)((p)[1]) << 8))
+#define LG(p) ((ulong)(SH(p)) | ((ulong)(SH((p)+2)) << 16))
+
+/*
+ * Check zip file and advance inptr to the start of the compressed data.
+ * Get ofname from the local header if necessary.
+ */
+void check_zipfile()
+{
+ uchar *h = inbuf + inptr; /* first local header */
+
+ /* Check validity of local header, and skip name and extra fields */
+ inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT);
+
+ if (inptr > insize || LG(h) != LOCSIG)
+ error("input not a zip");
+
+ method = h[LOCHOW];
+ if (method != STORED && method != DEFLATED)
+ error("first entry not deflated or stored--can't extract");
+
+ /* If entry encrypted, decrypt and validate encryption header */
+ if (h[LOCFLG] & CRPFLG)
+ error("encrypted file");
+
+ /* Save flags for unzip() */
+ extended = (h[LOCFLG] & EXTFLG) != 0;
+ pkzip = 1;
+}
+
+int
+Flush (void *nu, u_char *buf, u_long cnt)
+{
+ outcnt = cnt;
+ flush_window();
+ return 0;
+}
+
+int
+NextByte (void *nu)
+{
+ return ((int) get_byte ());
+}
+
+struct inflate infl; /* put it into the BSS */
+
+/*
+ * Unzip in to out. This routine works on both gzip and pkzip files.
+ *
+ * IN assertions: the buffer inbuf contains already the beginning of
+ * the compressed data, from offsets inptr to insize-1 included.
+ * The magic header has already been checked. The output buffer is cleared.
+ */
+
+void unzip()
+{
+ ulong orig_crc = 0; /* original crc */
+ ulong orig_len = 0; /* original uncompressed length */
+ uchar buf[EXTHDR]; /* extended local header */
+ int n, res;
+
+ crc = 0xffffffffL; /* initialize crc */
+
+ if (pkzip && !extended) { /* crc and length at the end otherwise */
+ orig_crc = LG(inbuf + LOCCRC);
+ orig_len = LG(inbuf + LOCLEN);
+ }
+
+ if (method != DEFLATED)
+ error("internal error, invalid method");
+ infl.gz_input = NextByte;
+ infl.gz_output = Flush;
+ infl.gz_slide = window;
+ res = inflate (&infl);
+ if (res == 3)
+ error("out of memory");
+ else if (res != 0)
+ error("invalid compressed format");
+
+ /* Get the crc and original length */
+ if (!pkzip) {
+ /* crc32 (see algorithm.doc)
+ * uncompressed input size modulo 2^32
+ */
+ for (n = 0; n < 8; n++)
+ buf[n] = get_byte(); /* may cause an error if EOF */
+ orig_crc = LG(buf);
+ orig_len = LG(buf+4);
+
+ } else if (extended) { /* If extended header, check it */
+ /* signature - 4bytes: 0x50 0x4b 0x07 0x08
+ * CRC-32 value
+ * compressed size 4-bytes
+ * uncompressed size 4-bytes
+ */
+ for (n = 0; n < EXTHDR; n++)
+ buf[n] = get_byte(); /* may cause an error if EOF */
+ orig_crc = LG(buf+4);
+ orig_len = LG(buf+12);
+ }
+
+ /* Validate decompression */
+ if (orig_crc != (crc ^ 0xffffffffL))
+ error("crc error");
+ if (orig_len != output_ptr)
+ error("length error");
+
+ /* Check if there are more entries in a pkzip file */
+ if (pkzip && inptr+4 < insize && LG(inbuf+inptr) == LOCSIG)
+ error("zip file has more than one entry");
+}
diff --git a/sys/pc98/boot/netboot/3c509.h b/sys/pc98/boot/netboot/3c509.h
new file mode 100644
index 0000000..8bfa368
--- /dev/null
+++ b/sys/pc98/boot/netboot/3c509.h
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) 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. The name
+ * of the author may not be used to endorse or promote products derived from
+ * this software withough 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.
+ *
+ * if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by:
+ *
+ October 2, 1994
+
+ Modified by: Andres Vega Garcia
+
+ INRIA - Sophia Antipolis, France
+ e-mail: avega@sophia.inria.fr
+ finger: avega@pax.inria.fr
+
+ */
+
+/*
+ * Ethernet software status per interface.
+ */
+/*
+ * Some global constants
+ */
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+
+#define TX_INIT_RATE 16
+#define TX_INIT_MAX_RATE 64
+#define RX_INIT_LATENCY 64
+#define RX_INIT_EARLY_THRESH 64
+#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */
+#define MIN_RX_EARLY_THRESHL 4
+
+#define EEPROMSIZE 0x40
+#define MAX_EEPROMBUSY 1000
+#define EP_LAST_TAG 0xd7
+#define EP_MAX_BOARDS 16
+#define EP_ID_PORT 0x100
+
+/*
+ * some macros to acces long named fields
+ */
+#define IS_BASE (eth_base)
+#define BASE (eth_base)
+
+/*
+ * Commands to read/write EEPROM trough EEPROM command register (Window 0,
+ * Offset 0xa)
+ */
+#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
+#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
+#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
+#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
+
+#define EEPROM_BUSY (1<<15)
+#define EEPROM_TST_MODE (1<<14)
+
+/*
+ * Some short functions, worth to let them be a macro
+ */
+#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY)
+#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|(x))
+
+/**************************************************************************
+ * *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existance of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+
+/**************************************************************************
+ * *
+ * These are the registers for the 3Com 3c509 and their bit patterns when *
+ * applicable. They have been taken out the the "EtherLink III Parallel *
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
+ * from 3com. *
+ * *
+ **************************************************************************/
+
+#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a
+ * command reg. */
+#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status
+ * reg. */
+#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window
+ * reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+/* Write */
+#define EP_W0_EEPROM_DATA 0x0c
+#define EP_W0_EEPROM_COMMAND 0x0a
+#define EP_W0_RESOURCE_CFG 0x08
+#define EP_W0_ADDRESS_CFG 0x06
+#define EP_W0_CONFIG_CTRL 0x04
+/* Read */
+#define EP_W0_PRODUCT_ID 0x02
+#define EP_W0_MFG_ID 0x00
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+/* Write */
+#define EP_W1_TX_PIO_WR_2 0x02
+#define EP_W1_TX_PIO_WR_1 0x00
+/* Read */
+#define EP_W1_FREE_TX 0x0c
+#define EP_W1_TX_STATUS 0x0b /* byte */
+#define EP_W1_TIMER 0x0a /* byte */
+#define EP_W1_RX_STATUS 0x08
+#define EP_W1_RX_PIO_RD_2 0x02
+#define EP_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+/* Read/Write */
+#define EP_W2_ADDR_5 0x05
+#define EP_W2_ADDR_4 0x04
+#define EP_W2_ADDR_3 0x03
+#define EP_W2_ADDR_2 0x02
+#define EP_W2_ADDR_1 0x01
+#define EP_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+/* Read */
+#define EP_W3_FREE_TX 0x0c
+#define EP_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+/* Read/Write */
+#define EP_W4_MEDIA_TYPE 0x0a
+#define EP_W4_CTRLR_STATUS 0x08
+#define EP_W4_NET_DIAG 0x06
+#define EP_W4_FIFO_DIAG 0x04
+#define EP_W4_HOST_DIAG 0x02
+#define EP_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+/* Read */
+#define EP_W5_READ_0_MASK 0x0c
+#define EP_W5_INTR_MASK 0x0a
+#define EP_W5_RX_FILTER 0x08
+#define EP_W5_RX_EARLY_THRESH 0x06
+#define EP_W5_TX_AVAIL_THRESH 0x02
+#define EP_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+/* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms
+ * after issuing */
+#define WINDOW_SELECT (u_short) (0x1<<11)
+#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to
+ * determine whether
+ * this is needed. If
+ * so; wait 800 uSec
+ * before using trans-
+ * ceiver. */
+#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on
+ * power-up */
+#define RX_ENABLE (u_short) (0x4<<11)
+#define RX_RESET (u_short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11)
+#define TX_ENABLE (u_short) (0x9<<11)
+#define TX_DISABLE (u_short) (0xa<<11)
+#define TX_RESET (u_short) (0xb<<11)
+#define REQ_INTR (u_short) (0xc<<11)
+#define SET_INTR_MASK (u_short) (0xe<<11)
+#define SET_RD_0_MASK (u_short) (0xf<<11)
+#define SET_RX_FILTER (u_short) (0x10<<11)
+#define FIL_INDIVIDUAL (u_short) (0x1)
+#define FIL_GROUP (u_short) (0x2)
+#define FIL_BRDCST (u_short) (0x4)
+#define FIL_ALL (u_short) (0x8)
+#define SET_RX_EARLY_THRESH (u_short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11)
+#define SET_TX_START_THRESH (u_short) (0x13<<11)
+#define STATS_ENABLE (u_short) (0x15<<11)
+#define STATS_DISABLE (u_short) (0x16<<11)
+#define STOP_TRANSCEIVER (u_short) (0x17<<11)
+/*
+ * The following C_* acknowledge the various interrupts. Some of them don't
+ * do anything. See the manual.
+ */
+#define ACK_INTR (u_short) (0x6800)
+#define C_INTR_LATCH (u_short) (ACK_INTR|0x1)
+#define C_CARD_FAILURE (u_short) (ACK_INTR|0x2)
+#define C_TX_COMPLETE (u_short) (ACK_INTR|0x4)
+#define C_TX_AVAIL (u_short) (ACK_INTR|0x8)
+#define C_RX_COMPLETE (u_short) (ACK_INTR|0x10)
+#define C_RX_EARLY (u_short) (ACK_INTR|0x20)
+#define C_INT_RQD (u_short) (ACK_INTR|0x40)
+#define C_UPD_STATS (u_short) (ACK_INTR|0x80)
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (u_short) (0x1)
+#define S_CARD_FAILURE (u_short) (0x2)
+#define S_TX_COMPLETE (u_short) (0x4)
+#define S_TX_AVAIL (u_short) (0x8)
+#define S_RX_COMPLETE (u_short) (0x10)
+#define S_RX_EARLY (u_short) (0x20)
+#define S_INT_RQD (u_short) (0x40)
+#define S_UPD_STATS (u_short) (0x80)
+#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\
+ S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY)
+#define S_COMMAND_IN_PROGRESS (u_short) (0x1000)
+
+/*
+ * FIFO Registers.
+ * RX Status. Window 1/Port 08
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_RX_INCOMPLETE (u_short) (0x1<<15)
+#define ERR_RX (u_short) (0x1<<14)
+#define ERR_RX_OVERRUN (u_short) (0x8<<11)
+#define ERR_RX_RUN_PKT (u_short) (0xb<<11)
+#define ERR_RX_ALIGN (u_short) (0xc<<11)
+#define ERR_RX_CRC (u_short) (0xd<<11)
+#define ERR_RX_OVERSIZE (u_short) (0x9<<11)
+#define ERR_RX_DRIBBLE (u_short) (0x2<<11)
+
+/*
+ * FIFO Registers.
+ * TX Status. Window 1/Port 0B
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_SUCCES_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+/*
+ * Configuration control register.
+ * Window 0/Port 04
+ */
+/* Read */
+#define IS_AUI (1<<13)
+#define IS_BNC (1<<12)
+#define IS_UTP (1<<9)
+/* Write */
+#define ENABLE_DRQ_IRQ 0x0001
+#define W0_P4_CMD_RESET_ADAPTER 0x4
+#define W0_P4_CMD_ENABLE_ADAPTER 0x1
+/*
+ * Media type and status.
+ * Window 4/Port 0A
+ */
+#define ENABLE_UTP 0xc0
+#define DISABLE_UTP 0x0
+
+/*
+ * Resource control register
+ */
+
+#define SET_IRQ(i) ( ((i)<<12) | 0xF00) /* set IRQ i */
+
+/*
+ * Receive status register
+ */
+
+#define RX_BYTES_MASK (u_short) (0x07ff)
+#define RX_ERROR 0x4000
+#define RX_INCOMPLETE 0x8000
+
+
+/*
+ * Misc defines for various things.
+ */
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */
+#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */
+#define PROD_ID 0x9150
+
+#define AUI 0x1
+#define BNC 0x2
+#define UTP 0x4
+
+#define ETHER_ADDR_LEN 6
+#define ETHER_MAX 1536
+#define RX_BYTES_MASK (u_short) (0x07ff)
+
+ /* EISA support */
+#define EP_EISA_START 0x1000
+#define EP_EISA_W0 0x0c80
diff --git a/sys/pc98/boot/netboot/Makefile b/sys/pc98/boot/netboot/Makefile
new file mode 100644
index 0000000..340df03
--- /dev/null
+++ b/sys/pc98/boot/netboot/Makefile
@@ -0,0 +1,96 @@
+# Makefile,v 1.4 1994/12/31 17:16:49 jkh Exp
+#
+# Makefile for NETBOOT
+#
+# Basic Options:
+# -DASK_BOOT - Ask "Boot from Network (Y/N) ?" at startup
+# -DROMSIZE - Size of EPROM - Must be set (even for .COM files)
+# -DRELOC - Relocation address (usually 0x90000)
+#
+# NS8390 Options:
+# -DINCLUDE_WD - Include Western Digital/SMC support
+# -DINCLUDE_NE - Include NE1000/NE2000 support
+# -DINCLUDE_3COM - Include 3c503 support
+# -D_3COM_USE_AUI - Disable transceiver on 3c503 by default
+# -DNE_BASE - Base I/O address for NE1000/NE2000
+# -D_3COM_BASE - Base I/O address for 3c503
+# -DWD_DEFAULT_MEM- Default memory location for WD/SMC cards
+# -DINCLUDE_EGY - Include MELCO EGY support
+# -DINCLUDE_LGY - Include MELCO LGY support
+# -DINCLUDE_ICM - Include IF-2766 support
+# -DINCLUDE_SIC - Include SIC supoort
+# -DED_BASE - Base I/O address for NE1000/NE2000
+# -DSIC_DEFAULT_MEM- Default memory location for WD/SMC cards
+# XXX nothing depends on this Makefile so you must run `make clean' after
+# changing an option.
+#
+
+### options for PCI cards
+###
+#PCI_VENDOR=0x10ec
+#PCI_DEVICE=0x8029
+#PCI_CLASS=0x02,0x00,0x00
+
+PROG= nb8390.com nb8390.rom
+# Order is very important on the SRCS line for this prog
+SRCS= start2.S main.c misc.c bootmenu.c rpc.c
+
+BINDIR= /usr/mdec
+BINMODE= 555
+#CFLAGS= -O2 -DNFS -DROMSIZE=${ROMSIZE} -DRELOC=${RELOCADDR} -DASK_BOOT
+CFLAGS= -O2 -DNFS -DROMSIZE=${ROMSIZE} -DRELOC=${RELOCADDR} # -DASK_BOOT
+CFLAGS+= -DPC98
+#CFLAGS += -DPCI -DPCI_VENDOR=${PCI_VENDOR} -DPCI_DEVICE=${PCI_DEVICE}
+#CFLAGS += -DPCI_CLASS=${PCI_CLASS} -DASK_BOOT
+#NS8390= -DINCLUDE_WD -DWD_DEFAULT_MEM=0xD0000
+#NS8390= -DINCLUDE_WD -DWD_DEFAULT_MEM=0xD0000
+#NS8390= -DINCLUDE_NE
+#NS8390+= -DINCLUDE_3COM -D_3COM_BASE=0x300
+NS8390= -DINCLUDE_LGY -DNE_BASE=0xd0
+CLEANFILES+= netboot.com
+CLEANFILES+= makerom start2.ro 3c509.o ns8390.o
+LDFLAGS+= -N -T ${RELOCADDR} -e _start -nostdlib
+NOSHARED= YES
+NOMAN=
+STRIP=
+
+ROMSIZE=16384
+RELOCADDR=0x90000
+
+.SUFFIXES: .ro
+
+.S.ro:
+ ${CC} ${CFLAGS} -DBOOTROM -o ${.TARGET} -c ${.IMPSRC}
+
+ns8390.o: ns8390.c
+ ${CC} $(CFLAGS) $(NS8390) -o ${.TARGET} -c $<
+
+makerom: makerom.c
+ ${CC} -o ${.TARGET} -DROMSIZE=${ROMSIZE} ${.CURDIR}/makerom.c
+
+nb8390.rom: makerom start2.ro ${SRCS:N*.h:R:S/$/.o/g} ns8390.o
+ ${LD} ${LDFLAGS} -o ${.TARGET} ${OBJS:S/start2.o/start2.ro/} ns8390.o
+ strip ${.TARGET}
+ size ${.TARGET}
+ ${.OBJDIR}/makerom ${.TARGET}
+
+nb3c509.rom: makerom start2.ro ${SRCS:N*.h:R:S/$/.o/g} 3c509.o
+ ${LD} ${LDFLAGS} -o ${.TARGET} ${OBJS:S/start2.o/start2.ro/} 3c509.o
+ strip ${.TARGET}
+ size ${.TARGET}
+ ${.OBJDIR}/makerom ${.TARGET}
+
+nb8390.com: makerom start2.ro ${SRCS:N*.h:R:S/$/.o/g} ns8390.o
+ ${LD} ${LDFLAGS} -o netboot.com ${OBJS} ns8390.o
+ strip netboot.com
+ size netboot.com
+ dd ibs=32 skip=1 if=netboot.com of=${.TARGET}
+
+nb3c509.com: start2.o ${SRCS:N*.h:R:S/$/.o/g} 3c509.o
+ ${LD} ${LDFLAGS} -o netboot.com ${OBJS} 3c509.o
+ strip netboot.com
+ size netboot.com
+ dd ibs=32 skip=1 if=netboot.com of=${.TARGET}
+
+
+.include <bsd.prog.mk>
diff --git a/sys/pc98/boot/netboot/bootmenu.c b/sys/pc98/boot/netboot/bootmenu.c
new file mode 100644
index 0000000..a67ac93
--- /dev/null
+++ b/sys/pc98/boot/netboot/bootmenu.c
@@ -0,0 +1,346 @@
+/**************************************************************************
+NETBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: Dec/93
+
+**************************************************************************/
+#include "netboot.h"
+
+extern struct nfs_diskless nfsdiskless;
+extern int hostnamelen;
+extern unsigned long netmask;
+extern eth_reset();
+extern short aui;
+
+int cmd_ip(), cmd_server(), cmd_kernel(), cmd_help(), exit();
+int cmd_rootfs(), cmd_swapfs(), cmd_interface(), cmd_hostname();
+int cmd_netmask(), cmd_swapsize(), cmd_swapopts(), cmd_rootopts();
+int cmd_aui(), cmd_gateway();
+
+struct bootcmds_t {
+ char *name;
+ int (*func)();
+ char *help;
+} bootcmds[] = {
+ {"?", cmd_help, " this list"},
+ {"help", cmd_help, " this list"},
+ {"ip", cmd_ip, "<addr> set my IP addr"},
+ {"server", cmd_server, "<addr> set TFTP server IP addr"},
+ {"gateway", cmd_gateway, "<addr> set default router"},
+ {"netmask", cmd_netmask, "<addr> set network mask"},
+ {"hostname", cmd_hostname, "<name> set hostname"},
+ {"kernel", cmd_kernel, "<file> set boot filename"},
+ {"rootfs", cmd_rootfs, "ip:/fs set root filesystem"},
+ {"swapfs", cmd_swapfs, "ip:/fs set swap filesystem"},
+ {"swapsize", cmd_swapsize, "<nblks> set swap size"},
+ {"swapopts", cmd_swapopts, "<options> swap mount options"},
+ {"rootopts", cmd_rootopts, "<options> root mount options"},
+ {"diskboot", exit, " boot from disk"},
+ {"autoboot", NULL, " continue"},
+ {"trans", cmd_aui, "<on|off> turn transceiver on|off"},
+ {NULL, NULL, NULL}
+};
+
+/**************************************************************************
+CMD_HELP - Display help screen
+**************************************************************************/
+cmd_help()
+{
+ struct bootcmds_t *cmd = bootcmds;
+ printf("\r\n");
+ while (cmd->name) {
+ printf("%s %s\n\r",cmd->name,cmd->help);
+ cmd++;
+ }
+}
+
+/**************************************************************************
+CMD_IP - Set my IP address
+**************************************************************************/
+cmd_ip(p)
+ char *p;
+{
+ int i;
+ if (!setip(p, &arptable[ARP_CLIENT].ipaddr)) {
+ printf("IP address is %I\r\n",
+ arptable[ARP_CLIENT].ipaddr);
+ } else default_netmask();
+}
+
+/**************************************************************************
+CMD_AUI - Turn on-board transceiver on or off
+**************************************************************************/
+cmd_aui(p)
+ char *p;
+{
+ if (*(p+1) == 'f') {
+ aui = 1;
+ eth_reset();
+ return(0);
+ }
+ if (*(p+1) == 'n') {
+ aui = 0;
+ eth_reset();
+ return(0);
+ }
+ printf ("Transceiver is %s\r\n",aui ? "off" : "on");
+}
+
+/**************************************************************************
+CMD_GATEWAY - Set routers IP address
+**************************************************************************/
+cmd_gateway(p)
+ char *p;
+{
+ int i;
+ if (!setip(p, &arptable[ARP_GATEWAY].ipaddr)) {
+ printf("Server IP address is %I\r\n",
+ arptable[ARP_GATEWAY].ipaddr);
+ } else /* Need to clear arp entry if we change IP address */
+ for (i=0; i<6; i++) arptable[ARP_GATEWAY].node[i] = 0;
+}
+
+/**************************************************************************
+CMD_SERVER - Set server's IP address
+**************************************************************************/
+cmd_server(p)
+ char *p;
+{
+ int i;
+ if (!setip(p, &arptable[ARP_SERVER].ipaddr)) {
+ printf("Server IP address is %I\r\n",
+ arptable[ARP_SERVER].ipaddr);
+ } else /* Need to clear arp entry if we change IP address */
+ for (i=0; i<6; i++) arptable[ARP_SERVER].node[i] = 0;
+}
+
+/**************************************************************************
+CMD_NETMASK - Set network mask
+**************************************************************************/
+cmd_netmask(p)
+ char *p;
+{
+ int i;
+ if (!setip(p, &netmask)) {
+ netmask = ntohl(netmask);
+ printf("netmask is %I\r\n", netmask);
+ }
+ netmask = htonl(netmask);
+}
+
+/**************************************************************************
+CMD_SWAPSIZE - Set number of blocks for swap
+**************************************************************************/
+cmd_swapsize(p)
+ char *p;
+{
+ int blks = getdec(&p);
+ if (blks > 0) nfsdiskless.swap_nblks = blks;
+ else printf("Swap size is: %d blocks\r\n",nfsdiskless.swap_nblks);
+}
+
+extern char kernel_buf[], *kernel;
+/**************************************************************************
+CMD_KERNEL - set kernel filename
+**************************************************************************/
+cmd_kernel(p)
+ char *p;
+{
+ if (*p) sprintf(kernel = kernel_buf,"%s",p);
+ printf("Bootfile is: %s\r\n", kernel);
+}
+
+
+/**************************************************************************
+CMD_ROOTFS - Set root filesystem name
+**************************************************************************/
+cmd_rootfs(p)
+ char *p;
+{
+ if (!setip(p, &arptable[ARP_ROOTSERVER].ipaddr)) {
+ printf("Root filesystem is %I:%s\r\n",
+ nfsdiskless.root_saddr.sin_addr,
+ nfsdiskless.root_hostnam);
+ } else {
+ bcopy(&arptable[ARP_ROOTSERVER].ipaddr,
+ &nfsdiskless.root_saddr.sin_addr, 4);
+ while (*p && (*p != ':')) p++;
+ if (*p == ':') p++;
+ sprintf(&nfsdiskless.root_hostnam, "%s", p);
+ }
+}
+
+/**************************************************************************
+CMD_SWAPFS - Set swap filesystem name
+**************************************************************************/
+cmd_swapfs(p)
+ char *p;
+{
+ if (!setip(p, &arptable[ARP_SWAPSERVER].ipaddr)) {
+ printf("Swap filesystem is %I:%s\r\n",
+ nfsdiskless.swap_saddr.sin_addr,
+ nfsdiskless.swap_hostnam);
+ } else {
+ bcopy(&arptable[ARP_SWAPSERVER].ipaddr,
+ &nfsdiskless.swap_saddr.sin_addr, 4);
+ while (*p && (*p != ':')) p++;
+ if (*p == ':') p++;
+ sprintf(&nfsdiskless.swap_hostnam, "%s", p);
+ }
+}
+
+/**************************************************************************
+CMD_HOSTNAME - Set my hostname
+**************************************************************************/
+cmd_hostname(p)
+ char *p;
+{
+ if (*p)
+ hostnamelen = ((sprintf(&nfsdiskless.my_hostnam,"%s",p) -
+ (char*)&nfsdiskless.my_hostnam) + 3) & ~3;
+ else printf("Hostname is: %s\r\n",nfsdiskless.my_hostnam);
+}
+/**************************************************************************
+CMD_ROOTOPTS - Set root mount options
+**************************************************************************/
+cmd_rootopts(p)
+ char *p;
+{
+ char *tmp;
+
+ if (*p) {
+ nfsdiskless.root_args.flags = NFSMNT_RSIZE | NFSMNT_WSIZE;
+ nfsdiskless.root_args.sotype = SOCK_DGRAM;
+ if ((tmp = (char *)substr(p,"rsize=")))
+ nfsdiskless.root_args.rsize=getdec(&tmp);
+ if ((tmp = (char *)substr(p,"wsize=")))
+ nfsdiskless.root_args.wsize=getdec(&tmp);
+ if ((tmp = (char *)substr(p,"resvport")))
+ nfsdiskless.root_args.flags |= NFSMNT_RESVPORT;
+ if ((tmp = (char *)substr(p,"intr")))
+ nfsdiskless.root_args.flags |= NFSMNT_INT;
+ if ((tmp = (char *)substr(p,"soft")))
+ nfsdiskless.root_args.flags |= NFSMNT_SOFT;
+ if ((tmp = (char *)substr(p, "tcp")))
+ nfsdiskless.root_args.sotype = SOCK_STREAM;
+ } else {
+ printf("Rootfs mount options: rsize=%d,wsize=%d",
+ nfsdiskless.root_args.rsize,
+ nfsdiskless.root_args.wsize);
+ if (nfsdiskless.root_args.flags & NFSMNT_RESVPORT)
+ printf (",resvport");
+ if (nfsdiskless.root_args.flags & NFSMNT_SOFT)
+ printf (",soft");
+ if (nfsdiskless.root_args.flags & NFSMNT_INT)
+ printf (",intr");
+ if (nfsdiskless.root_args.sotype == SOCK_STREAM)
+ printf (",tcp");
+ else
+ printf (",udp");
+ printf ("\r\n");
+ }
+}
+
+/**************************************************************************
+CMD_SWAPOPTS - Set swap mount options
+**************************************************************************/
+cmd_swapopts(p)
+ char *p;
+{
+ char *tmp;
+
+ if (*p) {
+ nfsdiskless.swap_args.flags = NFSMNT_RSIZE | NFSMNT_WSIZE;
+ nfsdiskless.swap_args.sotype = SOCK_DGRAM;
+ if ((tmp = (char *)substr(p,"rsize=")))
+ nfsdiskless.swap_args.rsize=getdec(&tmp);
+ if ((tmp = (char *)substr(p,"wsize=")))
+ nfsdiskless.swap_args.wsize=getdec(&tmp);
+ if ((tmp = (char *)substr(p,"resvport")))
+ nfsdiskless.swap_args.flags |= NFSMNT_RESVPORT;
+ if ((tmp = (char *)substr(p,"intr")))
+ nfsdiskless.swap_args.flags |= NFSMNT_INT;
+ if ((tmp = (char *)substr(p,"soft")))
+ nfsdiskless.swap_args.flags |= NFSMNT_SOFT;
+ if ((tmp = (char *)substr(p, "tcp")))
+ nfsdiskless.swap_args.sotype = SOCK_STREAM;
+ } else {
+ printf("Swapfs mount options: rsize=%d,wsize=%d",
+ nfsdiskless.swap_args.rsize,
+ nfsdiskless.swap_args.wsize);
+ if (nfsdiskless.swap_args.flags & NFSMNT_RESVPORT)
+ printf (",resvport");
+ if (nfsdiskless.swap_args.flags & NFSMNT_SOFT)
+ printf (",soft");
+ if (nfsdiskless.swap_args.flags & NFSMNT_INT)
+ printf (",intr");
+ if (nfsdiskless.swap_args.sotype == SOCK_STREAM)
+ printf (",tcp");
+ else
+ printf (",udp");
+ printf ("\r\n");
+ }
+}
+
+/**************************************************************************
+EXECUTE - Decode command
+**************************************************************************/
+execute(buf)
+ char *buf;
+{
+ char *p, *q;
+ struct bootcmds_t *cmd = bootcmds;
+ while (*buf == ' ' || *buf == '\t')
+ buf++;
+ if ((!(*buf)) || (*buf == '#'))
+ return(0);
+ while(cmd->name) {
+ p = buf;
+ q = cmd->name;
+ while (*q && (*(q++) == *(p++))) ;
+ if ((!(*q)) && ((*p == ' ') || (*p == '\t') || (!(*p)))) {
+ if (!cmd->func)
+ return(1);
+ while (*p == ' ' || *p == '\t')
+ p++;
+ (cmd->func)(p);
+ return(0);
+ } else
+ cmd++;
+ }
+ printf("bad command - type 'help' for list\n\r");
+ return(0);
+}
+
+/**************************************************************************
+BOOTMENU - Present boot options
+**************************************************************************/
+bootmenu()
+{
+ char cmd[80];
+ int ptr, c;
+ printf("\r\n");
+ while (1) {
+ ptr = 0;
+ printf("boot> ");
+ while (ptr < 80) {
+ c = getchar();
+ if (c == '\r')
+ break;
+ else if (c == '\b') {
+ if (ptr > 0) {
+ ptr--;
+ printf("\b \b");
+ }
+ } else {
+ cmd[ptr++] = c;
+ putchar(c);
+ }
+ }
+ cmd[ptr] = 0;
+ printf("\r\n");
+ if (execute(cmd)) break;
+ }
+ eth_reset();
+}
diff --git a/sys/pc98/boot/netboot/ether.c b/sys/pc98/boot/netboot/ether.c
new file mode 100644
index 0000000..8a2ee0e
--- /dev/null
+++ b/sys/pc98/boot/netboot/ether.c
@@ -0,0 +1,572 @@
+
+/**************************************************************************
+NETBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters.
+ Date: Mar 22 1995
+
+ This code is based heavily on David Greenman's if_ed.c driver and
+ Andres Vega Garcia's if_ep.c driver.
+
+ Copyright (C) 1993-1994, David Greenman, Martin Renters.
+ Copyright (C) 1993-1995, Andres Vega Garcia.
+ Copyright (C) 1995, Serge Babkin.
+ This software may be used, modified, copied, distributed, and sold, in
+ both source and binary form provided that the above copyright and these
+ terms are retained. Under no circumstances are the authors responsible for
+ the proper functioning of this software, nor do the authors assume any
+ responsibility for damages incurred with its use.
+
+3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
+SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
+3c509 support added by Serge Babkin (babkin@hq.icb.chel.su) on 03/22/95
+
+***************************************************************************/
+
+/* #define EDEBUG */
+
+#include "netboot.h"
+#include "ether.h"
+
+extern short aui;
+char bnc=0, utp=0; /* for 3C509 */
+unsigned short eth_nic_base;
+unsigned short eth_asic_base;
+unsigned short eth_base;
+unsigned char eth_tx_start;
+unsigned char eth_laar;
+unsigned char eth_flags;
+unsigned char eth_vendor;
+unsigned char eth_memsize;
+unsigned char *eth_bmem;
+unsigned char *eth_rmem;
+unsigned char *eth_node_addr;
+
+/**************************************************************************
+The following two variables are used externally
+***************************************************************************/
+char packet[ETH_MAX_PACKET];
+int packetlen;
+
+/*************************************************************************
+ETH_FILLNAME - Fill name of adapter in NFS structure
+**************************************************************************/
+
+eth_fillname(where)
+char *where;
+{
+ switch(eth_vendor) {
+ case VENDOR_3C509:
+ where[0]='e'; where[1]='p'; where[2]='0'; where[3]=0;
+ break;
+ case VENDOR_WD:
+ case VENDOR_NOVELL:
+ case VENDOR_3COM:
+ where[0]='e'; where[1]='d'; where[2]='0'; where[3]=0;
+ break;
+ default:
+ where[0]='?'; where[1]='?'; where[2]='?'; where[3]=0;
+ break;
+ }
+}
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+***************************************************************************/
+eth_probe()
+{
+ /* common variables */
+ int i;
+#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
+ /* varaibles for 8390 */
+ struct wd_board *brd;
+ char *name;
+ unsigned short chksum;
+ unsigned char c;
+#endif
+
+ eth_vendor = VENDOR_NONE;
+
+#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
+#ifdef INCLUDE_SIC
+ /******************************************************************
+ Search for WD/SMC cards
+ *******************************************************************/
+ for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE;
+ eth_asic_base += 0x20) {
+ chksum = 0;
+ for (i=8; i<16; i++)
+ chksum += inb(i+eth_asic_base);
+ if ((chksum & 0x00FF) == 0x00FF)
+ break;
+ }
+ if (eth_asic_base <= WD_HIGH_BASE) { /* We've found a board */
+ eth_vendor = VENDOR_WD;
+ eth_nic_base = eth_asic_base + WD_NIC_ADDR;
+ c = inb(eth_asic_base+WD_BID); /* Get board id */
+ for (brd = wd_boards; brd->name; brd++)
+ if (brd->id == c) break;
+ if (!brd->name) {
+ printf("\r\nUnknown Ethernet type %x\r\n", c);
+ return(0); /* Unknown type */
+ }
+ eth_flags = brd->flags;
+ eth_memsize = brd->memsize;
+ eth_tx_start = 0;
+ if ((c == TYPE_WD8013EP) &&
+ (inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) {
+ eth_flags = FLAG_16BIT;
+ eth_memsize = MEM_16384;
+ }
+ if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) {
+ eth_bmem = (char *)(0x80000 |
+ ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13));
+ } else
+ eth_bmem = (char *)WD_DEFAULT_MEM;
+ if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) {
+ (unsigned int) *(eth_bmem + 8192) = (unsigned int)0;
+ if ((unsigned int) *(eth_bmem + 8192)) {
+ brd += 2;
+ eth_memsize = brd->memsize;
+ }
+ }
+ outb(eth_asic_base + WD_MSR, 0x80); /* Reset */
+ printf("\r\n%s base 0x%x, memory 0x%X, addr ",
+ brd->name, eth_asic_base, eth_bmem);
+ for (i=0; i<6; i++) {
+ printf("%b",(int)(arptable[ARP_CLIENT].node[i] =
+ inb(i+eth_asic_base+WD_LAR)));
+ if (i < 5) printf (":");
+ }
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base+WD_MSR, WD_MSR_MENB);
+ outb(eth_asic_base+0x04, (inb(eth_asic_base+0x04) |
+ 0x80));
+ outb(eth_asic_base+0x0B,
+ (((unsigned)eth_bmem >> 13) & 0x0F) |
+ (((unsigned)eth_bmem >> 11) & 0x40) |
+ (inb(eth_asic_base+0x0B) & 0xB0));
+ outb(eth_asic_base+0x04, (inb(eth_asic_base+0x04) &
+ ~0x80));
+ } else {
+ outb(eth_asic_base+WD_MSR,
+ (((unsigned)eth_bmem >> 13) & 0x3F) | 0x40);
+ }
+ if (eth_flags & FLAG_16BIT) {
+ if (eth_flags & FLAG_790) {
+ eth_laar = inb(eth_asic_base + WD_LAAR);
+ outb(eth_asic_base + WD_LAAR, WD_LAAR_M16EN);
+ inb(0x84);
+ } else {
+ outb(eth_asic_base + WD_LAAR, (eth_laar =
+ WD_LAAR_M16EN | WD_LAAR_L16EN | 1));
+ }
+ }
+ printf("\r\n");
+
+ }
+#endif
+#ifdef defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM)
+ /******************************************************************
+ Search for EGY/LGY/IF-2766 if no SIC cards
+ *******************************************************************/
+ if (eth_vendor == VENDOR_NONE) {
+ char romdata[16], testbuf[32];
+ char test[] = "NE1000/2000 memory";
+ eth_bmem = (char *)0; /* No shared memory */
+ eth_asic_base = ED_BASE + ED_ASIC_OFFSET;
+ eth_nic_base = ED_BASE;
+ eth_vendor = VENDOR_NOVELL;
+ eth_flags = FLAG_PIO;
+ eth_memsize = MEM_16384;
+ eth_tx_start = 32;
+ c = inb(eth_asic_base + ED_RESET);
+ outb(eth_asic_base + ED_RESET, c);
+ inb(0x84);
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_STP |
+ D8390_COMMAND_RD2);
+ outb(eth_nic_base + D8390_P0_RCR, D8390_RCR_MON);
+ outb(eth_nic_base + D8390_P0_DCR, D8390_DCR_FT1 | D8390_DCR_LS);
+ outb(eth_nic_base + D8390_P0_PSTART, MEM_8192);
+ outb(eth_nic_base + D8390_P0_PSTOP, MEM_16384);
+ eth_pio_write(test, 8192, sizeof(test));
+ eth_pio_read(8192, testbuf, sizeof(test));
+ if (!bcompare(test, testbuf, sizeof(test))) {
+ eth_flags |= FLAG_16BIT;
+ eth_memsize = MEM_32768;
+ eth_tx_start = 64;
+ outb(eth_nic_base + D8390_P0_DCR, D8390_DCR_WTS |
+ D8390_DCR_FT1 | D8390_DCR_LS);
+ outb(eth_nic_base + D8390_P0_PSTART, MEM_16384);
+ outb(eth_nic_base + D8390_P0_PSTOP, MEM_32768);
+ eth_pio_write(test, 16384, sizeof(test));
+ eth_pio_read(16384, testbuf, sizeof(test));
+ if (!bcompare(testbuf, test, sizeof(test))) return (0);
+ }
+ eth_pio_read(0, romdata, 16);
+ printf("\r\nNE1000/NE2000 base 0x%x, addr ", eth_nic_base);
+ for (i=0; i<6; i++) {
+ printf("%b",(int)(arptable[ARP_CLIENT].node[i] = romdata[i
+ + ((eth_flags & FLAG_16BIT) ? i : 0)]));
+ if (i < 5) printf (":");
+ }
+ printf("\r\n");
+ }
+ if (eth_vendor == VENDOR_NONE)
+ goto no8390;
+
+ if (eth_vendor != VENDOR_3COM) eth_rmem = eth_bmem;
+ eth_node_addr = arptable[ARP_CLIENT].node;
+ eth_reset();
+ return(eth_vendor);
+#endif /* NE */
+no8390:
+#endif /*8390 */
+
+ return VENDOR_NONE;
+}
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+eth_reset()
+{
+ int s, i;
+
+#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
+
+ /**************************************************************
+ Reset cards based on 8390 chip
+ ****************************************************************/
+
+ if(eth_vendor!=VENDOR_WD && eth_vendor!=VENDOR_NOVELL
+ && eth_vendor!=VENDOR_3COM)
+ goto no8390;
+
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND,
+ D8390_COMMAND_PS0 | D8390_COMMAND_STP);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND,
+ D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STP);
+ if (eth_flags & FLAG_16BIT)
+ outb(eth_nic_base+D8390_P0_DCR, 0x49);
+ else
+ outb(eth_nic_base+D8390_P0_DCR, 0x48);
+ outb(eth_nic_base+D8390_P0_RBCR0, 0);
+ outb(eth_nic_base+D8390_P0_RBCR1, 0);
+ outb(eth_nic_base+D8390_P0_RCR, 4); /* allow broadcast frames */
+ outb(eth_nic_base+D8390_P0_TCR, 2);
+ outb(eth_nic_base+D8390_P0_TPSR, eth_tx_start);
+ outb(eth_nic_base+D8390_P0_PSTART, eth_tx_start + D8390_TXBUF_SIZE);
+ if (eth_flags & FLAG_790) outb(eth_nic_base + 0x09, 0);
+ outb(eth_nic_base+D8390_P0_PSTOP, eth_memsize);
+ outb(eth_nic_base+D8390_P0_BOUND, eth_tx_start + D8390_TXBUF_SIZE);
+ outb(eth_nic_base+D8390_P0_ISR, 0xFF);
+ outb(eth_nic_base+D8390_P0_IMR, 0);
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1 |
+ D8390_COMMAND_STP);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STP);
+ for (i=0; i<6; i++)
+ outb(eth_nic_base+D8390_P1_PAR0+i, eth_node_addr[i]);
+ for (i=0; i<6; i++)
+ outb(eth_nic_base+D8390_P1_MAR0+i, 0xFF);
+ outb(eth_nic_base+D8390_P1_CURR, eth_tx_start + D8390_TXBUF_SIZE+1);
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_STA);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STA);
+ outb(eth_nic_base+D8390_P0_ISR, 0xFF);
+ outb(eth_nic_base+D8390_P0_TCR, 0);
+ return(1);
+no8390:
+#endif /* 8390 */
+}
+
+/**************************************************************************
+ETH_TRANSMIT - Transmit a frame
+***************************************************************************/
+static const char padmap[] = {
+ 0, 3, 2, 1};
+
+eth_transmit(d,t,s,p)
+char *d; /* Destination */
+unsigned short t; /* Type */
+unsigned short s; /* size */
+char *p; /* Packet */
+{
+ register u_int len;
+ int pad;
+ int status;
+ unsigned char c;
+
+#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
+
+
+ if(eth_vendor!=VENDOR_WD && eth_vendor!=VENDOR_NOVELL
+ && eth_vendor!=VENDOR_3COM)
+ goto no8390;
+
+#ifdef INCLUDE_WD
+ if (eth_vendor == VENDOR_WD) { /* Memory interface */
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_asic_base + WD_LAAR, eth_laar | WD_LAAR_M16EN);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base + WD_MSR, WD_MSR_MENB);
+ inb(0x84);
+ }
+ inb(0x84);
+ bcopy(d, eth_bmem, 6); /* dst */
+ bcopy(eth_node_addr, eth_bmem+6, ETHER_ADDR_SIZE); /* src */
+ *(eth_bmem+12) = t>>8; /* type */
+ *(eth_bmem+13) = t;
+ bcopy(p, eth_bmem+14, s);
+ s += 14;
+ while (s < ETH_MIN_PACKET) *(eth_bmem+(s++)) = 0;
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base + WD_MSR, 0);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_asic_base + WD_LAAR, eth_laar & ~WD_LAAR_M16EN);
+ inb(0x84);
+ }
+ }
+#endif
+#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM)
+ if (eth_vendor == VENDOR_NOVELL) { /* Programmed I/O */
+ unsigned short type;
+ type = (t >> 8) | (t << 8);
+ eth_pio_write(d, eth_tx_start<<8, 6);
+ eth_pio_write(eth_node_addr, (eth_tx_start<<8)+6, 6);
+ eth_pio_write(&type, (eth_tx_start<<8)+12, 2);
+ eth_pio_write(p, (eth_tx_start<<8)+14, s);
+ s += 14;
+ if (s < ETH_MIN_PACKET) s = ETH_MIN_PACKET;
+ }
+#endif
+ twiddle();
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_STA);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STA);
+ outb(eth_nic_base+D8390_P0_TPSR, eth_tx_start);
+ outb(eth_nic_base+D8390_P0_TBCR0, s);
+ outb(eth_nic_base+D8390_P0_TBCR1, s>>8);
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_TXP | D8390_COMMAND_STA);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_TXP | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA);
+ return(0);
+
+no8390:
+#endif /* 8390 */
+}
+
+/**************************************************************************
+ETH_POLL - Wait for a frame
+***************************************************************************/
+eth_poll()
+{
+ /* common variables */
+ unsigned short type = 0;
+ unsigned short len;
+ /* variables for 3C509 */
+ /* variables for 8390 */
+
+#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
+ int ret = 0;
+ unsigned char bound,curr,rstat;
+ unsigned short pktoff;
+ unsigned char *p;
+ struct ringbuffer pkthdr;
+#endif
+#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
+
+ if(eth_vendor!=VENDOR_WD && eth_vendor!=VENDOR_NOVELL
+ && eth_vendor!=VENDOR_3COM)
+ goto no8390;
+
+ rstat = inb(eth_nic_base+D8390_P0_RSR);
+ if (rstat & D8390_RSTAT_OVER) {
+ eth_reset();
+ return(0);
+ }
+ if (!(rstat & D8390_RSTAT_PRX)) return(0);
+ bound = inb(eth_nic_base+D8390_P0_BOUND)+1;
+ if (bound == eth_memsize) bound = eth_tx_start + D8390_TXBUF_SIZE;
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1);
+ curr = inb(eth_nic_base+D8390_P1_CURR);
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0);
+ if (curr == eth_memsize) curr=eth_tx_start + D8390_TXBUF_SIZE;
+ if (curr == bound) return(0);
+ if (eth_vendor == VENDOR_WD) {
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_asic_base + WD_LAAR, eth_laar | WD_LAAR_M16EN);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base + WD_MSR, WD_MSR_MENB);
+ inb(0x84);
+ }
+ inb(0x84);
+ }
+ pktoff = (bound << 8);
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, &pkthdr, 4);
+ else
+ bcopy(eth_rmem + pktoff, &pkthdr, 4);
+ len = pkthdr.len - 4; /* sub CRC */
+ pktoff += 4;
+ if (len > 1514) len = 1514;
+ bound = pkthdr.bound; /* New bound ptr */
+ if ( (pkthdr.status & D8390_RSTAT_PRX) && (len > 14) && (len < 1518)) {
+ p = packet;
+ packetlen = len;
+ len = (eth_memsize << 8) - pktoff;
+ if (packetlen > len) { /* We have a wrap-around */
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, p, len);
+ else
+ bcopy(eth_rmem + pktoff, p, len);
+ pktoff = (eth_tx_start + D8390_TXBUF_SIZE) << 8;
+ p += len;
+ packetlen -= len;
+ }
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, p, packetlen);
+ else
+ bcopy(eth_rmem + pktoff, p, packetlen);
+
+ type = (packet[12]<<8) | packet[13];
+ ret = 1;
+ }
+ if (eth_vendor == VENDOR_WD) {
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base + WD_MSR, 0);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_asic_base + WD_LAAR, eth_laar &
+ ~WD_LAAR_M16EN);
+ inb(0x84);
+ }
+ inb(0x84);
+ }
+ if (bound == (eth_tx_start + D8390_TXBUF_SIZE))
+ bound = eth_memsize;
+ outb(eth_nic_base+D8390_P0_BOUND, bound-1);
+ if (ret && (type == ARP)) {
+ struct arprequest *arpreq;
+ unsigned long reqip;
+ arpreq = (struct arprequest *)&packet[ETHER_HDR_SIZE];
+ convert_ipaddr(&reqip, arpreq->tipaddr);
+ if ((ntohs(arpreq->opcode) == ARP_REQUEST) &&
+ (reqip == arptable[ARP_CLIENT].ipaddr)) {
+ arpreq->opcode = htons(ARP_REPLY);
+ bcopy(arpreq->sipaddr, arpreq->tipaddr, 4);
+ bcopy(arpreq->shwaddr, arpreq->thwaddr, 6);
+ bcopy(arptable[ARP_CLIENT].node, arpreq->shwaddr, 6);
+ convert_ipaddr(arpreq->sipaddr, &reqip);
+ eth_transmit(arpreq->thwaddr, ARP, sizeof(struct arprequest),
+ arpreq);
+ return(0);
+ }
+ }
+ return(ret);
+no8390:
+#endif /* 8390 */
+}
+
+#ifdef INCLUDE_NE
+/**************************************************************************
+NE1000/NE2000 Support Routines
+***************************************************************************/
+
+/* inw and outw are not needed more - standard version of them is used */
+
+/**************************************************************************
+ETH_PIO_READ - Read a frame via Programmed I/O
+***************************************************************************/
+eth_pio_read(src, dst, cnt, init)
+unsigned short src;
+unsigned char *dst;
+unsigned short cnt;
+int init;
+{
+ if (cnt & 1) cnt++;
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA);
+ outb(eth_nic_base + D8390_P0_RBCR0, cnt);
+ outb(eth_nic_base + D8390_P0_RBCR1, cnt>>8);
+ outb(eth_nic_base + D8390_P0_RSAR0, src);
+ outb(eth_nic_base + D8390_P0_RSAR1, src>>8);
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD0 |
+ D8390_COMMAND_STA);
+ if (eth_flags & FLAG_16BIT) {
+ while (cnt) {
+ *((unsigned short *)dst) = inw(eth_asic_base + NE_DATA);
+ dst += 2;
+ cnt -= 2;
+ }
+ }
+ else {
+ while (cnt--)
+ *(dst++) = inb(eth_asic_base + NE_DATA);
+ }
+}
+
+/**************************************************************************
+ETH_PIO_WRITE - Write a frame via Programmed I/O
+***************************************************************************/
+eth_pio_write(src, dst, cnt, init)
+unsigned char *src;
+unsigned short dst;
+unsigned short cnt;
+int init;
+{
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA);
+ outb(eth_nic_base + D8390_P0_ISR, D8390_ISR_RDC);
+ outb(eth_nic_base + D8390_P0_RBCR0, cnt);
+ outb(eth_nic_base + D8390_P0_RBCR1, cnt>>8);
+ outb(eth_nic_base + D8390_P0_RSAR0, dst);
+ outb(eth_nic_base + D8390_P0_RSAR1, dst>>8);
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD1 |
+ D8390_COMMAND_STA);
+ if (eth_flags & FLAG_16BIT) {
+ if (cnt & 1) cnt++; /* Round up */
+ while (cnt) {
+ outw(eth_asic_base + NE_DATA, *((unsigned short *)src));
+ src += 2;
+ cnt -= 2;
+ }
+ }
+ else {
+ while (cnt--)
+ outb(eth_asic_base + NE_DATA, *(src++));
+ }
+ while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
+ != D8390_ISR_RDC);
+}
+#else
+/**************************************************************************
+ETH_PIO_READ - Dummy routine when NE2000 not compiled in
+***************************************************************************/
+eth_pio_read() {
+}
+#endif
+
diff --git a/sys/pc98/boot/netboot/ether.h b/sys/pc98/boot/netboot/ether.h
new file mode 100644
index 0000000..ae4d693
--- /dev/null
+++ b/sys/pc98/boot/netboot/ether.h
@@ -0,0 +1,267 @@
+/**************************************************************************
+NETBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: Jun/94
+
+**************************************************************************/
+
+#define TRUE 1
+#define FALSE 0
+
+#define ETH_MIN_PACKET 64
+#define ETH_MAX_PACKET 1518
+
+#define VENDOR_NONE 0
+#define VENDOR_WD 1
+#define VENDOR_NOVELL 2
+#define VENDOR_3COM 3
+#define VENDOR_3C509 4
+
+#define FLAG_PIO 0x01
+#define FLAG_16BIT 0x02
+#define FLAG_790 0x04
+
+#define MEM_8192 32
+#define MEM_16384 64
+#define MEM_32768 128
+
+/**************************************************************************
+Western Digital/SMC Board Definitions
+**************************************************************************/
+#define WD_LOW_BASE 0x200
+#define WD_HIGH_BASE 0x3e0
+#ifndef WD_DEFAULT_MEM
+#define WD_DEFAULT_MEM 0xD0000
+#endif
+#define WD_NIC_ADDR 0x10
+
+/**************************************************************************
+Western Digital/SMC ASIC Addresses
+**************************************************************************/
+#define WD_MSR 0x00
+#define WD_ICR 0x01
+#define WD_IAR 0x02
+#define WD_BIO 0x03
+#define WD_IRR 0x04
+#define WD_LAAR 0x05
+#define WD_IJR 0x06
+#define WD_GP2 0x07
+#define WD_LAR 0x08
+#define WD_BID 0x0E
+
+#define WD_ICR_16BIT 0x01
+
+#define WD_MSR_MENB 0x40
+
+#define WD_LAAR_L16EN 0x40
+#define WD_LAAR_M16EN 0x80
+
+#define WD_SOFTCONFIG 0x20
+
+/**************************************************************************
+Western Digital/SMC Board Types
+**************************************************************************/
+#define TYPE_WD8003S 0x02
+#define TYPE_WD8003E 0x03
+#define TYPE_WD8013EBT 0x05
+#define TYPE_WD8003W 0x24
+#define TYPE_WD8003EB 0x25
+#define TYPE_WD8013W 0x26
+#define TYPE_WD8013EP 0x27
+#define TYPE_WD8013WC 0x28
+#define TYPE_WD8013EPC 0x29
+#define TYPE_SMC8216T 0x2a
+#define TYPE_SMC8216C 0x2b
+#define TYPE_SMC8416T 0x00 /* Bogus entries: the 8416 generates the */
+#define TYPE_SMC8416C 0x00 /* the same codes as the 8216. */
+#define TYPE_SMC8013EBP 0x2c
+
+#ifdef INCLUDE_WD
+struct wd_board {
+ char *name;
+ char id;
+ char flags;
+ char memsize;
+} wd_boards[] = {
+ {"WD8003S", TYPE_WD8003S, 0, MEM_8192},
+ {"WD8003E", TYPE_WD8003E, 0, MEM_8192},
+ {"WD8013EBT", TYPE_WD8013EBT, FLAG_16BIT, MEM_16384},
+ {"WD8003W", TYPE_WD8003W, 0, MEM_8192},
+ {"WD8003EB", TYPE_WD8003EB, 0, MEM_8192},
+ {"WD8013W", TYPE_WD8013W, FLAG_16BIT, MEM_16384},
+ {"WD8003EP/WD8013EP",
+ TYPE_WD8013EP, 0, MEM_8192},
+ {"WD8013WC", TYPE_WD8013WC, FLAG_16BIT, MEM_16384},
+ {"WD8013EPC", TYPE_WD8013EPC, FLAG_16BIT, MEM_16384},
+ {"SMC8216T", TYPE_SMC8216T, FLAG_16BIT | FLAG_790, MEM_16384},
+ {"SMC8216C", TYPE_SMC8216C, FLAG_16BIT | FLAG_790, MEM_16384},
+ {"SMC8416T", TYPE_SMC8416T, FLAG_16BIT | FLAG_790, MEM_8192},
+ {"SMC8416C/BT", TYPE_SMC8416C, FLAG_16BIT | FLAG_790, MEM_8192},
+ {"SMC8013EBP", TYPE_SMC8013EBP,FLAG_16BIT, MEM_16384},
+ {NULL, 0, 0}
+};
+#endif
+/**************************************************************************
+3com 3c503 definitions
+**************************************************************************/
+
+#ifndef _3COM_BASE
+#define _3COM_BASE 0x300
+#endif
+
+#define _3COM_TX_PAGE_OFFSET_8BIT 0x20
+#define _3COM_TX_PAGE_OFFSET_16BIT 0x0
+#define _3COM_RX_PAGE_OFFSET_16BIT 0x20
+
+#define _3COM_ASIC_OFFSET 0x400
+#define _3COM_NIC_OFFSET 0x0
+
+#define _3COM_PSTR 0
+#define _3COM_PSPR 1
+
+#define _3COM_BCFR 3
+#define _3COM_BCFR_2E0 0x01
+#define _3COM_BCFR_2A0 0x02
+#define _3COM_BCFR_280 0x04
+#define _3COM_BCFR_250 0x08
+#define _3COM_BCFR_350 0x10
+#define _3COM_BCFR_330 0x20
+#define _3COM_BCFR_310 0x40
+#define _3COM_BCFR_300 0x80
+#define _3COM_PCFR 4
+#define _3COM_PCFR_C8000 0x10
+#define _3COM_PCFR_CC000 0x20
+#define _3COM_PCFR_D8000 0x40
+#define _3COM_PCFR_DC000 0x80
+#define _3COM_CR 6
+#define _3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define _3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define _3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define _3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define _3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define _3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define _3COM_CR_DDIR 0x40 /* DMA direction select */
+#define _3COM_CR_START 0x80 /* Start DMA controller */
+#define _3COM_GACFR 5
+#define _3COM_GACFR_MBS0 0x01
+#define _3COM_GACFR_MBS1 0x02
+#define _3COM_GACFR_MBS2 0x04
+#define _3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define _3COM_GACFR_TEST 0x10 /* for GA testing */
+#define _3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define _3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define _3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+#define _3COM_STREG 7
+#define _3COM_STREG_REV 0x07 /* GA revision */
+#define _3COM_STREG_DIP 0x08 /* DMA in progress */
+#define _3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define _3COM_STREG_OFLW 0x20 /* Overflow */
+#define _3COM_STREG_UFLW 0x40 /* Underflow */
+#define _3COM_STREG_DPRDY 0x80 /* Data port ready */
+#define _3COM_IDCFR 8
+#define _3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define _3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define _3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define _3COM_IDCFR_UNUSED 0x08 /* not used */
+#define _3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define _3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define _3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define _3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+#define _3COM_IRQ2 2
+#define _3COM_IRQ3 3
+#define _3COM_IRQ4 4
+#define _3COM_IRQ5 5
+#define _3COM_DAMSB 9
+#define _3COM_DALSB 0x0a
+#define _3COM_VPTR2 0x0b
+#define _3COM_VPTR1 0x0c
+#define _3COM_VPTR0 0x0d
+#define _3COM_RFMSB 0x0e
+#define _3COM_RFLSB 0x0f
+
+/**************************************************************************
+NE1000/2000 definitions
+**************************************************************************/
+#ifndef NE_BASE
+#define NE_BASE 0x320
+#endif
+#ifdef PC98
+#define NE_ASIC_OFFSET 0x00
+#define NE_RESET 0x300 /* Used to reset card */
+#define NE_DATA 0x200 /* Used to read/write NIC mem */
+#else
+#define NE_ASIC_OFFSET 0x10
+#define NE_RESET 0x0F /* Used to reset card */
+#define NE_DATA 0x00 /* Used to read/write NIC mem */
+#endif
+/**************************************************************************
+8390 Register Definitions
+**************************************************************************/
+#define D8390_P0_COMMAND 0x00
+#define D8390_P0_PSTART 0x01
+#define D8390_P0_PSTOP 0x02
+#define D8390_P0_BOUND 0x03
+#define D8390_P0_TSR 0x04
+#define D8390_P0_TPSR 0x04
+#define D8390_P0_TBCR0 0x05
+#define D8390_P0_TBCR1 0x06
+#define D8390_P0_ISR 0x07
+#define D8390_P0_RSAR0 0x08
+#define D8390_P0_RSAR1 0x09
+#define D8390_P0_RBCR0 0x0A
+#define D8390_P0_RBCR1 0x0B
+#define D8390_P0_RSR 0x0C
+#define D8390_P0_RCR 0x0C
+#define D8390_P0_TCR 0x0D
+#define D8390_P0_DCR 0x0E
+#define D8390_P0_IMR 0x0F
+#define D8390_P1_COMMAND 0x00
+#define D8390_P1_PAR0 0x01
+#define D8390_P1_PAR1 0x02
+#define D8390_P1_PAR2 0x03
+#define D8390_P1_PAR3 0x04
+#define D8390_P1_PAR4 0x05
+#define D8390_P1_PAR5 0x06
+#define D8390_P1_CURR 0x07
+#define D8390_P1_MAR0 0x08
+
+#define D8390_COMMAND_PS0 0x0 /* Page 0 select */
+#define D8390_COMMAND_PS1 0x40 /* Page 1 select */
+#define D8390_COMMAND_PS2 0x80 /* Page 2 select */
+#define D8390_COMMAND_RD2 0x20 /* Remote DMA control */
+#define D8390_COMMAND_RD1 0x10
+#define D8390_COMMAND_RD0 0x08
+#define D8390_COMMAND_TXP 0x04 /* transmit packet */
+#define D8390_COMMAND_STA 0x02 /* start */
+#define D8390_COMMAND_STP 0x01 /* stop */
+
+#define D8390_RCR_MON 0x20 /* monitor mode */
+
+#define D8390_DCR_FT1 0x40
+#define D8390_DCR_LS 0x08 /* Loopback select */
+#define D8390_DCR_WTS 0x01 /* Word transfer select */
+
+#define D8390_ISR_PRX 0x01 /* successful recv */
+#define D8390_ISR_PTX 0x02 /* successful xmit */
+#define D8390_ISR_RXE 0x04 /* receive error */
+#define D8390_ISR_TXE 0x08 /* transmit error */
+#define D8390_ISR_OVW 0x10 /* Overflow */
+#define D8390_ISR_CNT 0x20 /* Counter overflow */
+#define D8390_ISR_RDC 0x40 /* Remote DMA complete */
+#define D8390_ISR_RST 0x80 /* reset */
+
+#define D8390_RSTAT_PRX 0x01 /* successful recv */
+#define D8390_RSTAT_CRC 0x02 /* CRC error */
+#define D8390_RSTAT_FAE 0x04 /* Frame alignment error */
+#define D8390_RSTAT_OVER 0x08 /* overflow */
+
+#define D8390_TXBUF_SIZE 6
+#define D8390_RXBUF_END 32
+#define D8390_PAGE_SIZE 256
+
+struct ringbuffer {
+ unsigned char status;
+ unsigned char bound;
+ unsigned short len;
+};
diff --git a/sys/pc98/boot/netboot/if_epreg.h b/sys/pc98/boot/netboot/if_epreg.h
new file mode 100644
index 0000000..049f235
--- /dev/null
+++ b/sys/pc98/boot/netboot/if_epreg.h
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) 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. The name
+ * of the author may not be used to endorse or promote products derived from
+ * this software withough 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.
+ *
+ * if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by:
+ *
+ October 2, 1994
+
+ Modified by: Andres Vega Garcia
+
+ INRIA - Sophia Antipolis, France
+ e-mail: avega@sophia.inria.fr
+ finger: avega@pax.inria.fr
+
+ */
+
+/*
+ * Ethernet software status per interface.
+ */
+/*
+ * Some global constants
+ */
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+
+#define TX_INIT_RATE 16
+#define TX_INIT_MAX_RATE 64
+#define RX_INIT_LATENCY 64
+#define RX_INIT_EARLY_THRESH 64
+#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */
+#define MIN_RX_EARLY_THRESHL 4
+
+#define EEPROMSIZE 0x40
+#define MAX_EEPROMBUSY 1000
+#define EP_LAST_TAG 0xd7
+#define EP_MAX_BOARDS 16
+#define EP_ID_PORT 0x100
+
+/*
+ * some macros to acces long named fields
+ */
+#define IS_BASE (eth_base)
+#define BASE (eth_base)
+
+/*
+ * Commands to read/write EEPROM trough EEPROM command register (Window 0,
+ * Offset 0xa)
+ */
+#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
+#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
+#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
+#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
+
+#define EEPROM_BUSY (1<<15)
+#define EEPROM_TST_MODE (1<<14)
+
+/*
+ * Some short functions, worth to let them be a macro
+ */
+#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY)
+#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|(x))
+
+/**************************************************************************
+ * *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existance of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+
+/**************************************************************************
+ * *
+ * These are the registers for the 3Com 3c509 and their bit patterns when *
+ * applicable. They have been taken out the the "EtherLink III Parallel *
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
+ * from 3com. *
+ * *
+ **************************************************************************/
+
+#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a
+ * command reg. */
+#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status
+ * reg. */
+#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window
+ * reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+/* Write */
+#define EP_W0_EEPROM_DATA 0x0c
+#define EP_W0_EEPROM_COMMAND 0x0a
+#define EP_W0_RESOURCE_CFG 0x08
+#define EP_W0_ADDRESS_CFG 0x06
+#define EP_W0_CONFIG_CTRL 0x04
+/* Read */
+#define EP_W0_PRODUCT_ID 0x02
+#define EP_W0_MFG_ID 0x00
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+/* Write */
+#define EP_W1_TX_PIO_WR_2 0x02
+#define EP_W1_TX_PIO_WR_1 0x00
+/* Read */
+#define EP_W1_FREE_TX 0x0c
+#define EP_W1_TX_STATUS 0x0b /* byte */
+#define EP_W1_TIMER 0x0a /* byte */
+#define EP_W1_RX_STATUS 0x08
+#define EP_W1_RX_PIO_RD_2 0x02
+#define EP_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+/* Read/Write */
+#define EP_W2_ADDR_5 0x05
+#define EP_W2_ADDR_4 0x04
+#define EP_W2_ADDR_3 0x03
+#define EP_W2_ADDR_2 0x02
+#define EP_W2_ADDR_1 0x01
+#define EP_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+/* Read */
+#define EP_W3_FREE_TX 0x0c
+#define EP_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+/* Read/Write */
+#define EP_W4_MEDIA_TYPE 0x0a
+#define EP_W4_CTRLR_STATUS 0x08
+#define EP_W4_NET_DIAG 0x06
+#define EP_W4_FIFO_DIAG 0x04
+#define EP_W4_HOST_DIAG 0x02
+#define EP_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+/* Read */
+#define EP_W5_READ_0_MASK 0x0c
+#define EP_W5_INTR_MASK 0x0a
+#define EP_W5_RX_FILTER 0x08
+#define EP_W5_RX_EARLY_THRESH 0x06
+#define EP_W5_TX_AVAIL_THRESH 0x02
+#define EP_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+/* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms
+ * after issuing */
+#define WINDOW_SELECT (u_short) (0x1<<11)
+#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to
+ * determine whether
+ * this is needed. If
+ * so; wait 800 uSec
+ * before using trans-
+ * ceiver. */
+#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on
+ * power-up */
+#define RX_ENABLE (u_short) (0x4<<11)
+#define RX_RESET (u_short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11)
+#define TX_ENABLE (u_short) (0x9<<11)
+#define TX_DISABLE (u_short) (0xa<<11)
+#define TX_RESET (u_short) (0xb<<11)
+#define REQ_INTR (u_short) (0xc<<11)
+#define SET_INTR_MASK (u_short) (0xe<<11)
+#define SET_RD_0_MASK (u_short) (0xf<<11)
+#define SET_RX_FILTER (u_short) (0x10<<11)
+#define FIL_INDIVIDUAL (u_short) (0x1)
+#define FIL_GROUP (u_short) (0x2)
+#define FIL_BRDCST (u_short) (0x4)
+#define FIL_ALL (u_short) (0x8)
+#define SET_RX_EARLY_THRESH (u_short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11)
+#define SET_TX_START_THRESH (u_short) (0x13<<11)
+#define STATS_ENABLE (u_short) (0x15<<11)
+#define STATS_DISABLE (u_short) (0x16<<11)
+#define STOP_TRANSCEIVER (u_short) (0x17<<11)
+/*
+ * The following C_* acknowledge the various interrupts. Some of them don't
+ * do anything. See the manual.
+ */
+#define ACK_INTR (u_short) (0x6800)
+#define C_INTR_LATCH (u_short) (ACK_INTR|0x1)
+#define C_CARD_FAILURE (u_short) (ACK_INTR|0x2)
+#define C_TX_COMPLETE (u_short) (ACK_INTR|0x4)
+#define C_TX_AVAIL (u_short) (ACK_INTR|0x8)
+#define C_RX_COMPLETE (u_short) (ACK_INTR|0x10)
+#define C_RX_EARLY (u_short) (ACK_INTR|0x20)
+#define C_INT_RQD (u_short) (ACK_INTR|0x40)
+#define C_UPD_STATS (u_short) (ACK_INTR|0x80)
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (u_short) (0x1)
+#define S_CARD_FAILURE (u_short) (0x2)
+#define S_TX_COMPLETE (u_short) (0x4)
+#define S_TX_AVAIL (u_short) (0x8)
+#define S_RX_COMPLETE (u_short) (0x10)
+#define S_RX_EARLY (u_short) (0x20)
+#define S_INT_RQD (u_short) (0x40)
+#define S_UPD_STATS (u_short) (0x80)
+#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\
+ S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY)
+#define S_COMMAND_IN_PROGRESS (u_short) (0x1000)
+
+/*
+ * FIFO Registers.
+ * RX Status. Window 1/Port 08
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_RX_INCOMPLETE (u_short) (0x1<<15)
+#define ERR_RX (u_short) (0x1<<14)
+#define ERR_RX_OVERRUN (u_short) (0x8<<11)
+#define ERR_RX_RUN_PKT (u_short) (0xb<<11)
+#define ERR_RX_ALIGN (u_short) (0xc<<11)
+#define ERR_RX_CRC (u_short) (0xd<<11)
+#define ERR_RX_OVERSIZE (u_short) (0x9<<11)
+#define ERR_RX_DRIBBLE (u_short) (0x2<<11)
+
+/*
+ * FIFO Registers.
+ * TX Status. Window 1/Port 0B
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_SUCCES_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+/*
+ * Configuration control register.
+ * Window 0/Port 04
+ */
+/* Read */
+#define IS_AUI (1<<13)
+#define IS_BNC (1<<12)
+#define IS_UTP (1<<9)
+/* Write */
+#define ENABLE_DRQ_IRQ 0x0001
+#define W0_P4_CMD_RESET_ADAPTER 0x4
+#define W0_P4_CMD_ENABLE_ADAPTER 0x1
+/*
+ * Media type and status.
+ * Window 4/Port 0A
+ */
+#define ENABLE_UTP 0xc0
+#define DISABLE_UTP 0x0
+
+/*
+ * Resource control register
+ */
+
+#define SET_IRQ(i) ( ((i)<<12) | 0xF00) /* set IRQ i */
+
+/*
+ * Receive status register
+ */
+
+#define RX_BYTES_MASK (u_short) (0x07ff)
+#define RX_ERROR 0x4000
+#define RX_INCOMPLETE 0x8000
+
+
+/*
+ * Misc defines for various things.
+ */
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */
+#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */
+#define PROD_ID 0x9150
+
+#define AUI 0x1
+#define BNC 0x2
+#define UTP 0x4
+
+#define ETHER_ADDR_LEN 6
+#define ETHER_MAX 1536
+#define RX_BYTES_MASK (u_short) (0x07ff)
+
+ /* EISA support */
+#define EP_EISA_START 0x1000
+#define EP_EISA_W0 0x0c80
diff --git a/sys/pc98/boot/netboot/main.c b/sys/pc98/boot/netboot/main.c
new file mode 100644
index 0000000..d111d0d
--- /dev/null
+++ b/sys/pc98/boot/netboot/main.c
@@ -0,0 +1,657 @@
+/**************************************************************************
+NETBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: Dec/93
+
+**************************************************************************/
+
+/* #define MDEBUG */
+
+#include "netboot.h"
+
+int jmp_bootmenu[10];
+
+struct exec head;
+char *loadpoint;
+char *kernel;
+char kernel_buf[128];
+void (*kernelentry)();
+struct nfs_diskless nfsdiskless;
+int hostnamelen;
+char config_buffer[512]; /* Max TFTP packet */
+struct bootinfo bootinfo;
+int root_nfs_port;
+unsigned long netmask;
+char kernel_handle[32];
+int offset;
+
+extern char eth_driver[];
+extern char packet[];
+extern int packetlen, rpc_id;
+char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+/**************************************************************************
+MAIN - Kick off routine
+**************************************************************************/
+main()
+{
+ int c;
+ char *p;
+ extern char edata[], end[];
+ for (p=edata; p<end; p++) *p = 0; /* Zero BSS */
+#ifdef ASK_BOOT
+ while (1) {
+ printf("\n\rBoot from Network (Y/N) ? ");
+ c = getchar();
+ if ((c >= 'a') && (c <= 'z')) c &= 0x5F;
+ if (c == '\r') break;
+ putchar(c);
+ if (c == 'N')
+ exit(0);
+ if (c == 'Y')
+ break;
+ printf(" - bad response\n\r");
+ }
+#endif
+ gateA20();
+#ifdef PC98
+ /* set machine type to PC98_SYSTEM_PARAMETER */
+ /* machine_check(); */
+#endif
+ printf("\r\nBOOTP/TFTP/NFS bootstrap loader ESC for menu\n\r");
+ printf("\r\nSearching for adapter...");
+ if (!eth_probe()) {
+ printf("No adapter found.\r\n");
+ exit(0);
+ }
+ kernel = DEFAULT_BOOTFILE;
+ while (1) {
+ if (setjmp(jmp_bootmenu))
+ bootmenu();
+ else
+ load();
+ }
+}
+
+void
+nfsload(length)
+{
+ int err, read_size;
+
+ while (length > 0) {
+ read_size = length > NFS_READ_SIZE ?
+ NFS_READ_SIZE : length;
+ if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port,
+ &kernel_handle, offset, read_size, loadpoint)) !=
+ read_size) {
+ if (err < 0) {
+ printf("Unable to read data: ");
+ nfs_err(err);
+ }
+ longjmp(jmp_bootmenu, 1);
+ }
+ loadpoint += err;
+ length -= err;
+ offset += err;
+ }
+}
+
+/**************************************************************************
+LOAD - Try to get booted
+**************************************************************************/
+load()
+{
+ char *p,*q;
+ char cfg[64];
+ int root_mount_port;
+ int swap_nfs_port;
+ int swap_mount_port;
+ char cmd_line[80];
+ int err, read_size, i;
+ long addr, broadcast;
+ unsigned long pad;
+
+/* Initialize this early on */
+
+ nfsdiskless.root_args.rsize = 8192;
+ nfsdiskless.root_args.wsize = 8192;
+ nfsdiskless.swap_args.rsize = 8192;
+ nfsdiskless.swap_args.wsize = 8192;
+ nfsdiskless.root_args.sotype = SOCK_DGRAM;
+ nfsdiskless.root_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE);
+ nfsdiskless.swap_args.sotype = SOCK_DGRAM;
+ nfsdiskless.swap_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE);
+
+
+ /* Find a server to get BOOTP reply from */
+ if (!arptable[ARP_CLIENT].ipaddr || !arptable[ARP_SERVER].ipaddr) {
+ printf("\r\nSearching for server...\r\n");
+ if (!bootp()) {
+ printf("No Server found.\r\n");
+ longjmp(jmp_bootmenu,1);
+ }
+ }
+ printf("My IP %I, Server IP %I, GW IP %I\r\n",
+ arptable[ARP_CLIENT].ipaddr,
+ arptable[ARP_SERVER].ipaddr,
+ arptable[ARP_GATEWAY].ipaddr);
+
+#ifdef MDEBUG
+ printf("\n=>>"); getchar();
+#endif
+
+ /* Now use TFTP to load configuration file */
+ sprintf(cfg,"/tftpboot/freebsd.%I",arptable[ARP_CLIENT].ipaddr);
+ if (tftp(cfg) || tftp(cfg+10))
+ goto cfg_done;
+ cfg[17]='\0';
+ if (tftp(cfg) || tftp(cfg+10))
+ goto cfg_done;
+ sprintf(cfg,"/tftpboot/cfg.%I",arptable[ARP_CLIENT].ipaddr);
+ if (tftp(cfg) || tftp(cfg+10))
+ goto cfg_done;
+ sprintf(config_buffer,"rootfs %I:/usr/diskless_root",
+ arptable[ARP_SERVER].ipaddr);
+ printf("Unable to load config file, guessing:\r\n\t%s\r\n",
+ config_buffer);
+
+cfg_done:
+#ifdef MDEBUG
+ printf("\n=>>"); getchar();
+#endif
+
+ p = config_buffer;
+ while(*p) {
+ q = cmd_line;
+ while ((*p != '\n') && (*p)) *(q++) = *(p++);
+ *q = 0;
+ printf("%s\r\n",cmd_line);
+ execute(cmd_line);
+ if (*p) p++;
+ }
+
+#ifdef MDEBUG
+ printf("\n=>>"); getchar();
+#endif
+
+ /* Check to make sure we've got a rootfs */
+ if (!arptable[ARP_ROOTSERVER].ipaddr) {
+ printf("No ROOT filesystem server!\r\n");
+ longjmp(jmp_bootmenu,1);
+ }
+
+ /* Fill in nfsdiskless.myif */
+ sprintf(&nfsdiskless.myif.ifra_name,eth_driver);
+ nfsdiskless.myif.ifra_addr.sa_len = sizeof(struct sockaddr);
+ nfsdiskless.myif.ifra_addr.sa_family = AF_INET;
+ addr = htonl(arptable[ARP_CLIENT].ipaddr);
+ bcopy(&addr, &nfsdiskless.myif.ifra_addr.sa_data[2], 4);
+ broadcast = (addr & netmask) | ~netmask;
+ nfsdiskless.myif.ifra_broadaddr.sa_len = sizeof(struct sockaddr);
+ nfsdiskless.myif.ifra_broadaddr.sa_family = AF_INET;
+ bcopy(&broadcast, &nfsdiskless.myif.ifra_broadaddr.sa_data[2], 4);
+ addr = htonl(arptable[ARP_GATEWAY].ipaddr);
+ if (addr) {
+ nfsdiskless.mygateway.sin_len = sizeof(struct sockaddr);
+ nfsdiskless.mygateway.sin_family = AF_INET;
+ bcopy(&addr, &nfsdiskless.mygateway.sin_addr, 4);
+ } else {
+ nfsdiskless.mygateway.sin_len = 0;
+ }
+ nfsdiskless.myif.ifra_mask.sa_len = sizeof(struct sockaddr);
+ nfsdiskless.myif.ifra_mask.sa_family = AF_UNSPEC;
+ bcopy(&netmask, &nfsdiskless.myif.ifra_mask.sa_data[2], 4);
+
+ rpc_id = currticks();
+
+ /* Lookup NFS/MOUNTD ports for SWAP using PORTMAP */
+ if (arptable[ARP_SWAPSERVER].ipaddr) {
+ char swapfs_fh[32], swapfile[32];
+ swap_nfs_port = rpclookup(ARP_SWAPSERVER, PROG_NFS, 2);
+ swap_mount_port = rpclookup(ARP_SWAPSERVER, PROG_MOUNT, 1);
+ if ((swap_nfs_port == -1) || (swap_mount_port == -1)) {
+ printf("Unable to get SWAP NFS/MOUNT ports\r\n");
+ longjmp(jmp_bootmenu,1);
+ }
+ if (err = nfs_mount(ARP_SWAPSERVER, swap_mount_port,
+ nfsdiskless.swap_hostnam, &swapfs_fh)) {
+ printf("Unable to mount SWAP filesystem: ");
+ nfs_err(err);
+ longjmp(jmp_bootmenu,1);
+ }
+ sprintf(swapfile,"swap.%I",arptable[ARP_CLIENT].ipaddr);
+ if (err = nfs_lookup(ARP_SWAPSERVER, swap_nfs_port,
+ &swapfs_fh, swapfile, &nfsdiskless.swap_fh)) {
+ printf("Unable to open %s: ",swapfile);
+ nfs_err(err);
+ longjmp(jmp_bootmenu,1);
+ }
+ nfsdiskless.swap_saddr.sin_len = sizeof(struct sockaddr_in);
+ nfsdiskless.swap_saddr.sin_family = AF_INET;
+ nfsdiskless.swap_saddr.sin_port = htons(swap_nfs_port);
+ nfsdiskless.swap_saddr.sin_addr.s_addr =
+ htonl(arptable[ARP_SWAPSERVER].ipaddr);
+ nfsdiskless.swap_args.timeo = 10;
+ nfsdiskless.swap_args.retrans = 100;
+ }
+
+ /* Lookup NFS/MOUNTD ports for ROOT using PORTMAP */
+ root_nfs_port = rpclookup(ARP_ROOTSERVER, PROG_NFS, 2);
+ root_mount_port = rpclookup(ARP_ROOTSERVER, PROG_MOUNT, 1);
+ if ((root_nfs_port == -1) || (root_mount_port == -1)) {
+ printf("Unable to get ROOT NFS/MOUNT ports\r\n");
+ longjmp(jmp_bootmenu,1);
+ }
+ if (err = nfs_mount(ARP_ROOTSERVER, root_mount_port,
+ nfsdiskless.root_hostnam, &nfsdiskless.root_fh)) {
+ printf("Unable to mount ROOT filesystem: ");
+ nfs_err(err);
+ longjmp(jmp_bootmenu,1);
+ }
+ nfsdiskless.root_saddr.sin_len = sizeof(struct sockaddr_in);
+ nfsdiskless.root_saddr.sin_family = AF_INET;
+ nfsdiskless.root_saddr.sin_port = htons(root_nfs_port);
+ nfsdiskless.root_saddr.sin_addr.s_addr =
+ htonl(arptable[ARP_ROOTSERVER].ipaddr);
+ nfsdiskless.root_args.timeo = 10;
+ nfsdiskless.root_args.retrans = 100;
+ nfsdiskless.root_time = 0;
+
+ if (err = nfs_lookup(ARP_ROOTSERVER, root_nfs_port,
+ &nfsdiskless.root_fh, *kernel == '/' ? kernel+1 : kernel,
+ &kernel_handle)) {
+ printf("Unable to open %s: ",kernel);
+ nfs_err(err);
+ longjmp(jmp_bootmenu,1);
+ }
+
+ /* Load the kernel using NFS */
+ printf("Loading %s...\r\n",kernel);
+ if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port, &kernel_handle, 0,
+ sizeof(struct exec), &head)) < 0) {
+ printf("Unable to read %s: ",kernel);
+ nfs_err(err);
+ longjmp(jmp_bootmenu,1);
+ }
+ if (N_BADMAG(head)) {
+ printf("Bad executable format!\r\n");
+ longjmp(jmp_bootmenu, 1);
+ }
+ loadpoint = (char *)0x100000;
+ offset = N_TXTOFF(head);
+ printf("text=0x%X, ",head.a_text);
+ nfsload(head.a_text);
+ while (((int)loadpoint) & PAGE_MASK)
+ *(loadpoint++) = 0;
+
+ printf("data=0x%X, ",head.a_data);
+ nfsload(head.a_data);
+
+ printf("bss=0x%X, ",head.a_bss);
+ while(head.a_bss--) *(loadpoint++) = 0;
+
+ while (((int)loadpoint) & PAGE_MASK)
+ *(loadpoint++) = 0;
+
+ bootinfo.bi_symtab = (int) loadpoint;
+
+ p = (char*)&head.a_syms;
+ for (i=0;i<sizeof(head.a_syms);i++)
+ *loadpoint++ = *p++;
+
+ printf("symbols=[+0x%x+0x%x", sizeof(head.a_syms), head.a_syms);
+
+ nfsload(head.a_syms);
+ i = sizeof(int);
+ p = loadpoint;
+ nfsload(i);
+ i = *(int*)p;
+ printf("+0x%x]\n", i);
+ i -= sizeof(int);
+ nfsload(i);
+ bootinfo.bi_esymtab = (int) loadpoint;
+
+ printf("entry=0x%X.\n\r",head.a_entry);
+
+ /* Jump to kernel */
+ bootinfo.bi_version = BOOTINFO_VERSION;
+ bootinfo.bi_kernelname = kernel;
+ bootinfo.bi_nfs_diskless = &nfsdiskless;
+ bootinfo.bi_size = sizeof bootinfo;
+ kernelentry = (void *)(head.a_entry & 0x00FFFFFF);
+ (*kernelentry)(RB_BOOTINFO,NODEV,0,0,0,&bootinfo,0,0,0);
+ printf("*** %s execute failure ***\n",kernel);
+}
+
+/**************************************************************************
+POLLKBD - Check for Interrupt from keyboard
+**************************************************************************/
+pollkbd()
+{
+ if (iskey() && (getchar() == ESC)) longjmp(jmp_bootmenu,1);
+}
+
+/**************************************************************************
+DEFAULT_NETMASK - Set a default netmask for IP address
+**************************************************************************/
+default_netmask()
+{
+ int net = arptable[ARP_CLIENT].ipaddr >> 24;
+ if (net <= 127)
+ netmask = htonl(0xff000000);
+ else if (net < 192)
+ netmask = htonl(0xffff0000);
+ else
+ netmask = htonl(0xffffff00);
+}
+/**************************************************************************
+UDP_TRANSMIT - Send a UDP datagram
+**************************************************************************/
+udp_transmit(destip, srcsock, destsock, len, buf)
+ unsigned long destip;
+ unsigned short srcsock, destsock;
+ int len;
+ char *buf;
+{
+ struct iphdr *ip;
+ struct udphdr *udp;
+ struct arprequest arpreq;
+ int arpentry, i;
+ unsigned long time;
+ int retry = MAX_ARP_RETRIES;
+
+ ip = (struct iphdr *)buf;
+ udp = (struct udphdr *)(buf + sizeof(struct iphdr));
+ ip->verhdrlen = 0x45;
+ ip->service = 0;
+ ip->len = htons(len);
+ ip->ident = 0;
+ ip->frags = 0;
+ ip->ttl = 60;
+ ip->protocol = IP_UDP;
+ ip->chksum = 0;
+ convert_ipaddr(ip->src, &arptable[ARP_CLIENT].ipaddr);
+ convert_ipaddr(ip->dest, &destip);
+ ip->chksum = ipchksum(buf, sizeof(struct iphdr));
+ udp->src = htons(srcsock);
+ udp->dest = htons(destsock);
+ udp->len = htons(len - sizeof(struct iphdr));
+ udp->chksum = 0;
+ if (destip == IP_BROADCAST) {
+ eth_transmit(broadcast, IP, len, buf);
+ } else {
+ long h_netmask = ntohl(netmask);
+ /* Check to see if we need gateway */
+ if (((destip & h_netmask) !=
+ (arptable[ARP_CLIENT].ipaddr & h_netmask)) &&
+ arptable[ARP_GATEWAY].ipaddr)
+ destip = arptable[ARP_GATEWAY].ipaddr;
+ for(arpentry = 0; arpentry<MAX_ARP; arpentry++)
+ if (arptable[arpentry].ipaddr == destip) break;
+ if (arpentry == MAX_ARP) {
+ printf("%I is not in my arp table!\n");
+ return(0);
+ }
+ for (i = 0; i<ETHER_ADDR_SIZE; i++)
+ if (arptable[arpentry].node[i]) break;
+ if (i == ETHER_ADDR_SIZE) { /* Need to do arp request */
+ arpreq.hwtype = htons(1);
+ arpreq.protocol = htons(IP);
+ arpreq.hwlen = ETHER_ADDR_SIZE;
+ arpreq.protolen = 4;
+ arpreq.opcode = htons(ARP_REQUEST);
+ bcopy(arptable[ARP_CLIENT].node, arpreq.shwaddr,
+ ETHER_ADDR_SIZE);
+ convert_ipaddr(arpreq.sipaddr,
+ &arptable[ARP_CLIENT].ipaddr);
+ bzero(arpreq.thwaddr, ETHER_ADDR_SIZE);
+ convert_ipaddr(arpreq.tipaddr, &destip);
+ while (retry--) {
+ eth_transmit(broadcast, ARP, sizeof(arpreq),
+ &arpreq);
+ if (await_reply(AWAIT_ARP, arpentry,
+ arpreq.tipaddr)) goto xmit;
+ }
+ return(0);
+ }
+xmit: eth_transmit(arptable[arpentry].node, IP, len, buf);
+ }
+ return(1);
+}
+
+/**************************************************************************
+TFTP - Try to load configuation file
+**************************************************************************/
+tftp(name)
+ char *name;
+{
+ struct tftp_t *tr;
+ int retry = MAX_TFTP_RETRIES;
+ static unsigned short isocket = 2000;
+ unsigned short osocket = TFTP;
+ unsigned short len, block=1;
+ struct tftp_t tp;
+ int code;
+ printf("Loading %s...\r\n",name);
+ isocket++;
+ tp.opcode = htons(TFTP_RRQ);
+ len = (sprintf((char *)tp.u.rrq,"%s%c%s",name,0,"octet")
+ - ((char *)&tp)) + 1;
+ while(retry--) {
+ if (!udp_transmit(arptable[ARP_SERVER].ipaddr, isocket, osocket,
+ len, &tp)) return(0);
+ if (await_reply(AWAIT_TFTP, isocket, NULL)) {
+ tr = (struct tftp_t *)&packet[ETHER_HDR_SIZE];
+ if (tr->opcode == ntohs(TFTP_ERROR)) {
+ printf("TFTP error %d (%s)\r\n",
+ ntohs(tr->u.err.errcode),
+ tr->u.err.errmsg);
+ return(0);
+ } /* ACK PACKET */
+ if (tr->opcode != ntohs(TFTP_DATA)) return(0);
+ tp.opcode = htons(TFTP_ACK);
+ tp.u.ack.block = tr->u.data.block;
+ udp_transmit(arptable[ARP_SERVER].ipaddr, isocket,
+ osocket, TFTP_MIN_PACKET_SIZE, &tp);
+ len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
+ if (len >= 512) {
+ printf("Config file too large.\r\n");
+ config_buffer[0] = 0;
+ return(0);
+ } else {
+ bcopy(tr->u.data.download, config_buffer, len);
+ config_buffer[len] = 0;
+ }
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/**************************************************************************
+BOOTP - Get my IP address and load information
+**************************************************************************/
+bootp()
+{
+ int retry = MAX_BOOTP_RETRIES;
+ struct bootp_t bp;
+ unsigned long starttime;
+ bzero(&bp, sizeof(struct bootp_t));
+ bp.bp_op = BOOTP_REQUEST;
+ bp.bp_htype = 1;
+ bp.bp_hlen = ETHER_ADDR_SIZE;
+ bp.bp_xid = starttime = currticks();
+ bcopy(arptable[ARP_CLIENT].node, bp.bp_hwaddr, ETHER_ADDR_SIZE);
+ while(retry--) {
+ udp_transmit(IP_BROADCAST, 0, BOOTP_SERVER,
+ sizeof(struct bootp_t), &bp);
+ if (await_reply(AWAIT_BOOTP, 0, NULL))
+ return(1);
+ bp.bp_secs = htons((currticks()-starttime)/20);
+ }
+ return(0);
+}
+
+
+/**************************************************************************
+AWAIT_REPLY - Wait until we get a response for our request
+**************************************************************************/
+await_reply(type, ival, ptr)
+ int type, ival;
+ char *ptr;
+{
+ unsigned long time;
+ struct iphdr *ip;
+ struct udphdr *udp;
+ struct arprequest *arpreply;
+ struct bootp_t *bootpreply;
+ struct rpc_t *rpc;
+
+ int protohdrlen = ETHER_HDR_SIZE + sizeof(struct iphdr) +
+ sizeof(struct udphdr);
+ time = currticks() + TIMEOUT;
+ while(time > currticks()) {
+ pollkbd();
+ if (eth_poll()) { /* We have something! */
+ /* Check for ARP - No IP hdr */
+ if ((type == AWAIT_ARP) &&
+ (packetlen >= ETHER_HDR_SIZE +
+ sizeof(struct arprequest)) &&
+ (((packet[12] << 8) | packet[13]) == ARP)) {
+ arpreply = (struct arprequest *)
+ &packet[ETHER_HDR_SIZE];
+ if ((arpreply->opcode == ntohs(ARP_REPLY)) &&
+ bcompare(arpreply->sipaddr, ptr, 4)) {
+ bcopy(arpreply->shwaddr,
+ arptable[ival].node,
+ ETHER_ADDR_SIZE);
+ return(1);
+ }
+ continue;
+ }
+
+ /* Anything else has IP header */
+ if ((packetlen < protohdrlen) ||
+ (((packet[12] << 8) | packet[13]) != IP)) continue;
+ ip = (struct iphdr *)&packet[ETHER_HDR_SIZE];
+ if ((ip->verhdrlen != 0x45) ||
+ ipchksum(ip, sizeof(struct iphdr)) ||
+ (ip->protocol != IP_UDP)) continue;
+ udp = (struct udphdr *)&packet[ETHER_HDR_SIZE +
+ sizeof(struct iphdr)];
+
+ /* BOOTP ? */
+ bootpreply = (struct bootp_t *)&packet[ETHER_HDR_SIZE];
+ if ((type == AWAIT_BOOTP) &&
+ (packetlen >= (ETHER_HDR_SIZE +
+ sizeof(struct bootp_t))) &&
+ (ntohs(udp->dest) == BOOTP_CLIENT) &&
+ (bootpreply->bp_op == BOOTP_REPLY)) {
+ convert_ipaddr(&arptable[ARP_CLIENT].ipaddr,
+ bootpreply->bp_yiaddr);
+ default_netmask();
+ convert_ipaddr(&arptable[ARP_SERVER].ipaddr,
+ bootpreply->bp_siaddr);
+ bzero(arptable[ARP_SERVER].node,
+ ETHER_ADDR_SIZE); /* Kill arp */
+ convert_ipaddr(&arptable[ARP_GATEWAY].ipaddr,
+ bootpreply->bp_giaddr);
+ bzero(arptable[ARP_GATEWAY].node,
+ ETHER_ADDR_SIZE); /* Kill arp */
+ if (bootpreply->bp_file[0]) {
+ bcopy(bootpreply->bp_file,
+ kernel_buf, 128);
+ kernel = kernel_buf;
+ }
+ decode_rfc1048(bootpreply->bp_vend);
+ return(1);
+ }
+
+ /* TFTP ? */
+ if ((type == AWAIT_TFTP) &&
+ (ntohs(udp->dest) == ival)) return(1);
+
+ /* RPC */
+ rpc = (struct rpc_t *)&packet[ETHER_HDR_SIZE];
+ if ((type == AWAIT_RPC) &&
+ (ntohs(udp->dest) == RPC_SOCKET) &&
+ (ntohl(rpc->u.reply.id) == ival) &&
+ (ntohl(rpc->u.reply.type) == MSG_REPLY)) {
+ rpc_id++;
+ return(1);
+ }
+ }
+ }
+ return(0);
+}
+
+/**************************************************************************
+DECODE_RFC1048 - Decodes RFC1048 header
+**************************************************************************/
+decode_rfc1048(p)
+ unsigned char *p;
+{
+ static char rfc1048_cookie[4] = RFC1048_COOKIE;
+ unsigned char *end = p + BOOTP_VENDOR_LEN,*q;
+ if (bcompare(p, rfc1048_cookie, 4)) { /* RFC 1048 header */
+ p += 4;
+ while(p < end) {
+ switch (*p) {
+ case RFC1048_PAD:
+ p++;
+ continue;
+ case RFC1048_END:
+ p = end;
+ continue;
+ case RFC1048_GATEWAY:
+ convert_ipaddr(&arptable[ARP_GATEWAY].ipaddr,
+ p+2);
+ break;
+ case RFC1048_NETMASK:
+ bcopy(p+2,&netmask,4);
+ break;
+ case RFC1048_HOSTNAME:
+ bcopy(p+2, &nfsdiskless.my_hostnam, TAG_LEN(p));
+ hostnamelen = (TAG_LEN(p) + 3) & ~3;
+ break;
+ default:
+ printf("Unknown RFC1048-tag ");
+ for(q=p;q<p+2+TAG_LEN(p);q++)
+ printf("%x ",*q);
+ printf("\n\r");
+ }
+ p += TAG_LEN(p) + 2;
+ }
+ }
+}
+
+/**************************************************************************
+IPCHKSUM - Checksum IP Header
+**************************************************************************/
+ipchksum(ip, len)
+ unsigned short *ip;
+ int len;
+{
+ unsigned long sum = 0;
+ len >>= 1;
+ while (len--) {
+ sum += *(ip++);
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+ return((~sum) & 0x0000FFFF);
+}
+
+
+/**************************************************************************
+CONVERT_IPADDR - Convert IP address from net to machine order
+**************************************************************************/
+convert_ipaddr(d, s)
+ char *d,*s;
+{
+ *(d+3) = *s;
+ *(d+2) = *(s+1);
+ *(d+1) = *(s+2);
+ *d = *(s+3);
+}
diff --git a/sys/pc98/boot/netboot/makerom.c b/sys/pc98/boot/netboot/makerom.c
new file mode 100644
index 0000000..715e9b7
--- /dev/null
+++ b/sys/pc98/boot/netboot/makerom.c
@@ -0,0 +1,53 @@
+/************************************************************************
+
+Program to put ROM checksum in ROM image.
+
+This program strips off the FreeBSD a.out header!
+
+************************************************************************/
+#include <stdio.h>
+#include <fcntl.h>
+
+unsigned char rom[ROMSIZE];
+unsigned int sum;
+
+main(argc,argv)
+ int argc; char *argv[];
+{
+ int i, fd;
+ if (argc < 1) {
+ fprintf(stderr,"usage: %s rom-file\n",argv[0]);
+ exit(2);
+ }
+ if ((fd = open(argv[1], O_RDWR)) < 0) {
+ perror("unable to open file");
+ exit(2);
+ }
+ bzero(rom, ROMSIZE);
+ if (lseek(fd, (off_t)32, SEEK_SET) < 0) {
+ perror("lseek error");
+ exit(2);
+ }
+ if (read(fd, rom, ROMSIZE) < 0) {
+ perror("read error");
+ exit(2);
+ }
+ rom[5] = 0;
+ for (i=0,sum=0; i<ROMSIZE; i++)
+ sum += rom[i];
+ rom[5] = -sum;
+ for (i=0,sum=0; i<ROMSIZE; i++);
+ sum += rom[i];
+ if (sum)
+ printf("checksum fails.\n");
+ if (lseek(fd, (off_t)0, SEEK_SET) < 0) {
+ perror("unable to seek");
+ exit(2);
+ }
+ if (write(fd, rom, ROMSIZE) < 0) {
+ perror("unable to write");
+ exit(2);
+ }
+ close(fd);
+ exit(0);
+}
diff --git a/sys/pc98/boot/netboot/misc.c b/sys/pc98/boot/netboot/misc.c
new file mode 100644
index 0000000..8c7be73
--- /dev/null
+++ b/sys/pc98/boot/netboot/misc.c
@@ -0,0 +1,290 @@
+/**************************************************************************
+MISC Support Routines
+**************************************************************************/
+
+#include "netboot.h"
+
+#define NO_SWITCH /* saves space */
+
+/**************************************************************************
+TWIDDLE
+**************************************************************************/
+twiddle()
+{
+ static int count=0;
+ char tiddles[]="-\\|/";
+ putchar(tiddles[(count++)&3]);
+ putchar('\b');
+}
+
+/**************************************************************************
+BCOPY
+**************************************************************************/
+bcopy(s,d,n)
+ char *s, *d;
+ int n;
+{
+ while ((n--) > 0) {
+ *(d++) = *(s++);
+ }
+}
+
+/**************************************************************************
+BZERO
+**************************************************************************/
+bzero(d,n)
+ char *d;
+ int n;
+{
+ while ((n--) > 0) {
+ *(d++) = 0;
+ }
+}
+
+/**************************************************************************
+BCOMPARE
+**************************************************************************/
+bcompare(d,s,n)
+ char *d,*s;
+ int n;
+{
+ while ((n--) > 0) {
+ if (*(d++) != *(s++)) return(0);
+ }
+ return(1);
+}
+
+/**************************************************************************
+SUBSTR (slightly wacky but functional)
+**************************************************************************/
+char *substr(a,b)
+char *a,*b;
+{
+char *loc1;
+char *loc2;
+
+ while (*a != '\0') {
+ loc1 = a;
+ loc2 = b;
+ while (*loc1 == *loc2++) {
+ if (*loc1 == '\0') return (0);
+ loc1++;
+ if (*loc2 == '\0') return (loc1);
+ }
+ a++;
+ }
+ return (0);
+}
+
+/**************************************************************************
+PRINTF and friends
+
+ Formats:
+ %X - 4 byte ASCII (8 hex digits)
+ %x - 2 byte ASCII (4 hex digits)
+ %b - 1 byte ASCII (2 hex digits)
+ %d - decimal
+ %c - ASCII char
+ %s - ASCII string
+ %I - Internet address in x.x.x.x notation
+ %L - Binary long
+ %S - String (multiple of 4 bytes) preceded with 4 byte
+ binary length
+ %M - Copy memory. Takes two args, len and ptr
+**************************************************************************/
+static char hex[]="0123456789ABCDEF";
+char *do_printf(buf, fmt, dp)
+ char *buf, *fmt;
+ int *dp;
+{
+ register char *p;
+ char tmp[16];
+ while (*fmt) {
+ if (*fmt == '%') { /* switch() uses more space */
+ fmt++;
+ if (*fmt == 'L') {
+ register int h = *(dp++);
+ *(buf++) = h>>24;
+ *(buf++) = h>>16;
+ *(buf++) = h>>8;
+ *(buf++) = h;
+ }
+ if (*fmt == 'S') {
+ register int len = 0;
+ char *lenptr = buf;
+ p = (char *)*dp++;
+ buf += 4;
+ while (*p) {
+ *(buf++) = *p++;
+ len ++;
+ }
+ *(lenptr++) = len>>24;
+ *(lenptr++) = len>>16;
+ *(lenptr++) = len>>8;
+ *lenptr = len;
+ while (len & 3) {
+ *(buf++) = 0;
+ len ++;
+ }
+ }
+ if (*fmt == 'M') {
+ register int len = *(dp++);
+ bcopy((char *)*dp++, buf, len);
+ buf += len;
+ }
+ if (*fmt == 'X') {
+ register int h = *(dp++);
+ *(buf++) = hex[(h>>28)& 0x0F];
+ *(buf++) = hex[(h>>24)& 0x0F];
+ *(buf++) = hex[(h>>20)& 0x0F];
+ *(buf++) = hex[(h>>16)& 0x0F];
+ *(buf++) = hex[(h>>12)& 0x0F];
+ *(buf++) = hex[(h>>8)& 0x0F];
+ *(buf++) = hex[(h>>4)& 0x0F];
+ *(buf++) = hex[h& 0x0F];
+ }
+ if (*fmt == 'x') {
+ register int h = *(dp++);
+ *(buf++) = hex[(h>>12)& 0x0F];
+ *(buf++) = hex[(h>>8)& 0x0F];
+ *(buf++) = hex[(h>>4)& 0x0F];
+ *(buf++) = hex[h& 0x0F];
+ }
+ if (*fmt == 'b') {
+ register int h = *(dp++);
+ *(buf++) = hex[(h>>4)& 0x0F];
+ *(buf++) = hex[h& 0x0F];
+ }
+ if (*fmt == 'd') {
+ register int dec = *(dp++);
+ p = tmp;
+ if (dec < 0) {
+ *(buf++) = '-';
+ dec = -dec;
+ }
+ do {
+ *(p++) = '0' + (dec%10);
+ dec = dec/10;
+ } while(dec);
+ while ((--p) >= tmp) *(buf++) = *p;
+ }
+ if (*fmt == 'I') {
+ buf = sprintf(buf,"%d.%d.%d.%d",
+ (*(dp)>>24) & 0x00FF,
+ (*(dp)>>16) & 0x00FF,
+ (*(dp)>>8) & 0x00FF,
+ *dp & 0x00FF);
+ dp++;
+ }
+ if (*fmt == 'c')
+ *(buf++) = *(dp++);
+ if (*fmt == 's') {
+ p = (char *)*dp++;
+ while (*p) *(buf++) = *p++;
+ }
+ } else *(buf++) = *fmt;
+ fmt++;
+ }
+ *buf = 0;
+ return(buf);
+}
+
+char *sprintf(buf, fmt, data)
+ char *fmt, *buf;
+ int data;
+{
+ return(do_printf(buf,fmt, &data));
+}
+
+printf(fmt,data)
+ char *fmt;
+ int data;
+{
+ char buf[1024],*p;
+ p = buf;
+ do_printf(buf,fmt,&data);
+ while (*p) {
+ if (*p=='\n') putchar('\r');
+ putchar(*p++);
+ }
+}
+
+/**************************************************************************
+SETIP - Convert an ascii x.x.x.x to binary form
+**************************************************************************/
+setip(p, i)
+ char *p;
+ unsigned *i;
+{
+ unsigned ip = 0;
+ int val;
+ if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
+ if (*p != '.') return(0);
+ p++;
+ ip = val;
+ if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
+ if (*p != '.') return(0);
+ p++;
+ ip = (ip << 8) | val;
+ if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
+ if (*p != '.') return(0);
+ p++;
+ ip = (ip << 8) | val;
+ if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
+ *i = (ip << 8) | val;
+ return(1);
+}
+
+getdec(ptr)
+ char **ptr;
+{
+ char *p = *ptr;
+ int ret=0;
+ if ((*p < '0') || (*p > '9')) return(-1);
+ while ((*p >= '0') && (*p <= '9')) {
+ ret = ret*10 + (*p - '0');
+ p++;
+ }
+ *ptr = p;
+ return(ret);
+}
+
+
+#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
+#define K_STATUS 0x64 /* keyboard status */
+#define K_CMD 0x64 /* keybd ctlr command (write-only) */
+
+#define K_OBUF_FUL 0x01 /* output buffer full */
+#define K_IBUF_FUL 0x02 /* input buffer full */
+
+#define KC_CMD_WIN 0xd0 /* read output port */
+#define KC_CMD_WOUT 0xd1 /* write output port */
+#define KB_A20 0x9f /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+
+/*
+ * Gate A20 for high memory
+ */
+unsigned char x_20 = KB_A20;
+gateA20()
+{
+#ifdef PC98
+ outb(0xf2, 0x00);
+ outb(0xf6, 0x02);
+#else
+#ifdef IBM_L40
+ outb(0x92, 0x2);
+#else IBM_L40
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ while (inb(K_STATUS) & K_OBUF_FUL)
+ (void)inb(K_RDWR);
+
+ outb(K_CMD, KC_CMD_WOUT);
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ outb(K_RDWR, x_20);
+ while (inb(K_STATUS) & K_IBUF_FUL);
+#endif IBM_L40
+#endif
+}
diff --git a/sys/pc98/boot/netboot/netboot.doc b/sys/pc98/boot/netboot/netboot.doc
new file mode 100644
index 0000000..d9d261e
--- /dev/null
+++ b/sys/pc98/boot/netboot/netboot.doc
@@ -0,0 +1,42 @@
+
+Configuring FreeBSD to run diskless Oct 15/1994
+===================================
+
+1) Find a machine that will be your server. This machine will require
+ enough disk space to hold the FreeBSD 2.0 binaries and have bootp and
+ tftp services available.
+
+2) Create a bootptab entry for the diskless FreeBSD machine.
+
+ sample entry:
+
+ diskless:\
+ :ht=ether:\
+ :ha=0000c01f848a:\
+ :sm=255.255.255.0:\
+ :hn:\
+ :ds=192.1.2.3:\
+ :ip=192.1.2.4:\
+ :vm=rfc1048:
+
+
+3) Create a cfg.x.x.x.x file for your diskless machine. This is now an
+ ASCII file with netboot commands in it.
+
+ sample cfg.x.x.x.x:
+
+ hostname diskless.freebsd.com
+ rootfs server.freebsd.com:/var/rootfs/diskless
+ swapfs server.freebsd.com:/var/swap/diskless
+
+4) On the server, export the root and swap filesystems to the client. This
+ usually involves putting them in the /etc/exports file and one some
+ machines running /usr/etc/exportfs -av
+
+5) Make a BOOTROM by copying netboot.rom to an EPROM, or copy netboot.com to
+ a DOS diskette.
+
+6) Boot the diskless machine and run netboot.com if you're using DOS.
+
+
+Martin Renters martin@innovus.com
diff --git a/sys/pc98/boot/netboot/netboot.h b/sys/pc98/boot/netboot/netboot.h
new file mode 100644
index 0000000..7f724e6
--- /dev/null
+++ b/sys/pc98/boot/netboot/netboot.h
@@ -0,0 +1,247 @@
+/**************************************************************************
+NETBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: Dec/93
+
+**************************************************************************/
+
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <a.out.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/mount.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsdiskless.h>
+#include <machine/bootinfo.h>
+#include <machine/cpufunc.h>
+
+#define ESC 0x1B
+
+#ifndef DEFAULT_BOOTFILE
+#define DEFAULT_BOOTFILE "/kernel"
+#endif
+
+#ifndef MAX_TFTP_RETRIES
+#define MAX_TFTP_RETRIES 20
+#endif
+
+#ifndef MAX_BOOTP_RETRIES
+#define MAX_BOOTP_RETRIES 20
+#endif
+
+#ifndef MAX_ARP_RETRIES
+#define MAX_ARP_RETRIES 20
+#endif
+
+#ifndef MAX_RPC_RETRIES
+#define MAX_RPC_RETRIES 20
+#endif
+
+#ifndef TIMEOUT /* Inter-packet retry in ticks 18/sec */
+#define TIMEOUT 20
+#endif
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+#define ETHER_ADDR_SIZE 6 /* Size of Ethernet address */
+#define ETHER_HDR_SIZE 14 /* Size of ethernet header */
+#define ETH_MIN_PACKET 64
+#define ETH_MAX_PACKET 1518
+
+#define VENDOR_NONE 0
+#define VENDOR_WD 1
+#define VENDOR_NOVELL 2
+#define VENDOR_3COM 3
+#define VENDOR_3C509 4
+
+#define FLAG_PIO 0x01
+#define FLAG_16BIT 0x02
+#define FLAG_790 0x04
+
+#define ARP_CLIENT 0
+#define ARP_SERVER 1
+#define ARP_GATEWAY 2
+#define ARP_NS 3
+#define ARP_ROOTSERVER 4
+#define ARP_SWAPSERVER 5
+#define MAX_ARP ARP_SWAPSERVER+1
+
+#define IP 0x0800
+#define ARP 0x0806
+
+#define BOOTP_SERVER 67
+#define BOOTP_CLIENT 68
+#define TFTP 69
+#define SUNRPC 111
+
+#define RPC_SOCKET 620 /* Arbitrary */
+
+#define IP_UDP 17
+#define IP_BROADCAST 0xFFFFFFFF
+
+#define ARP_REQUEST 1
+#define ARP_REPLY 2
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+#define TAG_LEN(p) (*((p)+1))
+#define RFC1048_COOKIE { 99, 130, 83, 99 }
+#define RFC1048_PAD 0
+#define RFC1048_NETMASK 1
+#define RFC1048_GATEWAY 3
+#define RFC1048_HOSTNAME 12
+#define RFC1048_END 255
+#define BOOTP_VENDOR_LEN 64
+
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+
+#define TFTP_CODE_EOF 1
+#define TFTP_CODE_MORE 2
+#define TFTP_CODE_ERROR 3
+#define TFTP_CODE_BOOT 4
+#define TFTP_CODE_CFG 5
+
+#define PROG_PORTMAP 100000
+#define PROG_NFS 100003
+#define PROG_MOUNT 100005
+
+#define MSG_CALL 0
+#define MSG_REPLY 1
+
+#define PORTMAP_LOOKUP 3
+
+#define MOUNT_ADDENTRY 1
+#define NFS_LOOKUP 4
+#define NFS_READ 6
+
+#define NFS_READ_SIZE 1024
+
+
+#define AWAIT_ARP 0
+#define AWAIT_BOOTP 1
+#define AWAIT_TFTP 2
+#define AWAIT_RPC 3
+
+struct arptable_t {
+ unsigned long ipaddr;
+ unsigned char node[6];
+} arptable[MAX_ARP];
+
+struct arprequest {
+ unsigned short hwtype;
+ unsigned short protocol;
+ char hwlen;
+ char protolen;
+ unsigned short opcode;
+ char shwaddr[6];
+ char sipaddr[4];
+ char thwaddr[6];
+ char tipaddr[4];
+};
+
+struct iphdr {
+ char verhdrlen;
+ char service;
+ unsigned short len;
+ unsigned short ident;
+ unsigned short frags;
+ char ttl;
+ char protocol;
+ unsigned short chksum;
+ char src[4];
+ char dest[4];
+};
+
+struct udphdr {
+ unsigned short src;
+ unsigned short dest;
+ unsigned short len;
+ unsigned short chksum;
+};
+
+struct bootp_t {
+ struct iphdr ip;
+ struct udphdr udp;
+ char bp_op;
+ char bp_htype;
+ char bp_hlen;
+ char bp_hops;
+ unsigned long bp_xid;
+ unsigned short bp_secs;
+ unsigned short unused;
+ char bp_ciaddr[4];
+ char bp_yiaddr[4];
+ char bp_siaddr[4];
+ char bp_giaddr[4];
+ char bp_hwaddr[16];
+ char bp_sname[64];
+ char bp_file[128];
+ char bp_vend[64];
+};
+
+struct tftp_t {
+ struct iphdr ip;
+ struct udphdr udp;
+ unsigned short opcode;
+ union {
+ char rrq[512];
+ struct {
+ unsigned short block;
+ char download[512];
+ } data;
+ struct {
+ unsigned short block;
+ } ack;
+ struct {
+ unsigned short errcode;
+ char errmsg[512];
+ } err;
+ } u;
+};
+
+struct rpc_t {
+ struct iphdr ip;
+ struct udphdr udp;
+ union {
+ char data[1400];
+ struct {
+ long id;
+ long type;
+ long rstatus;
+ long verifier;
+ long v2;
+ long astatus;
+ long data[1];
+ } reply;
+ } u;
+};
+
+#define TFTP_MIN_PACKET_SIZE (sizeof(struct iphdr) + sizeof(struct udphdr) + 4)
+
+/***************************************************************************
+RPC Functions
+***************************************************************************/
+#define PUTLONG(val) {\
+ register int lval = val; \
+ *(rpcptr++) = ((lval) >> 24); \
+ *(rpcptr++) = ((lval) >> 16); \
+ *(rpcptr++) = ((lval) >> 8); \
+ *(rpcptr++) = (lval); \
+ rpclen+=4; }
+
+char *sprintf();
diff --git a/sys/pc98/boot/netboot/ns8390.c b/sys/pc98/boot/netboot/ns8390.c
new file mode 100644
index 0000000..9fb328d
--- /dev/null
+++ b/sys/pc98/boot/netboot/ns8390.c
@@ -0,0 +1,651 @@
+
+/**************************************************************************
+NETBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: May/94
+
+ This code is based heavily on David Greenman's if_ed.c driver
+
+ Copyright (C) 1993-1994, David Greenman, Martin Renters.
+ This software may be used, modified, copied, distributed, and sold, in
+ both source and binary form provided that the above copyright and these
+ terms are retained. Under no circumstances are the authors responsible for
+ the proper functioning of this software, nor do the authors assume any
+ responsibility for damages incurred with its use.
+
+3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
+SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
+
+**************************************************************************/
+
+#include "netboot.h"
+#include "ns8390.h"
+
+#ifdef _3COM_USE_AUI
+short aui=1;
+#else
+short aui=0;
+#endif
+
+unsigned short eth_nic_base;
+unsigned short eth_asic_base;
+unsigned char eth_tx_start;
+unsigned char eth_laar;
+unsigned char eth_flags;
+unsigned char eth_vendor;
+unsigned char eth_memsize;
+unsigned char *eth_bmem;
+unsigned char *eth_rmem;
+unsigned char *eth_node_addr;
+
+/**************************************************************************
+The following two variables are used externally
+**************************************************************************/
+char eth_driver[] = "ed0";
+char packet[ETH_MAX_PACKET];
+int packetlen;
+
+#ifdef INCLUDE_NE
+static unsigned short ne_base_list[]= {
+#ifdef NE_BASE
+ NE_BASE,
+#endif
+ 0xff80, 0xff40, 0xff00, 0xfec0,
+ 0x280, 0x300, 0
+};
+#endif
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+**************************************************************************/
+eth_probe()
+{
+ int i;
+ struct wd_board *brd;
+ char *name;
+ unsigned short chksum;
+ unsigned char c;
+
+ eth_vendor = VENDOR_NONE;
+
+#ifdef INCLUDE_WD
+ /******************************************************************
+ Search for WD/SMC cards
+ ******************************************************************/
+ for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE;
+ eth_asic_base += 0x20) {
+ chksum = 0;
+ for (i=8; i<16; i++)
+ chksum += inb(i+eth_asic_base);
+ if ((chksum & 0x00FF) == 0x00FF)
+ break;
+ }
+ if (eth_asic_base <= WD_HIGH_BASE) { /* We've found a board */
+ eth_vendor = VENDOR_WD;
+ eth_nic_base = eth_asic_base + WD_NIC_ADDR;
+ c = inb(eth_asic_base+WD_BID); /* Get board id */
+ for (brd = wd_boards; brd->name; brd++)
+ if (brd->id == c) break;
+ if (!brd->name) {
+ printf("\r\nUnknown Ethernet type %x\r\n", c);
+ return(0); /* Unknown type */
+ }
+ eth_flags = brd->flags;
+ eth_memsize = brd->memsize;
+ eth_tx_start = 0;
+ if ((c == TYPE_WD8013EP) &&
+ (inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) {
+ eth_flags = FLAG_16BIT;
+ eth_memsize = MEM_16384;
+ }
+ if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) {
+ eth_bmem = (char *)(0x80000 |
+ ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13));
+ } else
+ eth_bmem = (char *)WD_DEFAULT_MEM;
+ if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) {
+ (unsigned int) *(eth_bmem + 8192) = (unsigned int)0;
+ if ((unsigned int) *(eth_bmem + 8192)) {
+ brd += 2;
+ eth_memsize = brd->memsize;
+ }
+ }
+ outb(eth_asic_base + WD_MSR, 0x80); /* Reset */
+ printf("\r\n%s base 0x%x, memory 0x%X, addr ",
+ brd->name, eth_asic_base, eth_bmem);
+ for (i=0; i<6; i++) {
+ printf("%b",(int)(arptable[ARP_CLIENT].node[i] =
+ inb(i+eth_asic_base+WD_LAR)));
+ if (i < 5) printf (":");
+ }
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base+WD_MSR, WD_MSR_MENB);
+ outb(eth_asic_base+0x04, (inb(eth_asic_base+0x04) |
+ 0x80));
+ outb(eth_asic_base+0x0B,
+ (((unsigned)eth_bmem >> 13) & 0x0F) |
+ (((unsigned)eth_bmem >> 11) & 0x40) |
+ (inb(eth_asic_base+0x0B) & 0xB0));
+ outb(eth_asic_base+0x04, (inb(eth_asic_base+0x04) &
+ ~0x80));
+ } else {
+ outb(eth_asic_base+WD_MSR,
+ (((unsigned)eth_bmem >> 13) & 0x3F) | 0x40);
+ }
+ if (eth_flags & FLAG_16BIT) {
+ if (eth_flags & FLAG_790) {
+ eth_laar = inb(eth_asic_base + WD_LAAR);
+ outb(eth_asic_base + WD_LAAR, WD_LAAR_M16EN);
+ inb(0x84);
+ } else {
+ outb(eth_asic_base + WD_LAAR, (eth_laar =
+ WD_LAAR_M16EN | WD_LAAR_L16EN | 1));
+ }
+ }
+ goto found_board;
+ }
+#endif
+#ifdef INCLUDE_3COM
+ /******************************************************************
+ Search for 3Com 3c503 if no WD/SMC cards
+ ******************************************************************/
+ if (eth_vendor == VENDOR_NONE) {
+ eth_asic_base = _3COM_BASE + _3COM_ASIC_OFFSET;
+ eth_nic_base = _3COM_BASE;
+ eth_vendor = VENDOR_3COM;
+ /*
+ * Note that we use the same settings for both 8 and 16 bit cards:
+ * both have an 8K bank of memory at page 1 while only the 16 bit
+ * cards have a bank at page 0.
+ */
+ eth_memsize = MEM_16384;
+ eth_tx_start = 32;
+
+ /* Check our base address */
+
+ switch(inb(eth_asic_base + _3COM_BCFR)) {
+ case _3COM_BCFR_300:
+ if ((int)eth_nic_base != 0x300)
+ return(0);
+ break;
+ case _3COM_BCFR_310:
+ if ((int)eth_nic_base != 0x310)
+ return(0);
+ break;
+ case _3COM_BCFR_330:
+ if ((int)eth_nic_base != 0x330)
+ return(0);
+ break;
+ case _3COM_BCFR_350:
+ if ((int)eth_nic_base != 0x350)
+ return(0);
+ break;
+ case _3COM_BCFR_250:
+ if ((int)eth_nic_base != 0x250)
+ return(0);
+ break;
+ case _3COM_BCFR_280:
+ if ((int)eth_nic_base != 0x280)
+ return(0);
+ break;
+ case _3COM_BCFR_2A0:
+ if ((int)eth_nic_base != 0x2a0)
+ return(0);
+ break;
+ case _3COM_BCFR_2E0:
+ if ((int)eth_nic_base != 0x2e0)
+ return(0);
+ break;
+ default:
+ return (0);
+ }
+
+ /* Now get the shared memory address */
+
+ switch (inb(eth_asic_base + _3COM_PCFR)) {
+ case _3COM_PCFR_DC000:
+ eth_bmem = (char *)0xdc000;
+ break;
+ case _3COM_PCFR_D8000:
+ eth_bmem = (char *)0xd8000;
+ break;
+ case _3COM_PCFR_CC000:
+ eth_bmem = (char *)0xcc000;
+ break;
+ case _3COM_PCFR_C8000:
+ eth_bmem = (char *)0xc8000;
+ break;
+ default:
+ return (0);
+ }
+
+ /* Need this to make eth_poll() happy. */
+
+ eth_rmem = eth_bmem - 0x2000;
+
+ /* Reset NIC and ASIC */
+
+ outb (eth_asic_base + _3COM_CR , _3COM_CR_RST | _3COM_CR_XSEL);
+ outb (eth_asic_base + _3COM_CR , _3COM_CR_XSEL);
+
+ /* Get our ethernet address */
+
+ outb(eth_asic_base + _3COM_CR, _3COM_CR_EALO | _3COM_CR_XSEL);
+ printf("\r\n3Com 3c503 base 0x%x, memory 0x%X addr ",
+ eth_nic_base, eth_bmem);
+ for (i=0; i<6; i++) {
+ printf("%b",(int)(arptable[ARP_CLIENT].node[i] =
+ inb(eth_nic_base+i)));
+ if (i < 5) printf (":");
+ }
+ outb(eth_asic_base + _3COM_CR, _3COM_CR_XSEL);
+ /*
+ * Initialize GA configuration register. Set bank and enable shared
+ * mem. We always use bank 1.
+ */
+ outb(eth_asic_base + _3COM_GACFR, _3COM_GACFR_RSEL |
+ _3COM_GACFR_MBS0);
+
+ outb(eth_asic_base + _3COM_VPTR2, 0xff);
+ outb(eth_asic_base + _3COM_VPTR1, 0xff);
+ outb(eth_asic_base + _3COM_VPTR0, 0x00);
+ /*
+ * Clear memory and verify that it worked (we use only 8K)
+ */
+ bzero(eth_bmem, 0x2000);
+ for(i = 0; i < 0x2000; ++i)
+ if (*((eth_bmem)+i)) {
+ printf ("Failed to clear 3c503 shared mem.\r\n");
+ return (0);
+ }
+ /*
+ * Initialize GA page/start/stop registers.
+ */
+ outb(eth_asic_base + _3COM_PSTR, eth_tx_start);
+ outb(eth_asic_base + _3COM_PSPR, eth_memsize);
+
+ goto found_board;
+
+ }
+#endif
+#ifdef INCLUDE_NE
+ /******************************************************************
+ Search for NE1000/2000 if no WD/SMC or 3com cards
+ ******************************************************************/
+ if (eth_vendor == VENDOR_NONE) {
+ char romdata[16], testbuf[32];
+ char test[] = "NE1000/2000 memory";
+ unsigned short *tent_base=ne_base_list;
+ eth_bmem = (char *)0; /* No shared memory */
+ne_again:
+ eth_asic_base = *tent_base + NE_ASIC_OFFSET;
+ eth_nic_base = *tent_base;
+
+ eth_vendor = VENDOR_NOVELL;
+ eth_flags = FLAG_PIO;
+ eth_memsize = MEM_16384;
+ eth_tx_start = 32;
+ c = inb(eth_asic_base + NE_RESET);
+ outb(eth_asic_base + NE_RESET, c);
+ inb(0x84);
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_STP |
+ D8390_COMMAND_RD2);
+ outb(eth_nic_base + D8390_P0_RCR, D8390_RCR_MON);
+ outb(eth_nic_base + D8390_P0_DCR, D8390_DCR_FT1 | D8390_DCR_LS);
+ outb(eth_nic_base + D8390_P0_PSTART, MEM_8192);
+ outb(eth_nic_base + D8390_P0_PSTOP, MEM_16384);
+ eth_pio_write(test, 8192, sizeof(test));
+ eth_pio_read(8192, testbuf, sizeof(test));
+ if (!bcompare(test, testbuf, sizeof(test))) {
+ eth_flags |= FLAG_16BIT;
+ eth_memsize = MEM_32768;
+ eth_tx_start = 64;
+ outb(eth_nic_base + D8390_P0_DCR, D8390_DCR_WTS |
+ D8390_DCR_FT1 | D8390_DCR_LS);
+ outb(eth_nic_base + D8390_P0_PSTART, MEM_16384);
+ outb(eth_nic_base + D8390_P0_PSTOP, MEM_32768);
+ eth_pio_write(test, 16384, sizeof(test));
+ eth_pio_read(16384, testbuf, sizeof(test));
+ if (!bcompare(testbuf, test, sizeof(test)))
+ if (*++tent_base)
+ goto ne_again;
+ else
+ return (0);
+ }
+ eth_pio_read(0, romdata, 16);
+ printf("\r\nNE1000/NE2000 base 0x%x, addr ", eth_nic_base);
+ for (i=0; i<6; i++) {
+ printf("%b",(int)(arptable[ARP_CLIENT].node[i] = romdata[i
+ + ((eth_flags & FLAG_16BIT) ? i : 0)]));
+ if (i < 5) printf (":");
+ }
+ goto found_board;
+ }
+#endif
+found_board:
+ printf("\r\n");
+ if (eth_vendor == VENDOR_NONE) return(0);
+
+ if (eth_vendor != VENDOR_3COM) eth_rmem = eth_bmem;
+ eth_node_addr = arptable[ARP_CLIENT].node;
+ eth_reset();
+ return(eth_vendor);
+}
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+**************************************************************************/
+eth_reset()
+{
+ int i;
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND,
+ D8390_COMMAND_PS0 | D8390_COMMAND_STP);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND,
+ D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STP);
+ if (eth_flags & FLAG_16BIT)
+ outb(eth_nic_base+D8390_P0_DCR, 0x49);
+ else
+ outb(eth_nic_base+D8390_P0_DCR, 0x48);
+ outb(eth_nic_base+D8390_P0_RBCR0, 0);
+ outb(eth_nic_base+D8390_P0_RBCR1, 0);
+ outb(eth_nic_base+D8390_P0_RCR, 4); /* allow broadcast frames */
+ outb(eth_nic_base+D8390_P0_TCR, 2);
+ outb(eth_nic_base+D8390_P0_TPSR, eth_tx_start);
+ outb(eth_nic_base+D8390_P0_PSTART, eth_tx_start + D8390_TXBUF_SIZE);
+ if (eth_flags & FLAG_790) outb(eth_nic_base + 0x09, 0);
+ outb(eth_nic_base+D8390_P0_PSTOP, eth_memsize);
+ outb(eth_nic_base+D8390_P0_BOUND, eth_tx_start + D8390_TXBUF_SIZE);
+ outb(eth_nic_base+D8390_P0_ISR, 0xFF);
+ outb(eth_nic_base+D8390_P0_IMR, 0);
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1 |
+ D8390_COMMAND_STP);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STP);
+ for (i=0; i<6; i++)
+ outb(eth_nic_base+D8390_P1_PAR0+i, eth_node_addr[i]);
+ for (i=0; i<6; i++)
+ outb(eth_nic_base+D8390_P1_MAR0+i, 0xFF);
+ outb(eth_nic_base+D8390_P1_CURR, eth_tx_start + D8390_TXBUF_SIZE+1);
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_STA);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STA);
+ outb(eth_nic_base+D8390_P0_ISR, 0xFF);
+ outb(eth_nic_base+D8390_P0_TCR, 0);
+#ifdef INCLUDE_3COM
+ if (eth_vendor == VENDOR_3COM) {
+ /*
+ * No way to tell whether or not we're supposed to use
+ * the 3Com's transceiver unless the user tells us.
+ * 'aui' should have some compile time default value
+ * which can be changed from the command menu.
+ */
+ if (aui)
+ outb(eth_asic_base + _3COM_CR, 0);
+ else
+ outb(eth_asic_base + _3COM_CR, _3COM_CR_XSEL);
+ }
+#endif
+ return(1);
+}
+
+/**************************************************************************
+ETH_TRANSMIT - Transmit a frame
+**************************************************************************/
+eth_transmit(d,t,s,p)
+ char *d; /* Destination */
+ unsigned short t; /* Type */
+ unsigned short s; /* size */
+ char *p; /* Packet */
+{
+ unsigned char c;
+#ifdef INCLUDE_3COM
+ if (eth_vendor == VENDOR_3COM) {
+ bcopy(d, eth_bmem, 6); /* dst */
+ bcopy(eth_node_addr, eth_bmem+6, ETHER_ADDR_SIZE); /* src */
+ *(eth_bmem+12) = t>>8; /* type */
+ *(eth_bmem+13) = t;
+ bcopy(p, eth_bmem+14, s);
+ s += 14;
+ while (s < ETH_MIN_PACKET) *(eth_bmem+(s++)) = 0;
+ }
+#endif
+#ifdef INCLUDE_WD
+ if (eth_vendor == VENDOR_WD) { /* Memory interface */
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_asic_base + WD_LAAR, eth_laar | WD_LAAR_M16EN);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base + WD_MSR, WD_MSR_MENB);
+ inb(0x84);
+ }
+ inb(0x84);
+ bcopy(d, eth_bmem, 6); /* dst */
+ bcopy(eth_node_addr, eth_bmem+6, ETHER_ADDR_SIZE); /* src */
+ *(eth_bmem+12) = t>>8; /* type */
+ *(eth_bmem+13) = t;
+ bcopy(p, eth_bmem+14, s);
+ s += 14;
+ while (s < ETH_MIN_PACKET) *(eth_bmem+(s++)) = 0;
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base + WD_MSR, 0);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_asic_base + WD_LAAR, eth_laar & ~WD_LAAR_M16EN);
+ inb(0x84);
+ }
+ }
+#endif
+#ifdef INCLUDE_NE
+ if (eth_vendor == VENDOR_NOVELL) { /* Programmed I/O */
+ unsigned short type;
+ type = (t >> 8) | (t << 8);
+ eth_pio_write(d, eth_tx_start<<8, 6);
+ eth_pio_write(eth_node_addr, (eth_tx_start<<8)+6, 6);
+ eth_pio_write(&type, (eth_tx_start<<8)+12, 2);
+ eth_pio_write(p, (eth_tx_start<<8)+14, s);
+ s += 14;
+ if (s < ETH_MIN_PACKET) s = ETH_MIN_PACKET;
+ }
+#endif
+ twiddle();
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_STA);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STA);
+ outb(eth_nic_base+D8390_P0_TPSR, eth_tx_start);
+ outb(eth_nic_base+D8390_P0_TBCR0, s);
+ outb(eth_nic_base+D8390_P0_TBCR1, s>>8);
+ if (eth_flags & FLAG_790)
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_TXP | D8390_COMMAND_STA);
+ else
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
+ D8390_COMMAND_TXP | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA);
+ return(0);
+}
+
+/**************************************************************************
+ETH_POLL - Wait for a frame
+**************************************************************************/
+eth_poll()
+{
+ int ret = 0;
+ unsigned short type = 0;
+ unsigned char bound,curr,rstat;
+ unsigned short len;
+ unsigned short pktoff;
+ unsigned char *p;
+ struct ringbuffer pkthdr;
+ rstat = inb(eth_nic_base+D8390_P0_RSR);
+ if (rstat & D8390_RSTAT_OVER) {
+ eth_reset();
+ return(0);
+ }
+ if (!(rstat & D8390_RSTAT_PRX)) return(0);
+ bound = inb(eth_nic_base+D8390_P0_BOUND)+1;
+ if (bound == eth_memsize) bound = eth_tx_start + D8390_TXBUF_SIZE;
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1);
+ curr = inb(eth_nic_base+D8390_P1_CURR);
+ outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0);
+ if (curr == eth_memsize) curr=eth_tx_start + D8390_TXBUF_SIZE;
+ if (curr == bound) return(0);
+ if (eth_vendor == VENDOR_WD) {
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_asic_base + WD_LAAR, eth_laar | WD_LAAR_M16EN);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base + WD_MSR, WD_MSR_MENB);
+ inb(0x84);
+ }
+ inb(0x84);
+ }
+ pktoff = (bound << 8);
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, &pkthdr, 4);
+ else
+ bcopy(eth_rmem + pktoff, &pkthdr, 4);
+ len = pkthdr.len - 4; /* sub CRC */
+ pktoff += 4;
+ if (len > 1514) len = 1514;
+ bound = pkthdr.bound; /* New bound ptr */
+ if ( (pkthdr.status & D8390_RSTAT_PRX) && (len > 14) && (len < 1518)) {
+ p = packet;
+ packetlen = len;
+ len = (eth_memsize << 8) - pktoff;
+ if (packetlen > len) { /* We have a wrap-around */
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, p, len);
+ else
+ bcopy(eth_rmem + pktoff, p, len);
+ pktoff = (eth_tx_start + D8390_TXBUF_SIZE) << 8;
+ p += len;
+ packetlen -= len;
+ }
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, p, packetlen);
+ else
+ bcopy(eth_rmem + pktoff, p, packetlen);
+
+ type = (packet[12]<<8) | packet[13];
+ ret = 1;
+ }
+ if (eth_vendor == VENDOR_WD) {
+ if (eth_flags & FLAG_790) {
+ outb(eth_asic_base + WD_MSR, 0);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_asic_base + WD_LAAR, eth_laar &
+ ~WD_LAAR_M16EN);
+ inb(0x84);
+ }
+ inb(0x84);
+ }
+ if (bound == (eth_tx_start + D8390_TXBUF_SIZE))
+ bound = eth_memsize;
+ outb(eth_nic_base+D8390_P0_BOUND, bound-1);
+ if (ret && (type == ARP)) {
+ struct arprequest *arpreq;
+ unsigned long reqip;
+ arpreq = (struct arprequest *)&packet[ETHER_HDR_SIZE];
+ convert_ipaddr(&reqip, arpreq->tipaddr);
+ if ((ntohs(arpreq->opcode) == ARP_REQUEST) &&
+ (reqip == arptable[ARP_CLIENT].ipaddr)) {
+ arpreq->opcode = htons(ARP_REPLY);
+ bcopy(arpreq->sipaddr, arpreq->tipaddr, 4);
+ bcopy(arpreq->shwaddr, arpreq->thwaddr, 6);
+ bcopy(arptable[ARP_CLIENT].node, arpreq->shwaddr, 6);
+ convert_ipaddr(arpreq->sipaddr, &reqip);
+ eth_transmit(arpreq->thwaddr, ARP, sizeof(struct arprequest),
+ arpreq);
+ return(0);
+ }
+ }
+ return(ret);
+}
+
+#ifdef INCLUDE_NE
+/**************************************************************************
+ETH_PIO_READ - Read a frame via Programmed I/O
+**************************************************************************/
+eth_pio_read(src, dst, cnt, init)
+ unsigned short src;
+ unsigned char *dst;
+ unsigned short cnt;
+ int init;
+{
+ if (cnt & 1) cnt++;
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA);
+ outb(eth_nic_base + D8390_P0_RBCR0, cnt);
+ outb(eth_nic_base + D8390_P0_RBCR1, cnt>>8);
+ outb(eth_nic_base + D8390_P0_RSAR0, src);
+ outb(eth_nic_base + D8390_P0_RSAR1, src>>8);
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD0 |
+ D8390_COMMAND_STA);
+ if (eth_flags & FLAG_16BIT) {
+ while (cnt) {
+ *((unsigned short *)dst) = inw(eth_asic_base + NE_DATA);
+ dst += 2;
+ cnt -= 2;
+ }
+ }
+ else {
+ while (cnt--)
+ *(dst++) = inb(eth_asic_base + NE_DATA);
+ }
+}
+
+/**************************************************************************
+ETH_PIO_WRITE - Write a frame via Programmed I/O
+**************************************************************************/
+eth_pio_write(src, dst, cnt, init)
+ unsigned char *src;
+ unsigned short dst;
+ unsigned short cnt;
+ int init;
+{
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA);
+ outb(eth_nic_base + D8390_P0_ISR, D8390_ISR_RDC);
+ outb(eth_nic_base + D8390_P0_RBCR0, cnt);
+ outb(eth_nic_base + D8390_P0_RBCR1, cnt>>8);
+ outb(eth_nic_base + D8390_P0_RSAR0, dst);
+ outb(eth_nic_base + D8390_P0_RSAR1, dst>>8);
+ outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD1 |
+ D8390_COMMAND_STA);
+ if (eth_flags & FLAG_16BIT) {
+ if (cnt & 1) cnt++; /* Round up */
+ while (cnt) {
+ outw(eth_asic_base + NE_DATA, *((unsigned short *)src));
+ src += 2;
+ cnt -= 2;
+ }
+ }
+ else {
+ while (cnt--)
+ outb(eth_asic_base + NE_DATA, *(src++));
+ }
+ while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
+ != D8390_ISR_RDC);
+}
+#else
+/**************************************************************************
+ETH_PIO_READ - Dummy routine when NE2000 not compiled in
+**************************************************************************/
+eth_pio_read() {}
+#endif
diff --git a/sys/pc98/boot/netboot/ns8390.h b/sys/pc98/boot/netboot/ns8390.h
new file mode 100644
index 0000000..a6616bc
--- /dev/null
+++ b/sys/pc98/boot/netboot/ns8390.h
@@ -0,0 +1,252 @@
+/**************************************************************************
+NETBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: Jun/94
+
+**************************************************************************/
+
+#define MEM_8192 32
+#define MEM_16384 64
+#define MEM_32768 128
+
+/**************************************************************************
+Western Digital/SMC Board Definitions
+**************************************************************************/
+#define WD_LOW_BASE 0x200
+#define WD_HIGH_BASE 0x3e0
+#ifndef WD_DEFAULT_MEM
+#define WD_DEFAULT_MEM 0xD0000
+#endif
+#define WD_NIC_ADDR 0x10
+
+/**************************************************************************
+Western Digital/SMC ASIC Addresses
+**************************************************************************/
+#define WD_MSR 0x00
+#define WD_ICR 0x01
+#define WD_IAR 0x02
+#define WD_BIO 0x03
+#define WD_IRR 0x04
+#define WD_LAAR 0x05
+#define WD_IJR 0x06
+#define WD_GP2 0x07
+#define WD_LAR 0x08
+#define WD_BID 0x0E
+
+#define WD_ICR_16BIT 0x01
+
+#define WD_MSR_MENB 0x40
+
+#define WD_LAAR_L16EN 0x40
+#define WD_LAAR_M16EN 0x80
+
+#define WD_SOFTCONFIG 0x20
+
+/**************************************************************************
+Western Digital/SMC Board Types
+**************************************************************************/
+#define TYPE_WD8003S 0x02
+#define TYPE_WD8003E 0x03
+#define TYPE_WD8013EBT 0x05
+#define TYPE_WD8003W 0x24
+#define TYPE_WD8003EB 0x25
+#define TYPE_WD8013W 0x26
+#define TYPE_WD8013EP 0x27
+#define TYPE_WD8013WC 0x28
+#define TYPE_WD8013EPC 0x29
+#define TYPE_SMC8216T 0x2a
+#define TYPE_SMC8216C 0x2b
+#define TYPE_SMC8416T 0x00 /* Bogus entries: the 8416 generates the */
+#define TYPE_SMC8416C 0x00 /* the same codes as the 8216. */
+#define TYPE_SMC8013EBP 0x2c
+
+#ifdef INCLUDE_WD
+struct wd_board {
+ char *name;
+ char id;
+ char flags;
+ char memsize;
+} wd_boards[] = {
+ {"WD8003S", TYPE_WD8003S, 0, MEM_8192},
+ {"WD8003E", TYPE_WD8003E, 0, MEM_8192},
+ {"WD8013EBT", TYPE_WD8013EBT, FLAG_16BIT, MEM_16384},
+ {"WD8003W", TYPE_WD8003W, 0, MEM_8192},
+ {"WD8003EB", TYPE_WD8003EB, 0, MEM_8192},
+ {"WD8013W", TYPE_WD8013W, FLAG_16BIT, MEM_16384},
+ {"WD8003EP/WD8013EP",
+ TYPE_WD8013EP, 0, MEM_8192},
+ {"WD8013WC", TYPE_WD8013WC, FLAG_16BIT, MEM_16384},
+ {"WD8013EPC", TYPE_WD8013EPC, FLAG_16BIT, MEM_16384},
+ {"SMC8216T", TYPE_SMC8216T, FLAG_16BIT | FLAG_790, MEM_16384},
+ {"SMC8216C", TYPE_SMC8216C, FLAG_16BIT | FLAG_790, MEM_16384},
+ {"SMC8416T", TYPE_SMC8416T, FLAG_16BIT | FLAG_790, MEM_8192},
+ {"SMC8416C/BT", TYPE_SMC8416C, FLAG_16BIT | FLAG_790, MEM_8192},
+ {"SMC8013EBP", TYPE_SMC8013EBP,FLAG_16BIT, MEM_16384},
+ {NULL, 0, 0}
+};
+#endif
+/**************************************************************************
+3com 3c503 definitions
+**************************************************************************/
+
+#ifndef _3COM_BASE
+#define _3COM_BASE 0x300
+#endif
+
+#define _3COM_TX_PAGE_OFFSET_8BIT 0x20
+#define _3COM_TX_PAGE_OFFSET_16BIT 0x0
+#define _3COM_RX_PAGE_OFFSET_16BIT 0x20
+
+#define _3COM_ASIC_OFFSET 0x400
+#define _3COM_NIC_OFFSET 0x0
+
+#define _3COM_PSTR 0
+#define _3COM_PSPR 1
+
+#define _3COM_BCFR 3
+#define _3COM_BCFR_2E0 0x01
+#define _3COM_BCFR_2A0 0x02
+#define _3COM_BCFR_280 0x04
+#define _3COM_BCFR_250 0x08
+#define _3COM_BCFR_350 0x10
+#define _3COM_BCFR_330 0x20
+#define _3COM_BCFR_310 0x40
+#define _3COM_BCFR_300 0x80
+#define _3COM_PCFR 4
+#define _3COM_PCFR_C8000 0x10
+#define _3COM_PCFR_CC000 0x20
+#define _3COM_PCFR_D8000 0x40
+#define _3COM_PCFR_DC000 0x80
+#define _3COM_CR 6
+#define _3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define _3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define _3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define _3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define _3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define _3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define _3COM_CR_DDIR 0x40 /* DMA direction select */
+#define _3COM_CR_START 0x80 /* Start DMA controller */
+#define _3COM_GACFR 5
+#define _3COM_GACFR_MBS0 0x01
+#define _3COM_GACFR_MBS1 0x02
+#define _3COM_GACFR_MBS2 0x04
+#define _3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define _3COM_GACFR_TEST 0x10 /* for GA testing */
+#define _3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define _3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define _3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+#define _3COM_STREG 7
+#define _3COM_STREG_REV 0x07 /* GA revision */
+#define _3COM_STREG_DIP 0x08 /* DMA in progress */
+#define _3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define _3COM_STREG_OFLW 0x20 /* Overflow */
+#define _3COM_STREG_UFLW 0x40 /* Underflow */
+#define _3COM_STREG_DPRDY 0x80 /* Data port ready */
+#define _3COM_IDCFR 8
+#define _3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define _3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define _3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define _3COM_IDCFR_UNUSED 0x08 /* not used */
+#define _3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define _3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define _3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define _3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+#define _3COM_IRQ2 2
+#define _3COM_IRQ3 3
+#define _3COM_IRQ4 4
+#define _3COM_IRQ5 5
+#define _3COM_DAMSB 9
+#define _3COM_DALSB 0x0a
+#define _3COM_VPTR2 0x0b
+#define _3COM_VPTR1 0x0c
+#define _3COM_VPTR0 0x0d
+#define _3COM_RFMSB 0x0e
+#define _3COM_RFLSB 0x0f
+
+/**************************************************************************
+NE1000/2000 definitions
+**************************************************************************/
+#ifndef NE_BASE
+#define NE_BASE 0x320
+#endif
+#ifdef PC98
+#define NE_ASIC_OFFSET 0x00
+#define NE_RESET 0x300 /* Used to reset card */
+#define NE_DATA 0x200 /* Used to read/write NIC mem */
+#else
+#define NE_ASIC_OFFSET 0x10
+#define NE_RESET 0x0F /* Used to reset card */
+#define NE_DATA 0x00 /* Used to read/write NIC mem */
+#endif
+
+/**************************************************************************
+8390 Register Definitions
+**************************************************************************/
+#define D8390_P0_COMMAND 0x00
+#define D8390_P0_PSTART 0x01
+#define D8390_P0_PSTOP 0x02
+#define D8390_P0_BOUND 0x03
+#define D8390_P0_TSR 0x04
+#define D8390_P0_TPSR 0x04
+#define D8390_P0_TBCR0 0x05
+#define D8390_P0_TBCR1 0x06
+#define D8390_P0_ISR 0x07
+#define D8390_P0_RSAR0 0x08
+#define D8390_P0_RSAR1 0x09
+#define D8390_P0_RBCR0 0x0A
+#define D8390_P0_RBCR1 0x0B
+#define D8390_P0_RSR 0x0C
+#define D8390_P0_RCR 0x0C
+#define D8390_P0_TCR 0x0D
+#define D8390_P0_DCR 0x0E
+#define D8390_P0_IMR 0x0F
+#define D8390_P1_COMMAND 0x00
+#define D8390_P1_PAR0 0x01
+#define D8390_P1_PAR1 0x02
+#define D8390_P1_PAR2 0x03
+#define D8390_P1_PAR3 0x04
+#define D8390_P1_PAR4 0x05
+#define D8390_P1_PAR5 0x06
+#define D8390_P1_CURR 0x07
+#define D8390_P1_MAR0 0x08
+
+#define D8390_COMMAND_PS0 0x0 /* Page 0 select */
+#define D8390_COMMAND_PS1 0x40 /* Page 1 select */
+#define D8390_COMMAND_PS2 0x80 /* Page 2 select */
+#define D8390_COMMAND_RD2 0x20 /* Remote DMA control */
+#define D8390_COMMAND_RD1 0x10
+#define D8390_COMMAND_RD0 0x08
+#define D8390_COMMAND_TXP 0x04 /* transmit packet */
+#define D8390_COMMAND_STA 0x02 /* start */
+#define D8390_COMMAND_STP 0x01 /* stop */
+
+#define D8390_RCR_MON 0x20 /* monitor mode */
+
+#define D8390_DCR_FT1 0x40
+#define D8390_DCR_LS 0x08 /* Loopback select */
+#define D8390_DCR_WTS 0x01 /* Word transfer select */
+
+#define D8390_ISR_PRX 0x01 /* successful recv */
+#define D8390_ISR_PTX 0x02 /* successful xmit */
+#define D8390_ISR_RXE 0x04 /* receive error */
+#define D8390_ISR_TXE 0x08 /* transmit error */
+#define D8390_ISR_OVW 0x10 /* Overflow */
+#define D8390_ISR_CNT 0x20 /* Counter overflow */
+#define D8390_ISR_RDC 0x40 /* Remote DMA complete */
+#define D8390_ISR_RST 0x80 /* reset */
+
+#define D8390_RSTAT_PRX 0x01 /* successful recv */
+#define D8390_RSTAT_CRC 0x02 /* CRC error */
+#define D8390_RSTAT_FAE 0x04 /* Frame alignment error */
+#define D8390_RSTAT_OVER 0x08 /* overflow */
+
+#define D8390_TXBUF_SIZE 6
+#define D8390_RXBUF_END 32
+#define D8390_PAGE_SIZE 256
+
+struct ringbuffer {
+ unsigned char status;
+ unsigned char bound;
+ unsigned short len;
+};
diff --git a/sys/pc98/boot/netboot/rpc.c b/sys/pc98/boot/netboot/rpc.c
new file mode 100644
index 0000000..f035248
--- /dev/null
+++ b/sys/pc98/boot/netboot/rpc.c
@@ -0,0 +1,187 @@
+/***********************************************************************
+
+Remote Procedure Call Support Routines
+
+Author: Martin Renters
+ Date: Oct/1994
+
+***********************************************************************/
+
+#include "netboot.h"
+
+int rpc_id;
+extern char packet[];
+extern struct nfs_diskless nfsdiskless;
+extern int hostnamelen;
+/***************************************************************************
+
+RPCLOOKUP: Lookup RPC Port numbers
+
+***************************************************************************/
+rpclookup(addr, prog, ver)
+ int addr, prog, ver;
+{
+ struct rpc_t buf, *rpc;
+ char *rpcptr;
+ int retries = MAX_RPC_RETRIES;
+ rpcptr = sprintf(&buf.u.data,"%L%L%L%L%L%L%L%L%L%L%L%L%L%L",
+ rpc_id, MSG_CALL, 2, PROG_PORTMAP, 2, PORTMAP_LOOKUP,
+ 0, 0, 0, 0, prog, ver, IP_UDP, 0);
+ while(retries--) {
+ udp_transmit(arptable[addr].ipaddr, RPC_SOCKET,
+ SUNRPC, rpcptr - (char *)&buf, &buf);
+ if (await_reply(AWAIT_RPC, rpc_id, NULL)) {
+ rpc = (struct rpc_t *)&packet[ETHER_HDR_SIZE];
+ if (rpc->u.reply.rstatus == rpc->u.reply.verifier ==
+ rpc->u.reply.astatus == 0)
+ return(ntohl(rpc->u.reply.data[0]));
+ else {
+ rpc_err(rpc);
+ return(-1);
+ }
+ }
+ }
+ return(-1);
+}
+
+/***************************************************************************
+
+NFS_MOUNT: Mount an NFS Filesystem
+
+***************************************************************************/
+nfs_mount(server, port, path, fh)
+ int server;
+ int port;
+ char *path;
+ char *fh;
+{
+ struct rpc_t buf, *rpc;
+ char *rpcptr;
+ int retries = MAX_RPC_RETRIES;
+ rpcptr = sprintf(&buf.u.data,"%L%L%L%L%L%L%L%L%L%S%L%L%L%L%L%L%L%S",
+ rpc_id, MSG_CALL, 2, PROG_MOUNT, 1, MOUNT_ADDENTRY,
+ 1, hostnamelen + 28,0,&nfsdiskless.my_hostnam,0,0,2,0,0,0,0,
+ path);
+ while(retries--) {
+ udp_transmit(arptable[server].ipaddr, RPC_SOCKET,
+ port, rpcptr - (char *)&buf, &buf);
+ if (await_reply(AWAIT_RPC, rpc_id, NULL)) {
+ rpc = (struct rpc_t *)&packet[ETHER_HDR_SIZE];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_err(rpc);
+ return(-(ntohl(rpc->u.reply.data[0])));
+ } else {
+ bcopy(&rpc->u.reply.data[1],fh, 32);
+ return(0);
+ }
+ }
+ }
+ return(-1);
+}
+
+
+/***************************************************************************
+
+NFS_LOOKUP: Lookup Pathname
+
+***************************************************************************/
+nfs_lookup(server, port, fh, path, file_fh)
+ int server;
+ int port;
+ char *fh;
+ char *path;
+ char *file_fh;
+{
+ struct rpc_t buf, *rpc;
+ char *rpcptr;
+ int retries = MAX_RPC_RETRIES;
+ rpcptr = sprintf(&buf.u.data,"%L%L%L%L%L%L%L%L%L%S%L%L%L%L%L%L%L%M%S",
+ rpc_id, MSG_CALL, 2, PROG_NFS, 2, NFS_LOOKUP,
+ 1, hostnamelen + 28,0,&nfsdiskless.my_hostnam,0,0,2,0,0,0,0,
+ 32, fh, path);
+ while(retries--) {
+ udp_transmit(arptable[server].ipaddr, RPC_SOCKET,
+ port, rpcptr - (char *)&buf, &buf);
+ if (await_reply(AWAIT_RPC, rpc_id, NULL)) {
+ rpc = (struct rpc_t *)&packet[ETHER_HDR_SIZE];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_err(rpc);
+ return(-(ntohl(rpc->u.reply.data[0])));
+ } else {
+ bcopy(&rpc->u.reply.data[1],file_fh, 32);
+ return(0);
+ }
+ }
+ }
+ return(-1);
+}
+
+/***************************************************************************
+
+NFS_READ: Read File
+
+***************************************************************************/
+nfs_read(server, port, fh, offset, len, buffer)
+ int server;
+ int port;
+ char *fh;
+ int offset, len;
+ char *buffer;
+{
+ struct rpc_t buf, *rpc;
+ char *rpcptr;
+ int retries = MAX_RPC_RETRIES;
+ int rlen;
+ rpcptr = sprintf(&buf.u.data,
+ "%L%L%L%L%L%L%L%L%L%S%L%L%L%L%L%L%L%M%L%L%L",
+ rpc_id, MSG_CALL, 2, PROG_NFS, 2, NFS_READ,
+ 1, hostnamelen + 28,0,&nfsdiskless.my_hostnam,0,0,2,0,0,0,0,
+ 32, fh, offset, len, 0);
+ while(retries--) {
+ udp_transmit(arptable[server].ipaddr, RPC_SOCKET,
+ port, rpcptr - (char *)&buf, &buf);
+ if (await_reply(AWAIT_RPC, rpc_id, NULL)) {
+ rpc = (struct rpc_t *)&packet[ETHER_HDR_SIZE];
+ if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
+ rpc->u.reply.astatus || rpc->u.reply.data[0]) {
+ rpc_err(rpc);
+ return(-(ntohl(rpc->u.reply.data[0])));
+ } else {
+ rlen = ntohl(rpc->u.reply.data[18]);
+ if (len < rlen) rlen = len;
+ if (len > rlen) printf("short read\r\n");
+ bcopy(&rpc->u.reply.data[19], buffer, rlen);
+ return(rlen);
+ }
+ }
+ }
+ return(-1);
+}
+
+/***************************************************************************
+
+RPC_ERR - Print RPC Errors
+
+***************************************************************************/
+rpc_err(rpc)
+ struct rpc_t *rpc;
+{
+ int err = ntohl(rpc->u.reply.data[0]);
+ printf("***RPC Error: (%d,%d,%d):\r\n ",
+ ntohl(rpc->u.reply.rstatus),
+ ntohl(rpc->u.reply.verifier),
+ ntohl(rpc->u.reply.astatus));
+}
+
+nfs_err(err)
+ int err;
+{
+ err = -err;
+ if (err == NFSERR_PERM) printf("Not owner");
+ else if (err == NFSERR_NOENT) printf("No such file or directory");
+ else if (err == NFSERR_ACCES) printf("Permission denied");
+ else printf("Error %d",err);
+ printf("\r\n");
+}
diff --git a/sys/pc98/boot/netboot/start2.S b/sys/pc98/boot/netboot/start2.S
new file mode 100644
index 0000000..5bbc96c
--- /dev/null
+++ b/sys/pc98/boot/netboot/start2.S
@@ -0,0 +1,349 @@
+
+#define STACKADDR 0xe000 /* Needs to be end of bss + stacksize */
+#define KERN_CODE_SEG 0x08
+#define KERN_DATA_SEG 0x10
+#define REAL_MODE_SEG 0x18
+#define CR0_PE 1
+
+#define opsize .byte 0x66
+#define addrsize .byte 0x67
+
+/* At entry, the processor is in 16 bit real mode and the code is being
+ * executed from an address it was not linked to. Code must be pic and
+ * 32 bit sensitive until things are fixed up.
+ */
+#ifdef BOOTROM
+ .word 0xaa55 /* bios extension signature */
+ .byte (ROMSIZE>>9) /* no. of 512B blocks */
+ jmp 1f /* enter from bios here */
+ .byte 0 /* checksum */
+#ifdef PCI
+ .ascii "FreeBSD boot ROM.." /* 18 bytes total */
+ .word 0x1a
+/* PCI rom data structure format */
+ .ascii "PCIR" /* signature */
+ .word PCI_VENDOR /* vendor ID */
+ .word PCI_DEVICE /* device ID */
+ .word 0 /* vital product data */
+ .word 0x0018 /* PCI data structure */
+ .byte 0 /* PCI data struct. rev -- 0 */
+ .byte PCI_CLASS /* Class code */
+ .word (ROMSIZE>>9) /* no. of 512B blocks */
+ .byte 0,0 /* rev. level */
+ .byte 0 /* code type - 0 =x86 */
+ .byte 0x80 /* indicator of last block */
+ .word 0 /* reserved */
+#endif
+1: push %eax
+ push %ds
+ xor %eax,%eax
+ mov %ax,%ds
+ .byte 0xa1 /* MOV 0x304,%ax */
+ .word 0x304
+ .byte 0x3d /* CMP $0x4d52, %ax == 'MR' */
+ .word 0x4d52
+ jz 2f
+ .byte 0xa1 /* MOV 0x64, %ax */
+ .word 0x64
+ .byte 0xa3 /* MOV %ax, 0x300 */
+ .word 0x300
+ .byte 0xa1 /* MOV 0x66, %ax */
+ .word 0x66
+ .byte 0xa3 /* MOV %ax, 0x302 */
+ .word 0x302
+ .byte 0xb8 /* MOV $_start-RELOCADDR, %ax */
+ .word (_start-RELOC)
+ .byte 0xa3 /* MOV %ax, 0x64 */
+ .word 0x64
+ mov %cs,%ax
+ .byte 0xa3 /* MOV %ax, 0x66 */
+ .word 0x66
+ .byte 0xb8 /* MOV 'MR',%ax */
+ .word 0x4d52
+ .byte 0xa3 /* MOV %ax, 0x304 */
+ .word 0x304
+2: pop %ds
+ pop %eax
+ lret
+#endif
+
+/**************************************************************************
+START - Where all the fun begins....
+**************************************************************************/
+ .globl _start
+_start:
+ cli
+ cld
+#ifdef BOOTROM /* relocate ourselves */
+ xor %esi, %esi /* zero for ROMs */
+#else
+ .byte 0xbe /* MOV $0x100,%si -- 100h for .COM */
+ .word 0x100
+#endif
+ xor %edi,%edi
+ .byte 0xb8 /* MOV $RELOCADDR>>4, %ax */
+ .word (RELOC>>4)
+ mov %ax, %es
+ .byte 0xb9 /* MOV $ROMSIZE, %cx */
+ .word ROMSIZE
+ cs
+ rep
+ movsb
+ opsize
+ ljmp $(RELOC>>4),$1f-RELOC /* Jmp to RELOC:1f */
+1:
+ nop
+ mov %cs,%ax
+ mov %ax,%ds
+ mov %ax,%es
+ mov %ax,%ss
+ .byte 0xb8 /* MOV $STACKADDR, %ax */
+ .word STACKADDR
+ mov %eax,%esp
+ opsize
+ call _real_to_prot
+ call _main
+ .globl _exit
+_exit:
+ call _prot_to_real
+#ifdef BOOTROM
+ xor %eax,%eax
+ mov %ax,%ds
+ .byte 0xa1 /* MOV 0x302, %ax */
+ .word 0x302
+ push %eax
+ .byte 0xa1 /* MOV 0x300, %ax */
+ .word 0x300
+ push %eax
+ lret
+#else
+ int $0x19
+#endif
+
+/**************************************************************************
+CURRTICKS - Get Time
+**************************************************************************/
+ .globl _currticks
+_currticks:
+ push %ebp
+ mov %esp,%ebp
+ push %ecx
+ push %edx
+ xor %edx,%edx
+ call _prot_to_real
+ xor %eax,%eax
+ int $0x1a
+ opsize
+ call _real_to_prot
+ xor %eax,%eax
+ shl $16,%ecx
+ mov %edx,%eax
+ or %ecx,%eax
+ pop %edx
+ pop %ecx
+ pop %ebp
+ ret
+
+/**************************************************************************
+PUTCHAR - Print a character
+**************************************************************************/
+ .globl _putchar
+_putchar:
+ push %ebp
+ mov %esp,%ebp
+ push %ecx
+ push %ebx
+ movb 8(%ebp),%cl
+ call _prot_to_real
+ opsize
+ mov $1,%ebx
+ movb $0x0e,%ah
+ movb %cl,%al
+ int $0x10
+ opsize
+ call _real_to_prot
+ pop %ebx
+ pop %ecx
+ pop %ebp
+ ret
+
+/**************************************************************************
+GETCHAR - Get a character
+**************************************************************************/
+ .globl _getchar
+_getchar:
+ push %ebp
+ mov %esp,%ebp
+ push %ebx
+ call _prot_to_real
+ movb $0x0,%ah
+ int $0x16
+ movb %al,%bl
+ opsize
+ call _real_to_prot
+ xor %eax,%eax
+ movb %bl,%al
+ pop %ebx
+ pop %ebp
+ ret
+
+/**************************************************************************
+ISKEY - Check for keyboard interrupt
+**************************************************************************/
+ .globl _iskey
+_iskey:
+ push %ebp
+ mov %esp,%ebp
+ push %ebx
+ call _prot_to_real
+ xor %ebx,%ebx
+ movb $0x1,%ah
+ int $0x16
+ opsize
+ jz 1f
+ movb %al,%bl
+1:
+ opsize
+ call _real_to_prot
+ xor %eax,%eax
+ movb %bl,%al
+ pop %ebx
+ pop %ebp
+ ret
+
+
+/*
+ * C library -- _setjmp, _longjmp
+ *
+ * longjmp(a,v)
+ * will generate a "return(v)" from the last call to
+ * setjmp(a)
+ * by restoring registers from the stack.
+ * The previous signal state is restored.
+ */
+
+ .globl _setjmp
+_setjmp:
+ movl 4(%esp),%ecx
+ movl 0(%esp),%edx
+ movl %edx, 0(%ecx)
+ movl %ebx, 4(%ecx)
+ movl %esp, 8(%ecx)
+ movl %ebp,12(%ecx)
+ movl %esi,16(%ecx)
+ movl %edi,20(%ecx)
+ movl %eax,24(%ecx)
+ movl $0,%eax
+ ret
+
+ .globl _longjmp
+_longjmp:
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ movl 0(%edx),%ecx
+ movl 4(%edx),%ebx
+ movl 8(%edx),%esp
+ movl 12(%edx),%ebp
+ movl 16(%edx),%esi
+ movl 20(%edx),%edi
+ cmpl $0,%eax
+ jne 1f
+ movl $1,%eax
+1: movl %ecx,0(%esp)
+ ret
+
+/**************************************************************************
+___MAIN - Dummy to keep GCC happy
+**************************************************************************/
+ .globl ___main
+___main:
+ ret
+
+/**************************************************************************
+REAL_TO_PROT - Go from REAL mode to Protected Mode
+**************************************************************************/
+ .globl _real_to_prot
+_real_to_prot:
+ cli
+ cs
+ addrsize
+ lgdt gdtarg-RELOC
+ mov %cr0, %eax
+ opsize
+ or $CR0_PE, %eax
+ mov %eax, %cr0 /* turn on protected mode */
+
+ /* jump to relocation, flush prefetch queue, and reload %cs */
+ opsize
+ ljmp $KERN_CODE_SEG, $1f
+1:
+ /* reload other segment registers */
+ movl $KERN_DATA_SEG, %eax
+ movl %ax, %ds
+ movl %ax, %es
+ movl %ax, %ss
+ add $RELOC,%esp /* Fix up stack pointer */
+ pop %eax /* Fix up return Address */
+ add $RELOC,%eax
+ push %eax
+ ret
+
+
+/**************************************************************************
+PROT_TO_REAL - Go from Protected Mode to REAL Mode
+**************************************************************************/
+ .globl _prot_to_real
+_prot_to_real:
+ pop %eax
+ sub $RELOC,%eax /* Adjust return address */
+ push %eax
+ sub $RELOC,%esp /* Adjust stack pointer */
+ ljmp $REAL_MODE_SEG, $1f /* jump to a 16 bit segment */
+1:
+ /* clear the PE bit of CR0 */
+ mov %cr0, %eax
+ opsize
+ andl $0!CR0_PE, %eax
+ mov %eax, %cr0
+
+ /* make intersegment jmp to flush the processor pipeline
+ * and reload CS register
+ */
+ opsize
+ ljmp $(RELOC)>>4, $2f-RELOC
+2:
+ /* we are in real mode now
+ * set up the real mode segment registers : DS, SS, ES
+ */
+ mov %cs, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %ss
+ sti
+ opsize
+ ret
+
+/**************************************************************************
+GLOBAL DESCRIPTOR TABLE
+**************************************************************************/
+ .align 4
+gdt:
+ .word 0, 0
+ .byte 0, 0x00, 0x00, 0
+
+ /* code segment */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+
+ /* data segment */
+ .word 0xffff, 0
+ .byte 0, 0x93, 0xcf, 0
+
+ /* 16 bit real mode */
+ .word 0xffff, 0
+ .byte 0, 0x9b, 0x0f, 0
+
+ .align 4
+gdtarg:
+ .word 0x1f /* limit */
+ .long gdt /* addr */
diff --git a/sys/pc98/conf/GENERIC98 b/sys/pc98/conf/GENERIC98
new file mode 100644
index 0000000..3df81d4
--- /dev/null
+++ b/sys/pc98/conf/GENERIC98
@@ -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/conf/Makefile.pc98 b/sys/pc98/conf/Makefile.pc98
new file mode 100644
index 0000000..d0915fa
--- /dev/null
+++ b/sys/pc98/conf/Makefile.pc98
@@ -0,0 +1,196 @@
+# Makefile for FreeBSD(98) after:
+#
+# Makefile.i386 -- with config changes.
+# Copyright 1990 W. Jolitz
+# from: @(#)Makefile.i386 7.1 5/10/91
+# $Id: Makefile.i386,v 1.84 1996/06/08 23:27:16 jkh Exp $
+#
+# Makefile for FreeBSD
+#
+# This makefile is constructed from a machine description:
+# config machineid
+# Most changes should be made in the machine description
+# /sys/i386/conf/``machineid''
+# after which you should do
+# config machineid
+# Generic makefile changes should be made in
+# /sys/i386/conf/Makefile.i386
+# after which config should be rerun for all machines.
+#
+CC?= cc
+CPP?= cpp
+LD?= /usr/bin/ld
+
+.if exists(./@/.)
+S= ./@
+.else
+S= ../..
+.endif
+PC98= ${S}/pc98
+I386= ${S}/i386
+
+CWARNFLAGS?= -W -Wreturn-type -Wcomment -Wredundant-decls -Wimplicit \
+ -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes \
+ -Winline
+#
+# The following flags are next up for working on:
+# -Wall
+#
+# When working on removing warnings from code, the `-Werror' flag should be
+# of material assistance.
+#
+COPTFLAGS?=-O
+# Not ready for -I- yet. #include "foo.h" where foo.h is in the srcdir fails.
+INCLUDES= -nostdinc -I. -I$S -I$S/sys
+# This hack is to allow kernel compiles to succeed on machines w/out srcdist
+.if exists($S/../include)
+INCLUDES+= -I$S/../include
+.else
+INCLUDES+= -I/usr/include
+.endif
+COPTS= ${INCLUDES} ${IDENT} -DKERNEL
+CFLAGS= ${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS}
+LOAD_ADDRESS?= F0100000
+
+NORMAL_C= ${CC} -c ${CFLAGS} ${PROF} $<
+NORMAL_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
+# XXX LOCORE means "don't declare C stuff" not "for locore.s".
+NORMAL_S= ${CC} -c -x assembler-with-cpp -DLOCORE ${COPTS} $<
+DRIVER_C= ${CC} -c ${CFLAGS} ${PROF} $<
+DRIVER_C_C= ${CC} -c ${CFLAGS} ${PROF} ${PARAM} $<
+DRIVER_S= ${CC} -c -x assembler-with-cpp -DLOCORE ${COPTS} $<
+PROFILE_C= ${CC} -c ${CFLAGS} ${PARAM} $<
+
+SYSTEM_CFILES= ioconf.c param.c vnode_if.c config.c
+SYSTEM_SFILES= ${PC98}/i386/locore.s
+SYSTEM_OBJS= locore.o vnode_if.o ${OBJS} ioconf.o param.o config.o
+SYSTEM_DEP= Makefile symbols.exclude symbols.sort ${SYSTEM_OBJS}
+SYSTEM_LD_HEAD= @echo loading $@; rm -f $@
+SYSTEM_LD= @${LD} -Bstatic -Z -T ${LOAD_ADDRESS} -o $@ -X ${SYSTEM_OBJS} vers.o
+.if ${CFLAGS:M-g} == ""
+SYMORDER_EXCLUDE=-x symbols.exclude
+.endif
+SYSTEM_LD_TAIL= @echo rearranging symbols; \
+ symorder -m ${SYMORDER_EXCLUDE} symbols.sort $@; \
+ size $@; chmod 755 $@
+
+%BEFORE_DEPEND
+
+%OBJS
+
+%CFILES
+
+%SFILES
+
+%LOAD
+
+%CLEAN
+
+clean:
+ rm -f *.o *.s eddep errs genassym kernel linterrs \
+ makelinks param.c symbols.exclude symbols.sort tags \
+ vers.c vnode_if.c vnode_if.h ${CLEAN}
+
+#lint: /tmp param.c
+# @lint -hbxn -DGENERIC -Dvolatile= ${COPTS} ${PARAM} \
+# ${PC98}/i386/Locore.c ${CFILES} ioconf.c param.c | \
+# grep -v 'struct/union .* never defined' | \
+# grep -v 'possible pointer alignment problem'
+
+symbols.exclude: Makefile
+ echo "gcc2_compiled." >symbols.exclude
+ echo "___gnu_compiled_c" >>symbols.exclude
+
+symbols.sort: ${I386}/i386/symbols.raw
+ grep -v '^#' ${I386}/i386/symbols.raw \
+ | sed 's/^ //' | sort -u > symbols.sort
+
+locore.o: ${PC98}/i386/locore.s assym.s
+ ${NORMAL_S}
+
+# everything potentially depends on the Makefile since everything potentially
+# depends on the options. Some things are more dependent on the Makefile for
+# historical reasons.
+machdep.o: Makefile
+
+# the following is necessary because autoconf.o depends on #if GENERIC
+autoconf.o: Makefile
+
+# XXX - may no longer be needed
+locore.o: Makefile
+
+# depends on KDB (cons.o also depends on GENERIC)
+trap.o cons.o: Makefile
+
+# this rule stops ./assym.s in .depend from causing problems
+./assym.s: assym.s
+
+assym.s: genassym
+ ./genassym >assym.s
+
+# Some of the defines that genassym outputs may well depend on the
+# value of kernel options.
+genassym.o: ${I386}/i386/genassym.c Makefile
+ rm -f ./machine ; ln -s ${I386}/include ./machine
+ ${CC} -c ${CFLAGS} ${PARAM} -UKERNEL ${I386}/i386/genassym.c
+
+genassym: genassym.o
+ ${CC} -static ${CFLAGS} ${PARAM} genassym.o -o $@
+
+# XXX this assumes that the options for NORMAL_C* and DRIVER_C* are identical.
+depend: assym.s param.c vnode_if.h ${BEFORE_DEPEND}
+ rm -f .newdep
+ mkdep -a -f .newdep ${COPTS} ${CFILES} ${SYSTEM_CFILES}
+ mkdep -a -f .newdep ${COPTS} ${PARAM} -UKERNEL ${I386}/i386/genassym.c
+ MKDEP_CPP="${CPP}" ; export MKDEP_CPP ; \
+ mkdep -a -f .newdep -DLOCORE ${COPTS} ${SFILES} ${SYSTEM_SFILES}
+ rm -f .depend
+ mv -f .newdep .depend
+
+links:
+ egrep '#if' ${CFILES:Nswapkernel.c} | sed -f $S/conf/defines | \
+ sed -e 's/:.*//' -e 's/\.c/.o/' | sort -u > dontlink
+ echo ${CFILES:Nswapkernel.c} | tr -s ' ' '\12' | sed 's/\.c/.o/' | \
+ sort -u | comm -23 - dontlink | \
+ sed 's,../.*/\(.*.o\),rm -f \1;ln -s ../GENERIC/\1 \1,' > makelinks
+ sh makelinks && rm -f dontlink
+
+tags:
+ @echo "see $S/kern/Makefile for tags"
+
+install:
+ @if [ ! -f kernel ] ; then \
+ echo "You must first build your kernel before trying to install." ; \
+ exit 1 ; \
+ fi
+ chflags noschg /kernel
+ mv /kernel /kernel.old
+ if [ `sysctl -n kern.bootfile` = /kernel ] ; then \
+ sysctl -w kern.bootfile=/kernel.old ; \
+ mv -f /var/db/kvm_kernel.db /var/db/kvm_kernel.old.db ; \
+ fi
+ install -c -m 555 -o root -g wheel -fschg kernel /
+
+ioconf.o: ioconf.c $S/sys/param.h $S/sys/buf.h \
+ ${PC98}/pc98/pc98_device.h ${PC98}/pc98/pc98.h ${PC98}/pc98/icu.h
+ ${CC} -c ${CFLAGS} ioconf.c
+
+param.c: $S/conf/param.c
+ -rm -f param.c
+ cp $S/conf/param.c .
+
+param.o: param.c Makefile
+ ${CC} -c ${CFLAGS} ${PARAM} param.c
+
+vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP}
+ sh $S/conf/newvers.sh ${KERN_IDENT} ${IDENT}
+ ${CC} ${CFLAGS} -c vers.c
+
+vnode_if.c: $S/kern/vnode_if.sh $S/kern/vnode_if.src
+ sh $S/kern/vnode_if.sh $S/kern/vnode_if.src
+vnode_if.h: $S/kern/vnode_if.sh $S/kern/vnode_if.src
+ sh $S/kern/vnode_if.sh $S/kern/vnode_if.src
+
+%RULES
+
+# DO NOT DELETE THIS LINE -- make depend uses it
diff --git a/sys/pc98/conf/devices.pc98 b/sys/pc98/conf/devices.pc98
new file mode 100644
index 0000000..904a399
--- /dev/null
+++ b/sys/pc98/conf/devices.pc98
@@ -0,0 +1,17 @@
+# This file tells what major numbers the various possible swap devices have.
+#
+# $Id: devices.i386,v 1.9 1995/08/19 15:59:25 joerg Exp $
+#
+wd 0
+dk 1
+fd 2
+wt 3
+sd 4
+st 5
+cd 6
+mcd 7
+vn 15
+scd 16
+pcd 17
+wcd 19
+od 20
diff --git a/sys/pc98/conf/files.pc98 b/sys/pc98/conf/files.pc98
new file mode 100644
index 0000000..eed4f92
--- /dev/null
+++ b/sys/pc98/conf/files.pc98
@@ -0,0 +1,277 @@
+# This file tells config what files go into building a kernel,
+# files marked standard are always included.
+#
+# modified for PC-9801 after:
+# $Id: files.i386,v 1.136 1996/06/07 22:26:59 nate Exp $
+#
+aic7xxx_asm optional ahc device-driver \
+ dependency "$S/dev/aic7xxx/aic7xxx_asm.c" \
+ compile-with "${CC} -Wall -o $@ $>" \
+ no-obj no-implicit-rule \
+ clean "aic7xxx_asm"
+#
+aic7xxx_seq.h optional ahc device-driver \
+ compile-with "./aic7xxx_asm -o $@ $S/dev/aic7xxx/aic7xxx.seq" \
+ no-obj no-implicit-rule before-depend \
+ clean "aic7xxx_seq.h" \
+ dependency "$S/dev/aic7xxx/aic7xxx_reg.h $S/dev/aic7xxx/aic7xxx.seq aic7xxx_asm"
+#
+linux_genassym optional compat_linux \
+ dependency "$S/i386/linux/linux_genassym.c $S/i386/linux/linux.h" \
+ compile-with "${CC} ${CFLAGS} -o $@ $<" \
+ no-obj no-implicit-rule \
+ clean "linux_genassym"
+#
+linux_assym.h optional compat_linux \
+ dependency "linux_genassym" \
+ compile-with "./linux_genassym > $@" \
+ no-obj no-implicit-rule before-depend \
+ clean "linux_assym.h"
+#
+i386/scsi/93cx6.c optional ahc device-driver
+pc98/apm/apm.c optional apm device-driver
+i386/apm/apm_setup.s optional apm
+#i386/eisa/3c5x9.c optional ep device-driver
+#i386/eisa/aic7770.c optional ahc device-driver
+#i386/eisa/aha1742.c optional ahb device-driver
+#i386/eisa/bt74x.c optional bt device-driver
+#i386/eisa/eisaconf.c optional eisa
+pc98/i386/autoconf.c standard device-driver
+i386/i386/cons.c standard
+i386/i386/db_disasm.c optional ddb
+i386/i386/db_interface.c optional ddb
+i386/i386/db_trace.c optional ddb
+pc98/i386/exception.s standard
+i386/i386/in_cksum.c optional inet
+# locore.s needs to be handled in Makefile to put it first. Otherwise it's
+# now normal.
+# i386/i386/locore.s standard
+pc98/i386/machdep.c standard
+i386/i386/math_emulate.c optional math_emulate
+i386/i386/mem.c standard
+pc98/i386/microtime.s standard
+i386/i386/perfmon.c optional perfmon
+pc98/i386/pmap.c standard
+i386/i386/procfs_machdep.c standard
+pc98/i386/support.s standard
+i386/i386/swtch.s standard
+i386/i386/sys_machdep.c standard
+pc98/i386/trap.c standard
+pc98/i386/userconfig.c standard
+pc98/i386/vm_machdep.c standard
+i386/ibcs2/ibcs2_fcntl.c optional ibcs2
+i386/ibcs2/ibcs2_stat.c optional ibcs2
+i386/ibcs2/ibcs2_ipc.c optional ibcs2
+i386/ibcs2/ibcs2_msg.c optional ibcs2
+i386/ibcs2/ibcs2_misc.c optional ibcs2
+i386/ibcs2/ibcs2_other.c optional ibcs2
+i386/ibcs2/ibcs2_signal.c optional ibcs2
+i386/ibcs2/ibcs2_ioctl.c optional ibcs2
+i386/ibcs2/ibcs2_socksys.c optional ibcs2
+i386/ibcs2/ibcs2_sysi86.c optional ibcs2
+i386/ibcs2/ibcs2_util.c optional ibcs2
+i386/ibcs2/ibcs2_isc.c optional ibcs2
+i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2
+i386/ibcs2/ibcs2_xenix.c optional ibcs2
+i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2
+i386/ibcs2/ibcs2_errno.c optional ibcs2
+i386/ibcs2/ibcs2_sysent.c optional ibcs2
+i386/ibcs2/ibcs2_sysvec.c optional ibcs2
+i386/ibcs2/imgact_coff.c optional ibcs2
+pc98/pc98/bs/bs.c optional bs device-driver
+pc98/pc98/bs/bsfunc.c optional bs device-driver
+pc98/pc98/bs/bshw.c optional bs device-driver
+pc98/pc98/bs/bsif.c optional bs device-driver
+pc98/pc98/sbic55.c optional sbic device-driver
+#i386/pc98/aha1542.c optional aha device-driver
+pc98/pc98/aic6360.c optional aic device-driver
+pc98/pc98/b004.c optional bqu device-driver
+i386/pc98/bt742a.c optional bt device-driver
+i386/pc98/bt5xx-445.c optional bt device-driver
+pc98/pc98/clock.c standard
+pc98/isa/cronyx.c optional cx device-driver
+pc98/isa/ctx.c optional ctx device-driver
+pc98/isa/cx.c optional cx device-driver
+pc98/isa/cy.c optional cy device-driver
+pc98/pc98/diskslice_machdep.c standard
+pc98/pc98/atcompat_diskslice.c optional compat_atdisk
+i386/isa/elink.c optional ep device-driver
+#i386/isa/elink.c optional ie device-driver
+pc98/pc98/fd.c optional fd device-driver
+pc98/pc98/ft.c optional ft device-driver
+pc98/pc98/gpib.c optional gp device-driver
+pc98/isa/asc.c optional asc device-driver
+pc98/isa/gsc.c optional gsc device-driver
+pc98/isa/if_ar.c optional ar device-driver
+pc98/isa/if_cx.c optional cx device-driver
+pc98/pc98/if_ed.c optional ed device-driver
+pc98/pc98/if_el.c optional el device-driver
+pc98/pc98/if_ep.c optional ep device-driver
+pc98/pc98/if_fe.c optional fe device-driver
+#pc98/isa/if_ie.c optional ie device-driver
+#pc98/isa/if_ix.c optional ix device-driver
+#pc98/isa/if_le.c optional le device-driver
+#pc98/isa/if_lnc.c optional lnc device-driver
+#pc98/isa/if_ze.c optional ze device-driver
+pc98/pc98/if_zp.c optional zp device-driver
+pc98/pc98/pc98.c optional nec device-driver
+pc98/pc98/pc98.c optional epson device-driver
+pc98/isa/istallion.c optional stli device-driver
+pc98/isa/joy.c optional joy device-driver
+pc98/pc98/labpc.c optional labpc device-driver
+pc98/pc98/lpt.c optional lpt device-driver
+pc98/pc98/mcd.c optional mcd device-driver
+pc98/pc98/mse.c optional mse device-driver
+pc98/isa/ncr5380.c optional nca device-driver
+pc98/pc98/npx.c optional npx device-driver
+pc98/pc98/pcaudio.c optional pca device-driver
+pc98/pc98/matcd/matcd.c optional matcd device-driver
+pc98/pc98/pcibus.c optional pci device-driver
+pc98/pc98/pcicx.c optional ze device-driver
+pc98/pc98/pcicx.c optional zp device-driver
+pc98/isa/pcvt/pcvt_drv.c optional vt device-driver
+pc98/isa/pcvt/pcvt_ext.c optional vt device-driver
+pc98/isa/pcvt/pcvt_kbd.c optional vt device-driver
+pc98/isa/pcvt/pcvt_out.c optional vt device-driver
+pc98/isa/pcvt/pcvt_sup.c optional vt device-driver
+pc98/isa/pcvt/pcvt_vtf.c optional vt device-driver
+pc98/pc98/prof_machdep.c optional profiling-routine
+pc98/pc98/psm.c optional psm device-driver
+pc98/isa/qcam.c optional qcam device-driver
+pc98/isa/qcamio.c optional qcam device-driver
+pc98/pc98/random_machdep.c standard
+pc98/isa/rc.c optional rc device-driver
+pc98/pc98/scd.c optional scd device-driver
+pc98/isa/seagate.c optional sea device-driver
+pc98/isa/si.c optional si device-driver
+pc98/isa/si_code.c optional si device-driver
+pc98/pc98/sio.c optional sio device-driver
+pc98/pc98/sound/pcm86.c optional pcm device-driver
+pc98/pc98/sound/dev_table.c optional snd device-driver
+pc98/pc98/sound/soundcard.c optional snd device-driver
+pc98/pc98/sound/sound_switch.c optional snd device-driver
+pc98/pc98/sound/audio.c optional snd device-driver
+pc98/pc98/sound/dmabuf.c optional snd device-driver
+pc98/pc98/sound/sys_timer.c optional snd device-driver
+pc98/pc98/sound/sequencer.c optional snd device-driver
+pc98/pc98/sound/patmgr.c optional snd device-driver
+pc98/pc98/sound/adlib_card.c optional opl device-driver
+pc98/pc98/sound/opl3.c optional opl device-driver
+pc98/pc98/sound/gus_card.c optional gus device-driver
+pc98/pc98/sound/gus_midi.c optional gus device-driver
+pc98/pc98/sound/gus_vol.c optional gus device-driver
+pc98/pc98/sound/gus_wave.c optional gus device-driver
+pc98/pc98/sound/ics2101.c optional gus device-driver
+pc98/pc98/sound/sound_timer.c optional gus device-driver
+pc98/pc98/sound/midi_synth.c optional gus device-driver
+pc98/pc98/sound/midibuf.c optional gus device-driver
+pc98/pc98/sound/ad1848.c optional gusxvi device-driver
+pc98/pc98/sound/ad1848.c optional gus device-driver
+pc98/pc98/sound/ad1848.c optional mss device-driver
+pc98/pc98/sound/midi_synth.c optional mss device-driver
+pc98/pc98/sound/midibuf.c optional mss device-driver
+pc98/pc98/sound/mpu401.c optional mpu device-driver
+pc98/pc98/sound/midi_synth.c optional mpu device-driver
+pc98/pc98/sound/midibuf.c optional mpu device-driver
+pc98/pc98/sound/pas2_card.c optional pas device-driver
+pc98/pc98/sound/pas2_midi.c optional pas device-driver
+pc98/pc98/sound/pas2_mixer.c optional pas device-driver
+pc98/pc98/sound/pas2_pcm.c optional pas device-driver
+pc98/pc98/sound/midi_synth.c optional pas device-driver
+pc98/pc98/sound/midibuf.c optional pas device-driver
+pc98/pc98/sound/sb_card.c optional sb device-driver
+pc98/pc98/sound/sb_dsp.c optional sb device-driver
+pc98/pc98/sound/sb_midi.c optional sb device-driver
+pc98/pc98/sound/sb_mixer.c optional sb device-driver
+pc98/pc98/sound/midi_synth.c optional sb device-driver
+pc98/pc98/sound/midibuf.c optional sb device-driver
+pc98/pc98/sound/sb16_dsp.c optional sbxvi device-driver
+pc98/pc98/sound/sb16_midi.c optional sbmidi device-driver
+pc98/pc98/sound/uart6850.c optional uart device-driver
+pc98/pc98/sound/midi_synth.c optional uart device-driver
+pc98/pc98/sound/midibuf.c optional uart device-driver
+pc98/pc98/sound/trix.c optional trix device-driver
+pc98/pc98/sound/sscape.c optional sscape device-driver
+pc98/isa/spigot.c optional spigot device-driver
+pc98/pc98/spkr.c optional speaker device-driver
+pc98/isa/stallion.c optional stl device-driver
+pc98/pc98/syscons.c optional sc device-driver
+pc98/isa/tw.c optional tw device-driver
+pc98/isa/ultra14f.c optional uha device-driver
+pc98/pc98/wd.c optional wdc device-driver
+pc98/pc98/wd.c optional wd device-driver
+pc98/pc98/atapi.c optional atapi device-driver
+pc98/pc98/wcd.c optional wcd device-driver
+pc98/isa/wd7000.c optional wds device-driver
+pc98/pc98/wt.c optional wt device-driver
+i386/linux/imgact_linux.c optional compat_linux
+i386/linux/linux_dummy.c optional compat_linux
+i386/linux/linux_file.c optional compat_linux
+i386/linux/linux_ioctl.c optional compat_linux
+i386/linux/linux_ipc.c optional compat_linux
+i386/linux/linux_locore.s optional compat_linux \
+ dependency "linux_assym.h"
+i386/linux/linux_misc.c optional compat_linux
+i386/linux/linux_signal.c optional compat_linux
+i386/linux/linux_socket.c optional compat_linux
+i386/linux/linux_stats.c optional compat_linux
+i386/linux/linux_sysent.c optional compat_linux
+i386/linux/linux_sysvec.c optional compat_linux
+i386/linux/linux_util.c optional compat_linux
+i386/scsi/aic7xxx.c optional ahc device-driver \
+ dependency "$S/dev/aic7xxx/aic7xxx_reg.h aic7xxx_seq.h"
+pc98/scsi/bt.c optional bt device-driver
+libkern/bcd.c standard
+libkern/divdi3.c standard
+libkern/inet_ntoa.c standard
+libkern/index.c standard
+libkern/mcount.c optional profiling-routine
+libkern/moddi3.c standard
+libkern/qdivrem.c standard
+libkern/qsort.c standard
+libkern/random.c standard
+libkern/scanc.c standard
+libkern/skpc.c standard
+libkern/strcat.c standard
+libkern/strcmp.c standard
+libkern/strcpy.c standard
+libkern/strlen.c standard
+libkern/strncmp.c standard
+libkern/strncpy.c standard
+libkern/udivdi3.c standard
+libkern/umoddi3.c standard
+gnu/i386/fpemul/div_small.s optional gpl_math_emulate
+gnu/i386/fpemul/errors.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_aux.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_entry.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_etc.c optional gpl_math_emulate
+gnu/i386/fpemul/fpu_trig.c optional gpl_math_emulate
+gnu/i386/fpemul/get_address.c optional gpl_math_emulate
+gnu/i386/fpemul/load_store.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_2xm1.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_atan.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_div.s optional gpl_math_emulate
+gnu/i386/fpemul/poly_l2.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_mul64.s optional gpl_math_emulate
+gnu/i386/fpemul/poly_sin.c optional gpl_math_emulate
+gnu/i386/fpemul/poly_tan.c optional gpl_math_emulate
+gnu/i386/fpemul/polynomial.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_add_sub.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_compare.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_constant.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_div.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_ld_str.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_mul.c optional gpl_math_emulate
+gnu/i386/fpemul/reg_norm.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_round.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_u_add.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_u_div.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_u_mul.s optional gpl_math_emulate
+gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate
+gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate
+gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate
+gnu/i386/isa/dgb.c optional dgb device-driver
+gnu/i386/isa/nic3008.c optional nic device-driver
+gnu/i386/isa/nic3009.c optional nnic device-driver
+pci/wd82371.c optional wd device-driver
diff --git a/sys/pc98/conf/majors.pc98 b/sys/pc98/conf/majors.pc98
new file mode 100644
index 0000000..fcfee6c
--- /dev/null
+++ b/sys/pc98/conf/majors.pc98
@@ -0,0 +1,115 @@
+$Id: majors.i386,v 1.5 1996/05/04 08:40:13 peter Exp $
+
+Hopefully, this list will one day be obsoleted by DEVFS, but for now
+this is the current allocation of device major numbers.
+
+For local use, you are encouraged to use the reserved numbers.
+
+If you intend the driver to be available, send email to the
+hackers@freebsd.org mailing list to see about having a number
+reserved for you.
+
+The most "complete" version of this will be the one in FreeBSD-current.
+(see http://www.freebsd.org/ or from ftp://ftp.cdrom.com/pub/FreeBSD/)
+
+blkdev name comments
+0 wd ST506 disk controller (with IDE extensions)
+1 ?? unassigned?
+2 fd floppy disk
+3 wt QIC-02/36 tape
+4 sd SCSI "disk" type
+5 st SCSI "tape" type
+6 cd SCSI "cdrom" type
+7 mcd Mitsumi CDROM interface
+8 lkm assigned to Loadable Kernel modules
+9 lkm assigned to Loadable Kernel modules
+10 lkm assigned to Loadable Kernel modules
+11 lkm assigned to Loadable Kernel modules
+12 lkm assigned to Loadable Kernel modules
+13 lkm assigned to Loadable Kernel modules
+14 ?? reserved for local use
+15 vn vnode disk device
+16 scd Sony CDROM interface
+17 matcd Matsushita/Panasonic/Creative(SB) CDROM interface
+18 ata "device independent" ATA/IDE driver
+19 wcdb ATAPI CDROM client of "ata"
+20 od SCSI "magneto-optical" disk
+21 ccd concatenated disk
+
+chrdev name comments
+0 cn console
+1 ctty /dev/tty
+2 mm /dev/mem,kmem,etc
+3 wd ST506 disk controller (with IDE extensions)
+4 swap /dev/drum
+5 pts pseudo tty "tty" half
+6 ptc pseudo tty "master" half
+7 log system log
+8 bqu B004 transputer board
+9 fd floppy disk
+10 wt QIC-02/36 tape
+11 spigot Video capture?
+12 sc syscons/pcvt virtual consoles
+13 sd SCSI "disk type"
+14 st SCSI "tape type"
+15 cd SCSI "CDROM type"
+16 lpt PC parallel printer port
+17 ch SCSI changer
+18 su SCSI universal type
+19 tw X-10 power interface
+20 ?? reserved for local use
+21 psm PS/2 Mouse
+22 fd (/dev/stdin etc)
+23 bpf Berkeley Packet Filter
+24 pca PC speaker (/dev/pcaudio)
+25 ?? was vat
+26 spkr PC speaker (/dev/spkr)
+27 mse Microsoft bus-mouse
+28 sio 16450/16550 serial
+29 mcd Mitsumi CDROM interface
+30 snd sound driver system
+31 uk SCSI "unknown device type"
+32 lkmc Loadable Kernel Module Controller
+33 lkm assigned to Loadable Kernel Modules
+34 lkm assigned to Loadable Kernel Modules
+35 lkm assigned to Loadable Kernel Modules
+36 lkm assigned to Loadable Kernel Modules
+37 lkm assigned to Loadable Kernel Modules
+38 lkm assigned to Loadable Kernel Modules
+39 apm Advanced Power Management
+40 ctx Cortex
+41 ?? was socksys
+42 cx Cronyx
+43 vn vnode "disk" device
+44 gp GPIB
+45 scd Sony CDROM interface
+46 matcd Matsushita/Panasonic/Creative(SB) CDROM interface
+47 gsc Genius Scanner
+48 cy Cyclades
+49 ssc SCSI super device
+50 crd pcmcia cards
+51 joy joystick
+52 tun IP tunnel device
+53 snp tty snoop
+54 nic ISDN system
+55 isdn ISDN system
+56 ity ISDN system
+57 itel ISDN system
+58 dgb Digiboard
+59 ispy ISDN system
+60 nnic ISDN system
+61 pt SCSI "processor type"
+62 worm SCSI "worm type"
+63 rc Riscom/8 driver
+64 ?? Talisman
+65 sctarg SCSI target
+66 labpc National Instruments LabPC
+67 meteor Matrox Meteor video capture
+68 si Specialix SI/XIO (peter@freebsd.org)
+69 wcd ATAPI CDROM client of "ata"
+70 od SCSI "magneto-optical disk type"
+71 asc AmiScan driver
+72 stl Stallion (cd1400 based) (gerg@stallion.oz.au)
+73 qcam quickcam
+74 ccd concatenated disk
+75 stli Stallion (intelligent cdk based) (gerg@stallion.oz.au)
diff --git a/sys/pc98/conf/options.pc98 b/sys/pc98/conf/options.pc98
new file mode 100644
index 0000000..1f4b112
--- /dev/null
+++ b/sys/pc98/conf/options.pc98
@@ -0,0 +1,25 @@
+# $Id: options.i386,v 1.13 1996/05/11 04:39:44 bde Exp $
+BOUNCEPAGES opt_bounce.h
+USER_LDT
+MATH_EMULATE opt_math_emulate.h
+GPL_MATH_EMULATE opt_math_emulate.h
+
+IBCS2 opt_dontuse.h
+COMPAT_LINUX opt_dontuse.h
+
+SHOW_BUSYBUFS opt_machdep.h
+PANIC_REBOOT_WAIT_TIME opt_machdep.h
+LARGEMEM opt_machdep.h
+MAXMEM opt_machdep.h
+PERFMON opt_perfmon.h
+AUTO_EOI_1 opt_auto_eoi.h
+AUTO_EOI_2 opt_auto_eoi.h
+BREAK_TO_DEBUGGER opt_comconsole.h
+COMCONSOLE opt_comconsole.h
+COM_ESP opt_sio.h
+COM_MULTIPORT opt_sio.h
+DSI_SOFT_MODEM opt_sio.h
+FAT_CURSOR opt_pcvt.h
+PCVT_FREEBSD opt_pcvt.h
+PCVT_SCANSET opt_pcvt.h
+XSERVER opt_pcvt.h
diff --git a/sys/pc98/i386/autoconf.c b/sys/pc98/i386/autoconf.c
new file mode 100644
index 0000000..d5f5d0e
--- /dev/null
+++ b/sys/pc98/i386/autoconf.c
@@ -0,0 +1,408 @@
+/*-
+ * 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: @(#)autoconf.c 7.1 (Berkeley) 5/9/91
+ * $Id: autoconf.c,v 1.55 1996/06/08 09:37:35 bde Exp $
+ */
+
+/*
+ * Setup the system to run on the current machine.
+ *
+ * Configure() is called at boot time and initializes the vba
+ * device tables and the memory controller monitoring. Available
+ * devices are determined (from possibilities mentioned in ioconf.c),
+ * and the drivers are initialized.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/dmap.h>
+#include <sys/reboot.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+
+#include <machine/cons.h>
+#include <machine/md_var.h>
+#ifdef PC98
+#include <pc98/pc98/icu.h> /* For interrupts */
+#else
+#include <i386/isa/icu.h> /* For interrupts */
+#endif
+
+#ifdef PC98
+#include "nec.h"
+#include "epson.h"
+#if NNEC > 0 || NEPSON > 0
+#include <pc98/pc98/pc98_device.h>
+#endif
+#else /* !PC98 */
+#include "isa.h"
+#if NISA > 0
+#include <i386/isa/isa_device.h>
+#endif
+
+#include "eisa.h"
+#if NEISA > 0
+#include <i386/eisa/eisaconf.h>
+#endif
+#endif /* PC98 */
+
+#include "pci.h"
+#if NPCI > 0
+#include <pci/pcivar.h>
+#endif
+
+#include "crd.h"
+#if NCRD > 0
+#include <pccard/driver.h>
+#endif
+
+#include "scbus.h"
+#if NSCBUS > 0
+#include <scsi/scsiconf.h>
+#endif
+
+static void configure __P((void *));
+SYSINIT(configure, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure, NULL)
+
+#ifdef MFS_ROOT
+extern struct vfsops mfs_vfsops;
+#endif
+#ifdef FFS
+extern struct vfsops ufs_vfsops;
+#endif
+#ifdef LFS
+extern struct vfsops lfs_vfsops;
+#endif
+#ifdef NFS
+extern int nfs_mountroot __P((void *));
+#endif
+#ifdef CD9660
+extern int cd9660_mountroot __P((void *));
+#endif
+#ifdef MSDOSFS
+extern int msdosfs_mountroot __P((void *));
+#endif
+
+static void configure_finish __P((void));
+static void configure_start __P((void));
+static int setdumpdev __P((dev_t dev));
+static void setroot __P((void));
+
+#ifdef CD9660
+/* We need to try out all our potential CDROM drives, so we need a table. */
+static struct {
+ char *name;
+ int major;
+} try_cdrom[] = {
+ { "cd", 6 },
+ { "mcd", 7 },
+ { "scd", 16 },
+ { "matcd", 17 },
+ { 0, 0}
+};
+
+static int find_cdrom_root __P((void *));
+
+static int
+find_cdrom_root(dummy)
+ void *dummy;
+{
+ int i,j,k;
+
+ for (j = 0 ; j < 2; j++)
+ for (k = 0 ; try_cdrom[k].name ; k++) {
+ rootdev = makedev(try_cdrom[k].major,j*8);
+ printf("trying rootdev=0x%lx (%s%d)\n",
+ rootdev, try_cdrom[k].name,j);
+ i = (*cd9660_mountroot)((void *)NULL);
+ if (!i) return i;
+ }
+ return EINVAL;
+}
+#endif /* CD9660 */
+
+static void
+configure_start()
+{
+#if NSCBUS > 0
+ scsi_configure_start();
+#endif
+}
+
+static void
+configure_finish()
+{
+#if NSCBUS > 0
+ scsi_configure_finish();
+#endif
+}
+
+/*
+ * Determine i/o configuration for a machine.
+ */
+static void
+configure(dummy)
+ void *dummy;
+{
+
+ configure_start();
+
+ /* Allow all routines to decide for themselves if they want intrs */
+ enable_intr();
+ INTREN(IRQ_SLAVE);
+
+#if NCRD > 0
+ /* Before isa_configure to avoid ISA drivers finding our cards */
+ pccard_configure();
+#endif
+#ifdef PC98
+#if NNEC > 0 || NEPSON > 0
+ pc98_configure();
+#endif
+#else /* IBM-PC */
+#if NEISA > 0
+ eisa_configure();
+#endif
+#endif /* PC98 */
+
+#if NPCI > 0
+ pci_configure();
+#endif
+
+#ifndef PC98
+#if NISA > 0
+ isa_configure();
+#endif
+#endif /* !PC98 */
+
+ configure_finish();
+
+ cninit_finish();
+
+ if (bootverbose)
+ printf("Device configuration finished.\n");
+
+#ifdef CD9660
+ if ((boothowto & RB_CDROM) && !mountroot) {
+ if (bootverbose)
+ printf("Considering CD-ROM root f/s.\n");
+ mountroot = find_cdrom_root;
+ }
+#endif
+
+#ifdef NFS
+ if (!mountroot && nfs_diskless_valid) {
+ if (bootverbose)
+ printf("Considering NFS root f/s.\n");
+ mountroot = nfs_mountroot;
+ }
+#endif /* NFS */
+
+#ifdef MFS_ROOT
+ if (!mountroot) {
+ if (bootverbose)
+ printf("Considering MFS root f/s.\n");
+ mountroot = vfs_mountroot; /* XXX goes away*/
+ mountrootvfsops = &mfs_vfsops;
+ /*
+ * Ignore the -a flag if this kernel isn't compiled
+ * with a generic root/swap configuration: if we skip
+ * setroot() and we aren't a generic kernel, chaos
+ * will ensue because setconf() will be a no-op.
+ * (rootdev is always initialized to NODEV in a
+ * generic configuration, so we test for that.)
+ */
+ if ((boothowto & RB_ASKNAME) == 0 || rootdev != NODEV)
+ setroot();
+ }
+#endif
+#ifdef FFS
+ if (!mountroot) {
+ if (bootverbose)
+ printf("Considering FFS root f/s.\n");
+ mountroot = vfs_mountroot; /* XXX goes away*/
+ mountrootvfsops = &ufs_vfsops;
+ /*
+ * Ignore the -a flag if this kernel isn't compiled
+ * with a generic root/swap configuration: if we skip
+ * setroot() and we aren't a generic kernel, chaos
+ * will ensue because setconf() will be a no-op.
+ * (rootdev is always initialized to NODEV in a
+ * generic configuration, so we test for that.)
+ */
+ if ((boothowto & RB_ASKNAME) == 0 || rootdev != NODEV)
+ setroot();
+ }
+#endif
+#ifdef LFS
+ if (!mountroot) {
+ if (bootverbose)
+ printf("Considering LFS root f/s.\n");
+ mountroot = vfs_mountroot; /* XXX goes away*/
+ mountrootvfsops = &lfs_vfsops;
+ /*
+ * Ignore the -a flag if this kernel isn't compiled
+ * with a generic root/swap configuration: if we skip
+ * setroot() and we aren't a generic kernel, chaos
+ * will ensue because setconf() will be a no-op.
+ * (rootdev is always initialized to NODEV in a
+ * generic configuration, so we test for that.)
+ */
+ if ((boothowto & RB_ASKNAME) == 0 || rootdev != NODEV)
+ setroot();
+ }
+#endif
+ if (!mountroot) {
+ panic("Nobody wants to mount my root for me");
+ }
+ /*
+ * Configure swap area and related system
+ * parameter based on device(s) used.
+ */
+ if (bootverbose)
+ printf("Configuring root and swap devs.\n");
+ setconf();
+ cold = 0;
+ if (bootverbose)
+ printf("configure() finished.\n");
+}
+
+static int
+setdumpdev(dev)
+ dev_t dev;
+{
+ int maj, psize;
+ long newdumplo;
+
+ if (dev == NODEV) {
+ dumpdev = dev;
+ dumplo = 0;
+ return (0);
+ }
+ maj = major(dev);
+ if (maj >= nblkdev)
+ return (ENXIO);
+ if (bdevsw[maj] == NULL)
+ return (ENXIO); /* XXX is this right? */
+ if (bdevsw[maj]->d_psize == NULL)
+ return (ENXIO); /* XXX should be ENODEV ? */
+ psize = bdevsw[maj]->d_psize(dev);
+ if (psize == -1)
+ return (ENXIO); /* XXX should be ENODEV ? */
+ newdumplo = psize - Maxmem * PAGE_SIZE / DEV_BSIZE;
+ if (newdumplo < 0)
+ return (ENOSPC);
+ dumpdev = dev;
+ dumplo = newdumplo;
+ return (0);
+}
+
+u_long bootdev = 0; /* not a dev_t - encoding is different */
+
+static char devname[][2] = {
+ {'w','d'}, /* 0 = wd */
+ {'s','w'}, /* 1 = sw */
+#define FDMAJOR 2
+ {'f','d'}, /* 2 = fd */
+ {'w','t'}, /* 3 = wt */
+ {'s','d'}, /* 4 = sd -- new SCSI system */
+};
+
+#define PARTITIONMASK 0x7
+#define PARTITIONSHIFT 3
+#define FDUNITSHIFT 6
+#define RAW_PART 2
+
+/*
+ * Attempt to find the device from which we were booted.
+ * If we can do so, and not instructed not to do so,
+ * change rootdev to correspond to the load device.
+ */
+static void
+setroot()
+{
+ int majdev, mindev, unit, part, adaptor;
+ dev_t orootdev;
+
+/*printf("howto %x bootdev %x ", boothowto, bootdev);*/
+ if (boothowto & RB_DFLTROOT ||
+ (bootdev & B_MAGICMASK) != (u_long)B_DEVMAGIC)
+ return;
+ majdev = (bootdev >> B_TYPESHIFT) & B_TYPEMASK;
+ if (majdev > sizeof(devname) / sizeof(devname[0]))
+ return;
+ adaptor = (bootdev >> B_ADAPTORSHIFT) & B_ADAPTORMASK;
+ unit = (bootdev >> B_UNITSHIFT) & B_UNITMASK;
+ if (majdev == FDMAJOR) {
+ part = RAW_PART;
+ mindev = unit << FDUNITSHIFT;
+ }
+ else {
+ part = (bootdev >> B_PARTITIONSHIFT) & B_PARTITIONMASK;
+ mindev = (unit << PARTITIONSHIFT) + part;
+ }
+ orootdev = rootdev;
+ rootdev = makedev(majdev, mindev);
+ /*
+ * If the original rootdev is the same as the one
+ * just calculated, don't need to adjust the swap configuration.
+ */
+ if (rootdev == orootdev)
+ return;
+ printf("changing root device to %c%c%d%c\n",
+ devname[majdev][0], devname[majdev][1],
+ mindev >> (majdev == FDMAJOR ? FDUNITSHIFT : PARTITIONSHIFT),
+ part + 'a');
+}
+
+static int
+sysctl_kern_dumpdev SYSCTL_HANDLER_ARGS
+{
+ int error;
+ dev_t ndumpdev;
+
+ ndumpdev = dumpdev;
+ error = sysctl_handle_opaque(oidp, &ndumpdev, sizeof ndumpdev, req);
+ if (!error && ndumpdev != dumpdev) {
+ error = setdumpdev(ndumpdev);
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_kern, KERN_DUMPDEV, dumpdev, CTLTYPE_OPAQUE|CTLFLAG_RW,
+ 0, sizeof dumpdev, sysctl_kern_dumpdev, "I", "");
diff --git a/sys/pc98/i386/exception.s b/sys/pc98/i386/exception.s
new file mode 100644
index 0000000..b040c88
--- /dev/null
+++ b/sys/pc98/i386/exception.s
@@ -0,0 +1,277 @@
+/*-
+ * Copyright (c) 1990 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.
+ *
+ * $Id: exception.s,v 1.18 1996/05/31 01:08:02 peter Exp $
+ */
+
+#include "npx.h" /* NNPX */
+#include "assym.s" /* system defines */
+#include <sys/errno.h> /* error return codes */
+#include <machine/spl.h> /* SWI_AST_MASK ... */
+#include <machine/psl.h> /* PSL_I */
+#include <machine/trap.h> /* trap codes */
+#include <sys/syscall.h> /* syscall numbers */
+#include <machine/asmacros.h> /* miscellaneous macros */
+#include <sys/cdefs.h> /* CPP macros */
+
+#define KDSEL 0x10 /* kernel data selector */
+#define SEL_RPL_MASK 0x0003
+#define TRAPF_CS_OFF (13 * 4)
+
+ .text
+
+/*****************************************************************************/
+/* Trap handling */
+/*****************************************************************************/
+/*
+ * Trap and fault vector routines
+ */
+#define IDTVEC(name) ALIGN_TEXT; .globl __CONCAT(_X,name); __CONCAT(_X,name):
+#define TRAP(a) pushl $(a) ; jmp _alltraps
+
+/*
+ * XXX - debugger traps are now interrupt gates so at least bdb doesn't lose
+ * control. The sti's give the standard losing behaviour for ddb and kgdb.
+ */
+#ifdef BDE_DEBUGGER
+#define BDBTRAP(name) \
+ ss ; \
+ cmpb $0,_bdb_exists ; \
+ je 1f ; \
+ testb $SEL_RPL_MASK,4(%esp) ; \
+ jne 1f ; \
+ ss ; \
+ .globl __CONCAT(__CONCAT(bdb_,name),_ljmp); \
+__CONCAT(__CONCAT(bdb_,name),_ljmp): \
+ ljmp $0,$0 ; \
+1:
+#else
+#define BDBTRAP(name)
+#endif
+
+#define BPTTRAP(a) testl $PSL_I,4+8(%esp) ; je 1f ; sti ; 1: ; TRAP(a)
+
+MCOUNT_LABEL(user)
+MCOUNT_LABEL(btrap)
+
+IDTVEC(div)
+ pushl $0; TRAP(T_DIVIDE)
+IDTVEC(dbg)
+ BDBTRAP(dbg)
+ pushl $0; BPTTRAP(T_TRCTRAP)
+IDTVEC(nmi)
+ pushl $0; TRAP(T_NMI)
+IDTVEC(bpt)
+ BDBTRAP(bpt)
+ pushl $0; BPTTRAP(T_BPTFLT)
+IDTVEC(ofl)
+ pushl $0; TRAP(T_OFLOW)
+IDTVEC(bnd)
+ pushl $0; TRAP(T_BOUND)
+IDTVEC(ill)
+ pushl $0; TRAP(T_PRIVINFLT)
+IDTVEC(dna)
+ pushl $0; TRAP(T_DNA)
+IDTVEC(fpusegm)
+ pushl $0; TRAP(T_FPOPFLT)
+IDTVEC(tss)
+ TRAP(T_TSSFLT)
+IDTVEC(missing)
+ TRAP(T_SEGNPFLT)
+IDTVEC(stk)
+ TRAP(T_STKFLT)
+IDTVEC(prot)
+ TRAP(T_PROTFLT)
+IDTVEC(page)
+ TRAP(T_PAGEFLT)
+IDTVEC(rsvd)
+ pushl $0; TRAP(T_RESERVED)
+IDTVEC(fpu)
+#if NNPX > 0
+ /*
+ * Handle like an interrupt (except for accounting) so that we can
+ * call npxintr to clear the error. It would be better to handle
+ * npx interrupts as traps. This used to be difficult for nested
+ * interrupts, but now it is fairly easy - mask nested ones the
+ * same as SWI_AST's.
+ */
+ pushl $0 /* dummy error code */
+ pushl $0 /* dummy trap type */
+ pushal
+ pushl %ds
+ pushl %es /* now the stack frame is a trap frame */
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+ movl _cpl,%eax
+ pushl %eax
+ pushl $0 /* dummy unit to finish building intr frame */
+ incl _cnt+V_TRAP
+ orl $SWI_AST_MASK,%eax
+ movl %eax,_cpl
+ call _npxintr
+ incb _intr_nesting_level
+ MEXITCOUNT
+ jmp _doreti
+#else /* NNPX > 0 */
+ pushl $0; TRAP(T_ARITHTRAP)
+#endif /* NNPX > 0 */
+IDTVEC(align)
+ TRAP(T_ALIGNFLT)
+
+ SUPERALIGN_TEXT
+ .globl _alltraps
+_alltraps:
+ pushal
+ pushl %ds
+ pushl %es
+alltraps_with_regs_pushed:
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ FAKE_MCOUNT(12*4(%esp))
+calltrap:
+ FAKE_MCOUNT(_btrap) /* init "from" _btrap -> calltrap */
+ incl _cnt+V_TRAP
+ orl $SWI_AST_MASK,_cpl
+ call _trap
+
+ /*
+ * There was no place to save the cpl so we have to recover it
+ * indirectly. For traps from user mode it was 0, and for traps
+ * from kernel mode Oring SWI_AST_MASK into it didn't change it.
+ */
+ subl %eax,%eax
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
+ jne 1f
+ movl _cpl,%eax
+1:
+ /*
+ * Return via _doreti to handle ASTs. Have to change trap frame
+ * to interrupt frame.
+ */
+ pushl %eax
+ subl $4,%esp
+ incb _intr_nesting_level
+ MEXITCOUNT
+ jmp _doreti
+
+/*
+ * Call gate entry for syscall.
+ * The intersegment call has been set up to specify one dummy parameter.
+ * This leaves a place to put eflags so that the call frame can be
+ * converted to a trap frame. Note that the eflags is (semi-)bogusly
+ * pushed into (what will be) tf_err and then copied later into the
+ * final spot. It has to be done this way because esp can't be just
+ * temporarily altered for the pushfl - an interrupt might come in
+ * and clobber the saved cs/eip.
+ */
+ SUPERALIGN_TEXT
+IDTVEC(syscall)
+ pushfl /* save eflags in tf_err for now */
+ subl $4,%esp /* skip over tf_trapno */
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax /* switch to kernel segments */
+ movl %ax,%ds
+ movl %ax,%es
+ movl TF_ERR(%esp),%eax /* copy saved eflags to final spot */
+ movl %eax,TF_EFLAGS(%esp)
+ movl $7,TF_ERR(%esp) /* sizeof "lcall 7,0" */
+ FAKE_MCOUNT(12*4(%esp))
+ incl _cnt+V_SYSCALL
+ movl $SWI_AST_MASK,_cpl
+ call _syscall
+ /*
+ * Return via _doreti to handle ASTs.
+ */
+ pushl $0 /* cpl to restore */
+ subl $4,%esp
+ movb $1,_intr_nesting_level
+ MEXITCOUNT
+ jmp _doreti
+
+/*
+ * Call gate entry for Linux/NetBSD syscall (int 0x80)
+ */
+ SUPERALIGN_TEXT
+IDTVEC(int0x80_syscall)
+ subl $8,%esp /* skip over tf_trapno and tf_err */
+ pushal
+ pushl %ds
+ pushl %es
+ movl $KDSEL,%eax /* switch to kernel segments */
+ movl %ax,%ds
+ movl %ax,%es
+ movl $2,TF_ERR(%esp) /* sizeof "int 0x80" */
+ FAKE_MCOUNT(12*4(%esp))
+ incl _cnt+V_SYSCALL
+ movl $SWI_AST_MASK,_cpl
+ call _syscall
+ /*
+ * Return via _doreti to handle ASTs.
+ */
+ pushl $0 /* cpl to restore */
+ subl $4,%esp
+ movb $1,_intr_nesting_level
+ MEXITCOUNT
+ jmp _doreti
+
+/*
+ * Include what was once config+isa-dependent code.
+ * XXX it should be in a stand-alone file. It's still icu-dependent and
+ * belongs in i386/isa.
+ */
+#ifdef PC98
+#include "pc98/pc98/vector.s"
+#else
+#include "i386/isa/vector.s"
+#endif
+
+/*
+ * Include what was once icu-dependent code.
+ * XXX it should be merged into this file (also move the definition of
+ * imen to vector.s or isa.c).
+ * Before including it, set up a normal asm environment so that vector.s
+ * doesn't have to know that stuff is included after it.
+ */
+ .data
+ ALIGN_DATA
+ .text
+ SUPERALIGN_TEXT
+#ifdef PC98
+#include "pc98/pc98/icu.s"
+#else
+#include "i386/isa/icu.s"
+#endif
diff --git a/sys/pc98/i386/locore.s b/sys/pc98/i386/locore.s
new file mode 100644
index 0000000..bffecc5
--- /dev/null
+++ b/sys/pc98/i386/locore.s
@@ -0,0 +1,1079 @@
+/*-
+ * 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: @(#)locore.s 7.3 (Berkeley) 5/13/91
+ * $Id: locore.s,v 1.72 1996/05/27 06:51:46 phk Exp $
+ *
+ * originally from: locore.s, by William F. Jolitz
+ *
+ * Substantially rewritten by David Greenman, Rod Grimes,
+ * Bruce Evans, Wolfgang Solfrank, Poul-Henning Kamp
+ * and many others.
+ */
+
+#include "apm.h"
+#include "opt_ddb.h"
+
+#include <sys/errno.h>
+#include <sys/syscall.h>
+#include <sys/reboot.h>
+
+#include <machine/asmacros.h>
+#include <machine/cputypes.h>
+#include <machine/psl.h>
+#include <machine/pmap.h>
+#include <machine/specialreg.h>
+
+#include "assym.s"
+
+/*
+ * XXX
+ *
+ * Note: This version greatly munged to avoid various assembler errors
+ * that may be fixed in newer versions of gas. Perhaps newer versions
+ * will have more pleasant appearance.
+ */
+
+/*
+ * PTmap is recursive pagemap at top of virtual address space.
+ * Within PTmap, the page directory can be found (third indirection).
+ */
+ .globl _PTmap,_PTD,_PTDpde
+ .set _PTmap,(PTDPTDI << PDRSHIFT)
+ .set _PTD,_PTmap + (PTDPTDI * PAGE_SIZE)
+ .set _PTDpde,_PTD + (PTDPTDI * PDESIZE)
+
+/*
+ * APTmap, APTD is the alternate recursive pagemap.
+ * It's used when modifying another process's page tables.
+ */
+ .globl _APTmap,_APTD,_APTDpde
+ .set _APTmap,APTDPTDI << PDRSHIFT
+ .set _APTD,_APTmap + (APTDPTDI * PAGE_SIZE)
+ .set _APTDpde,_PTD + (APTDPTDI * PDESIZE)
+
+/*
+ * Access to each processes kernel stack is via a region of
+ * per-process address space (at the beginning), immediately above
+ * the user process stack.
+ */
+ .set _kstack,USRSTACK
+ .globl _kstack
+
+/*
+ * Globals
+ */
+ .data
+ ALIGN_DATA /* just to be sure */
+
+ .globl tmpstk
+ .space 0x2000 /* space for tmpstk - temporary stack */
+tmpstk:
+
+ .globl _boothowto,_bootdev
+
+ .globl _cpu,_cpu_vendor,_cpu_id,_bootinfo
+ .globl _cpu_high, _cpu_feature
+
+_cpu: .long 0 /* are we 386, 386sx, or 486 */
+_cpu_id: .long 0 /* stepping ID */
+_cpu_high: .long 0 /* highest arg to CPUID */
+_cpu_feature: .long 0 /* features */
+_cpu_vendor: .space 20 /* CPU origin code */
+_bootinfo: .space BOOTINFO_SIZE /* bootinfo that we can handle */
+
+_KERNend: .long 0 /* phys addr end of kernel (just after bss) */
+physfree: .long 0 /* phys addr of next free page */
+upa: .long 0 /* phys addr of proc0's UPAGES */
+p0upt: .long 0 /* phys addr of proc0's UPAGES page table */
+
+ .globl _IdlePTD
+_IdlePTD: .long 0 /* phys addr of kernel PTD */
+
+_KPTphys: .long 0 /* phys addr of kernel page tables */
+
+ .globl _proc0paddr
+_proc0paddr: .long 0 /* address of proc 0 address space */
+
+#ifdef BDE_DEBUGGER
+ .globl _bdb_exists /* flag to indicate BDE debugger is present */
+_bdb_exists: .long 0
+#endif
+
+
+/**********************************************************************
+ *
+ * Some handy macros
+ *
+ */
+
+#define R(foo) ((foo)-KERNBASE)
+
+#define ALLOCPAGES(foo) \
+ movl R(physfree), %esi ; \
+ movl $((foo)*PAGE_SIZE), %eax ; \
+ addl %esi, %eax ; \
+ movl %eax, R(physfree) ; \
+ movl %esi, %edi ; \
+ movl $((foo)*PAGE_SIZE),%ecx ; \
+ xorl %eax,%eax ; \
+ cld ; \
+ rep ; \
+ stosb
+
+/*
+ * fillkpt
+ * eax = page frame address
+ * ebx = index into page table
+ * ecx = how many pages to map
+ * base = base address of page dir/table
+ * prot = protection bits
+ */
+#define fillkpt(base, prot) \
+ shll $2, %ebx ; \
+ addl base, %ebx ; \
+ orl $PG_V+prot, %eax ; \
+1: movl %eax,(%ebx) ; \
+ addl $PAGE_SIZE,%eax ; /* increment physical address */ \
+ addl $4,%ebx ; /* next pte */ \
+ loop 1b
+
+/*
+ * fillkptphys(prot)
+ * eax = physical address
+ * ecx = how many pages to map
+ * prot = protection bits
+ */
+#define fillkptphys(prot) \
+ movl %eax, %ebx ; \
+ shrl $PAGE_SHIFT, %ebx ; \
+ fillkpt(R(_KPTphys), prot)
+
+ .text
+/**********************************************************************
+ *
+ * This is where the bootblocks start us, set the ball rolling...
+ *
+ */
+NON_GPROF_ENTRY(btext)
+
+#ifdef PC98
+ jmp 1f
+ .globl _pc98_system_parameter
+ .org 0x400
+_pc98_system_parameter:
+ .space 0x240 /* skip over warm boot shit */
+#else /* IBM-PC */
+#ifdef BDE_DEBUGGER
+#ifdef BIOS_STEALS_3K
+ cmpl $0x0375c339,0x95504
+#else
+ cmpl $0x0375c339,0x96104 /* XXX - debugger signature */
+#endif
+ jne 1f
+ movb $1,R(_bdb_exists)
+1:
+#endif
+
+/* Tell the bios to warmboot next time */
+ movw $0x1234,0x472
+#endif /* PC98 */
+
+#ifdef PC98
+1:
+#endif
+/* Set up a real frame in case the double return in newboot is executed. */
+ pushl %ebp
+ movl %esp, %ebp
+
+/* Don't trust what the BIOS gives for eflags. */
+ pushl $PSL_KERNEL
+ popfl
+
+#ifdef PC98
+ /* save SYSTEM PARAMETER for resume (NS/T or other) */
+ movl $0xa1000,%esi
+ movl $0x100000,%edi
+ movl $0x0630,%ecx
+ cld
+ rep
+ movsb
+#endif
+
+/*
+ * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap
+ * to set %cs, %ds, %es and %ss.
+ */
+ mov %ds, %ax
+ mov %ax, %fs
+ mov %ax, %gs
+
+ call recover_bootinfo
+
+/* Get onto a stack that we can trust. */
+/*
+ * XXX this step is delayed in case recover_bootinfo needs to return via
+ * the old stack, but it need not be, since recover_bootinfo actually
+ * returns via the old frame.
+ */
+ movl $R(tmpstk),%esp
+
+#ifdef PC98
+ testb $0x02,0x100620 /* pc98_machine_type & M_EPSON_PC98 */
+ jz 3f
+ cmpb $0x0b,0x100624 /* epson_machine_id <= 0x0b */
+ ja 3f
+
+ /* count up memory */
+ movl $0x100000,%eax /* next, talley remaining memory */
+ movl $(0xFFF-0x100),%ecx
+1: movl 0(%eax),%ebx /* save location to check */
+ movl $0xa55a5aa5,0(%eax) /* write test pattern */
+ cmpl $0xa55a5aa5,0(%eax) /* does not check yet for rollover */
+ jne 2f
+ movl %ebx,0(%eax) /* restore memory */
+ addl $ PAGE_SIZE,%eax
+ loop 1b
+2: subl $0x100000,%eax
+ shrl $17,%eax
+ movb %al,0x100401
+3:
+#endif
+
+ call identify_cpu
+
+/* clear bss */
+/*
+ * XXX this should be done a little earlier. (bde)
+ *
+ * XXX we don't check that there is memory for our bss or page tables
+ * before using it. (bde)
+ *
+ * XXX the boot program somewhat bogusly clears the bss. We still have
+ * to do it in case we were unzipped by kzipboot. Then the boot program
+ * only clears kzipboot's bss.
+ *
+ * XXX the gdt and idt are still somewhere in the boot program. We
+ * depend on the convention that the boot program is below 1MB and we
+ * are above 1MB to keep the gdt and idt away from the bss and page
+ * tables. The idT is only used if BDE_DEBUGGER is enabled.
+ */
+ movl $R(_end),%ecx
+ movl $R(_edata),%edi
+ subl %edi,%ecx
+ xorl %eax,%eax
+ cld
+ rep
+ stosb
+
+#if NAPM > 0
+/*
+ * XXX it's not clear that APM can live in the current environonment.
+ * Only pc-relative addressing works.
+ */
+ call _apm_setup
+#endif
+
+ call create_pagetables
+
+#ifdef BDE_DEBUGGER
+/*
+ * Adjust as much as possible for paging before enabling paging so that the
+ * adjustments can be traced.
+ */
+ call bdb_prepare_paging
+#endif
+
+/* Now enable paging */
+ movl R(_IdlePTD), %eax
+ movl %eax,%cr3 /* load ptd addr into mmu */
+ movl %cr0,%eax /* get control word */
+ orl $CR0_PE|CR0_PG,%eax /* enable paging */
+ movl %eax,%cr0 /* and let's page NOW! */
+
+#ifdef BDE_DEBUGGER
+/*
+ * Complete the adjustments for paging so that we can keep tracing through
+ * initi386() after the low (physical) addresses for the gdt and idT become
+ * invalid.
+ */
+ call bdb_commit_paging
+#endif
+
+ pushl $begin /* jump to high virtualized address */
+ ret
+
+/* now running relocated at KERNBASE where the system is linked to run */
+begin:
+#ifdef PC98
+ /* BIOS $401:available Protect Memory (/128KB)*/
+ xorl %eax,%eax
+ movb _pc98_system_parameter+0x401-0x400,%al
+ shll $17,%eax
+ addl $0x100000,%eax
+ shrl $12,%eax
+ movl %eax,_Maxmem /* Maxmem=(%ax*128K+1M)/4096 */
+ movl %eax,_Maxmem_under16M
+ /* BIOS $594:available Protect Memory over 16M (/1MB) */
+ xorl %edx,%edx
+ movw _pc98_system_parameter+0x594-0x400,%dx
+ cmpl $0,%edx
+ je 1f
+
+ addl $16,%edx
+ shll $8,%edx
+ movl %edx,_Maxmem /* Maxmem=(%dx*1M+16M)/4096 */
+1:
+
+ testb $8,_pc98_system_parameter+0x501-0x400 /* hireso check */
+ jz 1f
+ movb $0xff,_hireso /* set hireso */
+1:
+#endif /* PC98 */
+
+ /* set up bootstrap stack */
+ movl $_kstack+UPAGES*PAGE_SIZE,%esp /* bootstrap stack end location */
+ xorl %eax,%eax /* mark end of frames */
+ movl %eax,%ebp
+ movl _proc0paddr,%eax
+ movl _IdlePTD, %esi
+ movl %esi,PCB_CR3(%eax)
+
+ movl physfree, %esi
+ pushl %esi /* value of first for init386(first) */
+ call _init386 /* wire 386 chip for unix operation */
+#ifdef PC98
+#if defined(CYRIX_486DLC) && defined(I486_CPU)
+ /* Cyrix 486DLC/SLC/DLC2/SLC2 CPU */
+ cmpl $CPU_486DLC,_cpu
+ jne 1f
+ cli
+ movl %cr0,%eax
+ orl $0x40000000,%eax /* disable cache */
+ mov %eax,%cr0
+ .byte 0x0f,0x08 /* invd */
+
+ movb $0xc0,%al
+ outb %al,$0x22 /* Cyrix486[SD]LC cache controler index */
+ movb $0x02,%al /* CCR0 = 0x02 (disable cache 640K-1M) */
+ outb %al,$0x23 /* window */
+ movb $0xc6,%al
+ outb %al,$0x22
+ movb $0x00,%al
+ outb %al,$0x23 /* NCR1: enable cache in all area */
+ movb $0x00,%al
+ outb %al,$0x22 /* dummy window */
+ movb $0x00,%al
+ outb %al,$0x23 /* dummy write */
+
+ movl %cr0,%eax
+ andl $0x9fffffff,%eax /* enable cache !! */
+ movl %eax,%cr0
+ sti
+1:
+#endif
+#if defined(IBM_486SLC) && defined(I486_CPU)
+ cli
+ movl %cr0,%eax
+ orl $0x40000000,%eax # disable cache
+ mov %eax,%cr0
+ .byte 0x0f,0x08 # invd
+
+ movl $0x00000000,%edx
+ movl $0x00009c92,%eax # If using Intel-FPU,set "1c92" instead.
+ movl $0x00001000,%ecx
+ .byte 0x0f,0x30 # wrmsr
+
+ movl $0x000000d0,%edx # 13MB(0x0d) cache.(over1MB-14MB region)
+ movl $0x000003ff,%eax # 0-640KB cache. (64KB block * 10)
+ movl $0x00001001,%ecx
+ .byte 0x0f,0x30 # wrmsr
+
+ movl $0x00000000,%edx
+ movl $0x03000000,%eax # "3" means double-clock-mode. or set all-zero.
+ movl $0x00001002,%ecx
+ .byte 0x0f,0x30 # wrmsr
+
+ movl %cr0,%eax
+ andl $0x9fffffff,%eax # enable cache !!
+ movl %eax,%cr0
+ sti
+#endif
+#ifdef CYRIX_5X86
+ /* CYRIX 5x86 CPU */
+ cli
+ mov %cr0,%eax
+ orl $0x40000000,%eax # disable cache
+ movl %eax,%cr0
+
+ wbinvd # flush buffer
+
+ movb $0xc3,%al
+ outb %al,$0x22
+ inb $0x23,%al
+
+ movb $0x0c1,%al # CCR1
+ outb %al,$0x22
+ movb $0x00,%al
+ outb %al,$0x23
+ movb $0x0c2,%al # CCR2
+ outb %al,$0x22
+#ifdef SUSP_HLT
+ movb $0x0a,%al # USE_WBAK, SUSP_HLT
+#else
+ movb $0x02,%al # USE_WBAK
+#endif
+ outb %al,$0x23 # Interface Pins
+ movb $0xc3,%al # CCR3
+ outb %al,$0x22 #
+ movb $0x10,%al # MAPEN0 (to access CCR4)
+ outb %al,$0x23
+ movb $0x0e8,%al # CCR4
+ outb %al,$0x22
+#ifdef FASTER_5X86_FPU
+ movb $0x38,%al # DTE_EN,MEM_BYP,no clock delay
+ # UNDOCUMENTED OPTION (20H)
+#else
+ movb $0x18,%al # DTE_EN,MEM_BYP,no clock delay
+#endif
+ outb %al,$0x23
+ movb $0x020,%al # PCR0
+ outb %al,$0x22
+#ifdef RSTK_EN
+#define RSTK_EN_BIT 1
+#else
+#define RSTK_EN_BIT 0
+#endif
+#ifdef LOOP_EN
+#define LOOP_EN_BIT 4
+#else
+#define LOOP_EN_BIT 0
+#endif
+#ifdef DISABLE_5X86_LSSER
+ movb $(0x02 | RSTK_EN_BIT | LOOP_EN_BIT) ,%al # BTB_EN
+#else
+ movb $(0x82 | RSTK_EN_BIT | LOOP_EN_BIT),%al # BTB_EN,LSSER
+#endif
+ outb %al,$0x23
+ movb $0x0c3,%al # CCR3
+ outb %al,$0x22
+ movb $0x00,%al
+ outb %al,$0x23
+ movb $0x80,%al # dummy
+ outb %al,$0x22
+ inb $0x23,%al
+
+ movl %cr0,%ebx
+ andl $0x0bfffffff,%ebx # enable cache
+ orl $0x020000000,%ebx # write back mode
+ movl %ebx,%cr0 # go!
+
+ sti
+#endif /* CYRIX_5X86 */
+#endif /* PC98 */
+ popl %esi
+
+ .globl __ucodesel,__udatasel
+
+ pushl $0 /* unused */
+ pushl __udatasel /* ss */
+ pushl $0 /* esp - filled in by execve() */
+ pushl $PSL_USER /* eflags (IOPL 0, int enab) */
+ pushl __ucodesel /* cs */
+ pushl $0 /* eip - filled in by execve() */
+ subl $(12*4),%esp /* space for rest of registers */
+
+ pushl %esp /* call main with frame pointer */
+ call _main /* autoconfiguration, mountroot etc */
+
+ addl $(13*4),%esp /* back to a frame we can return with */
+
+ /*
+ * now we've run main() and determined what cpu-type we are, we can
+ * enable write protection and alignment checking on i486 cpus and
+ * above.
+ */
+#if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ je 1f
+ movl %cr0,%eax /* get control word */
+ orl $CR0_WP|CR0_AM,%eax /* enable i486 features */
+ movl %eax,%cr0 /* and do it */
+1:
+#endif
+ /*
+ * on return from main(), we are process 1
+ * set up address space and stack so that we can 'return' to user mode
+ */
+ movl __ucodesel,%eax
+ movl __udatasel,%ecx
+
+ movl %cx,%ds
+ movl %cx,%es
+ movl %ax,%fs /* double map cs to fs */
+ movl %cx,%gs /* and ds to gs */
+ iret /* goto user! */
+
+#define LCALL(x,y) .byte 0x9a ; .long y ; .word x
+
+/*
+ * Signal trampoline, copied to top of user stack
+ */
+NON_GPROF_ENTRY(sigcode)
+ call SIGF_HANDLER(%esp)
+ lea SIGF_SC(%esp),%eax /* scp (the call may have clobbered the */
+ /* copy at 8(%esp)) */
+ pushl %eax
+ pushl %eax /* junk to fake return address */
+ movl $SYS_sigreturn,%eax /* sigreturn() */
+ LCALL(0x7,0) /* enter kernel with args on stack */
+ hlt /* never gets here */
+ .align 2,0x90 /* long word text-align */
+_esigcode:
+
+ .data
+ .globl _szsigcode
+_szsigcode:
+ .long _esigcode-_sigcode
+ .text
+
+/**********************************************************************
+ *
+ * Recover the bootinfo passed to us from the boot program
+ *
+ */
+recover_bootinfo:
+ /*
+ * This code is called in different ways depending on what loaded
+ * and started the kernel. This is used to detect how we get the
+ * arguments from the other code and what we do with them.
+ *
+ * Old disk boot blocks:
+ * (*btext)(howto, bootdev, cyloffset, esym);
+ * [return address == 0, and can NOT be returned to]
+ * [cyloffset was not supported by the FreeBSD boot code
+ * and always passed in as 0]
+ * [esym is also known as total in the boot code, and
+ * was never properly supported by the FreeBSD boot code]
+ *
+ * Old diskless netboot code:
+ * (*btext)(0,0,0,0,&nfsdiskless,0,0,0);
+ * [return address != 0, and can NOT be returned to]
+ * If we are being booted by this code it will NOT work,
+ * so we are just going to halt if we find this case.
+ *
+ * New uniform boot code:
+ * (*btext)(howto, bootdev, 0, 0, 0, &bootinfo)
+ * [return address != 0, and can be returned to]
+ *
+ * There may seem to be a lot of wasted arguments in here, but
+ * that is so the newer boot code can still load very old kernels
+ * and old boot code can load new kernels.
+ */
+
+ /*
+ * The old style disk boot blocks fake a frame on the stack and
+ * did an lret to get here. The frame on the stack has a return
+ * address of 0.
+ */
+ cmpl $0,4(%ebp)
+ je olddiskboot
+
+ /*
+ * We have some form of return address, so this is either the
+ * old diskless netboot code, or the new uniform code. That can
+ * be detected by looking at the 5th argument, if it is 0
+ * we are being booted by the new uniform boot code.
+ */
+ cmpl $0,24(%ebp)
+ je newboot
+
+ /*
+ * Seems we have been loaded by the old diskless boot code, we
+ * don't stand a chance of running as the diskless structure
+ * changed considerably between the two, so just halt.
+ */
+ hlt
+
+ /*
+ * We have been loaded by the new uniform boot code.
+ * Let's check the bootinfo version, and if we do not understand
+ * it we return to the loader with a status of 1 to indicate this error
+ */
+newboot:
+ movl 28(%ebp),%ebx /* &bootinfo.version */
+ movl BI_VERSION(%ebx),%eax
+ cmpl $1,%eax /* We only understand version 1 */
+ je 1f
+ movl $1,%eax /* Return status */
+ leave
+ /*
+ * XXX this returns to our caller's caller (as is required) since
+ * we didn't set up a frame and our caller did.
+ */
+ ret
+
+1:
+ /*
+ * If we have a kernelname copy it in
+ */
+ movl BI_KERNELNAME(%ebx),%esi
+ cmpl $0,%esi
+ je 2f /* No kernelname */
+ movl $MAXPATHLEN,%ecx /* Brute force!!! */
+ movl $R(_kernelname),%edi
+ cmpb $'/',(%esi) /* Make sure it starts with a slash */
+ je 1f
+ movb $'/',(%edi)
+ incl %edi
+ decl %ecx
+1:
+ cld
+ rep
+ movsb
+
+2:
+ /*
+ * Determine the size of the boot loader's copy of the bootinfo
+ * struct. This is impossible to do properly because old versions
+ * of the struct don't contain a size field and there are 2 old
+ * versions with the same version number.
+ */
+ movl $BI_ENDCOMMON,%ecx /* prepare for sizeless version */
+ testl $RB_BOOTINFO,8(%ebp) /* bi_size (and bootinfo) valid? */
+ je got_bi_size /* no, sizeless version */
+ movl BI_SIZE(%ebx),%ecx
+got_bi_size:
+
+ /*
+ * Copy the common part of the bootinfo struct
+ */
+ movl %ebx,%esi
+ movl $R(_bootinfo),%edi
+ cmpl $BOOTINFO_SIZE,%ecx
+ jbe got_common_bi_size
+ movl $BOOTINFO_SIZE,%ecx
+got_common_bi_size:
+ cld
+ rep
+ movsb
+
+#ifdef NFS
+ /*
+ * If we have a nfs_diskless structure copy it in
+ */
+ movl BI_NFS_DISKLESS(%ebx),%esi
+ cmpl $0,%esi
+ je olddiskboot
+ movl $R(_nfs_diskless),%edi
+ movl $NFSDISKLESS_SIZE,%ecx
+ cld
+ rep
+ movsb
+ movl $R(_nfs_diskless_valid),%edi
+ movl $1,(%edi)
+#endif
+
+ /*
+ * The old style disk boot.
+ * (*btext)(howto, bootdev, cyloffset, esym);
+ * Note that the newer boot code just falls into here to pick
+ * up howto and bootdev, cyloffset and esym are no longer used
+ */
+olddiskboot:
+ movl 8(%ebp),%eax
+ movl %eax,R(_boothowto)
+ movl 12(%ebp),%eax
+ movl %eax,R(_bootdev)
+
+ ret
+
+
+/**********************************************************************
+ *
+ * Identify the CPU and initialize anything special about it
+ *
+ */
+identify_cpu:
+
+ /* Try to toggle alignment check flag; does not exist on 386. */
+ pushfl
+ popl %eax
+ movl %eax,%ecx
+ orl $PSL_AC,%eax
+ pushl %eax
+ popfl
+ pushfl
+ popl %eax
+ xorl %ecx,%eax
+ andl $PSL_AC,%eax
+ pushl %ecx
+ popfl
+
+ testl %eax,%eax
+ jnz 1f
+ movl $CPU_386,R(_cpu)
+ jmp 3f
+
+1: /* Try to toggle identification flag; does not exist on early 486s. */
+ pushfl
+ popl %eax
+ movl %eax,%ecx
+ xorl $PSL_ID,%eax
+ pushl %eax
+ popfl
+ pushfl
+ popl %eax
+ xorl %ecx,%eax
+ andl $PSL_ID,%eax
+ pushl %ecx
+ popfl
+
+ testl %eax,%eax
+ jnz 1f
+ movl $CPU_486,R(_cpu)
+
+ /* check for Cyrix 486DLC -- based on check routine */
+ /* documented in "Cx486SLC/e SMM Programmer's Guide" */
+ xorw %dx,%dx
+ cmpw %dx,%dx # set flags to known state
+ pushfw
+ popw %cx # store flags in ecx
+ movw $0xffff,%ax
+ movw $0x0004,%bx
+ divw %bx
+ pushfw
+ popw %ax
+ andw $0x08d5,%ax # mask off important bits
+ andw $0x08d5,%cx
+ cmpw %ax,%cx
+
+ jnz 3f # if flags changed, Intel chip
+
+ movl $CPU_486DLC,R(_cpu) # set CPU value for Cyrix
+ movl $0x69727943,R(_cpu_vendor) # store vendor string
+ movw $0x0078,R(_cpu_vendor+4)
+
+#ifndef CYRIX_CACHE_WORKS
+ /* Disable caching of the ISA hole only. */
+ invd
+ movb $CCR0,%al # Configuration Register index (CCR0)
+ outb %al,$0x22
+ inb $0x23,%al
+ orb $(CCR0_NC1|CCR0_BARB),%al
+ movb %al,%ah
+ movb $CCR0,%al
+ outb %al,$0x22
+ movb %ah,%al
+ outb %al,$0x23
+ invd
+#else /* CYRIX_CACHE_WORKS */
+ /* Set cache parameters */
+ invd # Start with guaranteed clean cache
+ movb $CCR0,%al # Configuration Register index (CCR0)
+ outb %al,$0x22
+ inb $0x23,%al
+ andb $~CCR0_NC0,%al
+#ifndef CYRIX_CACHE_REALLY_WORKS
+ orb $(CCR0_NC1|CCR0_BARB),%al
+#else /* CYRIX_CACHE_REALLY_WORKS */
+ orb $CCR0_NC1,%al
+#endif /* !CYRIX_CACHE_REALLY_WORKS */
+ movb %al,%ah
+ movb $CCR0,%al
+ outb %al,$0x22
+ movb %ah,%al
+ outb %al,$0x23
+ /* clear non-cacheable region 1 */
+ movb $(NCR1+2),%al
+ outb %al,$0x22
+ movb $NCR_SIZE_0K,%al
+ outb %al,$0x23
+ /* clear non-cacheable region 2 */
+ movb $(NCR2+2),%al
+ outb %al,$0x22
+ movb $NCR_SIZE_0K,%al
+ outb %al,$0x23
+ /* clear non-cacheable region 3 */
+ movb $(NCR3+2),%al
+ outb %al,$0x22
+ movb $NCR_SIZE_0K,%al
+ outb %al,$0x23
+ /* clear non-cacheable region 4 */
+ movb $(NCR4+2),%al
+ outb %al,$0x22
+ movb $NCR_SIZE_0K,%al
+ outb %al,$0x23
+ /* enable caching in CR0 */
+ movl %cr0,%eax
+ andl $~(CR0_CD|CR0_NW),%eax
+ movl %eax,%cr0
+ invd
+#endif /* !CYRIX_CACHE_WORKS */
+ jmp 3f
+
+1: /* Use the `cpuid' instruction. */
+ xorl %eax,%eax
+ .byte 0x0f,0xa2 # cpuid 0
+ movl %eax,R(_cpu_high) # highest capability
+ movl %ebx,R(_cpu_vendor) # store vendor string
+ movl %edx,R(_cpu_vendor+4)
+ movl %ecx,R(_cpu_vendor+8)
+ movb $0,R(_cpu_vendor+12)
+
+ movl $1,%eax
+ .byte 0x0f,0xa2 # cpuid 1
+ movl %eax,R(_cpu_id) # store cpu_id
+ movl %edx,R(_cpu_feature) # store cpu_feature
+ rorl $8,%eax # extract family type
+ andl $15,%eax
+ cmpl $5,%eax
+ jae 1f
+
+ /* less than Pentium; must be 486 */
+ movl $CPU_486,R(_cpu)
+ jmp 3f
+1:
+ /* a Pentium? */
+ cmpl $5,%eax
+ jne 2f
+ movl $CPU_586,R(_cpu)
+ jmp 3f
+2:
+ /* Greater than Pentium...call it a Pentium Pro */
+ movl $CPU_686,R(_cpu)
+3:
+ ret
+
+
+/**********************************************************************
+ *
+ * Create the first page directory and its page tables.
+ *
+ */
+
+create_pagetables:
+
+/* Find end of kernel image (rounded up to a page boundary). */
+ movl $R(_end),%esi
+
+/* include symbols in "kernel image" if they are loaded and useful */
+#ifdef DDB
+ movl R(_bootinfo+BI_ESYMTAB),%edi
+ testl %edi,%edi
+ je over_symalloc
+ movl %edi,%esi
+ movl $KERNBASE,%edi
+ addl %edi,R(_bootinfo+BI_SYMTAB)
+ addl %edi,R(_bootinfo+BI_ESYMTAB)
+over_symalloc:
+#endif
+
+ addl $PAGE_MASK,%esi
+ andl $~PAGE_MASK,%esi
+ movl %esi,R(_KERNend) /* save end of kernel */
+ movl %esi,R(physfree) /* next free page is at end of kernel */
+
+/* Allocate Kernel Page Tables */
+ ALLOCPAGES(NKPT)
+ movl %esi,R(_KPTphys)
+
+/* Allocate Page Table Directory */
+ ALLOCPAGES(1)
+ movl %esi,R(_IdlePTD)
+
+/* Allocate UPAGES */
+ ALLOCPAGES(UPAGES)
+ movl %esi,R(upa)
+ addl $KERNBASE, %esi
+ movl %esi, R(_proc0paddr)
+
+/* Allocate proc0's page table for the UPAGES. */
+ ALLOCPAGES(1)
+ movl %esi,R(p0upt)
+
+/* Map read-only from zero to the end of the kernel text section */
+ xorl %eax, %eax
+#ifdef BDE_DEBUGGER
+/* If the debugger is present, actually map everything read-write. */
+ cmpl $0,R(_bdb_exists)
+ jne map_read_write
+#endif
+ movl $R(_etext),%ecx
+ addl $PAGE_MASK,%ecx
+ shrl $PAGE_SHIFT,%ecx
+ fillkptphys(0)
+
+/* Map read-write, data, bss and symbols */
+ movl $R(_etext),%eax
+ addl $PAGE_MASK, %eax
+ andl $~PAGE_MASK, %eax
+map_read_write:
+ movl R(_KERNend),%ecx
+ subl %eax,%ecx
+ shrl $PAGE_SHIFT,%ecx
+ fillkptphys(PG_RW)
+
+/* Map page directory. */
+ movl R(_IdlePTD), %eax
+ movl $1, %ecx
+ fillkptphys(PG_RW)
+
+/* Map proc0's page table for the UPAGES the physical way. */
+ movl R(p0upt), %eax
+ movl $1, %ecx
+ fillkptphys(PG_RW)
+
+/* Map proc0s UPAGES the physical way */
+ movl R(upa), %eax
+ movl $UPAGES, %ecx
+ fillkptphys(PG_RW)
+
+/* Map ISA hole */
+ movl $ISA_HOLE_START, %eax
+ movl $ISA_HOLE_LENGTH>>PAGE_SHIFT, %ecx
+ fillkptphys(PG_RW|PG_N)
+
+/* Map proc0s UPAGES in the special page table for this purpose. */
+ movl R(upa), %eax
+ movl $KSTKPTEOFF, %ebx
+ movl $UPAGES, %ecx
+ fillkpt(R(p0upt), PG_RW)
+
+/* and put the page table in the pde. */
+ movl R(p0upt), %eax
+ movl $KSTKPTDI, %ebx
+ movl $1, %ecx
+ fillkpt(R(_IdlePTD), PG_RW)
+
+/* install a pde for temporary double map of bottom of VA */
+ movl R(_KPTphys), %eax
+ xorl %ebx, %ebx
+ movl $1, %ecx
+ fillkpt(R(_IdlePTD), PG_RW)
+
+/* install pde's for pt's */
+ movl R(_KPTphys), %eax
+ movl $KPTDI, %ebx
+ movl $NKPT, %ecx
+ fillkpt(R(_IdlePTD), PG_RW)
+
+/* install a pde recursively mapping page directory as a page table */
+ movl R(_IdlePTD), %eax
+ movl $PTDPTDI, %ebx
+ movl $1,%ecx
+ fillkpt(R(_IdlePTD), PG_RW)
+
+ ret
+
+#ifdef BDE_DEBUGGER
+bdb_prepare_paging:
+ cmpl $0,R(_bdb_exists)
+ je bdb_prepare_paging_exit
+
+ subl $6,%esp
+
+ /*
+ * Copy and convert debugger entries from the bootstrap gdt and idt
+ * to the kernel gdt and idt. Everything is still in low memory.
+ * Tracing continues to work after paging is enabled because the
+ * low memory addresses remain valid until everything is relocated.
+ * However, tracing through the setidt() that initializes the trace
+ * trap will crash.
+ */
+ sgdt (%esp)
+ movl 2(%esp),%esi /* base address of bootstrap gdt */
+ movl $R(_gdt),%edi
+ movl %edi,2(%esp) /* prepare to load kernel gdt */
+ movl $8*18/4,%ecx
+ cld
+ rep /* copy gdt */
+ movsl
+ movl $R(_gdt),-8+2(%edi) /* adjust gdt self-ptr */
+ movb $0x92,-8+5(%edi)
+ lgdt (%esp)
+
+ sidt (%esp)
+ movl 2(%esp),%esi /* base address of current idt */
+ movl 8+4(%esi),%eax /* convert dbg descriptor to ... */
+ movw 8(%esi),%ax
+ movl %eax,R(bdb_dbg_ljmp+1) /* ... immediate offset ... */
+ movl 8+2(%esi),%eax
+ movw %ax,R(bdb_dbg_ljmp+5) /* ... and selector for ljmp */
+ movl 24+4(%esi),%eax /* same for bpt descriptor */
+ movw 24(%esi),%ax
+ movl %eax,R(bdb_bpt_ljmp+1)
+ movl 24+2(%esi),%eax
+ movw %ax,R(bdb_bpt_ljmp+5)
+ movl $R(_idt),%edi
+ movl %edi,2(%esp) /* prepare to load kernel idt */
+ movl $8*4/4,%ecx
+ cld
+ rep /* copy idt */
+ movsl
+ lidt (%esp)
+
+ addl $6,%esp
+
+bdb_prepare_paging_exit:
+ ret
+
+/* Relocate debugger gdt entries and gdt and idt pointers. */
+bdb_commit_paging:
+ cmpl $0,_bdb_exists
+ je bdb_commit_paging_exit
+
+ movl $_gdt+8*9,%eax /* adjust slots 9-17 */
+ movl $9,%ecx
+reloc_gdt:
+ movb $KERNBASE>>24,7(%eax) /* top byte of base addresses, was 0, */
+ addl $8,%eax /* now KERNBASE>>24 */
+ loop reloc_gdt
+
+ subl $6,%esp
+ sgdt (%esp)
+ addl $KERNBASE,2(%esp)
+ lgdt (%esp)
+ sidt (%esp)
+ addl $KERNBASE,2(%esp)
+ lidt (%esp)
+ addl $6,%esp
+
+ int $3
+
+bdb_commit_paging_exit:
+ ret
+
+#endif /* BDE_DEBUGGER */
diff --git a/sys/pc98/i386/machdep.c b/sys/pc98/i386/machdep.c
new file mode 100644
index 0000000..029bbf5
--- /dev/null
+++ b/sys/pc98/i386/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);
+}
diff --git a/sys/pc98/i386/microtime.s b/sys/pc98/i386/microtime.s
new file mode 100644
index 0000000..cd08213
--- /dev/null
+++ b/sys/pc98/i386/microtime.s
@@ -0,0 +1,262 @@
+/* -*- Fundamental -*- keep Emacs from f***ing up the formatting */
+/*
+ * Copyright (c) 1993 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: Steve McCanne's microtime code
+ * $Id: microtime.s,v 1.13 1996/05/31 01:08:02 peter Exp $
+ */
+
+#include <machine/asmacros.h>
+#include <machine/clock.h>
+
+#ifdef PC98
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/timerreg.h>
+#else
+#include <i386/isa/icu.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/timerreg.h>
+#endif
+
+ENTRY(microtime)
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ movl _i586_ctr_rate, %ecx
+ testl %ecx, %ecx
+ jne pentium_microtime
+#else
+ xorl %ecx, %ecx /* clear ecx */
+#endif
+
+ movb $TIMER_SEL0|TIMER_LATCH, %al /* prepare to latch */
+
+ cli /* disable interrupts */
+
+ outb %al, $TIMER_MODE /* latch timer 0's counter */
+ inb $TIMER_CNTR0, %al /* read counter value, LSB first */
+ movb %al, %cl
+ inb $TIMER_CNTR0, %al
+ movb %al, %ch
+
+ /*
+ * Now check for counter overflow. This is tricky because the
+ * timer chip doesn't let us atomically read the current counter
+ * value and the output state (i.e., overflow state). We have
+ * to read the ICU interrupt request register (IRR) to see if the
+ * overflow has occured. Because we lack atomicity, we use
+ * the (very accurate) heuristic that we only check for
+ * overflow if the value read is close to the interrupt period.
+ * E.g., if we just checked the IRR, we might read a non-overflowing
+ * value close to 0, experience overflow, then read this overflow
+ * from the IRR, and mistakenly add a correction to the "close
+ * to zero" value.
+ *
+ * We compare the counter value to the prepared overflow threshold.
+ * If the counter value is less than this, we assume the counter
+ * didn't overflow between disabling timer interrupts and latching
+ * the counter value above. For example, we assume that interrupts
+ * are enabled when we are called (or were disabled just a few
+ * cycles before we are called and that the instructions before the
+ * "cli" are fast) and that the "cli" and "outb" instructions take
+ * less than 10 timer cycles to execute. The last assumption is
+ * very safe.
+ *
+ * Otherwise, the counter might have overflowed. We check for this
+ * condition by reading the interrupt request register out of the ICU.
+ * If it overflowed, we add in one clock period.
+ *
+ * The heuristic is "very accurate" because it works 100% if we're
+ * called with interrupts enabled. Otherwise, it might not work.
+ * Currently, only siointrts() calls us with interrupts disabled, so
+ * the problem can be avoided at some cost to the general case. The
+ * costs are complications in callers to disable interrupts in
+ * IO_ICU1 and extra reads of the IRR forced by a conservative
+ * overflow threshold.
+ *
+ * In 2.0, we are called at splhigh() from mi_switch(), so we have
+ * to allow for the overflow bit being in ipending instead of in
+ * the IRR. Our caller may have executed many instructions since
+ * ipending was set, so the heuristic for the IRR is inappropriate
+ * for ipending. However, we don't need another heuristic, since
+ * the "cli" suffices to lock ipending.
+ */
+
+ movl _timer0_max_count, %edx /* prepare for 2 uses */
+
+ testb $IRQ0, _ipending /* is a soft timer interrupt pending? */
+ jne overflow
+
+ /* Do we have a possible overflow condition? */
+ cmpl _timer0_overflow_threshold, %ecx
+ jbe 1f
+
+ inb $IO_ICU1, %al /* read IRR in ICU */
+ testb $IRQ0, %al /* is a hard timer interrupt pending? */
+ je 1f
+overflow:
+ subl %edx, %ecx /* some intr pending, count timer down through 0 */
+1:
+
+ /*
+ * Subtract counter value from max count since it is a count-down value.
+ */
+ subl %ecx, %edx
+
+ /* Adjust for partial ticks. */
+ addl _timer0_prescaler_count, %edx
+
+ /*
+ * To divide by 1.193200, we multiply by 27465 and shift right by 15.
+ *
+ * The multiplier was originally calculated to be
+ *
+ * 2^18 * 1000000 / 1193200 = 219698.
+ *
+ * The frequency is 1193200 to be compatible with rounding errors in
+ * the calculation of the usual maximum count. 2^18 is the largest
+ * power of 2 such that multiplying `i' by it doesn't overflow for i
+ * in the range of interest ([0, 11932 + 5)). We adjusted the
+ * multiplier a little to minimise the average of
+ *
+ * fabs(i / 1.1193200 - ((multiplier * i) >> 18))
+ *
+ * for i in the range and then removed powers of 2 to speed up the
+ * multiplication and to avoid overflow for i outside the range
+ * (i may be as high as 2^17 if the timer is programmed to its
+ * maximum maximum count). The absolute error is less than 1 for
+ * all i in the range.
+ */
+
+#ifdef PC98
+#ifndef AUTO_CLOCK
+#ifndef PC98_8M
+#if 0
+ imul $6667, %edx
+#else
+ leal (%edx,%edx,4), %eax /* a = 5 */
+ leal (%edx,%eax,2), %eax /* a = 11 */
+ movl %eax, %ecx /* c = 11 */
+ addl %edx, %eax /* a = 12 */
+ addl %edx, %eax /* a = 13 */
+ shl $9, %eax /* a = 6656 */
+ addl %ecx, %eax /* a = 6667 */
+#endif /* 0 */
+ shr $14, %eax
+#else /* !PC98_8M */
+#if 0
+ imul $16411, %edx
+#else
+ leal (%edx,%edx,2), %eax /* a = 3 */
+ leal (%eax,%eax,8), %eax /* a = 27 */
+ movl %eax, %ecx /* c = 27 */
+ movl %edx, %eax /* a = 1 */
+ shl $14, %eax /* a = 16384 */
+ addl %ecx, %eax /* a = 16411 */
+#endif /* 0 */
+ shr $15, %eax
+#endif /* !PC98_8M */
+#else /* !AUTO_CLOCK */
+ .globl _pc98_system_parameter
+ testb $0x80, _pc98_system_parameter + 0x501 - 0x400
+ jnz 1f
+#if 0
+ imul $6667, %edx
+#else
+ leal (%edx,%edx,4), %eax /* a = 5 */
+ leal (%edx,%eax,2), %eax /* a = 11 */
+ movl %eax, %ecx /* c = 11 */
+ addl %edx, %eax /* a = 12 */
+ addl %edx, %eax /* a = 13 */
+ shl $9, %eax /* a = 6656 */
+ addl %ecx, %eax /* a = 6667 */
+#endif /* 0 */
+ shr $14, %eax
+ jmp 2f
+1:
+#if 0
+ imul $16411, %edx
+#else
+ leal (%edx,%edx,2), %eax /* a = 3 */
+ leal (%eax,%eax,8), %eax /* a = 27 */
+ movl %eax, %ecx /* c = 27 */
+ movl %edx, %eax /* a = 1 */
+ shl $14, %eax /* a = 16384 */
+ addl %ecx, %eax /* a = 16411 */
+#endif /* 0 */
+ shr $15, %eax
+2:
+#endif /* !AUTO_CLOCK */
+#else /* IBM-PC */
+#if 0
+ imul $27645, %edx /* 25 cycles on a 486 */
+#else
+ leal (%edx,%edx,2), %eax /* a = 3 2 cycles on a 486 */
+ leal (%edx,%eax,4), %eax /* a = 13 2 */
+ movl %eax, %ecx /* c = 13 1 */
+ shl $5, %eax /* a = 416 2 */
+ addl %ecx, %eax /* a = 429 1 */
+ leal (%edx,%eax,8), %eax /* a = 3433 2 */
+ leal (%edx,%eax,8), %eax /* a = 27465 2 (total 12 cycles) */
+#endif /* 0 */
+ shr $15, %eax
+#endif /* PC98 */
+
+common_microtime:
+ addl _time+4, %eax /* usec += time.tv_sec */
+ movl _time, %edx /* sec = time.tv_sec */
+
+ sti /* enable interrupts */
+
+ cmpl $1000000, %eax /* usec valid? */
+ jb 1f
+ subl $1000000, %eax /* adjust usec */
+ incl %edx /* bump sec */
+1:
+ movl 4(%esp), %ecx /* load timeval pointer arg */
+ movl %edx, (%ecx) /* tvp->tv_sec = sec */
+ movl %eax, 4(%ecx) /* tvp->tv_usec = usec */
+
+ ret
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ ALIGN_TEXT
+pentium_microtime:
+ cli
+ .byte 0x0f, 0x31 /* RDTSC */
+ subl _i586_ctr_bias, %eax
+ sbbl _i586_ctr_bias+4, %edx
+ shldl $I586_CTR_RATE_SHIFT, %eax, %edx /* magic suggested by */
+ shll $I586_CTR_RATE_SHIFT, %eax /* math_emulate.c */
+ divl %ecx /* get value in usec */
+ jmp common_microtime
+#endif
diff --git a/sys/pc98/i386/pmap.c b/sys/pc98/i386/pmap.c
new file mode 100644
index 0000000..4109f8c
--- /dev/null
+++ b/sys/pc98/i386/pmap.c
@@ -0,0 +1,2556 @@
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ * Copyright (c) 1994 John S. Dyson
+ * All rights reserved.
+ * Copyright (c) 1994 David Greenman
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department and William Jolitz of UUNET Technologies Inc.
+ *
+ * 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: @(#)pmap.c 7.7 (Berkeley) 5/12/91
+ * $Id: pmap.c,v 1.102 1996/06/08 06:48:27 dyson Exp $
+ */
+
+/*
+ * Derived from hp300 version by Mike Hibler, this version by William
+ * Jolitz uses a recursive map [a pde points to the page directory] to
+ * map the page tables using the pagetables themselves. This is done to
+ * reduce the impact on kernel virtual memory for lots of sparse address
+ * space, and to reduce the cost of memory to each process.
+ *
+ * Derived from: hp300/@(#)pmap.c 7.1 (Berkeley) 12/5/90
+ */
+
+/*
+ * Manages physical address maps.
+ *
+ * In addition to hardware address maps, this
+ * module is called upon to provide software-use-only
+ * maps which may or may not be stored in the same
+ * form as hardware maps. These pseudo-maps are
+ * used to store intermediate results from copy
+ * operations to and from address spaces.
+ *
+ * Since the information managed by this module is
+ * also stored by the logical address mapping module,
+ * this module may throw away valid virtual-to-physical
+ * mappings at almost any time. However, invalidations
+ * of virtual-to-physical mappings must be done as
+ * requested.
+ *
+ * In order to cope with hardware architectures which
+ * make virtual-to-physical map invalidates expensive,
+ * this module may delay invalidate or reduced protection
+ * operations until such time as they are actually
+ * necessary. This module is given full information as
+ * to which processors are currently using which maps,
+ * and to when physical maps must be made correct.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/msgbuf.h>
+#include <sys/queue.h>
+#include <sys/vmmeter.h>
+#include <sys/mman.h>
+
+#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_page.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_pageout.h>
+
+#include <machine/pcb.h>
+#include <machine/cputypes.h>
+#include <machine/md_var.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#else
+#include <i386/isa/isa.h>
+#endif
+
+#define PMAP_KEEP_PDIRS
+
+#if defined(DIAGNOSTIC)
+#define PMAP_DIAGNOSTIC
+#endif
+
+static void init_pv_entries __P((int));
+
+/*
+ * Get PDEs and PTEs for user/kernel address space
+ */
+#define pmap_pde(m, v) (&((m)->pm_pdir[(vm_offset_t)(v) >> PDRSHIFT]))
+#define pdir_pde(m, v) (m[(vm_offset_t)(v) >> PDRSHIFT])
+
+#define pmap_pde_v(pte) ((*(int *)pte & PG_V) != 0)
+#define pmap_pte_w(pte) ((*(int *)pte & PG_W) != 0)
+#define pmap_pte_m(pte) ((*(int *)pte & PG_M) != 0)
+#define pmap_pte_u(pte) ((*(int *)pte & PG_A) != 0)
+#define pmap_pte_v(pte) ((*(int *)pte & PG_V) != 0)
+
+#define pmap_pte_set_w(pte, v) ((v)?(*(int *)pte |= PG_W):(*(int *)pte &= ~PG_W))
+#define pmap_pte_set_prot(pte, v) ((*(int *)pte &= ~PG_PROT), (*(int *)pte |= (v)))
+
+/*
+ * Given a map and a machine independent protection code,
+ * convert to a vax protection code.
+ */
+#define pte_prot(m, p) (protection_codes[p])
+static int protection_codes[8];
+
+static struct pmap kernel_pmap_store;
+pmap_t kernel_pmap;
+
+vm_offset_t avail_start; /* PA of first available physical page */
+vm_offset_t avail_end; /* PA of last available physical page */
+vm_offset_t virtual_avail; /* VA of first avail page (after kernel bss) */
+vm_offset_t virtual_end; /* VA of last avail page (end of kernel AS) */
+static boolean_t pmap_initialized = FALSE; /* Has pmap_init completed? */
+static vm_offset_t vm_first_phys;
+
+static int nkpt;
+static vm_page_t nkpg;
+vm_offset_t kernel_vm_end;
+
+extern vm_offset_t clean_sva, clean_eva;
+extern int cpu_class;
+
+#if defined(I386_CPU) || defined(CYRIX_486DLC)
+extern int cpu;
+#endif
+
+#define PV_FREELIST_MIN ((PAGE_SIZE / sizeof (struct pv_entry)) / 2)
+
+/*
+ * Data for the pv entry allocation mechanism
+ */
+static int pv_freelistcnt;
+static pv_entry_t pv_freelist;
+static vm_offset_t pvva;
+static int npvvapg;
+
+/*
+ * All those kernel PT submaps that BSD is so fond of
+ */
+pt_entry_t *CMAP1;
+static pt_entry_t *CMAP2, *ptmmap;
+static pv_entry_t *pv_table;
+caddr_t CADDR1, ptvmmap;
+static caddr_t CADDR2;
+static pt_entry_t *msgbufmap;
+struct msgbuf *msgbufp;
+
+#ifdef PC98
+pt_entry_t *panic_kwin_pte;
+caddr_t panic_kwin;
+#endif
+
+static void free_pv_entry __P((pv_entry_t pv));
+static __inline unsigned * get_ptbase __P((pmap_t pmap));
+static pv_entry_t get_pv_entry __P((void));
+static void i386_protection_init __P((void));
+static void pmap_alloc_pv_entry __P((void));
+static void pmap_changebit __P((vm_offset_t pa, int bit, boolean_t setem));
+static void pmap_enter_quick __P((pmap_t pmap, vm_offset_t va,
+ vm_offset_t pa));
+static int pmap_is_managed __P((vm_offset_t pa));
+static void pmap_remove_all __P((vm_offset_t pa));
+static void pmap_remove_page __P((struct pmap *pmap, vm_offset_t va));
+static __inline int pmap_remove_entry __P((struct pmap *pmap, pv_entry_t *pv,
+ vm_offset_t va));
+static int pmap_remove_pte __P((struct pmap *pmap, unsigned *ptq,
+ vm_offset_t sva));
+static boolean_t
+ pmap_testbit __P((vm_offset_t pa, int bit));
+static __inline void pmap_insert_entry __P((pmap_t pmap, vm_offset_t va,
+ vm_page_t mpte, vm_offset_t pa));
+
+static __inline vm_page_t pmap_allocpte __P((pmap_t pmap, vm_offset_t va));
+static void pmap_remove_pte_mapping __P((vm_offset_t pa));
+static __inline int pmap_release_free_page __P((pmap_t pmap, vm_page_t p));
+static vm_page_t _pmap_allocpte __P((pmap_t pmap, vm_offset_t va, int ptepindex));
+
+#define PDSTACKMAX 16
+static vm_offset_t pdstack[PDSTACKMAX];
+static int pdstackptr;
+
+#if defined(PMAP_DIAGNOSTIC)
+
+/*
+ * This code checks for non-writeable/modified pages.
+ * This should be an invalid condition.
+ */
+static int
+pmap_nw_modified(pt_entry_t ptea) {
+ int pte;
+
+ pte = (int) ptea;
+
+ if ((pte & (PG_M|PG_RW)) == PG_M)
+ return 1;
+ else
+ return 0;
+}
+#endif
+
+/*
+ * The below are finer grained pmap_update routines. These eliminate
+ * the gratuitious tlb flushes on non-i386 architectures.
+ */
+static __inline void
+pmap_update_1pg( vm_offset_t va) {
+#if defined(I386_CPU) || defined(CYRIX_486DLC)
+ if (cpu_class == CPUCLASS_386 || cpu == CPU_486DLC)
+ pmap_update();
+ else
+#endif
+ __asm __volatile(".byte 0xf,0x1,0x38": :"a" (va));
+}
+
+static __inline void
+pmap_update_2pg( vm_offset_t va1, vm_offset_t va2) {
+#if defined(I386_CPU) || defined(CYRIX_486DLC)
+ if (cpu_class == CPUCLASS_386 || cpu == CPU_486DLC) {
+ pmap_update();
+ } else
+#endif
+ {
+ __asm __volatile(".byte 0xf,0x1,0x38": :"a" (va1));
+ __asm __volatile(".byte 0xf,0x1,0x38": :"a" (va2));
+ }
+}
+
+static __inline __pure unsigned *
+get_ptbase(pmap)
+ pmap_t pmap;
+{
+ unsigned frame = (unsigned) pmap->pm_pdir[PTDPTDI] & PG_FRAME;
+
+ /* are we current address space or kernel? */
+ if (pmap == kernel_pmap || frame == (((unsigned) PTDpde) & PG_FRAME)) {
+ return (unsigned *) PTmap;
+ }
+ /* otherwise, we are alternate address space */
+ if (frame != (((unsigned) APTDpde) & PG_FRAME)) {
+ APTDpde = (pd_entry_t) (frame | PG_RW | PG_V);
+ pmap_update();
+ }
+ return (unsigned *) APTmap;
+}
+
+/*
+ * Routine: pmap_pte
+ * Function:
+ * Extract the page table entry associated
+ * with the given map/virtual_address pair.
+ */
+
+__inline unsigned * __pure
+pmap_pte(pmap, va)
+ register pmap_t pmap;
+ vm_offset_t va;
+{
+ if (pmap && *pmap_pde(pmap, va)) {
+ return get_ptbase(pmap) + i386_btop(va);
+ }
+ return (0);
+}
+
+/*
+ * Routine: pmap_extract
+ * Function:
+ * Extract the physical page address associated
+ * with the given map/virtual_address pair.
+ */
+vm_offset_t __pure
+pmap_extract(pmap, va)
+ register pmap_t pmap;
+ vm_offset_t va;
+{
+ if (pmap && *pmap_pde(pmap, va)) {
+ unsigned *pte;
+ pte = get_ptbase(pmap) + i386_btop(va);
+ return ((*pte & PG_FRAME) | (va & PAGE_MASK));
+ }
+ return 0;
+
+}
+
+/*
+ * Add a list of wired pages to the kva
+ * this routine is only used for temporary
+ * kernel mappings that do not need to have
+ * page modification or references recorded.
+ * Note that old mappings are simply written
+ * over. The page *must* be wired.
+ */
+void
+pmap_qenter(va, m, count)
+ vm_offset_t va;
+ vm_page_t *m;
+ int count;
+{
+ int i;
+ register unsigned *pte;
+
+ for (i = 0; i < count; i++) {
+ vm_offset_t tva = va + i * PAGE_SIZE;
+ unsigned npte = VM_PAGE_TO_PHYS(m[i]) | PG_RW | PG_V;
+ unsigned opte;
+ pte = (unsigned *)vtopte(tva);
+ opte = *pte;
+ *pte = npte;
+ if (opte)
+ pmap_update_1pg(tva);
+ }
+}
+/*
+ * this routine jerks page mappings from the
+ * kernel -- it is meant only for temporary mappings.
+ */
+void
+pmap_qremove(va, count)
+ vm_offset_t va;
+ int count;
+{
+ int i;
+ register unsigned *pte;
+
+ for (i = 0; i < count; i++) {
+ pte = (unsigned *)vtopte(va);
+ *pte = 0;
+ pmap_update_1pg(va);
+ va += PAGE_SIZE;
+ }
+}
+
+/*
+ * add a wired page to the kva
+ * note that in order for the mapping to take effect -- you
+ * should do a pmap_update after doing the pmap_kenter...
+ */
+__inline void
+pmap_kenter(va, pa)
+ vm_offset_t va;
+ register vm_offset_t pa;
+{
+ register unsigned *pte;
+ unsigned npte, opte;
+
+ npte = pa | PG_RW | PG_V;
+ pte = (unsigned *)vtopte(va);
+ opte = *pte;
+ *pte = npte;
+ if (opte)
+ pmap_update_1pg(va);
+}
+
+/*
+ * remove a page from the kernel pagetables
+ */
+__inline void
+pmap_kremove(va)
+ vm_offset_t va;
+{
+ register unsigned *pte;
+
+ pte = (unsigned *)vtopte(va);
+ *pte = 0;
+ pmap_update_1pg(va);
+}
+
+/*
+ * determine if a page is managed (memory vs. device)
+ */
+static __inline __pure int
+pmap_is_managed(pa)
+ vm_offset_t pa;
+{
+ int i;
+
+ if (!pmap_initialized)
+ return 0;
+
+ for (i = 0; phys_avail[i + 1]; i += 2) {
+ if (pa < phys_avail[i + 1] && pa >= phys_avail[i])
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * This routine unholds page table pages, and if the hold count
+ * drops to zero, then it decrements the wire count.
+ */
+static __inline int
+pmap_unwire_pte_hold(vm_page_t m) {
+ vm_page_unhold(m);
+ if (m->hold_count == 0) {
+ --m->wire_count;
+ if (m->wire_count == 0) {
+ --cnt.v_wire_count;
+ m->dirty = 0;
+ vm_page_deactivate(m);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+#if !defined(PMAP_DIAGNOSTIC)
+__inline
+#endif
+int
+pmap_unuse_pt(pmap, va, mpte)
+ pmap_t pmap;
+ vm_offset_t va;
+ vm_page_t mpte;
+{
+ if (va >= UPT_MIN_ADDRESS)
+ return 0;
+
+ if (mpte == NULL) {
+ vm_offset_t ptepa;
+ ptepa = ((vm_offset_t) *pmap_pde(pmap, va)) /* & PG_FRAME */;
+#if defined(PMAP_DIAGNOSTIC)
+ if (!ptepa)
+ panic("pmap_unuse_pt: pagetable page missing, va: 0x%x", va);
+#endif
+ mpte = PHYS_TO_VM_PAGE(ptepa);
+ }
+
+#if defined(PMAP_DIAGNOSTIC)
+ if (mpte->hold_count == 0) {
+ panic("pmap_unuse_pt: hold count < 0, va: 0x%x", va);
+ }
+#endif
+
+/*
+ * We don't free page-table-pages anymore because it can have a negative
+ * impact on perf at times. Now we just deactivate, and it'll get cleaned
+ * up if needed... Also, if the page ends up getting used, it will be
+ * brought back into the process address space by pmap_allocpte and be
+ * reactivated.
+ */
+ return pmap_unwire_pte_hold(mpte);
+}
+
+/*
+ * Bootstrap the system enough to run with virtual memory.
+ *
+ * On the i386 this is called after mapping has already been enabled
+ * and just syncs the pmap module with what has already been done.
+ * [We can't call it easily with mapping off since the kernel is not
+ * mapped with PA == VA, hence we would have to relocate every address
+ * from the linked base (virtual) address "KERNBASE" to the actual
+ * (physical) address starting relative to 0]
+ */
+void
+pmap_bootstrap(firstaddr, loadaddr)
+ vm_offset_t firstaddr;
+ vm_offset_t loadaddr;
+{
+ vm_offset_t va;
+ pt_entry_t *pte;
+
+ avail_start = firstaddr;
+
+ /*
+ * XXX The calculation of virtual_avail is wrong. It's NKPT*PAGE_SIZE too
+ * large. It should instead be correctly calculated in locore.s and
+ * not based on 'first' (which is a physical address, not a virtual
+ * address, for the start of unused physical memory). The kernel
+ * page tables are NOT double mapped and thus should not be included
+ * in this calculation.
+ */
+ virtual_avail = (vm_offset_t) KERNBASE + firstaddr;
+ virtual_end = VM_MAX_KERNEL_ADDRESS;
+
+ /*
+ * Initialize protection array.
+ */
+ i386_protection_init();
+
+ /*
+ * The kernel's pmap is statically allocated so we don't have to use
+ * pmap_create, which is unlikely to work correctly at this part of
+ * the boot sequence (XXX and which no longer exists).
+ */
+ kernel_pmap = &kernel_pmap_store;
+
+ kernel_pmap->pm_pdir = (pd_entry_t *) (KERNBASE + IdlePTD);
+
+ kernel_pmap->pm_count = 1;
+ nkpt = NKPT;
+
+ /*
+ * Reserve some special page table entries/VA space for temporary
+ * mapping of pages.
+ */
+#define SYSMAP(c, p, v, n) \
+ v = (c)va; va += ((n)*PAGE_SIZE); p = pte; pte += (n);
+
+ va = virtual_avail;
+ pte = (pt_entry_t *) pmap_pte(kernel_pmap, va);
+
+ /*
+ * CMAP1/CMAP2 are used for zeroing and copying pages.
+ */
+ SYSMAP(caddr_t, CMAP1, CADDR1, 1)
+ SYSMAP(caddr_t, CMAP2, CADDR2, 1)
+
+ /*
+ * ptmmap is used for reading arbitrary physical pages via /dev/mem.
+ */
+ SYSMAP(caddr_t, ptmmap, ptvmmap, 1)
+
+ /*
+ * msgbufmap is used to map the system message buffer.
+ */
+ SYSMAP(struct msgbuf *, msgbufmap, msgbufp, 1)
+
+#ifdef PC98
+ /*
+ * panic_kwin is used for accessing phisical memory in boot()
+ */
+ SYSMAP(caddr_t, panic_kwin_pte, panic_kwin, 1)
+#endif
+ virtual_avail = va;
+
+ *(int *) CMAP1 = *(int *) CMAP2 = *(int *) PTD = 0;
+ pmap_update();
+
+}
+
+/*
+ * Initialize the pmap module.
+ * Called by vm_init, to initialize any structures that the pmap
+ * system needs to map virtual memory.
+ * pmap_init has been enhanced to support in a fairly consistant
+ * way, discontiguous physical memory.
+ */
+void
+pmap_init(phys_start, phys_end)
+ vm_offset_t phys_start, phys_end;
+{
+ vm_offset_t addr;
+ vm_size_t npg, s;
+ int i;
+
+ /*
+ * calculate the number of pv_entries needed
+ */
+ vm_first_phys = phys_avail[0];
+ for (i = 0; phys_avail[i + 1]; i += 2);
+ npg = (phys_avail[(i - 2) + 1] - vm_first_phys) / PAGE_SIZE;
+
+ /*
+ * Allocate memory for random pmap data structures. Includes the
+ * pv_head_table.
+ */
+ s = (vm_size_t) (sizeof(struct pv_entry *) * npg);
+ s = round_page(s);
+ addr = (vm_offset_t) kmem_alloc(kernel_map, s);
+ pv_table = (pv_entry_t *) addr;
+
+ /*
+ * init the pv free list
+ */
+ init_pv_entries(npg);
+ /*
+ * Now it is safe to enable pv_table recording.
+ */
+ pmap_initialized = TRUE;
+}
+
+/*
+ * Used to map a range of physical addresses into kernel
+ * virtual address space.
+ *
+ * For now, VM is already on, we only need to map the
+ * specified memory.
+ */
+vm_offset_t
+pmap_map(virt, start, end, prot)
+ vm_offset_t virt;
+ vm_offset_t start;
+ vm_offset_t end;
+ int prot;
+{
+ while (start < end) {
+ pmap_enter(kernel_pmap, virt, start, prot, FALSE);
+ virt += PAGE_SIZE;
+ start += PAGE_SIZE;
+ }
+ return (virt);
+}
+
+/*
+ * Initialize a preallocated and zeroed pmap structure,
+ * such as one in a vmspace structure.
+ */
+void
+pmap_pinit(pmap)
+ register struct pmap *pmap;
+{
+ vm_page_t ptdpg;
+ /*
+ * No need to allocate page table space yet but we do need a valid
+ * page directory table.
+ */
+
+ if (pdstackptr > 0) {
+ --pdstackptr;
+ pmap->pm_pdir =
+ (pd_entry_t *)pdstack[pdstackptr];
+ } else {
+ pmap->pm_pdir =
+ (pd_entry_t *)kmem_alloc_pageable(kernel_map, PAGE_SIZE);
+ }
+
+ /*
+ * allocate object for the ptes
+ */
+ pmap->pm_pteobj = vm_object_allocate( OBJT_DEFAULT, PTDPTDI + 1);
+
+ /*
+ * allocate the page directory page
+ */
+retry:
+ ptdpg = vm_page_alloc( pmap->pm_pteobj, PTDPTDI, VM_ALLOC_ZERO);
+ if (ptdpg == NULL) {
+ VM_WAIT;
+ goto retry;
+ }
+ vm_page_wire(ptdpg);
+ ptdpg->flags &= ~(PG_MAPPED|PG_BUSY); /* not mapped normally */
+ ptdpg->valid = VM_PAGE_BITS_ALL;
+
+ pmap_kenter((vm_offset_t) pmap->pm_pdir, VM_PAGE_TO_PHYS(ptdpg));
+ if ((ptdpg->flags & PG_ZERO) == 0)
+ bzero(pmap->pm_pdir, PAGE_SIZE);
+
+ /* wire in kernel global address entries */
+ bcopy(PTD + KPTDI, pmap->pm_pdir + KPTDI, nkpt * PTESIZE);
+
+ /* install self-referential address mapping entry */
+ *(unsigned *) (pmap->pm_pdir + PTDPTDI) =
+ VM_PAGE_TO_PHYS(ptdpg) | PG_V | PG_RW;
+
+ pmap->pm_count = 1;
+}
+
+static int
+pmap_release_free_page(pmap, p)
+ struct pmap *pmap;
+ vm_page_t p;
+{
+ int s;
+ /*
+ * This code optimizes the case of freeing non-busy
+ * page-table pages. Those pages are zero now, and
+ * might as well be placed directly into the zero queue.
+ */
+ s = splvm();
+ if (p->flags & PG_BUSY) {
+ p->flags |= PG_WANTED;
+ tsleep(p, PVM, "pmaprl", 0);
+ splx(s);
+ return 0;
+ }
+
+ pmap_remove_pte_mapping(VM_PAGE_TO_PHYS(p));
+
+ if (p->hold_count) {
+#if defined(PMAP_DIAGNOSTIC)
+ panic("pmap_release: freeing held page table page");
+#endif
+ /*
+ * HACK ALERT!!!
+ * If this failure happens, we must clear the page, because
+ * there is likely a mapping still valid. This condition
+ * is an error, but at least this zero operation will mitigate
+ * some Sig-11's or crashes, because this page is thought
+ * to be zero. This is a robustness fix, and not meant to
+ * be a long term work-around.
+ */
+ pmap_zero_page(VM_PAGE_TO_PHYS(p));
+ }
+ /*
+ * Page directory pages need to have the kernel
+ * stuff cleared, so they can go into the zero queue also.
+ */
+ if (p->pindex == PTDPTDI) {
+ unsigned *pde = (unsigned *) pmap->pm_pdir;
+ bzero(pde + KPTDI, nkpt * PTESIZE);
+ pde[APTDPTDI] = 0;
+ pde[PTDPTDI] = 0;
+ pmap_kremove((vm_offset_t) pmap->pm_pdir);
+ }
+
+ vm_page_free_zero(p);
+ splx(s);
+ return 1;
+}
+
+/*
+ * Release any resources held by the given physical map.
+ * Called when a pmap initialized by pmap_pinit is being released.
+ * Should only be called if the map contains no valid mappings.
+ */
+void
+pmap_release(pmap)
+ register struct pmap *pmap;
+{
+ vm_page_t p,n,ptdpg;
+ vm_object_t object = pmap->pm_pteobj;
+ int s;
+
+ if (object->ref_count != 1)
+ panic("pmap_release: pteobj reference count != 1");
+
+ ptdpg = NULL;
+retry:
+ for (p = TAILQ_FIRST(&object->memq); p != NULL; p = n) {
+ n = TAILQ_NEXT(p, listq);
+ if (p->pindex == PTDPTDI) {
+ ptdpg = p;
+ continue;
+ }
+ if (!pmap_release_free_page(pmap, p))
+ goto retry;
+ }
+ if (ptdpg == NULL)
+ panic("pmap_release: missing page table directory page");
+
+ if (!pmap_release_free_page(pmap, ptdpg))
+ goto retry;
+
+ vm_object_deallocate(object);
+ if (pdstackptr < PDSTACKMAX) {
+ pdstack[pdstackptr] = (vm_offset_t) pmap->pm_pdir;
+ ++pdstackptr;
+ } else {
+ kmem_free(kernel_map, (vm_offset_t) pmap->pm_pdir, PAGE_SIZE);
+ }
+}
+
+/*
+ * grow the number of kernel page table entries, if needed
+ */
+
+void
+pmap_growkernel(vm_offset_t addr)
+{
+ struct proc *p;
+ struct pmap *pmap;
+ int s;
+
+ s = splhigh();
+ if (kernel_vm_end == 0) {
+ kernel_vm_end = KERNBASE;
+ nkpt = 0;
+ while (pdir_pde(PTD, kernel_vm_end)) {
+ kernel_vm_end = (kernel_vm_end + PAGE_SIZE * NPTEPG) & ~(PAGE_SIZE * NPTEPG - 1);
+ ++nkpt;
+ }
+ }
+ addr = (addr + PAGE_SIZE * NPTEPG) & ~(PAGE_SIZE * NPTEPG - 1);
+ while (kernel_vm_end < addr) {
+ if (pdir_pde(PTD, kernel_vm_end)) {
+ kernel_vm_end = (kernel_vm_end + PAGE_SIZE * NPTEPG) & ~(PAGE_SIZE * NPTEPG - 1);
+ continue;
+ }
+ ++nkpt;
+ if (!nkpg) {
+ nkpg = vm_page_alloc(kernel_object, 0, VM_ALLOC_SYSTEM);
+ if (!nkpg)
+ panic("pmap_growkernel: no memory to grow kernel");
+ vm_page_wire(nkpg);
+ vm_page_remove(nkpg);
+ pmap_zero_page(VM_PAGE_TO_PHYS(nkpg));
+ }
+ pdir_pde(PTD, kernel_vm_end) = (pd_entry_t) (VM_PAGE_TO_PHYS(nkpg) | PG_V | PG_RW);
+ nkpg = NULL;
+
+ for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) {
+ if (p->p_vmspace) {
+ pmap = &p->p_vmspace->vm_pmap;
+ *pmap_pde(pmap, kernel_vm_end) = pdir_pde(PTD, kernel_vm_end);
+ }
+ }
+ *pmap_pde(kernel_pmap, kernel_vm_end) = pdir_pde(PTD, kernel_vm_end);
+ kernel_vm_end = (kernel_vm_end + PAGE_SIZE * NPTEPG) & ~(PAGE_SIZE * NPTEPG - 1);
+ }
+ splx(s);
+}
+
+/*
+ * Retire the given physical map from service.
+ * Should only be called if the map contains
+ * no valid mappings.
+ */
+void
+pmap_destroy(pmap)
+ register pmap_t pmap;
+{
+ int count;
+
+ if (pmap == NULL)
+ return;
+
+ count = --pmap->pm_count;
+ if (count == 0) {
+ pmap_release(pmap);
+ free((caddr_t) pmap, M_VMPMAP);
+ }
+}
+
+/*
+ * Add a reference to the specified pmap.
+ */
+void
+pmap_reference(pmap)
+ pmap_t pmap;
+{
+ if (pmap != NULL) {
+ pmap->pm_count++;
+ }
+}
+
+/*
+ * free the pv_entry back to the free list
+ */
+static __inline void
+free_pv_entry(pv)
+ pv_entry_t pv;
+{
+ ++pv_freelistcnt;
+ pv->pv_next = pv_freelist;
+ pv_freelist = pv;
+}
+
+/*
+ * get a new pv_entry, allocating a block from the system
+ * when needed.
+ * the memory allocation is performed bypassing the malloc code
+ * because of the possibility of allocations at interrupt time.
+ */
+static __inline pv_entry_t
+get_pv_entry()
+{
+ pv_entry_t tmp;
+
+ /*
+ * get more pv_entry pages if needed
+ */
+ if (pv_freelistcnt < PV_FREELIST_MIN || pv_freelist == 0) {
+ pmap_alloc_pv_entry();
+ }
+ /*
+ * get a pv_entry off of the free list
+ */
+ --pv_freelistcnt;
+ tmp = pv_freelist;
+ pv_freelist = tmp->pv_next;
+ return tmp;
+}
+
+/*
+ * This *strange* allocation routine eliminates the possibility of a malloc
+ * failure (*FATAL*) for a pv_entry_t data structure.
+ * also -- this code is MUCH MUCH faster than the malloc equiv...
+ * We really need to do the slab allocator thingie here.
+ */
+static void
+pmap_alloc_pv_entry()
+{
+ /*
+ * do we have any pre-allocated map-pages left?
+ */
+ if (npvvapg) {
+ vm_page_t m;
+
+ /*
+ * allocate a physical page out of the vm system
+ */
+ m = vm_page_alloc(kernel_object,
+ OFF_TO_IDX(pvva - vm_map_min(kernel_map)),
+ VM_ALLOC_INTERRUPT);
+ if (m) {
+ int newentries;
+ int i;
+ pv_entry_t entry;
+
+ newentries = (PAGE_SIZE / sizeof(struct pv_entry));
+ /*
+ * wire the page
+ */
+ vm_page_wire(m);
+ m->flags &= ~PG_BUSY;
+ /*
+ * let the kernel see it
+ */
+ pmap_kenter(pvva, VM_PAGE_TO_PHYS(m));
+
+ entry = (pv_entry_t) pvva;
+ /*
+ * update the allocation pointers
+ */
+ pvva += PAGE_SIZE;
+ --npvvapg;
+
+ /*
+ * free the entries into the free list
+ */
+ for (i = 0; i < newentries; i++) {
+ free_pv_entry(entry);
+ entry++;
+ }
+ }
+ }
+ if (!pv_freelist)
+ panic("get_pv_entry: cannot get a pv_entry_t");
+}
+
+/*
+ * init the pv_entry allocation system
+ */
+#define PVSPERPAGE 64
+void
+init_pv_entries(npg)
+ int npg;
+{
+ /*
+ * allocate enough kvm space for PVSPERPAGE entries per page (lots)
+ * kvm space is fairly cheap, be generous!!! (the system can panic if
+ * this is too small.)
+ */
+ npvvapg = ((npg * PVSPERPAGE) * sizeof(struct pv_entry)
+ + PAGE_SIZE - 1) / PAGE_SIZE;
+ pvva = kmem_alloc_pageable(kernel_map, npvvapg * PAGE_SIZE);
+ /*
+ * get the first batch of entries
+ */
+ pmap_alloc_pv_entry();
+}
+
+/*
+ * If it is the first entry on the list, it is actually
+ * in the header and we must copy the following entry up
+ * to the header. Otherwise we must search the list for
+ * the entry. In either case we free the now unused entry.
+ */
+static __inline int
+pmap_remove_entry(pmap, ppv, va)
+ struct pmap *pmap;
+ pv_entry_t *ppv;
+ vm_offset_t va;
+{
+ pv_entry_t npv;
+ int s;
+
+ s = splvm();
+ for (npv = *ppv; npv; (ppv = &npv->pv_next, npv = *ppv)) {
+ if (pmap == npv->pv_pmap && va == npv->pv_va) {
+ int rtval = pmap_unuse_pt(pmap, va, npv->pv_ptem);
+ *ppv = npv->pv_next;
+ free_pv_entry(npv);
+ splx(s);
+ return rtval;
+ }
+ }
+ splx(s);
+ return 0;
+}
+
+/*
+ * pmap_remove_pte: do the things to unmap a page in a process
+ */
+static
+#if !defined(PMAP_DIAGNOSTIC)
+__inline
+#endif
+int
+pmap_remove_pte(pmap, ptq, va)
+ struct pmap *pmap;
+ unsigned *ptq;
+ vm_offset_t va;
+{
+ unsigned oldpte;
+ pv_entry_t *ppv;
+ int rtval;
+
+ oldpte = *ptq;
+ *ptq = 0;
+ if (oldpte & PG_W)
+ pmap->pm_stats.wired_count -= 1;
+ pmap->pm_stats.resident_count -= 1;
+ if (oldpte & PG_MANAGED) {
+ if (oldpte & PG_M) {
+#if defined(PMAP_DIAGNOSTIC)
+ if (pmap_nw_modified((pt_entry_t) oldpte)) {
+ printf("pmap_remove: modified page not writable: va: 0x%lx, pte: 0x%lx\n", va, (int) oldpte);
+ }
+#endif
+
+ if (va < clean_sva || va >= clean_eva) {
+ if ((va < UPT_MIN_ADDRESS) || (va >= UPT_MAX_ADDRESS))
+ PHYS_TO_VM_PAGE(oldpte)->dirty = VM_PAGE_BITS_ALL;
+ }
+ }
+ ppv = pa_to_pvh(oldpte);
+ rtval = pmap_remove_entry(pmap, ppv, va);
+#if defined(notyet)
+ if (*ppv == NULL) {
+ PHYS_TO_VM_PAGE(oldpte)->flags &= ~PG_MAPPED;
+ }
+#endif
+ return rtval;
+ } else {
+ return pmap_unuse_pt(pmap, va, NULL);
+ }
+
+ return 0;
+}
+
+/*
+ * Remove a single page from a process address space
+ */
+static __inline void
+pmap_remove_page(pmap, va)
+ struct pmap *pmap;
+ register vm_offset_t va;
+{
+ register unsigned *ptq;
+
+ /*
+ * if there is no pte for this address, just skip it!!!
+ */
+ if (*pmap_pde(pmap, va) == 0) {
+ return;
+ }
+
+ /*
+ * get a local va for mappings for this pmap.
+ */
+ ptq = get_ptbase(pmap) + i386_btop(va);
+ if (*ptq) {
+ (void) pmap_remove_pte(pmap, ptq, va);
+ pmap_update_1pg(va);
+ }
+ return;
+}
+
+/*
+ * Remove the given range of addresses from the specified map.
+ *
+ * It is assumed that the start and end are properly
+ * rounded to the page size.
+ */
+void
+pmap_remove(pmap, sva, eva)
+ struct pmap *pmap;
+ register vm_offset_t sva;
+ register vm_offset_t eva;
+{
+ register unsigned *ptbase;
+ vm_offset_t pdnxt;
+ vm_offset_t ptpaddr;
+ vm_offset_t sindex, eindex;
+ vm_page_t mpte;
+ int anyvalid;
+
+ if (pmap == NULL)
+ return;
+
+ /*
+ * special handling of removing one page. a very
+ * common operation and easy to short circuit some
+ * code.
+ */
+ if ((sva + PAGE_SIZE) == eva) {
+ pmap_remove_page(pmap, sva);
+ return;
+ }
+
+ anyvalid = 0;
+
+ /*
+ * Get a local virtual address for the mappings that are being
+ * worked with.
+ */
+ ptbase = get_ptbase(pmap);
+
+ sindex = i386_btop(sva);
+ eindex = i386_btop(eva);
+
+ for (; sindex < eindex; sindex = pdnxt) {
+
+ /*
+ * Calculate index for next page table.
+ */
+ pdnxt = ((sindex + NPTEPG) & ~(NPTEPG - 1));
+ ptpaddr = (vm_offset_t) *pmap_pde(pmap, i386_ptob(sindex));
+
+ /*
+ * Weed out invalid mappings. Note: we assume that the page
+ * directory table is always allocated, and in kernel virtual.
+ */
+ if (ptpaddr == 0)
+ continue;
+
+ if (sindex < i386_btop(UPT_MIN_ADDRESS)) {
+ /*
+ * get the vm_page_t for the page table page
+ */
+ mpte = PHYS_TO_VM_PAGE(ptpaddr);
+
+ /*
+ * if the pte isn't wired, just skip it.
+ */
+ if (mpte->wire_count == 0)
+ continue;
+ }
+
+ /*
+ * Limit our scan to either the end of the va represented
+ * by the current page table page, or to the end of the
+ * range being removed.
+ */
+ if (pdnxt > eindex) {
+ pdnxt = eindex;
+ }
+
+ for ( ;sindex != pdnxt; sindex++) {
+ vm_offset_t va;
+ if (ptbase[sindex] == 0) {
+ continue;
+ }
+ va = i386_ptob(sindex);
+ anyvalid = 1;
+ if (pmap_remove_pte(pmap,
+ ptbase + sindex, va))
+ break;
+ }
+ }
+
+ if (anyvalid) {
+ pmap_update();
+ }
+}
+
+/*
+ * Remove pte mapping, don't do everything that we would do
+ * for normal pages because many things aren't necessary (like
+ * pmap_update())...
+ */
+void
+pmap_remove_pte_mapping(pa)
+ vm_offset_t pa;
+{
+ register pv_entry_t pv, *ppv, npv;
+ register unsigned *pte;
+ vm_offset_t va;
+ int anyvalid = 0;
+
+ ppv = pa_to_pvh(pa);
+
+ for (pv = *ppv; pv; pv=pv->pv_next) {
+ unsigned tpte;
+ struct pmap *pmap;
+
+ pmap = pv->pv_pmap;
+ pte = get_ptbase(pmap) + i386_btop(pv->pv_va);
+ if (tpte = *pte) {
+ pmap->pm_stats.resident_count--;
+ *pte = 0;
+ if (tpte & PG_W)
+ pmap->pm_stats.wired_count--;
+ }
+ }
+
+ for (pv = *ppv; pv; pv = npv) {
+ npv = pv->pv_next;
+ free_pv_entry(pv);
+ }
+ *ppv = NULL;
+}
+
+/*
+ * Routine: pmap_remove_all
+ * Function:
+ * Removes this physical page from
+ * all physical maps in which it resides.
+ * Reflects back modify bits to the pager.
+ *
+ * Notes:
+ * Original versions of this routine were very
+ * inefficient because they iteratively called
+ * pmap_remove (slow...)
+ */
+static void
+pmap_remove_all(pa)
+ vm_offset_t pa;
+{
+ register pv_entry_t pv, *ppv, npv;
+ register unsigned *pte, *ptbase;
+ vm_offset_t va;
+ vm_page_t m;
+ int s;
+
+#if defined(PMAP_DIAGNOSTIC)
+ /*
+ * XXX this makes pmap_page_protect(NONE) illegal for non-managed
+ * pages!
+ */
+ if (!pmap_is_managed(pa)) {
+ panic("pmap_page_protect: illegal for unmanaged page, va: 0x%lx", pa);
+ }
+#endif
+
+ m = PHYS_TO_VM_PAGE(pa);
+ ppv = pa_to_pvh(pa);
+
+ s = splvm();
+ for (pv = *ppv; pv; pv=pv->pv_next) {
+ int tpte;
+ struct pmap *pmap;
+
+ pmap = pv->pv_pmap;
+ ptbase = get_ptbase(pmap);
+ va = pv->pv_va;
+ if (*pmap_pde(pmap, va) == 0)
+ continue;
+ pte = ptbase + i386_btop(va);
+ if (tpte = ((int) *pte)) {
+ pmap->pm_stats.resident_count--;
+ *pte = 0;
+ if (tpte & PG_W)
+ pmap->pm_stats.wired_count--;
+ /*
+ * Update the vm_page_t clean and reference bits.
+ */
+ if (tpte & PG_M) {
+#if defined(PMAP_DIAGNOSTIC)
+ if (pmap_nw_modified((pt_entry_t) tpte)) {
+ printf("pmap_remove_all: modified page not writable: va: 0x%lx, pte: 0x%lx\n", va, tpte);
+ }
+#endif
+ if ((va >= UPT_MIN_ADDRESS) &&
+ (va < UPT_MAX_ADDRESS))
+ continue;
+
+ if (va < clean_sva || va >= clean_eva) {
+ m->dirty = VM_PAGE_BITS_ALL;
+ }
+ }
+ }
+ }
+
+ for (pv = *ppv; pv; pv = npv) {
+ npv = pv->pv_next;
+ pmap_unuse_pt(pv->pv_pmap, pv->pv_va, pv->pv_ptem);
+ free_pv_entry(pv);
+ }
+ *ppv = NULL;
+
+ splx(s);
+}
+
+/*
+ * Set the physical protection on the
+ * specified range of this map as requested.
+ */
+void
+pmap_protect(pmap, sva, eva, prot)
+ register pmap_t pmap;
+ vm_offset_t sva, eva;
+ vm_prot_t prot;
+{
+ register unsigned *pte;
+ register vm_offset_t va;
+ register unsigned *ptbase;
+ vm_offset_t pdnxt;
+ vm_offset_t ptpaddr;
+ vm_offset_t sindex, eindex;
+ vm_page_t mpte;
+ int anyvalid;
+
+
+ if (pmap == NULL)
+ return;
+
+ if ((prot & VM_PROT_READ) == VM_PROT_NONE) {
+ pmap_remove(pmap, sva, eva);
+ return;
+ }
+ if (prot & VM_PROT_WRITE)
+ return;
+
+ anyvalid = 0;
+
+ ptbase = get_ptbase(pmap);
+
+ sindex = i386_btop(sva);
+ eindex = i386_btop(eva);
+
+ for (; sindex < eindex; sindex = pdnxt) {
+
+ pdnxt = ((sindex + NPTEPG) & ~(NPTEPG - 1));
+ ptpaddr = (vm_offset_t) *pmap_pde(pmap, i386_ptob(sindex));
+
+ /*
+ * Weed out invalid mappings. Note: we assume that the page
+ * directory table is always allocated, and in kernel virtual.
+ */
+ if (ptpaddr == 0)
+ continue;
+
+ /*
+ * Don't look at kernel page table pages
+ */
+ if (sindex < i386_btop(UPT_MIN_ADDRESS)) {
+ mpte = PHYS_TO_VM_PAGE(ptpaddr);
+
+ if (mpte->wire_count == 0)
+ continue;
+ }
+
+ if (pdnxt > eindex) {
+ pdnxt = eindex;
+ }
+
+ for (; sindex != pdnxt; sindex++) {
+
+ unsigned pbits = ptbase[sindex];
+
+ if (pbits & PG_RW) {
+ if (pbits & PG_M) {
+ vm_page_t m = PHYS_TO_VM_PAGE(pbits);
+ m->dirty = VM_PAGE_BITS_ALL;
+ }
+ ptbase[sindex] = pbits & ~(PG_M|PG_RW);
+ anyvalid = 1;
+ }
+ }
+ }
+ if (anyvalid)
+ pmap_update();
+}
+
+/*
+ * Create a pv entry for page at pa for
+ * (pmap, va).
+ */
+static __inline void
+pmap_insert_entry(pmap, va, mpte, pa)
+ pmap_t pmap;
+ vm_offset_t va;
+ vm_page_t mpte;
+ vm_offset_t pa;
+{
+
+ int s;
+ pv_entry_t *ppv, pv;
+
+ s = splvm();
+ pv = get_pv_entry();
+ pv->pv_va = va;
+ pv->pv_pmap = pmap;
+ pv->pv_ptem = mpte;
+
+ ppv = pa_to_pvh(pa);
+ if (*ppv)
+ pv->pv_next = *ppv;
+ else
+ pv->pv_next = NULL;
+ *ppv = pv;
+ splx(s);
+}
+
+/*
+ * this routine is called if the page table page is not
+ * mapped correctly.
+ */
+static vm_page_t
+_pmap_allocpte(pmap, va, ptepindex)
+ pmap_t pmap;
+ vm_offset_t va;
+ int ptepindex;
+{
+ vm_offset_t pteva, ptepa;
+ vm_page_t m;
+ int s;
+
+ /*
+ * Find or fabricate a new pagetable page
+ */
+retry:
+ m = vm_page_lookup(pmap->pm_pteobj, ptepindex);
+ if (m == NULL) {
+ m = vm_page_alloc(pmap->pm_pteobj, ptepindex, VM_ALLOC_ZERO);
+ if (m == NULL) {
+ VM_WAIT;
+ goto retry;
+ }
+ if ((m->flags & PG_ZERO) == 0)
+ pmap_zero_page(VM_PAGE_TO_PHYS(m));
+ m->flags &= ~(PG_ZERO|PG_BUSY);
+ m->valid = VM_PAGE_BITS_ALL;
+ } else {
+ if ((m->flags & PG_BUSY) || m->busy) {
+ m->flags |= PG_WANTED;
+ tsleep(m, PVM, "ptewai", 0);
+ goto retry;
+ }
+ }
+
+ /*
+ * mark the object writeable
+ */
+ pmap->pm_pteobj->flags |= OBJ_WRITEABLE;
+
+ if (m->hold_count == 0) {
+ s = splvm();
+ vm_page_unqueue(m);
+ splx(s);
+ ++m->wire_count;
+ ++cnt.v_wire_count;
+ }
+
+ /*
+ * Increment the hold count for the page table page
+ * (denoting a new mapping.)
+ */
+ ++m->hold_count;
+
+ /*
+ * Map the pagetable page into the process address space, if
+ * it isn't already there.
+ */
+ pteva = ((vm_offset_t) vtopte(va)) & PG_FRAME;
+ ptepa = (vm_offset_t) pmap->pm_pdir[ptepindex];
+ if (ptepa == 0) {
+ pv_entry_t pv, *ppv;
+
+ pmap->pm_stats.resident_count++;
+
+ s = splvm();
+ pv = get_pv_entry();
+
+ pv->pv_va = pteva;
+ pv->pv_pmap = pmap;
+ pv->pv_next = NULL;
+ pv->pv_ptem = NULL;
+
+ ptepa = VM_PAGE_TO_PHYS(m);
+ ppv = pa_to_pvh(ptepa);
+#if defined(PMAP_DIAGNOSTIC)
+ if (*ppv)
+ panic("pmap_allocpte: page is already mapped");
+#endif
+ *ppv = pv;
+ splx(s);
+ pmap_update_1pg(pteva);
+ } else {
+#if defined(PMAP_DIAGNOSTIC)
+ if (VM_PAGE_TO_PHYS(m) != (ptepa & PG_FRAME))
+ panic("pmap_allocpte: mismatch");
+#endif
+ }
+ pmap->pm_pdir[ptepindex] =
+ (pd_entry_t) (ptepa | PG_U | PG_RW | PG_V | PG_MANAGED);
+ m->flags |= PG_MAPPED;
+ return m;
+}
+
+static __inline vm_page_t
+pmap_allocpte(pmap, va)
+ pmap_t pmap;
+ vm_offset_t va;
+{
+ int ptepindex;
+ vm_offset_t ptepa;
+ vm_page_t m;
+
+ /*
+ * Calculate pagetable page index
+ */
+ ptepindex = va >> PDRSHIFT;
+
+ /*
+ * Get the page directory entry
+ */
+ ptepa = (vm_offset_t) pmap->pm_pdir[ptepindex];
+
+ /*
+ * If the page table page is mapped, we just increment the
+ * hold count, and activate it.
+ */
+ if ((ptepa & (PG_RW|PG_U|PG_V)) == (PG_RW|PG_U|PG_V)) {
+ m = PHYS_TO_VM_PAGE(ptepa);
+ if (m->hold_count == 0) {
+ int s = splvm();
+ vm_page_unqueue(m);
+ splx(s);
+ ++m->wire_count;
+ ++cnt.v_wire_count;
+ }
+ ++m->hold_count;
+ return m;
+ }
+ return _pmap_allocpte(pmap, va, ptepindex);
+}
+
+/*
+ * Insert the given physical page (p) at
+ * the specified virtual address (v) in the
+ * target physical map with the protection requested.
+ *
+ * If specified, the page will be wired down, meaning
+ * that the related pte can not be reclaimed.
+ *
+ * NB: This is the only routine which MAY NOT lazy-evaluate
+ * or lose information. That is, this routine must actually
+ * insert this page into the given map NOW.
+ */
+void
+pmap_enter(pmap, va, pa, prot, wired)
+ register pmap_t pmap;
+ vm_offset_t va;
+ register vm_offset_t pa;
+ vm_prot_t prot;
+ boolean_t wired;
+{
+ register unsigned *pte;
+ vm_offset_t opa;
+ vm_offset_t origpte, newpte;
+ vm_page_t mpte;
+
+ if (pmap == NULL)
+ return;
+
+ va &= PG_FRAME;
+ if (va > VM_MAX_KERNEL_ADDRESS)
+ panic("pmap_enter: toobig");
+
+ mpte = NULL;
+ /*
+ * In the case that a page table page is not
+ * resident, we are creating it here.
+ */
+ if (va < UPT_MIN_ADDRESS)
+ mpte = pmap_allocpte(pmap, va);
+
+ pte = pmap_pte(pmap, va);
+ /*
+ * Page Directory table entry not valid, we need a new PT page
+ */
+ if (pte == NULL) {
+ printf("kernel page directory invalid pdir=%p, va=0x%lx\n",
+ pmap->pm_pdir[PTDPTDI], va);
+ panic("invalid kernel page directory");
+ }
+
+ origpte = *(vm_offset_t *)pte;
+ pa &= PG_FRAME;
+ opa = origpte & PG_FRAME;
+
+ /*
+ * Mapping has not changed, must be protection or wiring change.
+ */
+ if (opa == pa) {
+ /*
+ * Wiring change, just update stats. We don't worry about
+ * wiring PT pages as they remain resident as long as there
+ * are valid mappings in them. Hence, if a user page is wired,
+ * the PT page will be also.
+ */
+ if (wired && ((origpte & PG_W) == 0))
+ pmap->pm_stats.wired_count++;
+ else if (!wired && (origpte & PG_W))
+ pmap->pm_stats.wired_count--;
+
+#if defined(PMAP_DIAGNOSTIC)
+ if (pmap_nw_modified((pt_entry_t) origpte)) {
+ printf("pmap_enter: modified page not writable: va: 0x%lx, pte: 0x%lx\n", va, origpte);
+ }
+#endif
+
+ /*
+ * We might be turning off write access to the page,
+ * so we go ahead and sense modify status.
+ */
+ if (origpte & PG_MANAGED) {
+ vm_page_t m;
+ if (origpte & PG_M) {
+ m = PHYS_TO_VM_PAGE(pa);
+ m->dirty = VM_PAGE_BITS_ALL;
+ }
+ pa |= PG_MANAGED;
+ }
+
+ if (mpte)
+ --mpte->hold_count;
+
+ goto validate;
+ }
+ /*
+ * Mapping has changed, invalidate old range and fall through to
+ * handle validating new mapping.
+ */
+ if (opa)
+ (void) pmap_remove_pte(pmap, pte, va);
+
+ /*
+ * Enter on the PV list if part of our managed memory Note that we
+ * raise IPL while manipulating pv_table since pmap_enter can be
+ * called at interrupt time.
+ */
+ if (pmap_is_managed(pa)) {
+ pmap_insert_entry(pmap, va, mpte, pa);
+ pa |= PG_MANAGED;
+ }
+
+ /*
+ * Increment counters
+ */
+ pmap->pm_stats.resident_count++;
+ if (wired)
+ pmap->pm_stats.wired_count++;
+
+validate:
+ /*
+ * Now validate mapping with desired protection/wiring.
+ */
+ newpte = (vm_offset_t) (pa | pte_prot(pmap, prot) | PG_V);
+
+ if (wired)
+ newpte |= PG_W;
+ if (va < UPT_MIN_ADDRESS)
+ newpte |= PG_U;
+
+ /*
+ * if the mapping or permission bits are different, we need
+ * to update the pte.
+ */
+ if ((origpte & ~(PG_M|PG_A)) != newpte) {
+ *pte = newpte;
+ if (origpte)
+ pmap_update_1pg(va);
+ }
+}
+
+/*
+ * this code makes some *MAJOR* assumptions:
+ * 1. Current pmap & pmap exists.
+ * 2. Not wired.
+ * 3. Read access.
+ * 4. No page table pages.
+ * 5. Tlbflush is deferred to calling procedure.
+ * 6. Page IS managed.
+ * but is *MUCH* faster than pmap_enter...
+ */
+
+static void
+pmap_enter_quick(pmap, va, pa)
+ register pmap_t pmap;
+ vm_offset_t va;
+ register vm_offset_t pa;
+{
+ register unsigned *pte;
+ vm_page_t mpte;
+
+ mpte = NULL;
+ /*
+ * In the case that a page table page is not
+ * resident, we are creating it here.
+ */
+ if (va < UPT_MIN_ADDRESS)
+ mpte = pmap_allocpte(pmap, va);
+
+ pte = (unsigned *)vtopte(va);
+ if (*pte)
+ (void) pmap_remove_pte(pmap, pte, va);
+
+ /*
+ * Enter on the PV list if part of our managed memory Note that we
+ * raise IPL while manipulating pv_table since pmap_enter can be
+ * called at interrupt time.
+ */
+ pmap_insert_entry(pmap, va, mpte, pa);
+
+ /*
+ * Increment counters
+ */
+ pmap->pm_stats.resident_count++;
+
+ /*
+ * Now validate mapping with RO protection
+ */
+ *pte = pa | PG_V | PG_U | PG_MANAGED;
+
+ return;
+}
+
+#define MAX_INIT_PT (96)
+/*
+ * pmap_object_init_pt preloads the ptes for a given object
+ * into the specified pmap. This eliminates the blast of soft
+ * faults on process startup and immediately after an mmap.
+ */
+void
+pmap_object_init_pt(pmap, addr, object, pindex, size, limit)
+ pmap_t pmap;
+ vm_offset_t addr;
+ vm_object_t object;
+ vm_pindex_t pindex;
+ vm_size_t size;
+ int limit;
+{
+ vm_offset_t tmpidx;
+ int psize;
+ vm_page_t p;
+ int objpgs;
+
+ psize = (size >> PAGE_SHIFT);
+
+ if (!pmap || (object->type != OBJT_VNODE) ||
+ (limit && (psize > MAX_INIT_PT) &&
+ (object->resident_page_count > MAX_INIT_PT))) {
+ return;
+ }
+
+ /*
+ * if we are processing a major portion of the object, then scan the
+ * entire thing.
+ */
+ if (psize > (object->size >> 2)) {
+ objpgs = psize;
+
+ for (p = TAILQ_FIRST(&object->memq);
+ ((objpgs > 0) && (p != NULL));
+ p = TAILQ_NEXT(p, listq)) {
+
+ tmpidx = p->pindex;
+ if (tmpidx < pindex) {
+ continue;
+ }
+ tmpidx -= pindex;
+ if (tmpidx >= psize) {
+ continue;
+ }
+ if (((p->valid & VM_PAGE_BITS_ALL) == VM_PAGE_BITS_ALL) &&
+ (p->busy == 0) &&
+ (p->flags & (PG_BUSY | PG_FICTITIOUS)) == 0) {
+ if (p->queue == PQ_CACHE)
+ vm_page_deactivate(p);
+ p->flags |= PG_BUSY;
+ pmap_enter_quick(pmap,
+ addr + (tmpidx << PAGE_SHIFT),
+ VM_PAGE_TO_PHYS(p));
+ p->flags |= PG_MAPPED;
+ PAGE_WAKEUP(p);
+ }
+ objpgs -= 1;
+ }
+ } else {
+ /*
+ * else lookup the pages one-by-one.
+ */
+ for (tmpidx = 0; tmpidx < psize; tmpidx += 1) {
+ p = vm_page_lookup(object, tmpidx + pindex);
+ if (p &&
+ ((p->valid & VM_PAGE_BITS_ALL) == VM_PAGE_BITS_ALL) &&
+ (p->busy == 0) &&
+ (p->flags & (PG_BUSY | PG_FICTITIOUS)) == 0) {
+ if (p->queue == PQ_CACHE)
+ vm_page_deactivate(p);
+ p->flags |= PG_BUSY;
+ pmap_enter_quick(pmap,
+ addr + (tmpidx << PAGE_SHIFT),
+ VM_PAGE_TO_PHYS(p));
+ p->flags |= PG_MAPPED;
+ PAGE_WAKEUP(p);
+ }
+ }
+ }
+ return;
+}
+
+/*
+ * pmap_prefault provides a quick way of clustering
+ * pagefaults into a processes address space. It is a "cousin"
+ * of pmap_object_init_pt, except it runs at page fault time instead
+ * of mmap time.
+ */
+#define PFBAK 2
+#define PFFOR 2
+#define PAGEORDER_SIZE (PFBAK+PFFOR)
+
+static int pmap_prefault_pageorder[] = {
+ -PAGE_SIZE, PAGE_SIZE, -2 * PAGE_SIZE, 2 * PAGE_SIZE
+};
+
+void
+pmap_prefault(pmap, addra, entry, object)
+ pmap_t pmap;
+ vm_offset_t addra;
+ vm_map_entry_t entry;
+ vm_object_t object;
+{
+ int i;
+ vm_offset_t starta;
+ vm_offset_t addr;
+ vm_pindex_t pindex;
+ vm_page_t m;
+
+ if (entry->object.vm_object != object)
+ return;
+
+ if (!curproc || (pmap != &curproc->p_vmspace->vm_pmap))
+ return;
+
+ starta = addra - PFBAK * PAGE_SIZE;
+ if (starta < entry->start) {
+ starta = entry->start;
+ } else if (starta > addra) {
+ starta = 0;
+ }
+
+ for (i = 0; i < PAGEORDER_SIZE; i++) {
+ vm_object_t lobject;
+ unsigned *pte;
+
+ addr = addra + pmap_prefault_pageorder[i];
+ if (addr < starta || addr >= entry->end)
+ continue;
+
+ if ((*pmap_pde(pmap, addr)) == NULL)
+ continue;
+
+ pte = (unsigned *) vtopte(addr);
+ if (*pte)
+ continue;
+
+ pindex = ((addr - entry->start) + entry->offset) >> PAGE_SHIFT;
+ lobject = object;
+ for (m = vm_page_lookup(lobject, pindex);
+ (!m && (lobject->type == OBJT_DEFAULT) && (lobject->backing_object));
+ lobject = lobject->backing_object) {
+ if (lobject->backing_object_offset & PAGE_MASK)
+ break;
+ pindex += (lobject->backing_object_offset >> PAGE_SHIFT);
+ m = vm_page_lookup(lobject->backing_object, pindex);
+ }
+
+ /*
+ * give-up when a page is not in memory
+ */
+ if (m == NULL)
+ break;
+
+ if (((m->valid & VM_PAGE_BITS_ALL) == VM_PAGE_BITS_ALL) &&
+ (m->busy == 0) &&
+ (m->flags & (PG_BUSY | PG_FICTITIOUS)) == 0) {
+
+ if (m->queue == PQ_CACHE) {
+ vm_page_deactivate(m);
+ }
+ m->flags |= PG_BUSY;
+ pmap_enter_quick(pmap, addr, VM_PAGE_TO_PHYS(m));
+ m->flags |= PG_MAPPED;
+ PAGE_WAKEUP(m);
+ }
+ }
+}
+
+/*
+ * Routine: pmap_change_wiring
+ * Function: Change the wiring attribute for a map/virtual-address
+ * pair.
+ * In/out conditions:
+ * The mapping must already exist in the pmap.
+ */
+void
+pmap_change_wiring(pmap, va, wired)
+ register pmap_t pmap;
+ vm_offset_t va;
+ boolean_t wired;
+{
+ register unsigned *pte;
+
+ if (pmap == NULL)
+ return;
+
+ pte = pmap_pte(pmap, va);
+
+ if (wired && !pmap_pte_w(pte))
+ pmap->pm_stats.wired_count++;
+ else if (!wired && pmap_pte_w(pte))
+ pmap->pm_stats.wired_count--;
+
+ /*
+ * Wiring is not a hardware characteristic so there is no need to
+ * invalidate TLB.
+ */
+ pmap_pte_set_w(pte, wired);
+}
+
+
+
+/*
+ * Copy the range specified by src_addr/len
+ * from the source map to the range dst_addr/len
+ * in the destination map.
+ *
+ * This routine is only advisory and need not do anything.
+ */
+void
+pmap_copy(dst_pmap, src_pmap, dst_addr, len, src_addr)
+ pmap_t dst_pmap, src_pmap;
+ vm_offset_t dst_addr;
+ vm_size_t len;
+ vm_offset_t src_addr;
+{
+ vm_offset_t addr;
+ vm_offset_t end_addr = src_addr + len;
+ vm_offset_t pdnxt;
+ unsigned src_frame, dst_frame;
+
+ if (dst_addr != src_addr)
+ return;
+
+ src_frame = ((unsigned) src_pmap->pm_pdir[PTDPTDI]) & PG_FRAME;
+ if (src_frame != (((unsigned) PTDpde) & PG_FRAME))
+ return;
+
+ dst_frame = ((unsigned) dst_pmap->pm_pdir[PTDPTDI]) & PG_FRAME;
+ if (dst_frame != (((unsigned) APTDpde) & PG_FRAME)) {
+ APTDpde = (pd_entry_t) (dst_frame | PG_RW | PG_V);
+ pmap_update();
+ }
+
+ for(addr = src_addr; addr < end_addr; addr = pdnxt) {
+ unsigned *src_pte, *dst_pte;
+ vm_page_t dstmpte, srcmpte;
+ vm_offset_t srcptepaddr;
+
+ pdnxt = ((addr + PAGE_SIZE*NPTEPG) & ~(PAGE_SIZE*NPTEPG - 1));
+ srcptepaddr = (vm_offset_t) src_pmap->pm_pdir[addr >> PDRSHIFT];
+ if (srcptepaddr == 0) {
+ continue;
+ }
+
+ srcmpte = PHYS_TO_VM_PAGE(srcptepaddr);
+ if (srcmpte->hold_count == 0)
+ continue;
+
+ if (pdnxt > end_addr)
+ pdnxt = end_addr;
+
+ src_pte = (unsigned *) vtopte(addr);
+ dst_pte = (unsigned *) avtopte(addr);
+ while (addr < pdnxt) {
+ unsigned ptetemp;
+ ptetemp = *src_pte;
+ /*
+ * we only virtual copy managed pages
+ */
+ if ((ptetemp & PG_MANAGED) != 0) {
+ /*
+ * We have to check after allocpte for the
+ * pte still being around... allocpte can
+ * block.
+ */
+ dstmpte = pmap_allocpte(dst_pmap, addr);
+ if (ptetemp = *src_pte) {
+ /*
+ * Simply clear the modified and accessed (referenced)
+ * bits.
+ */
+ *dst_pte = ptetemp & ~(PG_M|PG_A);
+ dst_pmap->pm_stats.resident_count++;
+ pmap_insert_entry(dst_pmap, addr, dstmpte,
+ (ptetemp & PG_FRAME));
+ } else {
+ pmap_unwire_pte_hold(dstmpte);
+ }
+ if (dstmpte->hold_count >= srcmpte->hold_count)
+ break;
+ }
+ addr += PAGE_SIZE;
+ ++src_pte;
+ ++dst_pte;
+ }
+ }
+}
+
+/*
+ * Routine: pmap_kernel
+ * Function:
+ * Returns the physical map handle for the kernel.
+ */
+pmap_t
+pmap_kernel()
+{
+ return (kernel_pmap);
+}
+
+/*
+ * pmap_zero_page zeros the specified (machine independent)
+ * page by mapping the page into virtual memory and using
+ * bzero to clear its contents, one machine dependent page
+ * at a time.
+ */
+void
+pmap_zero_page(phys)
+ vm_offset_t phys;
+{
+ if (*(int *) CMAP2)
+ panic("pmap_zero_page: CMAP busy");
+
+ *(int *) CMAP2 = PG_V | PG_RW | (phys & PG_FRAME);
+ bzero(CADDR2, PAGE_SIZE);
+ *(int *) CMAP2 = 0;
+ pmap_update_1pg((vm_offset_t) CADDR2);
+}
+
+/*
+ * pmap_copy_page copies the specified (machine independent)
+ * page by mapping the page into virtual memory and using
+ * bcopy to copy the page, one machine dependent page at a
+ * time.
+ */
+void
+pmap_copy_page(src, dst)
+ vm_offset_t src;
+ vm_offset_t dst;
+{
+ if (*(int *) CMAP1 || *(int *) CMAP2)
+ panic("pmap_copy_page: CMAP busy");
+
+ *(int *) CMAP1 = PG_V | PG_RW | (src & PG_FRAME);
+ *(int *) CMAP2 = PG_V | PG_RW | (dst & PG_FRAME);
+
+#if __GNUC__ > 1
+ memcpy(CADDR2, CADDR1, PAGE_SIZE);
+#else
+ bcopy(CADDR1, CADDR2, PAGE_SIZE);
+#endif
+ *(int *) CMAP1 = 0;
+ *(int *) CMAP2 = 0;
+ pmap_update_2pg( (vm_offset_t) CADDR1, (vm_offset_t) CADDR2);
+}
+
+
+/*
+ * Routine: pmap_pageable
+ * Function:
+ * Make the specified pages (by pmap, offset)
+ * pageable (or not) as requested.
+ *
+ * A page which is not pageable may not take
+ * a fault; therefore, its page table entry
+ * must remain valid for the duration.
+ *
+ * This routine is merely advisory; pmap_enter
+ * will specify that these pages are to be wired
+ * down (or not) as appropriate.
+ */
+void
+pmap_pageable(pmap, sva, eva, pageable)
+ pmap_t pmap;
+ vm_offset_t sva, eva;
+ boolean_t pageable;
+{
+}
+
+/*
+ * this routine returns true if a physical page resides
+ * in the given pmap.
+ */
+boolean_t
+pmap_page_exists(pmap, pa)
+ pmap_t pmap;
+ vm_offset_t pa;
+{
+ register pv_entry_t *ppv, pv;
+ int s;
+
+ if (!pmap_is_managed(pa))
+ return FALSE;
+
+ s = splvm();
+
+ ppv = pa_to_pvh(pa);
+ /*
+ * Not found, check current mappings returning immediately if found.
+ */
+ for (pv = *ppv; pv; pv = pv->pv_next) {
+ if (pv->pv_pmap == pmap) {
+ splx(s);
+ return TRUE;
+ }
+ }
+ splx(s);
+ return (FALSE);
+}
+
+/*
+ * pmap_testbit tests bits in pte's
+ * note that the testbit/changebit routines are inline,
+ * and a lot of things compile-time evaluate.
+ */
+static __inline boolean_t
+pmap_testbit(pa, bit)
+ register vm_offset_t pa;
+ int bit;
+{
+ register pv_entry_t *ppv, pv;
+ unsigned *pte;
+ int s;
+
+ if (!pmap_is_managed(pa))
+ return FALSE;
+
+ s = splvm();
+
+ ppv = pa_to_pvh(pa);
+ /*
+ * Not found, check current mappings returning immediately if found.
+ */
+ for (pv = *ppv ;pv; pv = pv->pv_next) {
+ /*
+ * if the bit being tested is the modified bit, then
+ * mark UPAGES as always modified, and ptes as never
+ * modified.
+ */
+ if (bit & (PG_A|PG_M)) {
+ if ((pv->pv_va >= UPT_MIN_ADDRESS) &&
+ (pv->pv_va < UPT_MAX_ADDRESS)) {
+ continue;
+ }
+ if ((pv->pv_va >= clean_sva) &&
+ (pv->pv_va < clean_eva)) {
+ continue;
+ }
+ }
+ if (!pv->pv_pmap) {
+#if defined(PMAP_DIAGNOSTIC)
+ printf("Null pmap (tb) at va: 0x%lx\n", pv->pv_va);
+#endif
+ continue;
+ }
+ pte = pmap_pte(pv->pv_pmap, pv->pv_va);
+ if (pte == NULL)
+ continue;
+ if ((int) *pte & bit) {
+ splx(s);
+ return TRUE;
+ }
+ }
+ splx(s);
+ return (FALSE);
+}
+
+/*
+ * this routine is used to modify bits in ptes
+ */
+static __inline void
+pmap_changebit(pa, bit, setem)
+ vm_offset_t pa;
+ int bit;
+ boolean_t setem;
+{
+ register pv_entry_t pv, *ppv;
+ register unsigned *pte;
+ vm_offset_t va;
+ int changed;
+ int s;
+
+ if (!pmap_is_managed(pa))
+ return;
+
+ s = splvm();
+
+ changed = 0;
+ ppv = pa_to_pvh(pa);
+ /*
+ * Loop over all current mappings setting/clearing as appropos If
+ * setting RO do we need to clear the VAC?
+ */
+ for ( pv = *ppv; pv; pv = pv->pv_next) {
+ va = pv->pv_va;
+
+ /*
+ * don't write protect pager mappings
+ */
+ if (!setem && (bit == PG_RW)) {
+ if (va >= clean_sva && va < clean_eva)
+ continue;
+ }
+ if (!pv->pv_pmap) {
+#if defined(PMAP_DIAGNOSTIC)
+ printf("Null pmap (cb) at va: 0x%lx\n", va);
+#endif
+ continue;
+ }
+
+ pte = pmap_pte(pv->pv_pmap, va);
+ if (pte == NULL)
+ continue;
+ if (setem) {
+ *(int *)pte |= bit;
+ changed = 1;
+ } else {
+ vm_offset_t pbits = *(vm_offset_t *)pte;
+ if (pbits & bit)
+ changed = 1;
+ if (bit == PG_RW) {
+ if (pbits & PG_M) {
+ vm_page_t m;
+ vm_offset_t pa = pbits & PG_FRAME;
+ m = PHYS_TO_VM_PAGE(pa);
+ m->dirty = VM_PAGE_BITS_ALL;
+ }
+ *(int *)pte = pbits & ~(PG_M|PG_RW);
+ } else {
+ *(int *)pte = pbits & ~bit;
+ }
+ }
+ }
+ splx(s);
+ if (changed)
+ pmap_update();
+}
+
+/*
+ * pmap_page_protect:
+ *
+ * Lower the permission for all mappings to a given page.
+ */
+void
+pmap_page_protect(phys, prot)
+ vm_offset_t phys;
+ vm_prot_t prot;
+{
+ if ((prot & VM_PROT_WRITE) == 0) {
+ if (prot & (VM_PROT_READ | VM_PROT_EXECUTE))
+ pmap_changebit(phys, PG_RW, FALSE);
+ else {
+ pmap_remove_all(phys);
+ pmap_update();
+ }
+ }
+}
+
+vm_offset_t
+pmap_phys_address(ppn)
+ int ppn;
+{
+ return (i386_ptob(ppn));
+}
+
+/*
+ * pmap_is_referenced:
+ *
+ * Return whether or not the specified physical page was referenced
+ * by any physical maps.
+ */
+boolean_t
+pmap_is_referenced(vm_offset_t pa)
+{
+ return pmap_testbit((pa), PG_A);
+}
+
+/*
+ * pmap_is_modified:
+ *
+ * Return whether or not the specified physical page was modified
+ * in any physical maps.
+ */
+boolean_t
+pmap_is_modified(vm_offset_t pa)
+{
+ return pmap_testbit((pa), PG_M);
+}
+
+/*
+ * Clear the modify bits on the specified physical page.
+ */
+void
+pmap_clear_modify(vm_offset_t pa)
+{
+ pmap_changebit((pa), PG_M, FALSE);
+}
+
+/*
+ * pmap_clear_reference:
+ *
+ * Clear the reference bit on the specified physical page.
+ */
+void
+pmap_clear_reference(vm_offset_t pa)
+{
+ pmap_changebit((pa), PG_A, FALSE);
+}
+
+/*
+ * Miscellaneous support routines follow
+ */
+
+static void
+i386_protection_init()
+{
+ register int *kp, prot;
+
+ kp = protection_codes;
+ for (prot = 0; prot < 8; prot++) {
+ switch (prot) {
+ case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_NONE:
+ /*
+ * Read access is also 0. There isn't any execute bit,
+ * so just make it readable.
+ */
+ case VM_PROT_READ | VM_PROT_NONE | VM_PROT_NONE:
+ case VM_PROT_READ | VM_PROT_NONE | VM_PROT_EXECUTE:
+ case VM_PROT_NONE | VM_PROT_NONE | VM_PROT_EXECUTE:
+ *kp++ = 0;
+ break;
+ case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_NONE:
+ case VM_PROT_NONE | VM_PROT_WRITE | VM_PROT_EXECUTE:
+ case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_NONE:
+ case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE:
+ *kp++ = PG_RW;
+ break;
+ }
+ }
+}
+
+/*
+ * Map a set of physical memory pages into the kernel virtual
+ * address space. Return a pointer to where it is mapped. This
+ * routine is intended to be used for mapping device memory,
+ * NOT real memory. The non-cacheable bits are set on each
+ * mapped page.
+ */
+void *
+pmap_mapdev(pa, size)
+ vm_offset_t pa;
+ vm_size_t size;
+{
+ vm_offset_t va, tmpva;
+ unsigned *pte;
+
+ size = roundup(size, PAGE_SIZE);
+
+ va = kmem_alloc_pageable(kernel_map, size);
+ if (!va)
+ panic("pmap_mapdev: Couldn't alloc kernel virtual memory");
+
+ pa = pa & PG_FRAME;
+ for (tmpva = va; size > 0;) {
+ pte = (unsigned *)vtopte(tmpva);
+ *pte = pa | PG_RW | PG_V | PG_N;
+ size -= PAGE_SIZE;
+ tmpva += PAGE_SIZE;
+ pa += PAGE_SIZE;
+ }
+ pmap_update();
+
+ return ((void *) va);
+}
+
+int
+pmap_mincore(pmap, addr)
+ pmap_t pmap;
+ vm_offset_t addr;
+{
+
+ unsigned *ptep, pte;
+ int val = 0;
+
+ ptep = pmap_pte(pmap, addr);
+ if (ptep == 0) {
+ return 0;
+ }
+
+ if ((pte = *ptep)) {
+ vm_offset_t pa;
+ val = MINCORE_INCORE;
+ pa = pte & PG_FRAME;
+
+ /*
+ * Modified by us
+ */
+ if (pte & PG_M)
+ val |= MINCORE_MODIFIED|MINCORE_MODIFIED_OTHER;
+ /*
+ * Modified by someone
+ */
+ else if (PHYS_TO_VM_PAGE(pa)->dirty ||
+ pmap_is_modified(pa))
+ val |= MINCORE_MODIFIED_OTHER;
+ /*
+ * Referenced by us
+ */
+ if (pte & PG_U)
+ val |= MINCORE_REFERENCED|MINCORE_REFERENCED_OTHER;
+
+ /*
+ * Referenced by someone
+ */
+ else if ((PHYS_TO_VM_PAGE(pa)->flags & PG_REFERENCED) ||
+ pmap_is_referenced(pa))
+ val |= MINCORE_REFERENCED_OTHER;
+ }
+ return val;
+}
+
+#if defined(PMAP_DEBUG)
+pmap_pid_dump(int pid) {
+ pmap_t pmap;
+ struct proc *p;
+ int npte = 0;
+ int index;
+ for (p = allproc.lh_first; p != NULL; p = p->p_list.le_next) {
+ if (p->p_pid != pid)
+ continue;
+
+ if (p->p_vmspace) {
+ int i,j;
+ index = 0;
+ pmap = &p->p_vmspace->vm_pmap;
+ for(i=0;i<1024;i++) {
+ pd_entry_t *pde;
+ unsigned *pte;
+ unsigned base = i << PDRSHIFT;
+
+ pde = &pmap->pm_pdir[i];
+ if (pde && pmap_pde_v(pde)) {
+ for(j=0;j<1024;j++) {
+ unsigned va = base + (j << PAGE_SHIFT);
+ if (va >= (vm_offset_t) VM_MIN_KERNEL_ADDRESS) {
+ if (index) {
+ index = 0;
+ printf("\n");
+ }
+ return npte;
+ }
+ pte = pmap_pte( pmap, va);
+ if (pte && pmap_pte_v(pte)) {
+ vm_offset_t pa;
+ vm_page_t m;
+ pa = *(int *)pte;
+ m = PHYS_TO_VM_PAGE((pa & PG_FRAME));
+ printf("va: 0x%x, pt: 0x%x, h: %d, w: %d, f: 0x%x",
+ va, pa, m->hold_count, m->wire_count, m->flags);
+ npte++;
+ index++;
+ if (index >= 2) {
+ index = 0;
+ printf("\n");
+ } else {
+ printf(" ");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return npte;
+}
+#endif
+
+#if defined(DEBUG)
+
+static void pads __P((pmap_t pm));
+static void pmap_pvdump __P((vm_offset_t pa));
+
+/* print address space of pmap*/
+static void
+pads(pm)
+ pmap_t pm;
+{
+ unsigned va, i, j;
+ unsigned *ptep;
+
+ if (pm == kernel_pmap)
+ return;
+ for (i = 0; i < 1024; i++)
+ if (pm->pm_pdir[i])
+ for (j = 0; j < 1024; j++) {
+ va = (i << PDRSHIFT) + (j << PAGE_SHIFT);
+ if (pm == kernel_pmap && va < KERNBASE)
+ continue;
+ if (pm != kernel_pmap && va > UPT_MAX_ADDRESS)
+ continue;
+ ptep = pmap_pte(pm, va);
+ if (pmap_pte_v(ptep))
+ printf("%x:%x ", va, *(int *) ptep);
+ };
+
+}
+
+static void
+pmap_pvdump(pa)
+ vm_offset_t pa;
+{
+ register pv_entry_t pv;
+
+ printf("pa %x", pa);
+ for (pv = pa_to_pvh(pa); pv; pv = pv->pv_next) {
+#ifdef used_to_be
+ printf(" -> pmap %x, va %x, flags %x",
+ pv->pv_pmap, pv->pv_va, pv->pv_flags);
+#endif
+ printf(" -> pmap %x, va %x",
+ pv->pv_pmap, pv->pv_va);
+ pads(pv->pv_pmap);
+ }
+ printf(" ");
+}
+#endif
diff --git a/sys/pc98/i386/support.s b/sys/pc98/i386/support.s
new file mode 100644
index 0000000..9db2baf
--- /dev/null
+++ b/sys/pc98/i386/support.s
@@ -0,0 +1,1008 @@
+/*-
+ * Copyright (c) 1993 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.
+ *
+ * $Id: support.s,v 1.36 1996/05/31 01:08:03 peter Exp $
+ */
+
+#include "assym.s" /* system definitions */
+#include "errno.h" /* error return codes */
+#include "machine/asmacros.h" /* miscellaneous asm macros */
+#include "machine/cputypes.h" /* types of CPUs */
+
+#define KDSEL 0x10 /* kernel data selector */
+#define IDXSHIFT 10
+
+
+ .data
+ .globl _bzero
+_bzero: .long _generic_bzero
+
+ .text
+
+/*
+ * bcopy family
+ * void bzero(void *base, u_int cnt)
+ */
+
+ENTRY(generic_bzero)
+ pushl %edi
+ movl 8(%esp),%edi
+ movl 12(%esp),%ecx
+ xorl %eax,%eax
+ shrl $2,%ecx
+ cld
+ rep
+ stosl
+ movl 12(%esp),%ecx
+ andl $3,%ecx
+ rep
+ stosb
+ popl %edi
+ ret
+
+#if defined(I486_CPU)
+ENTRY(i486_bzero)
+ movl 4(%esp),%edx
+ movl 8(%esp),%ecx
+ xorl %eax,%eax
+/*
+ * do 64 byte chunks first
+ *
+ * XXX this is probably over-unrolled at least for DX2's
+ */
+2:
+ cmpl $64,%ecx
+ jb 3f
+ movl %eax,(%edx)
+ movl %eax,4(%edx)
+ movl %eax,8(%edx)
+ movl %eax,12(%edx)
+ movl %eax,16(%edx)
+ movl %eax,20(%edx)
+ movl %eax,24(%edx)
+ movl %eax,28(%edx)
+ movl %eax,32(%edx)
+ movl %eax,36(%edx)
+ movl %eax,40(%edx)
+ movl %eax,44(%edx)
+ movl %eax,48(%edx)
+ movl %eax,52(%edx)
+ movl %eax,56(%edx)
+ movl %eax,60(%edx)
+ addl $64,%edx
+ subl $64,%ecx
+ jnz 2b
+ ret
+
+/*
+ * do 16 byte chunks
+ */
+ SUPERALIGN_TEXT
+3:
+ cmpl $16,%ecx
+ jb 4f
+ movl %eax,(%edx)
+ movl %eax,4(%edx)
+ movl %eax,8(%edx)
+ movl %eax,12(%edx)
+ addl $16,%edx
+ subl $16,%ecx
+ jnz 3b
+ ret
+
+/*
+ * do 4 byte chunks
+ */
+ SUPERALIGN_TEXT
+4:
+ cmpl $4,%ecx
+ jb 5f
+ movl %eax,(%edx)
+ addl $4,%edx
+ subl $4,%ecx
+ jnz 4b
+ ret
+
+/*
+ * do 1 byte chunks
+ * a jump table seems to be faster than a loop or more range reductions
+ *
+ * XXX need a const section for non-text
+ */
+ SUPERALIGN_TEXT
+jtab:
+ .long do0
+ .long do1
+ .long do2
+ .long do3
+
+ SUPERALIGN_TEXT
+5:
+ jmp jtab(,%ecx,4)
+
+ SUPERALIGN_TEXT
+do3:
+ movw %ax,(%edx)
+ movb %al,2(%edx)
+ ret
+
+ SUPERALIGN_TEXT
+do2:
+ movw %ax,(%edx)
+ ret
+
+ SUPERALIGN_TEXT
+do1:
+ movb %al,(%edx)
+
+ SUPERALIGN_TEXT
+do0:
+ ret
+#endif
+
+#if 0 /* Actually lowers performance in real-world cases */
+#if defined(I586_CPU) || defined(I686_CPU)
+ALTENTRY(i586_bzero)
+ENTRY(i686_bzero)
+ pushl %edi
+ movl 8(%esp),%edi /* destination pointer */
+ movl 12(%esp),%edx /* size (in 8-bit words) */
+
+ xorl %eax,%eax /* store data */
+ cld
+
+/* If less than 100 bytes to write, skip tricky code. */
+ cmpl $100,%edx
+ movl %edx,%ecx /* needed when branch is taken! */
+ jl 2f
+
+/* First write 0-3 bytes to make the pointer 32-bit aligned. */
+ movl %edi,%ecx /* Copy ptr to ecx... */
+ negl %ecx /* ...and negate that and... */
+ andl $3,%ecx /* ...mask to get byte count. */
+ subl %ecx,%edx /* adjust global byte count */
+ rep
+ stosb
+
+ subl $32,%edx /* offset count for unrolled loop */
+ movl (%edi),%ecx /* Fetch destination cache line */
+
+ .align 2,0x90 /* supply 0x90 for broken assemblers */
+1:
+ movl 28(%edi),%ecx /* allocate cache line for destination */
+ subl $32,%edx /* decr loop count */
+ movl %eax,0(%edi) /* store words pairwise */
+ movl %eax,4(%edi)
+ movl %eax,8(%edi)
+ movl %eax,12(%edi)
+ movl %eax,16(%edi)
+ movl %eax,20(%edi)
+ movl %eax,24(%edi)
+ movl %eax,28(%edi)
+
+ leal 32(%edi),%edi /* update destination pointer */
+ jge 1b
+ leal 32(%edx),%ecx
+
+/* Write last 0-7 full 32-bit words (up to 8 words if loop was skipped). */
+2:
+ shrl $2,%ecx
+ rep
+ stosl
+
+/* Finally write the last 0-3 bytes. */
+ movl %edx,%ecx
+ andl $3,%ecx
+ rep
+ stosb
+
+ popl %edi
+ ret
+#endif
+#endif
+
+/* fillw(pat, base, cnt) */
+ENTRY(fillw)
+ pushl %edi
+ movl 8(%esp),%eax
+ movl 12(%esp),%edi
+ movl 16(%esp),%ecx
+ cld
+ rep
+ stosw
+ popl %edi
+ ret
+
+ENTRY(bcopyb)
+bcopyb:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ movl %edi,%eax
+ subl %esi,%eax
+ cmpl %ecx,%eax /* overlapping? */
+ jb 1f
+ cld /* nope, copy forwards */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards. */
+ addl %ecx,%esi
+ decl %edi
+ decl %esi
+ std
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+/*
+ * (ov)bcopy(src, dst, cnt)
+ * ws@tools.de (Wolfgang Solfrank, TooLs GmbH) +49-228-985800
+ */
+ALTENTRY(ovbcopy)
+ENTRY(bcopy)
+bcopy:
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+
+ movl %edi,%eax
+ subl %esi,%eax
+ cmpl %ecx,%eax /* overlapping? */
+ jb 1f
+ shrl $2,%ecx /* copy by 32-bit words */
+ cld /* nope, copy forwards */
+ rep
+ movsl
+ movl 20(%esp),%ecx
+ andl $3,%ecx /* any bytes left? */
+ rep
+ movsb
+ popl %edi
+ popl %esi
+ ret
+
+ ALIGN_TEXT
+1:
+ addl %ecx,%edi /* copy backwards */
+ addl %ecx,%esi
+ decl %edi
+ decl %esi
+ andl $3,%ecx /* any fractional bytes? */
+ std
+ rep
+ movsb
+ movl 20(%esp),%ecx /* copy remainder by 32-bit words */
+ shrl $2,%ecx
+ subl $3,%esi
+ subl $3,%edi
+ rep
+ movsl
+ popl %edi
+ popl %esi
+ cld
+ ret
+
+
+/*
+ * Note: memcpy does not support overlapping copies
+ */
+ENTRY(memcpy)
+ pushl %edi
+ pushl %esi
+ movl 12(%esp),%edi
+ movl 16(%esp),%esi
+ movl 20(%esp),%ecx
+ movl %edi,%eax
+ shrl $2,%ecx /* copy by 32-bit words */
+ cld /* nope, copy forwards */
+ rep
+ movsl
+ movl 20(%esp),%ecx
+ andl $3,%ecx /* any bytes left? */
+ rep
+ movsb
+ popl %esi
+ popl %edi
+ ret
+
+
+/*****************************************************************************/
+/* copyout and fubyte family */
+/*****************************************************************************/
+/*
+ * Access user memory from inside the kernel. These routines and possibly
+ * the math- and DOS emulators should be the only places that do this.
+ *
+ * We have to access the memory with user's permissions, so use a segment
+ * selector with RPL 3. For writes to user space we have to additionally
+ * check the PTE for write permission, because the 386 does not check
+ * write permissions when we are executing with EPL 0. The 486 does check
+ * this if the WP bit is set in CR0, so we can use a simpler version here.
+ *
+ * These routines set curpcb->onfault for the time they execute. When a
+ * protection violation occurs inside the functions, the trap handler
+ * returns to *curpcb->onfault instead of the function.
+ */
+
+
+ENTRY(copyout) /* copyout(from_kernel, to_user, len) */
+ movl _curpcb,%eax
+ movl $copyout_fault,PCB_ONFAULT(%eax)
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+ movl 16(%esp),%esi
+ movl 20(%esp),%edi
+ movl 24(%esp),%ebx
+ testl %ebx,%ebx /* anything to do? */
+ jz done_copyout
+
+ /*
+ * Check explicitly for non-user addresses. If 486 write protection
+ * is being used, this check is essential because we are in kernel
+ * mode so the h/w does not provide any protection against writing
+ * kernel addresses.
+ */
+
+ /*
+ * First, prevent address wrapping.
+ */
+ movl %edi,%eax
+ addl %ebx,%eax
+ jc copyout_fault
+/*
+ * XXX STOP USING VM_MAXUSER_ADDRESS.
+ * It is an end address, not a max, so every time it is used correctly it
+ * looks like there is an off by one error, and of course it caused an off
+ * by one error in several places.
+ */
+ cmpl $VM_MAXUSER_ADDRESS,%eax
+ ja copyout_fault
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 3f
+#endif
+/*
+ * We have to check each PTE for user write permission.
+ * The checking may cause a page fault, so it is important to set
+ * up everything for return via copyout_fault before here.
+ */
+ /* compute number of pages */
+ movl %edi,%ecx
+ andl $PAGE_MASK,%ecx
+ addl %ebx,%ecx
+ decl %ecx
+ shrl $IDXSHIFT+2,%ecx
+ incl %ecx
+
+ /* compute PTE offset for start address */
+ movl %edi,%edx
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+
+1: /* check PTE for each page */
+ movb _PTmap(%edx),%al
+ andb $0x07,%al /* Pages must be VALID + USERACC + WRITABLE */
+ cmpb $0x07,%al
+ je 2f
+
+ /* simulate a trap */
+ pushl %edx
+ pushl %ecx
+ shll $IDXSHIFT,%edx
+ pushl %edx
+ call _trapwrite /* trapwrite(addr) */
+ popl %edx
+ popl %ecx
+ popl %edx
+
+ testl %eax,%eax /* if not ok, return EFAULT */
+ jnz copyout_fault
+
+2:
+ addl $4,%edx
+ decl %ecx
+ jnz 1b /* check next page */
+#endif /* I386_CPU */
+
+ /* bcopy(%esi, %edi, %ebx) */
+3:
+ movl %ebx,%ecx
+ shrl $2,%ecx
+ cld
+ rep
+ movsl
+ movb %bl,%cl
+ andb $3,%cl
+ rep
+ movsb
+
+done_copyout:
+ popl %ebx
+ popl %edi
+ popl %esi
+ xorl %eax,%eax
+ movl _curpcb,%edx
+ movl %eax,PCB_ONFAULT(%edx)
+ ret
+
+ ALIGN_TEXT
+copyout_fault:
+ popl %ebx
+ popl %edi
+ popl %esi
+ movl _curpcb,%edx
+ movl $0,PCB_ONFAULT(%edx)
+ movl $EFAULT,%eax
+ ret
+
+/* copyin(from_user, to_kernel, len) */
+ENTRY(copyin)
+ movl _curpcb,%eax
+ movl $copyin_fault,PCB_ONFAULT(%eax)
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi /* caddr_t from */
+ movl 16(%esp),%edi /* caddr_t to */
+ movl 20(%esp),%ecx /* size_t len */
+
+ /*
+ * make sure address is valid
+ */
+ movl %esi,%edx
+ addl %ecx,%edx
+ jc copyin_fault
+ cmpl $VM_MAXUSER_ADDRESS,%edx
+ ja copyin_fault
+
+ movb %cl,%al
+ shrl $2,%ecx /* copy longword-wise */
+ cld
+ rep
+ movsl
+ movb %al,%cl
+ andb $3,%cl /* copy remaining bytes */
+ rep
+ movsb
+
+ popl %edi
+ popl %esi
+ xorl %eax,%eax
+ movl _curpcb,%edx
+ movl %eax,PCB_ONFAULT(%edx)
+ ret
+
+ ALIGN_TEXT
+copyin_fault:
+ popl %edi
+ popl %esi
+ movl _curpcb,%edx
+ movl $0,PCB_ONFAULT(%edx)
+ movl $EFAULT,%eax
+ ret
+
+/*
+ * fu{byte,sword,word} : fetch a byte (sword, word) from user memory
+ */
+ENTRY(fuword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx /* from */
+
+ cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address is valid */
+ ja fusufault
+
+ movl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+/*
+ * These two routines are called from the profiling code, potentially
+ * at interrupt time. If they fail, that's okay, good things will
+ * happen later. Fail all the time for now - until the trap code is
+ * able to deal with this.
+ */
+ALTENTRY(suswintr)
+ENTRY(fuswintr)
+ movl $-1,%eax
+ ret
+
+ENTRY(fusword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx
+ ja fusufault
+
+ movzwl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+ENTRY(fubyte)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx
+ ja fusufault
+
+ movzbl (%edx),%eax
+ movl $0,PCB_ONFAULT(%ecx)
+ ret
+
+ ALIGN_TEXT
+fusufault:
+ movl _curpcb,%ecx
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ decl %eax
+ ret
+
+/*
+ * su{byte,sword,word}: write a byte (word, longword) to user memory
+ */
+ENTRY(suword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f /* we only have to set the right segment selector */
+#endif /* I486_CPU || I586_CPU || I686_CPU */
+
+ /* XXX - page boundary crossing is still not handled */
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ testl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-4,%edx /* verify address validity */
+ ja fusufault
+
+ movl 8(%esp),%eax
+ movl %eax,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+ENTRY(susword)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f
+#endif /* I486_CPU || I586_CPU || I686_CPU */
+
+ /* XXX - page boundary crossing is still not handled */
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ testl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-2,%edx /* verify address validity */
+ ja fusufault
+
+ movw 8(%esp),%ax
+ movw %ax,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+ALTENTRY(suibyte)
+ENTRY(subyte)
+ movl _curpcb,%ecx
+ movl $fusufault,PCB_ONFAULT(%ecx)
+ movl 4(%esp),%edx
+
+#if defined(I386_CPU)
+
+#if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU)
+ cmpl $CPUCLASS_386,_cpu_class
+ jne 2f
+#endif /* I486_CPU || I586_CPU || I686_CPU */
+
+ movl %edx,%eax
+ shrl $IDXSHIFT,%edx
+ andb $0xfc,%dl
+ movb _PTmap(%edx),%dl
+ andb $0x7,%dl /* must be VALID + USERACC + WRITE */
+ cmpb $0x7,%dl
+ je 1f
+
+ /* simulate a trap */
+ pushl %eax
+ call _trapwrite
+ popl %edx /* remove junk parameter from stack */
+ movl _curpcb,%ecx /* restore trashed register */
+ testl %eax,%eax
+ jnz fusufault
+1:
+ movl 4(%esp),%edx
+#endif
+
+2:
+ cmpl $VM_MAXUSER_ADDRESS-1,%edx /* verify address validity */
+ ja fusufault
+
+ movb 8(%esp),%al
+ movb %al,(%edx)
+ xorl %eax,%eax
+ movl %eax,PCB_ONFAULT(%ecx)
+ ret
+
+/*
+ * copyinstr(from, to, maxlen, int *lencopied)
+ * copy a string from from to to, stop when a 0 character is reached.
+ * return ENAMETOOLONG if string is longer than maxlen, and
+ * EFAULT on protection violations. If lencopied is non-zero,
+ * return the actual length in *lencopied.
+ */
+ENTRY(copyinstr)
+ pushl %esi
+ pushl %edi
+ movl _curpcb,%ecx
+ movl $cpystrflt,PCB_ONFAULT(%ecx)
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+
+ movl $VM_MAXUSER_ADDRESS,%eax
+
+ /* make sure 'from' is within bounds */
+ subl %esi,%eax
+ jbe cpystrflt
+
+ /* restrict maxlen to <= VM_MAXUSER_ADDRESS-from */
+ cmpl %edx,%eax
+ jae 1f
+ movl %eax,%edx
+ movl %eax,20(%esp)
+1:
+ incl %edx
+ cld
+
+2:
+ decl %edx
+ jz 3f
+
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 2b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp cpystrflt_x
+3:
+ /* edx is zero - return ENAMETOOLONG or EFAULT */
+ cmpl $VM_MAXUSER_ADDRESS,%esi
+ jae cpystrflt
+4:
+ movl $ENAMETOOLONG,%eax
+ jmp cpystrflt_x
+
+cpystrflt:
+ movl $EFAULT,%eax
+
+cpystrflt_x:
+ /* set *lencopied and return %eax */
+ movl _curpcb,%ecx
+ movl $0,PCB_ONFAULT(%ecx)
+ movl 20(%esp),%ecx
+ subl %edx,%ecx
+ movl 24(%esp),%edx
+ testl %edx,%edx
+ jz 1f
+ movl %ecx,(%edx)
+1:
+ popl %edi
+ popl %esi
+ ret
+
+
+/*
+ * copystr(from, to, maxlen, int *lencopied)
+ */
+ENTRY(copystr)
+ pushl %esi
+ pushl %edi
+
+ movl 12(%esp),%esi /* %esi = from */
+ movl 16(%esp),%edi /* %edi = to */
+ movl 20(%esp),%edx /* %edx = maxlen */
+ incl %edx
+ cld
+1:
+ decl %edx
+ jz 4f
+ lodsb
+ stosb
+ orb %al,%al
+ jnz 1b
+
+ /* Success -- 0 byte reached */
+ decl %edx
+ xorl %eax,%eax
+ jmp 6f
+4:
+ /* edx is zero -- return ENAMETOOLONG */
+ movl $ENAMETOOLONG,%eax
+
+6:
+ /* set *lencopied and return %eax */
+ movl 20(%esp),%ecx
+ subl %edx,%ecx
+ movl 24(%esp),%edx
+ testl %edx,%edx
+ jz 7f
+ movl %ecx,(%edx)
+7:
+ popl %edi
+ popl %esi
+ ret
+
+ENTRY(bcmp)
+ pushl %edi
+ pushl %esi
+ movl 12(%esp),%edi
+ movl 16(%esp),%esi
+ movl 20(%esp),%edx
+ xorl %eax,%eax
+
+ movl %edx,%ecx
+ shrl $2,%ecx
+ cld /* compare forwards */
+ repe
+ cmpsl
+ jne 1f
+
+ movl %edx,%ecx
+ andl $3,%ecx
+ repe
+ cmpsb
+ je 2f
+1:
+ incl %eax
+2:
+ popl %esi
+ popl %edi
+ ret
+
+
+/*
+ * Handling of special 386 registers and descriptor tables etc
+ */
+/* void lgdt(struct region_descriptor *rdp); */
+ENTRY(lgdt)
+ /* reload the descriptor table */
+ movl 4(%esp),%eax
+ lgdt (%eax)
+
+ /* flush the prefetch q */
+ jmp 1f
+ nop
+1:
+ /* reload "stale" selectors */
+ movl $KDSEL,%eax
+ movl %ax,%ds
+ movl %ax,%es
+ movl %ax,%ss
+
+ /* reload code selector by turning return into intersegmental return */
+ movl (%esp),%eax
+ pushl %eax
+# movl $KCSEL,4(%esp)
+ movl $8,4(%esp)
+ lret
+
+/*
+ * void lidt(struct region_descriptor *rdp);
+ */
+ENTRY(lidt)
+ movl 4(%esp),%eax
+ lidt (%eax)
+ ret
+
+/*
+ * void lldt(u_short sel)
+ */
+ENTRY(lldt)
+ lldt 4(%esp)
+ ret
+
+/*
+ * void ltr(u_short sel)
+ */
+ENTRY(ltr)
+ ltr 4(%esp)
+ ret
+
+/* ssdtosd(*ssdp,*sdp) */
+ENTRY(ssdtosd)
+ pushl %ebx
+ movl 8(%esp),%ecx
+ movl 8(%ecx),%ebx
+ shll $16,%ebx
+ movl (%ecx),%edx
+ roll $16,%edx
+ movb %dh,%bl
+ movb %dl,%bh
+ rorl $8,%ebx
+ movl 4(%ecx),%eax
+ movw %ax,%dx
+ andl $0xf0000,%eax
+ orl %eax,%ebx
+ movl 12(%esp),%ecx
+ movl %edx,(%ecx)
+ movl %ebx,4(%ecx)
+ popl %ebx
+ ret
+
+/* load_cr0(cr0) */
+ENTRY(load_cr0)
+ movl 4(%esp),%eax
+ movl %eax,%cr0
+ ret
+
+/* rcr0() */
+ENTRY(rcr0)
+ movl %cr0,%eax
+ ret
+
+/* rcr3() */
+ENTRY(rcr3)
+ movl %cr3,%eax
+ ret
+
+/* void load_cr3(caddr_t cr3) */
+ENTRY(load_cr3)
+ movl 4(%esp),%eax
+ movl %eax,%cr3
+ ret
+
+
+/*****************************************************************************/
+/* setjump, longjump */
+/*****************************************************************************/
+
+ENTRY(setjmp)
+ movl 4(%esp),%eax
+ movl %ebx,(%eax) /* save ebx */
+ movl %esp,4(%eax) /* save esp */
+ movl %ebp,8(%eax) /* save ebp */
+ movl %esi,12(%eax) /* save esi */
+ movl %edi,16(%eax) /* save edi */
+ movl (%esp),%edx /* get rta */
+ movl %edx,20(%eax) /* save eip */
+ xorl %eax,%eax /* return(0); */
+ ret
+
+ENTRY(longjmp)
+ movl 4(%esp),%eax
+ movl (%eax),%ebx /* restore ebx */
+ movl 4(%eax),%esp /* restore esp */
+ movl 8(%eax),%ebp /* restore ebp */
+ movl 12(%eax),%esi /* restore esi */
+ movl 16(%eax),%edi /* restore edi */
+ movl 20(%eax),%edx /* get rta */
+ movl %edx,(%esp) /* put in return frame */
+ xorl %eax,%eax /* return(1); */
+ incl %eax
+ ret
+
+#ifdef PC98
+/* XXX:
+ * bcopy always uses overlapping copy (std flag on) if dst addr < src addr.
+ * However it is fatal for suspended memory io.
+ */
+ENTRY(memcopy)
+ pushl %esi
+ pushl %edi
+ movl 12(%esp),%esi
+ movl 16(%esp),%edi
+ movl 20(%esp),%ecx
+ cld /* nope, copy forward */
+ shrl $2,%ecx /* copy by 32-bit words */
+ rep
+ movsl
+ popl %edi
+ popl %esi
+ ret
+#endif
+
+/*
+ * Here for doing BB-profiling (gcc -a).
+ * We rely on the "bbset" instead, but need a dummy function.
+ */
+ .text
+ .align 2
+.globl ___bb_init_func
+___bb_init_func:
+ movl 4(%esp),%eax
+ movl $1,(%eax)
+ ret
diff --git a/sys/pc98/i386/trap.c b/sys/pc98/i386/trap.c
new file mode 100644
index 0000000..523fc14
--- /dev/null
+++ b/sys/pc98/i386/trap.c
@@ -0,0 +1,1006 @@
+/*-
+ * Copyright (C) 1994, David Greenman
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the University of Utah, and 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: @(#)trap.c 7.4 (Berkeley) 5/13/91
+ * $Id: trap.c,v 1.76 1996/05/18 03:36:19 dyson Exp $
+ */
+
+/*
+ * 386 Trap and System call handling
+ */
+
+#include "opt_ktrace.h"
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/acct.h>
+#include <sys/kernel.h>
+#include <sys/syscall.h>
+#include <sys/sysent.h>
+#include <sys/queue.h>
+#include <sys/vmmeter.h>
+#ifdef KTRACE
+#include <sys/ktrace.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_prot.h>
+#include <vm/lock.h>
+#include <vm/pmap.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_map.h>
+#include <vm/vm_page.h>
+#include <vm/vm_extern.h>
+
+#include <sys/user.h>
+
+#include <machine/cpu.h>
+#include <machine/md_var.h>
+#include <machine/psl.h>
+#include <machine/reg.h>
+#include <machine/trap.h>
+#ifdef PC98
+#include <pc98/pc98/pc98_device.h>
+
+#include "nec.h"
+#include "epson.h"
+#else
+#include <machine/../isa/isa_device.h>
+
+#include "isa.h"
+#endif
+
+#ifdef POWERFAIL_NMI
+# include <syslog.h>
+# include <machine/clock.h>
+#endif
+
+#include "npx.h"
+
+int (*pmath_emulate) __P((struct trapframe *));
+
+extern void trap __P((struct trapframe frame));
+extern int trapwrite __P((unsigned addr));
+extern void syscall __P((struct trapframe frame));
+
+#ifdef CYRIX_486DLC
+static int trap_pfault __P((struct trapframe *, int, vm_offset_t));
+#else
+static int trap_pfault __P((struct trapframe *, int));
+#endif
+static void trap_fatal __P((struct trapframe *));
+void dblfault_handler __P((void));
+
+extern inthand_t IDTVEC(syscall);
+
+#define MAX_TRAP_MSG 27
+static char *trap_msg[] = {
+ "", /* 0 unused */
+ "privileged instruction fault", /* 1 T_PRIVINFLT */
+ "", /* 2 unused */
+ "breakpoint instruction fault", /* 3 T_BPTFLT */
+ "", /* 4 unused */
+ "", /* 5 unused */
+ "arithmetic trap", /* 6 T_ARITHTRAP */
+ "system forced exception", /* 7 T_ASTFLT */
+ "", /* 8 unused */
+ "general protection fault", /* 9 T_PROTFLT */
+ "trace trap", /* 10 T_TRCTRAP */
+ "", /* 11 unused */
+ "page fault", /* 12 T_PAGEFLT */
+ "", /* 13 unused */
+ "alignment fault", /* 14 T_ALIGNFLT */
+ "", /* 15 unused */
+ "", /* 16 unused */
+ "", /* 17 unused */
+ "integer divide fault", /* 18 T_DIVIDE */
+ "non-maskable interrupt trap", /* 19 T_NMI */
+ "overflow trap", /* 20 T_OFLOW */
+ "FPU bounds check fault", /* 21 T_BOUND */
+ "FPU device not available", /* 22 T_DNA */
+ "double fault", /* 23 T_DOUBLEFLT */
+ "FPU operand fetch fault", /* 24 T_FPOPFLT */
+ "invalid TSS fault", /* 25 T_TSSFLT */
+ "segment not present fault", /* 26 T_SEGNPFLT */
+ "stack fault", /* 27 T_STKFLT */
+};
+
+static void userret __P((struct proc *p, struct trapframe *frame,
+ u_quad_t oticks));
+
+static inline void
+userret(p, frame, oticks)
+ struct proc *p;
+ struct trapframe *frame;
+ u_quad_t oticks;
+{
+ int sig, s;
+
+ while ((sig = CURSIG(p)) != 0)
+ postsig(sig);
+ p->p_priority = p->p_usrpri;
+ if (want_resched) {
+ /*
+ * Since we are curproc, clock will normally just change
+ * our priority without moving us from one queue to another
+ * (since the running process is not on a queue.)
+ * If that happened after we setrunqueue ourselves but before we
+ * mi_switch()'ed, we might not be on the queue indicated by
+ * our priority.
+ */
+ s = splclock();
+ setrunqueue(p);
+ p->p_stats->p_ru.ru_nivcsw++;
+ mi_switch();
+ splx(s);
+ while ((sig = CURSIG(p)) != 0)
+ postsig(sig);
+ }
+ /*
+ * Charge system time if profiling.
+ */
+ if (p->p_flag & P_PROFIL) {
+ u_quad_t ticks = p->p_sticks - oticks;
+
+ if (ticks) {
+#ifdef PROFTIMER
+ extern int profscale;
+ addupc(frame->tf_eip, &p->p_stats->p_prof,
+ ticks * profscale);
+#else
+ addupc(frame->tf_eip, &p->p_stats->p_prof, ticks);
+#endif
+ }
+ }
+ curpriority = p->p_priority;
+}
+
+/*
+ * Exception, fault, and trap interface to the FreeBSD kernel.
+ * This common code is called from assembly language IDT gate entry
+ * routines that prepare a suitable stack frame, and restore this
+ * frame after the exception has been processed.
+ */
+
+void
+trap(frame)
+ struct trapframe frame;
+{
+ struct proc *p = curproc;
+ u_quad_t sticks = 0;
+ int i = 0, ucode = 0, type, code;
+#ifdef DEBUG
+ u_long eva;
+#endif
+#ifdef CYRIX_486DLC
+ vm_offset_t va;
+#endif
+
+ type = frame.tf_trapno;
+ code = frame.tf_err;
+
+#ifdef CYRIX_486DLC
+ /* XXX:
+ * CYRIX 486 CPU FIX.
+ * If you use cyrix cpu, you often encouter strange signal 11's?
+ * I think this is due to cyrix cpu bugs.
+ * In any way, the following trick is effective for the problem.
+ * As soon as possible, we must get the fault page address.
+ */
+ va = (vm_offset_t)(rcr2());
+ if( type == T_PAGEFLT && ( frame.tf_eflags & PSL_I ) )
+ asm("sti");
+#endif /* CYRIX_486DLC */
+
+ if (ISPL(frame.tf_cs) == SEL_UPL) {
+ /* user trap */
+
+ sticks = p->p_sticks;
+ p->p_md.md_regs = (int *)&frame;
+
+ switch (type) {
+ case T_PRIVINFLT: /* privileged instruction fault */
+ ucode = type;
+ i = SIGILL;
+ break;
+
+ case T_BPTFLT: /* bpt instruction fault */
+ case T_TRCTRAP: /* trace trap */
+ frame.tf_eflags &= ~PSL_T;
+ i = SIGTRAP;
+ break;
+
+ case T_ARITHTRAP: /* arithmetic trap */
+ ucode = code;
+ i = SIGFPE;
+ break;
+
+ case T_ASTFLT: /* Allow process switch */
+ astoff();
+ cnt.v_soft++;
+ if (p->p_flag & P_OWEUPC) {
+ addupc(frame.tf_eip, &p->p_stats->p_prof, 1);
+ p->p_flag &= ~P_OWEUPC;
+ }
+ goto out;
+
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ case T_STKFLT: /* stack fault */
+ case T_TSSFLT: /* invalid TSS fault */
+ case T_DOUBLEFLT: /* double fault */
+ default:
+ ucode = code + BUS_SEGM_FAULT ;
+ i = SIGBUS;
+ break;
+
+ case T_PAGEFLT: /* page fault */
+#ifdef CYRIX_486DLC
+ i = trap_pfault(&frame, TRUE, va);
+#else
+ i = trap_pfault(&frame, TRUE);
+#endif
+ if (i == -1)
+ return;
+ if (i == 0)
+ goto out;
+
+ ucode = T_PAGEFLT;
+ break;
+
+ case T_DIVIDE: /* integer divide fault */
+ ucode = FPE_INTDIV_TRAP;
+ i = SIGFPE;
+ break;
+
+#if NNEC > 0 || NEPSON > 0
+ case T_NMI:
+#ifdef POWERFAIL_NMI
+ goto handle_powerfail;
+#else /* !POWERFAIL_NMI */
+#ifdef DDB
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif /* DDB */
+ /* machine/parity/power fail/"kitchen sink" faults */
+#ifdef PC98
+ if (pc98_nmi(code) == 0) return;
+#else
+ if (isa_nmi(code) == 0) return;
+#endif
+ panic("NMI indicates hardware failure");
+#endif /* POWERFAIL_NMI */
+#endif /* NNEC > 0 */
+
+ case T_OFLOW: /* integer overflow fault */
+ ucode = FPE_INTOVF_TRAP;
+ i = SIGFPE;
+ break;
+
+ case T_BOUND: /* bounds check fault */
+ ucode = FPE_SUBRNG_TRAP;
+ i = SIGFPE;
+ break;
+
+ case T_DNA:
+#if NNPX > 0
+ /* if a transparent fault (due to context switch "late") */
+ if (npxdna())
+ return;
+#endif /* NNPX > 0 */
+
+ if (!pmath_emulate) {
+ i = SIGFPE;
+ ucode = FPE_FPU_NP_TRAP;
+ break;
+ }
+ i = (*pmath_emulate)(&frame);
+ if (i == 0) {
+ if (!(frame.tf_eflags & PSL_T))
+ return;
+ frame.tf_eflags &= ~PSL_T;
+ i = SIGTRAP;
+ }
+ /* else ucode = emulator_only_knows() XXX */
+ break;
+
+ case T_FPOPFLT: /* FPU operand fetch fault */
+ ucode = T_FPOPFLT;
+ i = SIGILL;
+ break;
+ }
+ } else {
+ /* kernel trap */
+
+ switch (type) {
+ case T_PAGEFLT: /* page fault */
+#ifdef CYRIX_486DLC
+ (void) trap_pfault(&frame, FALSE, va);
+#else
+ (void) trap_pfault(&frame, FALSE);
+#endif
+ return;
+
+ case T_PROTFLT: /* general protection fault */
+ case T_SEGNPFLT: /* segment not present fault */
+ /*
+ * Invalid segment selectors and out of bounds
+ * %eip's and %esp's can be set up in user mode.
+ * This causes a fault in kernel mode when the
+ * kernel tries to return to user mode. We want
+ * to get this fault so that we can fix the
+ * problem here and not have to check all the
+ * selectors and pointers when the user changes
+ * them.
+ */
+#define MAYBE_DORETI_FAULT(where, whereto) \
+ do { \
+ if (frame.tf_eip == (int)where) { \
+ frame.tf_eip = (int)whereto; \
+ return; \
+ } \
+ } while (0)
+
+ if (intr_nesting_level == 0) {
+ MAYBE_DORETI_FAULT(doreti_iret,
+ doreti_iret_fault);
+ MAYBE_DORETI_FAULT(doreti_popl_ds,
+ doreti_popl_ds_fault);
+ MAYBE_DORETI_FAULT(doreti_popl_es,
+ doreti_popl_es_fault);
+ }
+ if (curpcb && curpcb->pcb_onfault) {
+ frame.tf_eip = (int)curpcb->pcb_onfault;
+ return;
+ }
+ break;
+
+ case T_TSSFLT:
+ /*
+ * PSL_NT can be set in user mode and isn't cleared
+ * automatically when the kernel is entered. This
+ * causes a TSS fault when the kernel attempts to
+ * `iret' because the TSS link is uninitialized. We
+ * want to get this fault so that we can fix the
+ * problem here and not every time the kernel is
+ * entered.
+ */
+ if (frame.tf_eflags & PSL_NT) {
+ frame.tf_eflags &= ~PSL_NT;
+ return;
+ }
+ break;
+
+ case T_TRCTRAP: /* trace trap */
+ if (frame.tf_eip == (int)IDTVEC(syscall)) {
+ /*
+ * We've just entered system mode via the
+ * syscall lcall. Continue single stepping
+ * silently until the syscall handler has
+ * saved the flags.
+ */
+ return;
+ }
+ if (frame.tf_eip == (int)IDTVEC(syscall) + 1) {
+ /*
+ * The syscall handler has now saved the
+ * flags. Stop single stepping it.
+ */
+ frame.tf_eflags &= ~PSL_T;
+ return;
+ }
+ /*
+ * Fall through.
+ */
+ case T_BPTFLT:
+ /*
+ * If DDB is enabled, let it handle the debugger trap.
+ * Otherwise, debugger traps "can't happen".
+ */
+#ifdef DDB
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif
+ break;
+
+#if NNEC > 0 || NEPSON > 0
+ case T_NMI:
+#ifdef POWERFAIL_NMI
+#ifndef TIMER_FREQ
+# define TIMER_FREQ 1193182
+#endif
+ handle_powerfail:
+ {
+ static unsigned lastalert = 0;
+
+ if(time.tv_sec - lastalert > 10)
+ {
+ log(LOG_WARNING, "NMI: power fail\n");
+ sysbeep(TIMER_FREQ/880, hz);
+ lastalert = time.tv_sec;
+ }
+ return;
+ }
+#else /* !POWERFAIL_NMI */
+#ifdef DDB
+ /* NMI can be hooked up to a pushbutton for debugging */
+ printf ("NMI ... going to debugger\n");
+ if (kdb_trap (type, 0, &frame))
+ return;
+#endif /* DDB */
+ /* machine/parity/power fail/"kitchen sink" faults */
+#ifdef PC98
+ if (pc98_nmi(code) == 0) return;
+#else
+ if (isa_nmi(code) == 0) return;
+#endif
+ /* FALL THROUGH */
+#endif /* POWERFAIL_NMI */
+#endif /* NNEC > 0 */
+ }
+
+ trap_fatal(&frame);
+ return;
+ }
+
+ trapsignal(p, i, ucode);
+
+#ifdef DEBUG
+ eva = rcr2();
+ if (type <= MAX_TRAP_MSG) {
+ uprintf("fatal process exception: %s",
+ trap_msg[type]);
+ if ((type == T_PAGEFLT) || (type == T_PROTFLT))
+ uprintf(", fault VA = 0x%x", eva);
+ uprintf("\n");
+ }
+#endif
+
+out:
+ userret(p, &frame, sticks);
+}
+
+#ifdef notyet
+/*
+ * This version doesn't allow a page fault to user space while
+ * in the kernel. The rest of the kernel needs to be made "safe"
+ * before this can be used. I think the only things remaining
+ * to be made safe are the iBCS2 code and the process tracing/
+ * debugging code.
+ */
+static int
+#ifdef CYRIX_486DLC
+trap_pfault(frame, usermode,faultva)
+ struct trapframe *frame;
+ int usermode;
+ vm_offset_t faultva;
+#else
+trap_pfault(frame, usermode)
+ struct trapframe *frame;
+ int usermode;
+#endif
+{
+ vm_offset_t va;
+ struct vmspace *vm = NULL;
+ vm_map_t map = 0;
+ int rv = 0;
+ vm_prot_t ftype;
+ int eva;
+ struct proc *p = curproc;
+
+ if (frame->tf_err & PGEX_W)
+ ftype = VM_PROT_READ | VM_PROT_WRITE;
+ else
+ ftype = VM_PROT_READ;
+
+#ifdef CYRIX_486DLC
+ eva = faultva;
+#else
+ eva = rcr2();
+#endif
+ va = trunc_page((vm_offset_t)eva);
+
+ if (va < VM_MIN_KERNEL_ADDRESS) {
+ vm_offset_t v;
+ vm_page_t mpte;
+
+ if (p == NULL ||
+ (!usermode && va < VM_MAXUSER_ADDRESS &&
+ (curpcb == NULL || curpcb->pcb_onfault == NULL))) {
+ trap_fatal(frame);
+ return (-1);
+ }
+
+ /*
+ * This is a fault on non-kernel virtual memory.
+ * vm is initialized above to NULL. If curproc is NULL
+ * or curproc->p_vmspace is NULL the fault is fatal.
+ */
+ vm = p->p_vmspace;
+ if (vm == NULL)
+ goto nogo;
+
+ map = &vm->vm_map;
+
+ /*
+ * Keep swapout from messing with us during this
+ * critical time.
+ */
+ ++p->p_lock;
+
+ /*
+ * Grow the stack if necessary
+ */
+ if ((caddr_t)va > vm->vm_maxsaddr
+ && (caddr_t)va < (caddr_t)USRSTACK) {
+ if (!grow(p, va)) {
+ rv = KERN_FAILURE;
+ --p->p_lock;
+ goto nogo;
+ }
+ }
+
+ /* Fault in the user page: */
+ rv = vm_fault(map, va, ftype, FALSE);
+
+ --p->p_lock;
+ } else {
+ /*
+ * Don't allow user-mode faults in kernel address space.
+ */
+ if (usermode)
+ goto nogo;
+
+ /*
+ * Since we know that kernel virtual address addresses
+ * always have pte pages mapped, we just have to fault
+ * the page.
+ */
+ rv = vm_fault(kernel_map, va, ftype, FALSE);
+ }
+
+ if (rv == KERN_SUCCESS)
+ return (0);
+nogo:
+ if (!usermode) {
+ if (curpcb && curpcb->pcb_onfault) {
+ frame->tf_eip = (int)curpcb->pcb_onfault;
+ return (0);
+ }
+ trap_fatal(frame);
+ return (-1);
+ }
+
+ /* kludge to pass faulting virtual address to sendsig */
+ frame->tf_err = eva;
+
+ return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV);
+}
+#endif
+
+int
+#ifdef CYRIX_486DLC
+trap_pfault(frame, usermode,faultva)
+ struct trapframe *frame;
+ int usermode;
+ vm_offset_t faultva;
+#else
+trap_pfault(frame, usermode)
+ struct trapframe *frame;
+ int usermode;
+#endif
+{
+ vm_offset_t va;
+ struct vmspace *vm = NULL;
+ vm_map_t map = 0;
+ int rv = 0;
+ vm_prot_t ftype;
+ int eva;
+ struct proc *p = curproc;
+
+#ifdef CYRIX_486DLC
+ eva = faultva;
+#else
+ eva = rcr2();
+#endif
+ va = trunc_page((vm_offset_t)eva);
+
+ if (va >= KERNBASE) {
+ /*
+ * Don't allow user-mode faults in kernel address space.
+ */
+ if (usermode)
+ goto nogo;
+
+ map = kernel_map;
+ } else {
+ /*
+ * This is a fault on non-kernel virtual memory.
+ * vm is initialized above to NULL. If curproc is NULL
+ * or curproc->p_vmspace is NULL the fault is fatal.
+ */
+ if (p != NULL)
+ vm = p->p_vmspace;
+
+ if (vm == NULL)
+ goto nogo;
+
+ map = &vm->vm_map;
+ }
+
+ if (frame->tf_err & PGEX_W)
+ ftype = VM_PROT_READ | VM_PROT_WRITE;
+ else
+ ftype = VM_PROT_READ;
+
+ if (map != kernel_map) {
+ vm_offset_t v;
+ vm_page_t mpte;
+
+ /*
+ * Keep swapout from messing with us during this
+ * critical time.
+ */
+ ++p->p_lock;
+
+ /*
+ * Grow the stack if necessary
+ */
+ if ((caddr_t)va > vm->vm_maxsaddr
+ && (caddr_t)va < (caddr_t)USRSTACK) {
+ if (!grow(p, va)) {
+ rv = KERN_FAILURE;
+ --p->p_lock;
+ goto nogo;
+ }
+ }
+
+ /* Fault in the user page: */
+ rv = vm_fault(map, va, ftype, FALSE);
+
+ --p->p_lock;
+ } else {
+ /*
+ * Since we know that kernel virtual address addresses
+ * always have pte pages mapped, we just have to fault
+ * the page.
+ */
+ rv = vm_fault(map, va, ftype, FALSE);
+ }
+
+ if (rv == KERN_SUCCESS)
+ return (0);
+nogo:
+ if (!usermode) {
+ if (curpcb && curpcb->pcb_onfault) {
+ frame->tf_eip = (int)curpcb->pcb_onfault;
+ return (0);
+ }
+ trap_fatal(frame);
+ return (-1);
+ }
+
+ /* kludge to pass faulting virtual address to sendsig */
+ frame->tf_err = eva;
+
+ return((rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV);
+}
+
+static void
+trap_fatal(frame)
+ struct trapframe *frame;
+{
+ int code, type, eva, ss, esp;
+ struct soft_segment_descriptor softseg;
+
+ code = frame->tf_err;
+ type = frame->tf_trapno;
+ eva = rcr2();
+ sdtossd(&gdt[IDXSEL(frame->tf_cs & 0xffff)].sd, &softseg);
+
+ if (type <= MAX_TRAP_MSG)
+ printf("\n\nFatal trap %d: %s while in %s mode\n",
+ type, trap_msg[type],
+ ISPL(frame->tf_cs) == SEL_UPL ? "user" : "kernel");
+ if (type == T_PAGEFLT) {
+ printf("fault virtual address = 0x%x\n", eva);
+ printf("fault code = %s %s, %s\n",
+ code & PGEX_U ? "user" : "supervisor",
+ code & PGEX_W ? "write" : "read",
+ code & PGEX_P ? "protection violation" : "page not present");
+ }
+ printf("instruction pointer = 0x%x:0x%x\n",
+ frame->tf_cs & 0xffff, frame->tf_eip);
+ if (ISPL(frame->tf_cs) == SEL_UPL) {
+ ss = frame->tf_ss & 0xffff;
+ esp = frame->tf_esp;
+ } else {
+ ss = GSEL(GDATA_SEL, SEL_KPL);
+ esp = (int)&frame->tf_esp;
+ }
+ printf("stack pointer = 0x%x:0x%x\n", ss, esp);
+ printf("frame pointer = 0x%x:0x%x\n", ss, frame->tf_ebp);
+ printf("code segment = base 0x%x, limit 0x%x, type 0x%x\n",
+ softseg.ssd_base, softseg.ssd_limit, softseg.ssd_type);
+ printf(" = DPL %d, pres %d, def32 %d, gran %d\n",
+ softseg.ssd_dpl, softseg.ssd_p, softseg.ssd_def32,
+ softseg.ssd_gran);
+ printf("processor eflags = ");
+ if (frame->tf_eflags & PSL_T)
+ printf("trace trap, ");
+ if (frame->tf_eflags & PSL_I)
+ printf("interrupt enabled, ");
+ if (frame->tf_eflags & PSL_NT)
+ printf("nested task, ");
+ if (frame->tf_eflags & PSL_RF)
+ printf("resume, ");
+ if (frame->tf_eflags & PSL_VM)
+ printf("vm86, ");
+ printf("IOPL = %d\n", (frame->tf_eflags & PSL_IOPL) >> 12);
+ printf("current process = ");
+ if (curproc) {
+ printf("%lu (%s)\n",
+ (u_long)curproc->p_pid, curproc->p_comm ?
+ curproc->p_comm : "");
+ } else {
+ printf("Idle\n");
+ }
+ printf("interrupt mask = ");
+ if ((cpl & net_imask) == net_imask)
+ printf("net ");
+ if ((cpl & tty_imask) == tty_imask)
+ printf("tty ");
+ if ((cpl & bio_imask) == bio_imask)
+ printf("bio ");
+ if (cpl == 0)
+ printf("none");
+ printf("\n");
+
+#ifdef KDB
+ if (kdb_trap(&psl))
+ return;
+#endif
+#ifdef DDB
+ if (kdb_trap (type, 0, frame))
+ return;
+#endif
+ if (type <= MAX_TRAP_MSG)
+ panic(trap_msg[type]);
+ else
+ panic("unknown/reserved trap");
+}
+
+/*
+ * Double fault handler. Called when a fault occurs while writing
+ * a frame for a trap/exception onto the stack. This usually occurs
+ * when the stack overflows (such is the case with infinite recursion,
+ * for example).
+ *
+ * XXX Note that the current PTD gets replaced by IdlePTD when the
+ * task switch occurs. This means that the stack that was active at
+ * the time of the double fault is not available at <kstack> unless
+ * the machine was idle when the double fault occurred. The downside
+ * of this is that "trace <ebp>" in ddb won't work.
+ */
+void
+dblfault_handler()
+{
+ struct pcb *pcb = curpcb;
+
+ if (pcb != NULL) {
+ printf("\nFatal double fault:\n");
+ printf("eip = 0x%x\n", pcb->pcb_tss.tss_eip);
+ printf("esp = 0x%x\n", pcb->pcb_tss.tss_esp);
+ printf("ebp = 0x%x\n", pcb->pcb_tss.tss_ebp);
+ }
+
+ panic("double fault");
+}
+
+/*
+ * Compensate for 386 brain damage (missing URKR).
+ * This is a little simpler than the pagefault handler in trap() because
+ * it the page tables have already been faulted in and high addresses
+ * are thrown out early for other reasons.
+ */
+int trapwrite(addr)
+ unsigned addr;
+{
+ struct proc *p;
+ vm_offset_t va, v;
+ struct vmspace *vm;
+ int rv;
+
+ va = trunc_page((vm_offset_t)addr);
+ /*
+ * XXX - MAX is END. Changed > to >= for temp. fix.
+ */
+ if (va >= VM_MAXUSER_ADDRESS)
+ return (1);
+
+ p = curproc;
+ vm = p->p_vmspace;
+
+ ++p->p_lock;
+
+ if ((caddr_t)va >= vm->vm_maxsaddr
+ && (caddr_t)va < (caddr_t)USRSTACK) {
+ if (!grow(p, va)) {
+ --p->p_lock;
+ return (1);
+ }
+ }
+
+ v = trunc_page(vtopte(va));
+
+ /*
+ * fault the data page
+ */
+ rv = vm_fault(&vm->vm_map, va, VM_PROT_READ|VM_PROT_WRITE, FALSE);
+
+ --p->p_lock;
+
+ if (rv != KERN_SUCCESS)
+ return 1;
+
+ return (0);
+}
+
+/*
+ * System call request from POSIX system call gate interface to kernel.
+ * Like trap(), argument is call by reference.
+ */
+void
+syscall(frame)
+ struct trapframe frame;
+{
+ caddr_t params;
+ int i;
+ struct sysent *callp;
+ struct proc *p = curproc;
+ u_quad_t sticks;
+ int error;
+ int args[8], rval[2];
+ u_int code;
+
+ sticks = p->p_sticks;
+ if (ISPL(frame.tf_cs) != SEL_UPL)
+ panic("syscall");
+
+ p->p_md.md_regs = (int *)&frame;
+ params = (caddr_t)frame.tf_esp + sizeof(int);
+ code = frame.tf_eax;
+ if (p->p_sysent->sv_prepsyscall) {
+ (*p->p_sysent->sv_prepsyscall)(&frame, args, &code, &params);
+ } else {
+ /*
+ * Need to check if this is a 32 bit or 64 bit syscall.
+ */
+ if (code == SYS_syscall) {
+ /*
+ * Code is first argument, followed by actual args.
+ */
+ code = fuword(params);
+ params += sizeof(int);
+ } else if (code == SYS___syscall) {
+ /*
+ * Like syscall, but code is a quad, so as to maintain
+ * quad alignment for the rest of the arguments.
+ */
+ code = fuword(params);
+ params += sizeof(quad_t);
+ }
+ }
+
+ if (p->p_sysent->sv_mask)
+ code &= p->p_sysent->sv_mask;
+
+ if (code >= p->p_sysent->sv_size)
+ callp = &p->p_sysent->sv_table[0];
+ else
+ callp = &p->p_sysent->sv_table[code];
+
+ if (params && (i = callp->sy_narg * sizeof(int)) &&
+ (error = copyin(params, (caddr_t)args, (u_int)i))) {
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSCALL))
+ ktrsyscall(p->p_tracep, code, callp->sy_narg, args);
+#endif
+ goto bad;
+ }
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSCALL))
+ ktrsyscall(p->p_tracep, code, callp->sy_narg, args);
+#endif
+ rval[0] = 0;
+ rval[1] = frame.tf_edx;
+
+ error = (*callp->sy_call)(p, args, rval);
+
+ switch (error) {
+
+ case 0:
+ /*
+ * Reinitialize proc pointer `p' as it may be different
+ * if this is a child returning from fork syscall.
+ */
+ p = curproc;
+ frame.tf_eax = rval[0];
+ frame.tf_edx = rval[1];
+ frame.tf_eflags &= ~PSL_C;
+ break;
+
+ case ERESTART:
+ /*
+ * Reconstruct pc, assuming lcall $X,y is 7 bytes,
+ * int 0x80 is 2 bytes. We saved this in tf_err.
+ */
+ frame.tf_eip -= frame.tf_err;
+ break;
+
+ case EJUSTRETURN:
+ break;
+
+ default:
+bad:
+ if (p->p_sysent->sv_errsize)
+ if (error >= p->p_sysent->sv_errsize)
+ error = -1; /* XXX */
+ else
+ error = p->p_sysent->sv_errtbl[error];
+ frame.tf_eax = error;
+ frame.tf_eflags |= PSL_C;
+ break;
+ }
+
+ if (frame.tf_eflags & PSL_T) {
+ /* Traced syscall. */
+ frame.tf_eflags &= ~PSL_T;
+ trapsignal(p, SIGTRAP, 0);
+ }
+
+ userret(p, &frame, sticks);
+
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_SYSRET))
+ ktrsysret(p->p_tracep, code, error, rval[0]);
+#endif
+}
diff --git a/sys/pc98/i386/userconfig.c b/sys/pc98/i386/userconfig.c
new file mode 100644
index 0000000..2e9f719
--- /dev/null
+++ b/sys/pc98/i386/userconfig.c
@@ -0,0 +1,2922 @@
+/**
+ ** Copyright (c) 1995
+ ** Michael Smith, msmith@atrad.adelaide.edu.au. All rights reserved.
+ **
+ ** This code contains a module marked :
+
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ * Copyright (c) 1994 Jordan K. Hubbard
+ * All rights reserved.
+ * Copyright (c) 1994 David Greenman
+ * All rights reserved.
+ *
+ * Many additional changes by Bruce Evans
+ *
+ * This code is derived from software contributed by the
+ * University of California Berkeley, Jordan K. Hubbard,
+ * David Greenman and Bruce Evans.
+
+ ** As such, it contains code subject to the above copyrights.
+ ** The module and its copyright can be found below.
+ **
+ ** 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. All advertising materials mentioning features or use of this software
+ ** must display the following acknowledgment:
+ ** This product includes software developed by Michael Smith.
+ ** 4. 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 MICHAEL SMITH ``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 MICHAEL SMITH 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.
+ **
+ ** $Id: userconfig.c,v 1.42 1996/04/13 18:33:04 bde Exp $
+ **/
+
+/**
+ ** USERCONFIG
+ **
+ ** Kernel boot-time configuration manipulation tool for FreeBSD.
+ **
+ ** Two modes of operation are supported : the default is the line-editor mode,
+ ** the command "visual" invokes the fullscreen mode.
+ **
+ ** The line-editor mode is the old favorite from FreeBSD 2.0/20.05 &c., the
+ ** fullscreen mode requires syscons or a minimal-ansi serial console.
+ **/
+
+/**
+ ** USERCONFIG, visual mode.
+ **
+ ** msmith@atrad.adelaide.edu.au
+ **
+ ** Look for "EDIT THIS LIST" to add to the list of known devices
+ **
+ **
+ ** There are a number of assumptions made in this code.
+ **
+ ** - That the console supports a minimal set of ANSI escape sequences
+ ** (See the screen manipulation section for a summary)
+ ** and has at least 24 rows.
+ ** - That values less than or equal to zero for any of the device
+ ** parameters indicate that the driver does not use the parameter.
+ ** - That the only tunable parameter for PCI devices are their flags.
+ ** - That flags are _always_ editable.
+ **
+ ** Devices marked as disabled are imported as such. It is possible to move
+ ** a PCI device onto the inactive list, but it is not possible to actually
+ ** prevent the device from being probed. The ability to move is considered
+ ** desirable in that people will complain otherwise 8)
+ **
+ ** For this tool to be useful, the list of devices below _MUST_ be updated
+ ** when a new driver is brought into the kernel. It is not possible to
+ ** extract this information from the drivers in the kernel, as the devconf
+ ** structure for the device is not registered until the device is probed,
+ ** which is too late.
+ **
+ ** XXX - TODO:
+ **
+ ** - FIX OPERATION WITH PCVT!
+ **
+ ** - Display _what_ a device conflicts with.
+ ** - Implement page up/down (as what?)
+ ** - Wizard mode (no restrictions)
+ ** - Find out how to put syscons back into low-intensity mode so that the
+ ** !b escape is useful on the console.
+ ** - The min and max values used for editing parameters are probably
+ ** very bogus - fix?
+ **
+ ** - Only display headings with devices under them. (difficult)
+ **/
+
+/*
+ * PC-9801 port by KATO Takenori <kato@eclogite.eps.nagoya-u.ac.jp>
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/clock.h>
+#include <machine/cons.h>
+#include <machine/md_var.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98_device.h>
+#else
+#include <i386/isa/isa_device.h>
+#endif
+
+#include <pci/pcivar.h>
+
+#ifdef PC98
+static struct pc98_device *devtabs[] = { pc98_devtab_bio, pc98_devtab_tty, pc98_devtab_net,
+ pc98_devtab_null, NULL };
+
+static struct pc98_device *isa_devlist; /* list read by dset to extract changes */
+#else
+static struct isa_device *devtabs[] = { isa_devtab_bio, isa_devtab_tty, isa_devtab_net,
+ isa_devtab_null, NULL };
+
+static struct isa_device *isa_devlist; /* list read by dset to extract changes */
+#endif
+
+#define putchar(x) cnputc(x)
+#define getchar() cngetc()
+
+
+#ifndef FALSE
+#define FALSE (0)
+#define TRUE (!FALSE)
+#endif
+
+
+typedef struct
+{
+ char dev[16]; /* device basename */
+ char name[60]; /* long name */
+ int attrib; /* things to do with the device */
+ int class; /* device classification */
+} DEV_INFO;
+
+#define FLG_INVISIBLE (1<<0) /* device should not be shown */
+#define FLG_MANDATORY (1<<1) /* device can be edited but not disabled */
+#define FLG_FIXIRQ (1<<2) /* device IRQ cannot be changed */
+#define FLG_FIXIOBASE (1<<3) /* device iobase cannot be changed */
+#define FLG_FIXMADDR (1<<4) /* device maddr cannot be changed */
+#define FLG_FIXMSIZE (1<<5) /* device msize cannot be changed */
+#define FLG_FIXDRQ (1<<6) /* device DRQ cannot be changed */
+#define FLG_FIXED (FLG_FIXIRQ|FLG_FIXIOBASE|FLG_FIXMADDR|FLG_FIXMSIZE|FLG_FIXDRQ)
+#define FLG_IMMUTABLE (FLG_FIXED|FLG_MANDATORY)
+
+#define CLS_STORAGE 1 /* storage devices */
+#define CLS_NETWORK 2 /* network interfaces */
+#define CLS_COMMS 3 /* serial, parallel ports */
+#define CLS_INPUT 4 /* user input : mice, keyboards, joysticks etc */
+#define CLS_MMEDIA 5 /* "multimedia" devices (sound, video, etc) */
+#define CLS_MISC 255 /* none of the above */
+
+
+typedef struct
+{
+ char name[60];
+ int number;
+} DEVCLASS_INFO;
+
+static DEVCLASS_INFO devclass_names[] = {
+{ "Storage : ", CLS_STORAGE},
+{ "Network : ", CLS_NETWORK},
+{ "Communications : ", CLS_COMMS},
+{ "Input : ", CLS_INPUT},
+{ "Multimedia : ", CLS_MMEDIA},
+{ "Miscellaneous : ", CLS_MISC},
+{ "",0}};
+
+
+/********************* EDIT THIS LIST **********************/
+
+/** Notes :
+ **
+ ** - PCI devices should be marked FLG_FIXED, not FLG_IMMUTABLE. Whilst
+ ** it's impossible to disable them, it should be possible to move them
+ ** from one list to another for peace of mind.
+ ** - Devices that shouldn't be seen or removed should be marked FLG_INVISIBLE.
+ ** - XXX The list below should be reviewed by the driver authors to verify
+ ** that the correct flags have been set for each driver, and that the
+ ** descriptions are accurate.
+ **/
+
+static DEV_INFO device_info[] = {
+/*---Name----- ---Description---------------------------------------------- */
+#ifdef PC98
+{"sbic", "PC-9801-55 SCSI Interface", 0, CLS_STORAGE},
+{"bs", "PC-9801-55 SCSI Interface", 0, CLS_STORAGE},
+{"aic", "Adaptec 152x SCSI and compatible sound cards", 0, CLS_STORAGE},
+{"wdc", "IDE/ESDI/MFM disk controller", 0, CLS_STORAGE},
+{"fdc", "Floppy disk controller", FLG_FIXED, CLS_STORAGE},
+{"scd", "Sony CD-ROM", 0, CLS_STORAGE},
+{"matcdc", "Matsushita/Panasonic/Creative CDROM", 0, CLS_STORAGE},
+{"wt", "Wangtek/Archive QIC-02 Tape drive", 0, CLS_STORAGE},
+{"ed", "NS8390 Ethernet adapters", 0, CLS_NETWORK},
+{"ep", "3C509 Ethernet adapter", 0, CLS_NETWORK},
+{"fe", "Fujitsu MD86960A/MB869685A Ethernet adapters", 0, CLS_NETWORK},
+{"zp", "3COM PCMCIA Etherlink III Ethernet adapter", 0, CLS_NETWORK},
+{"sio", "8250/16450/16550 Serial port", 0, CLS_COMMS},
+{"lpt", "Parallel printer port", 0, CLS_COMMS},
+{"mse", "PC-9801 Bus Mouse", 0, CLS_INPUT},
+{"sc", "Syscons console driver", FLG_FIXED, CLS_INPUT},
+
+{"pcm", "PC-9801-86 Sound Board", 0, CLS_MMEDIA},
+{"mss", "Microsoft Sound System", 0, CLS_MMEDIA},
+{"sb", "Soundblaster PCM (SB, SBPro, SB16, ProAudio Spectrum)",0,CLS_MMEDIA},
+{"sbxvi", "Soundblaster 16", 0, CLS_MMEDIA},
+{"sbmidi", "Soundblaster MIDI interface", 0, CLS_MMEDIA},
+{"opl", "OPL-2/3 FM, Soundblaster, SBPro, SB16, ProAudio Spectrum",0,CLS_MMEDIA},
+{"mpu", "Roland MPU401 MIDI", 0, CLS_MMEDIA},
+{"pca", "PC speaker PCM audio driver", FLG_FIXED, CLS_MMEDIA},
+
+{"apm", "Advanced Power Management", FLG_FIXED, CLS_MISC},
+{"npx", "Math coprocessor", FLG_INVISIBLE, CLS_MISC},
+{"lkm", "Loadable PCI driver support", FLG_INVISIBLE, CLS_MISC},
+{"vga", "Catchall PCI VGA driver", FLG_INVISIBLE, CLS_MISC},
+{"chip", "PCI chipset support", FLG_INVISIBLE, CLS_MISC},
+#else
+{"bt", "Buslogic SCSI controller", 0, CLS_STORAGE},
+{"ahc", "Adaptec 274x/284x/294x SCSI controller", 0, CLS_STORAGE},
+{"ahb", "Adaptec 174x SCSI controller", 0, CLS_STORAGE},
+{"aha", "Adaptec 154x SCSI controller", 0, CLS_STORAGE},
+{"uha", "Ultrastor 14F/24F/34F SCSI controller",0, CLS_STORAGE},
+{"aic", "Adaptec 152x SCSI and compatible sound cards", 0, CLS_STORAGE},
+{"nca", "ProAudio Spectrum SCSI and comaptibles", 0, CLS_STORAGE},
+{"sea", "Seagate ST01/ST02 SCSI and compatibles", 0, CLS_STORAGE},
+{"wds", "Western Digitial WD7000 SCSI controller", 0, CLS_STORAGE},
+{"ncr", "NCR 53C810 SCSI controller", FLG_FIXED, CLS_STORAGE},
+{"wdc", "IDE/ESDI/MFM disk controller", 0, CLS_STORAGE},
+{"fdc", "Floppy disk controller", FLG_FIXED, CLS_STORAGE},
+{"mcd", "Mitsumi CD-ROM", 0, CLS_STORAGE},
+{"scd", "Sony CD-ROM", 0, CLS_STORAGE},
+{"matcdc", "Matsushita/Panasonic/Creative CDROM", 0, CLS_STORAGE},
+{"wt", "Wangtek/Archive QIC-02 Tape drive", 0, CLS_STORAGE},
+
+{"ed", "NE1000,NE2000,3C503,WD/SMC80xx Ethernet adapters",0, CLS_NETWORK},
+{"el", "3C501 Ethernet adapter", 0, CLS_NETWORK},
+{"ep", "3C509 Ethernet adapter", 0, CLS_NETWORK},
+{"fe", "Fujitsu MD86960A/MB869685A Ethernet adapters", 0, CLS_NETWORK},
+{"fea", "DEC DEFEA EISA FDDI adapter", 0, CLS_NETWORK},
+{"ie", "AT&T Starlan 10 and EN100, 3C507, NI5210 Ethernet adapters",0,CLS_NETWORK},
+{"ix", "Intel EtherExpress Ethernet adapter", 0, CLS_NETWORK},
+{"le", "DEC Etherworks 2 and 3 Ethernet adapters", 0, CLS_NETWORK},
+{"lnc", "Isolan, Novell NE2100/NE32-VL Ethernet adapters", 0,CLS_NETWORK},
+{"ze", "IBM/National Semiconductor PCMCIA Ethernet adapter",0, CLS_NETWORK},
+{"zp", "3COM PCMCIA Etherlink III Ethernet adapter", 0, CLS_NETWORK},
+{"de", "DEC DC21040 Ethernet adapter", FLG_FIXED, CLS_NETWORK},
+{"fpa", "DEC DEFPA PCI FDDI adapter", FLG_FIXED, CLS_NETWORK},
+
+{"sio", "8250/16450/16550 Serial port", 0, CLS_COMMS},
+{"cx", "Cronyx/Sigma multiport sync/async adapter",0, CLS_COMMS},
+{"rc", "RISCom/8 multiport async adapter", 0, CLS_COMMS},
+{"cy", "Cyclades multiport async adapter", 0, CLS_COMMS},
+{"lpt", "Parallel printer port", 0, CLS_COMMS},
+{"nic", "ISDN driver", 0, CLS_COMMS},
+{"nnic", "ISDN driver", 0, CLS_COMMS},
+{"gp", "National Instruments AT-GPIB/TNT driver", 0, CLS_COMMS},
+
+{"mse", "Microsoft Bus Mouse", 0, CLS_INPUT},
+{"psm", "PS/2 Mouse", 0, CLS_INPUT},
+{"joy", "Joystick", FLG_FIXED, CLS_INPUT},
+{"vt", "PCVT console driver", FLG_FIXED, CLS_INPUT},
+{"sc", "Syscons console driver", FLG_FIXED, CLS_INPUT},
+
+{"sb", "Soundblaster PCM (SB, SBPro, SB16, ProAudio Spectrum)",0,CLS_MMEDIA},
+{"sbxvi", "Soundblaster 16", 0, CLS_MMEDIA},
+{"sbmidi", "Soundblaster MIDI interface", 0, CLS_MMEDIA},
+{"pas", "ProAudio Spectrum PCM and MIDI", 0, CLS_MMEDIA},
+{"gus", "Gravis Ultrasound, Ultrasound 16 and Ultrasound MAX",0,CLS_MMEDIA},
+{"gusxvi", "Gravis Ultrasound 16-bit PCM", 0, CLS_MMEDIA},
+{"gusmax", "Gravis Ultrasound MAX", 0, CLS_MMEDIA},
+{"mss", "Microsoft Sound System", 0, CLS_MMEDIA},
+{"opl", "OPL-2/3 FM, Soundblaster, SBPro, SB16, ProAudio Spectrum",0,CLS_MMEDIA},
+{"mpu", "Roland MPU401 MIDI", 0, CLS_MMEDIA},
+{"uart", "6850 MIDI UART", 0, CLS_MMEDIA},
+{"pca", "PC speaker PCM audio driver", FLG_FIXED, CLS_MMEDIA},
+{"ctx", "Coretex-I frame grabber", 0, CLS_MMEDIA},
+{"spigot", "Creative Labs Video Spigot video capture", 0, CLS_MMEDIA},
+{"gsc", "Genius GS-4500 hand scanner", 0, CLS_MMEDIA},
+{"qcam", "QuickCam parallel port camera", 0, CLS_MMEDIA},
+
+{"apm", "Advanced Power Management", FLG_FIXED, CLS_MISC},
+{"labpc", "National Instruments Lab-PC/Lab-PC+", 0, CLS_MISC},
+{"npx", "Math coprocessor", FLG_INVISIBLE, CLS_MISC},
+{"lkm", "Loadable PCI driver support", FLG_INVISIBLE, CLS_MISC},
+{"vga", "Catchall PCI VGA driver", FLG_INVISIBLE, CLS_MISC},
+{"chip", "PCI chipset support", FLG_INVISIBLE, CLS_MISC},
+#endif
+{"","",0,0}};
+
+
+typedef struct _devlist_struct
+{
+ char name[80];
+ int attrib; /* flag values as per the FLG_* defines above */
+ int class; /* disk, etc as per the CLS_* defines above */
+ char dev[16];
+ int iobase,irq,drq,maddr,msize,unit,flags,conflict_ok,id;
+ int comment; /* 0 = device, 1 = comment, 2 = collapsed comment */
+ int conflicts; /* set/reset by findconflict, count of conflicts */
+ int changed; /* nonzero if the device has been edited */
+#ifdef PC98
+ struct pc98_device *device;
+#else
+ struct isa_device *device;
+#endif
+ struct _devlist_struct *prev,*next;
+} DEV_LIST;
+
+
+#define DEV_DEVICE 0
+#define DEV_COMMENT 1
+#define DEV_ZOOMED 2
+
+#define LIST_CURRENT (1<<0)
+#define LIST_SELECTED (1<<1)
+
+#define KEY_EXIT 0 /* return codes from dolist() and friends */
+#define KEY_DO 1
+#define KEY_DEL 2
+#define KEY_TAB 3
+#define KEY_REDRAW 4
+
+#define KEY_UP 5 /* these only returned from editval() */
+#define KEY_DOWN 6
+#define KEY_LEFT 7
+#define KEY_RIGHT 8
+#define KEY_NULL 9 /* this allows us to spin & redraw */
+
+#define KEY_ZOOM 10 /* these for zoom all/collapse all */
+#define KEY_UNZOOM 11
+
+static void redraw(void);
+static void insdev(DEV_LIST *dev, DEV_LIST *list);
+static int devinfo(DEV_LIST *dev);
+static int visuserconfig(void);
+
+static DEV_LIST *active = NULL,*inactive = NULL; /* driver lists */
+static DEV_LIST *alist,*ilist; /* visible heads of the driver lists */
+static DEV_LIST scratch; /* scratch record */
+static int conflicts; /* total conflict count */
+
+
+static char lines[] = "--------------------------------------------------------------------------------";
+static char spaces[] = " ";
+
+
+/**
+ ** Device manipulation stuff : find, describe, configure.
+ **/
+
+/**
+ ** setdev
+ **
+ ** Sets the device referenced by (*dev) to the parameters in the struct,
+ ** and the enable flag according to (enabled)
+ **/
+static void
+setdev(DEV_LIST *dev, int enabled)
+{
+ if (!dev->device) /* PCI device */
+ return;
+ dev->device->id_iobase = dev->iobase; /* copy happy */
+ dev->device->id_irq = (u_short)(dev->irq < 16 ? 1<<dev->irq : 0); /* IRQ is bitfield */
+ dev->device->id_drq = (short)dev->drq;
+ dev->device->id_maddr = (caddr_t)dev->maddr;
+ dev->device->id_msize = dev->msize;
+ dev->device->id_flags = dev->flags;
+ dev->device->id_enabled = enabled;
+}
+
+
+/**
+ ** getdevs
+ **
+ ** Walk the kernel device tables and build the active and inactive lists
+ **/
+static void
+getdevs(void)
+{
+ int i,j;
+#ifdef PC98
+ struct pc98_device *ap;
+#else
+ struct isa_device *ap;
+#endif
+
+ for (j = 0; devtabs[j]; j++) /* ISA devices */
+ {
+ ap = devtabs[j]; /* pointer to array of devices */
+ for (i = 0; ap[i].id_id; i++) /* for each device in this table */
+ {
+ scratch.unit = ap[i].id_unit; /* device parameters */
+ strcpy(scratch.dev,ap[i].id_driver->name);
+ scratch.iobase = ap[i].id_iobase;
+ scratch.irq = ffs(ap[i].id_irq)-1;
+ scratch.drq = ap[i].id_drq;
+ scratch.maddr = (int)ap[i].id_maddr;
+ scratch.msize = ap[i].id_msize;
+ scratch.flags = ap[i].id_flags;
+ scratch.conflict_ok = ap[i].id_conflicts;
+
+ scratch.comment = DEV_DEVICE; /* admin stuff */
+ scratch.conflicts = 0;
+ scratch.device = &ap[i]; /* save pointer for later reference */
+ scratch.changed = 0;
+ if (!devinfo(&scratch)) /* get more info on the device */
+ insdev(&scratch,ap[i].id_enabled?active:inactive);
+ }
+ }
+#if NPCI > 0
+ for (i = 0; i < pcidevice_set.ls_length; i++)
+ {
+ if (pcidevice_set.ls_items[i])
+ {
+ if (((struct pci_device *)pcidevice_set.ls_items[i])->pd_name)
+ {
+ strcpy(scratch.dev,((struct pci_device *)pcidevice_set.ls_items[i])->pd_name);
+ scratch.iobase = -2; /* mark as PCI for future reference */
+ scratch.irq = -2;
+ scratch.drq = -2;
+ scratch.maddr = -2;
+ scratch.msize = -2;
+ scratch.flags = 0;
+ scratch.conflict_ok = 0; /* shouldn't conflict */
+ scratch.comment = DEV_DEVICE; /* is a device */
+ scratch.unit = 0; /* arbitrary number of them */
+ scratch.conflicts = 0;
+ scratch.device = NULL;
+ scratch.changed = 0;
+
+ if (!devinfo(&scratch))
+ insdev(&scratch,active); /* always active */
+ }
+ }
+ }
+#endif /* NPCI > 0 */
+}
+
+
+/**
+ ** Devinfo
+ **
+ ** Fill in (dev->name), (dev->attrib) and (dev->type) from the device_info array.
+ ** If the device is unknown, put it in the CLS_MISC class, with no flags.
+ **
+ ** If the device is marked "invisible", return nonzero; the caller should
+ ** not insert any such device into either list.
+ **/
+static int
+devinfo(DEV_LIST *dev)
+{
+ int i;
+
+ for (i = 0; device_info[i].class; i++)
+ {
+ if (!strcmp(dev->dev,device_info[i].dev))
+ {
+ if (device_info[i].attrib & FLG_INVISIBLE)
+ return(1);
+ strcpy(dev->name,device_info[i].name);
+ dev->attrib = device_info[i].attrib;
+ dev->class = device_info[i].class;
+ return(0);
+ }
+ }
+ strcpy(dev->name,"Unknown device");
+ dev->attrib = 0;
+ dev->class = CLS_MISC;
+ return(0);
+}
+
+
+/**
+ ** List manipulation stuff : add, move, initialise, free, traverse
+ **
+ ** Note that there are assumptions throughout this code that
+ ** the first entry in a list will never move. (assumed to be
+ ** a comment).
+ **/
+
+
+/**
+ ** Adddev
+ **
+ ** appends a copy of (dev) to the end of (*list)
+ **/
+static void
+addev(DEV_LIST *dev, DEV_LIST **list)
+{
+
+ DEV_LIST *lp,*ap;
+
+ lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK);
+ bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */
+
+ if (*list) /* list exists */
+ {
+ ap = *list;
+ while(ap->next)
+ ap = ap->next; /* scoot to end of list */
+ lp->prev = ap;
+ lp->next = NULL;
+ ap->next = lp;
+ }else{ /* list does not yet exist */
+ *list = lp;
+ lp->prev = lp->next = NULL; /* list now exists */
+ }
+}
+
+
+/**
+ ** Findspot
+ **
+ ** Finds the 'appropriate' place for (dev) in (list)
+ **
+ ** 'Appropriate' means in numeric order with other devices of the same type,
+ ** or in alphabetic order following a comment of the appropriate type.
+ ** or at the end of the list if an appropriate comment is not found. (this should
+ ** never happen)
+ ** (Note that the appropriate point is never the top, but may be the bottom)
+ **/
+static DEV_LIST *
+findspot(DEV_LIST *dev, DEV_LIST *list)
+{
+ DEV_LIST *ap;
+
+ for (ap = list; ap; ap = ap->next)
+ {
+ if (ap->comment != DEV_DEVICE) /* ignore comments */
+ continue;
+ if (!strcmp(dev->dev,ap->dev)) /* same base device */
+ {
+ if ((dev->unit <= ap->unit) /* belongs before (equal is bad) */
+ || !ap->next) /* or end of list */
+ {
+ ap = ap->prev; /* back up one */
+ break; /* done here */
+ }
+ if (ap->next) /* if the next item exists */
+ {
+ if (ap->next->comment != DEV_DEVICE) /* next is a comment */
+ break;
+ if (strcmp(dev->dev,ap->next->dev)) /* next is a different device */
+ break;
+ }
+ }
+ }
+
+ if (!ap)
+ {
+ for (ap = list; ap; ap = ap->next)
+ {
+ if (ap->comment != DEV_DEVICE) /* look for simlar devices */
+ continue;
+ if (dev->class != ap->class) /* of same class too 8) */
+ continue;
+ if (strcmp(dev->dev,ap->dev) < 0) /* belongs before the current entry */
+ {
+ ap = ap->prev; /* back up one */
+ break; /* done here */
+ }
+ if (ap->next) /* if the next item exists */
+ if (ap->next->comment != DEV_DEVICE) /* next is a comment, go here */
+ break;
+ }
+ }
+
+ if (!ap) /* didn't find a match */
+ {
+ for (ap = list; ap->next; ap = ap->next) /* try for a matching comment */
+ if ((ap->comment != DEV_DEVICE)
+ && (ap->class == dev->class)) /* appropriate place? */
+ break;
+ } /* or just put up with last */
+
+ return(ap);
+}
+
+
+/**
+ ** Insdev
+ **
+ ** Inserts a copy of (dev) at the appropriate point in (list)
+ **/
+static void
+insdev(DEV_LIST *dev, DEV_LIST *list)
+{
+ DEV_LIST *lp,*ap;
+
+ lp = (DEV_LIST *)malloc(sizeof(DEV_LIST),M_DEVL,M_WAITOK);
+ bcopy(dev,lp,sizeof(DEV_LIST)); /* create copied record */
+
+ ap = findspot(lp,list); /* find appropriate spot */
+ lp->next = ap->next; /* point to next */
+ if (ap->next)
+ ap->next->prev = lp; /* point next to new */
+ lp->prev = ap; /* point new to current */
+ ap->next = lp; /* and current to new */
+}
+
+
+/**
+ ** Movedev
+ **
+ ** Moves (dev) from its current list to an appropriate place in (list)
+ ** (dev) may not come from the top of a list, but it may from the bottom.
+ **/
+static void
+movedev(DEV_LIST *dev, DEV_LIST *list)
+{
+ DEV_LIST *ap;
+
+ ap = findspot(dev,list);
+ dev->prev->next = dev->next; /* remove from old list */
+ if (dev->next)
+ dev->next->prev = dev->prev;
+
+ dev->next = ap->next; /* insert in new list */
+ if (ap->next)
+ ap->next->prev = dev; /* point next to new */
+ dev->prev = ap; /* point new to current */
+ ap->next = dev; /* and current to new */
+}
+
+
+/**
+ ** Initlist
+ **
+ ** Initialises (*list) with the basic headings
+ **/
+static void
+initlist(DEV_LIST **list)
+{
+ int i;
+
+ for(i = 0; devclass_names[i].name[0]; i++) /* for each devtype name */
+ {
+ strcpy(scratch.name,devclass_names[i].name);
+ scratch.comment = DEV_ZOOMED;
+ scratch.class = devclass_names[i].number;
+ scratch.attrib = FLG_MANDATORY; /* can't be moved */
+ addev(&scratch,list); /* add to the list */
+ }
+}
+
+
+/**
+ ** savelist
+ **
+ ** Walks (list) and saves the settings of any entry marked as changed.
+ **
+ ** The device's active field is set according to (active).
+ **
+ ** Builds the isa_devlist used by dset to extract the changed device information.
+ ** The code for this was taken almost verbatim from the original module.
+ **/
+static void
+savelist(DEV_LIST *list, int active)
+{
+#ifdef PC98
+ struct pc98_device *id_p,*id_pn;
+#else
+ struct isa_device *id_p,*id_pn;
+#endif
+
+ while (list)
+ {
+ if ((list->comment == DEV_DEVICE) && list->changed)
+ {
+ setdev(list,active); /* set the device itself */
+
+ id_pn = NULL;
+ for (id_p=isa_devlist; id_p; id_p=id_p->id_next)
+ { /* look on the list for it */
+ if (id_p->id_id == list->device->id_id)
+ {
+ id_pn = id_p->id_next;
+#ifdef PC98
+ bcopy(list->device,id_p,sizeof(struct pc98_device));
+#else
+ bcopy(list->device,id_p,sizeof(struct isa_device));
+#endif
+ id_p->id_next = id_pn;
+ break;
+ }
+ }
+ if (!id_pn) /* not already on the list */
+ {
+#ifdef PC98
+ id_pn = malloc(sizeof(struct pc98_device),M_DEVL,M_WAITOK);
+ bcopy(list->device,id_pn,sizeof(struct pc98_device));
+#else
+ id_pn = malloc(sizeof(struct isa_device),M_DEVL,M_WAITOK);
+ bcopy(list->device,id_pn,sizeof(struct isa_device));
+#endif
+ id_pn->id_next = isa_devlist;
+ isa_devlist = id_pn; /* park at top of list */
+ }
+ }
+ list = list->next;
+ }
+}
+
+
+/**
+ ** nukelist
+ **
+ ** Frees all storage in use by a (list).
+ **/
+static void
+nukelist(DEV_LIST *list)
+{
+ DEV_LIST *dp;
+
+ if (!list)
+ return;
+ while(list->prev) /* walk to head of list */
+ list = list->prev;
+
+ while(list)
+ {
+ dp = list;
+ list = list->next;
+ free(dp,M_DEVL);
+ }
+}
+
+
+/**
+ ** prevent
+ **
+ ** Returns the previous entry in (list), skipping zoomed regions. Returns NULL
+ ** if there is no previous entry. (Only possible if list->prev == NULL given the
+ ** premise that there is always a comment at the head of the list)
+ **/
+static DEV_LIST *
+prevent(DEV_LIST *list)
+{
+ DEV_LIST *dp;
+
+ if (!list)
+ return(NULL);
+ dp = list->prev; /* start back one */
+ while(dp)
+ {
+ if (dp->comment == DEV_ZOOMED) /* previous section is zoomed */
+ return(dp); /* so skip to comment */
+ if (dp->comment == DEV_COMMENT) /* not zoomed */
+ return(list->prev); /* one back as normal */
+ dp = dp->prev; /* backpedal */
+ }
+ return(dp); /* NULL, we can assume */
+}
+
+
+/**
+ ** nextent
+ **
+ ** Returns the next entry in (list), skipping zoomed regions. Returns NULL
+ ** if there is no next entry. (Possible if the current entry is last, or
+ ** if the current entry is the last heading and it's collapsed)
+ **/
+static DEV_LIST *
+nextent(DEV_LIST *list)
+{
+ DEV_LIST *dp;
+
+ if (!list)
+ return(NULL);
+ if (list->comment != DEV_ZOOMED) /* no reason to skip */
+ return(list->next);
+ dp = list->next;
+ while(dp)
+ {
+ if (dp->comment != DEV_DEVICE) /* found another heading */
+ break;
+ dp = dp->next;
+ }
+ return(dp); /* back we go */
+}
+
+
+/**
+ ** ofsent
+ **
+ ** Returns the (ofs)th entry down from (list), or NULL if it doesn't exist
+ **/
+static DEV_LIST *
+ofsent(int ofs, DEV_LIST *list)
+{
+ while (ofs-- && list)
+ list = nextent(list);
+ return(list);
+}
+
+
+/**
+ ** findconflict
+ **
+ ** Scans every element of (list) and sets the conflict tags appropriately
+ ** Returns the number of conflicts found.
+ **/
+static int
+findconflict(DEV_LIST *list)
+{
+ int count = 0; /* number of conflicts found */
+ DEV_LIST *dp,*sp;
+
+ for (dp = list; dp; dp = dp->next) /* over the whole list */
+ {
+ if (dp->comment != DEV_DEVICE) /* comments don't usually conflict */
+ continue;
+
+ dp->conflicts = 0; /* assume the best */
+ for (sp = list; sp; sp = sp->next) /* scan the entire list for conflicts */
+ {
+ if (sp->comment != DEV_DEVICE) /* likewise */
+ continue;
+ if (sp == dp) /* always conflict with itself */
+ continue;
+ if (sp->conflict_ok && dp->conflict_ok)
+ continue; /* both allowed to conflict */
+
+ if ((dp->iobase > 0) && /* iobase conflict? */
+ (dp->iobase == sp->iobase))
+ dp->conflicts = 1;
+ if ((dp->irq > 0) && /* irq conflict? */
+ (dp->irq == sp->irq))
+ dp->conflicts = 1;
+ if ((dp->drq > 0) && /* drq conflict? */
+ (dp->drq == sp->drq))
+ dp->conflicts = 1;
+ if ((dp->maddr > 0) && /* maddr conflict? */
+ (dp->maddr == sp->maddr))
+ dp->conflicts = 1;
+ if ((dp->msize > 0) && /* msize conflict? */
+ (dp->msize == sp->msize))
+ dp->conflicts = 1;
+ }
+ count += dp->conflicts; /* count conflicts */
+ }
+ return(count);
+}
+
+
+/**
+ ** expandlist
+ **
+ ** Unzooms all headings in (list)
+ **/
+static void
+expandlist(DEV_LIST *list)
+{
+ while(list)
+ {
+ if (list->comment == DEV_COMMENT)
+ list->comment = DEV_ZOOMED;
+ list = list->next;
+ }
+}
+
+
+/**
+ ** collapselist
+ **
+ ** Zooms all headings in (list)
+ **/
+static void
+collapselist(DEV_LIST *list)
+{
+ while(list)
+ {
+ if (list->comment == DEV_ZOOMED)
+ list->comment = DEV_COMMENT;
+ list = list->next;
+ }
+}
+
+
+/**
+ ** Screen-manipulation stuff
+ **
+ ** This is the basic screen layout :
+ **
+ ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75
+ ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|....
+ ** +--------------------------------------------------------------------------------+
+ ** 0 -|---Active Drivers----------------------------xx Conflicts------Dev---IRQ--Port--|
+ ** 1 -| ........................ ....... .. 0x....|
+ ** 2 -| ........................ ....... .. 0x....|
+ ** 3 -| ........................ ....... .. 0x....|
+ ** 4 -| ........................ ....... .. 0x....|
+ ** 5 -| ........................ ....... .. 0x....|
+ ** 6 -| ........................ ....... .. 0x....|
+ ** 7 -| ........................ ....... .. 0x....|
+ ** 8 -| ........................ ....... .. 0x....|
+ ** 9 -|---Inactive Drivers--------------------------------------------Dev--------------|
+ ** 10-| ........................ ....... |
+ ** 11-| ........................ ....... |
+ ** 12-| ........................ ....... |
+ ** 13-| ........................ ....... |
+ ** 14-| ........................ ....... |
+ ** 15-| ........................ ....... |
+ ** 16-| ........................ ....... |
+ ** 17-|------------------------------------------------------UP-DOWN-------------------|
+ ** 18-| Relevant parameters for the current device |
+ ** 19-| |
+ ** 20-| |
+ ** 21-|--------------------------------------------------------------------------------|
+ ** 22-| Help texts go here |
+ ** 23-| |
+ ** +--------------------------------------------------------------------------------+
+ **
+ ** Help texts
+ **
+ ** On a collapsed comment :
+ **
+ ** [Enter] Expand device list [z] Expand all lists
+ ** [TAB] Change fields [Q] Save and Exit
+ **
+ ** On an expanded comment :
+ **
+ ** [Enter] Collapse device list [Z] Collapse all lists
+ ** [TAB] Change fields [Q] Save and Exit
+ **
+ ** On a comment with no followers
+ **
+ **
+ ** [TAB] Change fields [Q] Save and Exit
+ **
+ ** On a device in the active list
+ **
+ ** [Enter] Edit device parameters [DEL] Disable device
+ ** [TAB] Change fields [Q] Save and Exit
+ **
+ ** On a device in the inactive list
+ **
+ ** [Enter] Enable device
+ ** [TAB] Change fields [Q] Save and Exit
+ **
+ ** While editing parameters
+ **
+ ** <parameter-specific help here>
+ ** [TAB] Change fields [Q] Save device parameters
+ **/
+
+
+
+/**
+ **
+ ** The base-level screen primitives :
+ **
+ ** bold() - enter bold mode \E[1m (md)
+ ** inverse() - enter inverse mode \E[7m (so)
+ ** normal() - clear bold/inverse mode \E[m (se)
+ ** clear() - clear the screen \E[H\E[J (ce)
+ ** move(x,y) - move the cursor to x,y \E[y;xH: (cm)
+ **/
+
+static void
+bold(void)
+{
+ printf("\033[1m");
+}
+
+static void
+inverse(void)
+{
+ printf("\033[7m");
+}
+
+static void
+normal(void)
+{
+ printf("\033[m");
+}
+
+static void
+clear(void)
+{
+ normal();
+ printf("\033[H\033[J");
+}
+
+static void
+move(int x, int y)
+{
+ printf("\033[%d;%dH",y+1,x+1);
+}
+
+
+/**
+ **
+ ** High-level screen primitives :
+ **
+ ** putxyl(x,y,str,len) - put (len) bytes of (str) at (x,y), supports embedded formatting
+ ** putxy(x,y,str) - put (str) at (x,y), supports embedded formatting
+ ** erase(x,y,w,h) - clear the box (x,y,w,h)
+ ** txtbox(x,y,w,y,str) - put (str) in a region at (x,y,w,h)
+ ** putmsg(str) - put (str) in the message area
+ ** puthelp(str) - put (str) in the upper helpline
+ ** pad(str,len) - pad (str) to (len) with spaces
+ ** drawline(row,detail,list,inverse,*dhelp)
+ ** - draws a line for (*list) at (row) onscreen. If (detail) is
+ ** nonzero, include port, IRQ and maddr, if (inverse) is nonzero,
+ ** draw the line in inverse video, and display (*dhelp) on the
+ ** helpline.
+ ** drawlist(row,num,detail,list)
+ ** - draw (num) entries from (list) at (row) onscreen, passile (detail)
+ ** through to drawline().
+ ** showparams(dev) - displays the relevant parameters for (dev) below the lists onscreen.
+ ** yesno(str) - displays (str) in the message area, and returns nonzero on 'y' or 'Y'
+ ** redraw(); - Redraws the entire screen layout, including the
+ ** - two list panels.
+ **/
+
+/**
+ ** putxy
+ ** writes (str) at x,y onscreen
+ ** putxyl
+ ** writes up to (len) of (str) at x,y onscreen.
+ **
+ ** Supports embedded formatting :
+ ** !i - inverse mode.
+ ** !b - bold mode.
+ ** !n - normal mode.
+ **/
+static void
+putxyl(int x, int y, char *str, int len)
+{
+ move(x,y);
+ normal();
+
+ while((*str) && (len--))
+ {
+ if (*str == '!') /* format escape? */
+ {
+ switch(*(str+1)) /* depending on the next character */
+ {
+ case 'i':
+ inverse();
+ str +=2; /* skip formatting */
+ len++; /* doesn't count for length */
+ break;
+
+ case 'b':
+ bold();
+ str +=2; /* skip formatting */
+ len++; /* doesn't count for length */
+ break;
+
+ case 'n':
+ normal();
+ str +=2; /* skip formatting */
+ len++; /* doesn't count for length */
+ break;
+
+ default:
+ putchar(*str++); /* not an escape */
+ }
+ }else{
+ putchar(*str++); /* emit the character */
+ }
+ }
+}
+
+#define putxy(x,y,str) putxyl(x,y,str,-1)
+
+
+/**
+ ** erase
+ **
+ ** Erases the region (x,y,w,h)
+ **/
+static void
+erase(int x, int y, int w, int h)
+{
+ int i;
+
+ normal();
+ for (i = 0; i < h; i++)
+ putxyl(x,y++,spaces,w);
+}
+
+
+/**
+ ** txtbox
+ **
+ ** Writes (str) into the region (x,y,w,h), supports embedded formatting using
+ ** putxy. Lines are not wrapped, newlines must be forced with \n.
+ **/
+static void
+txtbox(int x, int y, int w, int h, char *str)
+{
+ int i = 0;
+
+ h--;
+ while((str[i]) && h)
+ {
+ if (str[i] == '\n') /* newline */
+ {
+ putxyl(x,y,str,(i<w)?i:w); /* write lesser of i or w */
+ y++; /* move down */
+ h--; /* room for one less */
+ str += (i+1); /* skip first newline */
+ i = 0; /* zero offset */
+ }else{
+ i++; /* next character */
+ }
+ }
+ if (h) /* end of string, not region */
+ putxyl(x,y,str,w);
+}
+
+
+/**
+ ** putmsg
+ **
+ ** writes (msg) in the helptext area
+ **/
+static void
+putmsg(char *msg)
+{
+ erase(0,18,80,3); /* clear area */
+ txtbox(0,18,80,3,msg);
+}
+
+
+/**
+ ** puthelp
+ **
+ ** Writes (msg) in the helpline area
+ **/
+static void
+puthelp(char *msg)
+{
+ erase(0,22,80,1);
+ putxy(0,22,msg);
+}
+
+
+/**
+ ** masterhelp
+ **
+ ** Draws the help message at the bottom of the screen
+ **/
+static void
+masterhelp(char *msg)
+{
+ erase(0,23,80,1);
+ putxy(0,23,msg);
+}
+
+
+/**
+ ** pad
+ **
+ ** space-pads a (str) to (len) characters
+ **/
+static void
+pad(char *str, int len)
+{
+ int i;
+
+ for (i = 0; str[i]; i++) /* find the end of the string */
+ ;
+ if (i >= len) /* no padding needed */
+ return;
+ while(i < len) /* pad */
+ str[i++] = ' ';
+ str[i] = '\0';
+}
+
+
+/**
+ ** drawline
+ **
+ ** Displays entry (ofs) of (list) in region at (row) onscreen, optionally displaying
+ ** the port and IRQ fields if (detail) is nonzero. If (inverse), in inverse video.
+ **
+ ** The text (dhelp) is displayed if the item is a normal device, otherwise
+ ** help is shown for normal or zoomed comments
+ **/
+static void
+drawline(int row, int detail, DEV_LIST *list, int inverse, char *dhelp)
+{
+ char lbuf[90],nb[70],db[20],ib[16],pb[16];
+
+ if (list->comment == DEV_DEVICE)
+ {
+ nb[0] = ' ';
+ strncpy(nb+1,list->name,57);
+ }else{
+ strncpy(nb,list->name,58);
+ if ((list->comment == DEV_ZOOMED) && (list->next))
+ if (list->next->comment == DEV_DEVICE) /* only mention if there's something hidden */
+ strcat(nb," (Collapsed)");
+ }
+ nb[58] = '\0';
+ pad(nb,60);
+ if (list->conflicts) /* device in conflict? */
+ if (inverse)
+ {
+ strcpy(nb+54," !nCONF!i "); /* tag conflict, careful of length */
+ }else{
+ strcpy(nb+54," !iCONF!n "); /* tag conflict, careful of length */
+ }
+
+ if (list->comment == DEV_DEVICE)
+ {
+ sprintf(db,"%s%d",list->dev,list->unit);
+ pad(db,8);
+ }else{
+ strcpy(db," ");
+ }
+ if ((list->irq > 0) && detail && (list->comment == DEV_DEVICE))
+ {
+ sprintf(ib," %d",list->irq);
+ pad(ib,4);
+ }else{
+ strcpy(ib," ");
+ }
+ if ((list->iobase > 0) && detail && (list->comment == DEV_DEVICE))
+ {
+ sprintf(pb,"0x%x",list->iobase);
+ pad(pb,7);
+ }else{
+ strcpy(pb," ");
+ }
+
+ sprintf(lbuf," %s%s%s%s%s",inverse?"!i":"",nb,db,ib,pb);
+
+ putxyl(0,row,lbuf,80);
+ if (dhelp)
+ {
+ switch(list->comment)
+ {
+ case DEV_DEVICE: /* ordinary device */
+ puthelp(dhelp);
+ break;
+ case DEV_COMMENT:
+ puthelp("");
+ if (list->next)
+ if (list->next->comment == DEV_DEVICE)
+ puthelp(" [!bEnter!n] Collapse device list [!bC!n] Collapse all lists");
+ break;
+ case DEV_ZOOMED:
+ puthelp("");
+ if (list->next)
+ if (list->next->comment == DEV_DEVICE)
+ puthelp(" [!bEnter!n] Expand device list [!bX!n] Expand all lists");
+ break;
+ default:
+ puthelp(" WARNING: This list entry corrupted!");
+ break;
+ }
+ }
+ move(0,row); /* put the cursor somewhere relevant */
+}
+
+
+/**
+ ** drawlist
+ **
+ ** Displays (num) lines of the contents of (list) at (row), optionally displaying the
+ ** port and IRQ fields as well if (detail) is nonzero
+ **
+ ** printf in the kernel is essentially useless, so we do most of the hard work ourselves here.
+ **/
+static void
+drawlist(int row, int num, int detail, DEV_LIST *list)
+{
+ int ofs;
+
+ for(ofs = 0; ofs < num; ofs++)
+ {
+ if (list)
+ {
+ drawline(row+ofs,detail,list,0,"");
+ list = nextent(list); /* move down visible list */
+ }else{
+ erase(0,row+ofs,80,1);
+ }
+ }
+}
+
+
+/**
+ ** redrawactive
+ **
+ ** Redraws the active list
+ **/
+static void
+redrawactive(void)
+{
+ char cbuf[16];
+
+ if (conflicts)
+ {
+ sprintf(cbuf,"!i%d conflict%s",conflicts,(conflicts>1)?"s":"");
+ putxy(45,0,cbuf);
+ }else{
+ putxyl(45,0,lines,16);
+ }
+ drawlist(1,8,1,alist); /* draw device lists */
+}
+
+/**
+ ** redrawinactive
+ **
+ ** Redraws the inactive list
+ **/
+static void
+redrawinactive(void)
+{
+ drawlist(10,7,0,ilist); /* draw device lists */
+}
+
+
+/**
+ ** redraw
+ **
+ ** Clear the screen and redraw the entire layout
+ **/
+static void
+redraw(void)
+{
+ clear();
+ putxy(0,0,lines);
+ putxy(3,0,"!bActive!n-!bDrivers");
+ putxy(63,0,"!bDev!n---!bIRQ!n--!bPort");
+ putxy(0,9,lines);
+ putxy(3,9,"!bInactive!n-!bDrivers");
+ putxy(63,9,"!bDev");
+ putxy(0,17,lines);
+ putxy(0,21,lines);
+ masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit");
+
+ redrawactive();
+ redrawinactive();
+}
+
+
+/**
+ ** yesnocancel
+ **
+ ** Put (str) in the message area, and return 1 if the user hits 'y' or 'Y',
+ ** 2 if they hit 'c' or 'C', or 0 for 'n' or 'N'.
+ **/
+static int
+yesnocancel(char *str)
+{
+
+ putmsg(str);
+ for(;;)
+ switch(getchar())
+ {
+ case 'n':
+ case 'N':
+ return(0);
+
+ case 'y':
+ case 'Y':
+ return(1);
+
+ case 'c':
+ case 'C':
+ return(2);
+ }
+}
+
+
+/**
+ ** showparams
+ **
+ ** Show device parameters in the region below the lists
+ **
+ ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75
+ ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|....
+ ** +--------------------------------------------------------------------------------+
+ ** 17-|--------------------------------------------------------------------------------|
+ ** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed |
+ ** 19-| IRQ number : 00 Memory size : 0x0000 |
+ ** 20-| Flags : 0x0000 DRQ number : 00 |
+ ** 21-|--------------------------------------------------------------------------------|
+ **/
+static void
+showparams(DEV_LIST *dev)
+{
+ char buf[80];
+
+ erase(0,18,80,3); /* clear area */
+ if (!dev)
+ return;
+ if (dev->comment != DEV_DEVICE)
+ return;
+
+
+ if (dev->iobase > 0)
+ {
+ sprintf(buf,"Port address : 0x%x",dev->iobase);
+ putxy(1,18,buf);
+ }
+ if (dev->irq > 0)
+ {
+ sprintf(buf,"IRQ number : %d",dev->irq);
+ putxy(1,19,buf);
+ }
+ sprintf(buf,"Flags : 0x%x",dev->flags);
+ putxy(1,20,buf);
+ if (dev->maddr > 0)
+ {
+ sprintf(buf,"Memory address : 0x%x",dev->maddr);
+ putxy(26,18,buf);
+ }
+ if (dev->msize > 0)
+ {
+ sprintf(buf,"Memory size : 0x%x",dev->msize);
+ putxy(26,19,buf);
+ }
+
+ if (dev->drq > 0)
+ {
+ sprintf(buf,"DRQ number : %d",dev->drq);
+ putxy(26,20,buf);
+ }
+ if (dev->conflict_ok)
+ putxy(54,18,"Conflict allowed");
+}
+
+
+/**
+ ** Editing functions for device parameters
+ **
+ ** editval(x,y,width,hex,min,max,val) - Edit (*val) in a field (width) wide at (x,y)
+ ** onscreen. Refuse values outsise (min) and (max).
+ ** editparams(dev) - Edit the parameters for (dev)
+ **/
+
+
+#define VetRet(code) \
+{ \
+ if ((i >= min) && (i <= max)) /* legit? */ \
+ { \
+ *val = i; \
+ sprintf(buf,hex?"0x%x":"%d",i); \
+ putxy(hex?x-2:x,y,buf); \
+ return(code); /* all done and exit */ \
+ } \
+ i = *val; /* restore original value */ \
+ delta = 1; /* restore other stuff */ \
+}
+
+
+/**
+ ** editval
+ **
+ ** Edit (*val) at (x,y) in (hex)?hex:decimal mode, allowing values between (min) and (max)
+ ** in a field (width) wide. (Allow one space)
+ ** If (ro) is set, we're in "readonly" mode, so disallow edits.
+ **
+ ** Return KEY_TAB on \t, KEY_EXIT on 'q'
+ **/
+static int
+editval(int x, int y, int width, int hex, int min, int max, int *val, int ro)
+{
+ int i = *val; /* work with copy of the value */
+ char buf[10],tc[8]; /* display buffer, text copy */
+ int xp = 0; /* cursor offset into text copy */
+ int delta = 1; /* force redraw first time in */
+ int c;
+ int extended = 0; /* stage counter for extended key sequences */
+
+ if (hex) /* we presume there's a leading 0x onscreen */
+ putxy(x-2,y,"!i0x"); /* coz there sure is now */
+
+ for (;;)
+ {
+ if (delta) /* only update if necessary */
+ {
+ sprintf(tc,hex?"%x":"%d",i); /* make a text copy of the value */
+ sprintf(buf,"!i%s",tc); /* format for printing */
+ erase(x,y,width,1); /* clear the area */
+ putxy(x,y,buf); /* write */
+ xp = strlen(tc); /* cursor always at end */
+ move(x+xp,y); /* position the cursor */
+ }
+
+ c = getchar();
+
+ switch(extended) /* escape handling */
+ {
+ case 0:
+ if (c == 0x1b) /* esc? */
+ {
+ extended = 1; /* flag and spin */
+ continue;
+ }
+ extended = 0;
+ break; /* nope, drop through */
+
+ case 1: /* there was an escape prefix */
+ if (c == '[') /* second character in sequence */
+ {
+ extended = 2;
+ continue;
+ }
+ if (c == 0x1b)
+ return(KEY_EXIT); /* double esc exits */
+ extended = 0;
+ break; /* nup, not a sequence. */
+
+ case 2:
+ extended = 0;
+ switch(c) /* looks like the real McCoy */
+ {
+ case 'A':
+ VetRet(KEY_UP); /* leave if OK */
+ continue;
+ case 'B':
+ VetRet(KEY_DOWN); /* leave if OK */
+ continue;
+ case 'C':
+ VetRet(KEY_RIGHT); /* leave if OK */
+ continue;
+ case 'D':
+ VetRet(KEY_LEFT); /* leave if OK */
+ continue;
+
+ default:
+ continue;
+ }
+ }
+
+ switch(c)
+ {
+ case '\t': /* trying to tab off */
+ VetRet(KEY_TAB); /* verify and maybe return */
+ break;
+
+ case 'q':
+ case 'Q':
+ VetRet(KEY_EXIT);
+ break;
+
+ case '\b':
+ case '\177': /* BS or DEL */
+ if (ro) /* readonly? */
+ {
+ puthelp(" !iThis value cannot be edited (Press ESC)");
+ while(getchar() != 0x1b); /* wait for key */
+ return(KEY_NULL); /* spin */
+ }
+ if (xp) /* still something left to delete */
+ {
+ i = i / (hex?0x10:10); /* strip last digit */
+ delta = 1; /* force update */
+ }
+ break;
+
+ case 588:
+ VetRet(KEY_UP);
+ break;
+
+ case 596:
+ VetRet(KEY_DOWN);
+ break;
+
+ case 591:
+ VetRet(KEY_LEFT);
+ break;
+
+ case 593:
+ VetRet(KEY_RIGHT);
+ break;
+
+ default:
+ if (ro) /* readonly? */
+ {
+ puthelp(" !iThis value cannot be edited (Press ESC)");
+ while(getchar() != 0x1b); /* wait for key */
+ return(KEY_NULL); /* spin */
+ }
+ if (xp >= width) /* no room for more characters anyway */
+ break;
+ if (hex)
+ {
+ if ((c >= '0') && (c <= '9'))
+ {
+ i = i*0x10 + (c-'0'); /* update value */
+ delta = 1;
+ break;
+ }
+ if ((c >= 'a') && (c <= 'f'))
+ {
+ i = i*0x10 + (c-'a'+0xa);
+ delta = 1;
+ break;
+ }
+ if ((c >= 'A') && (c <= 'F'))
+ {
+ i = i*0x10 + (c-'A'+0xa);
+ delta = 1;
+ break;
+ }
+ }else{
+ if ((c >= '0') && (c <= '9'))
+ {
+ i = i*10 + (c-'0'); /* update value */
+ delta = 1; /* force redraw */
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+/**
+ ** editparams
+ **
+ ** Edit the parameters for (dev)
+ **
+ ** Note that it's _always_ possible to edit the flags, otherwise it might be
+ ** possible for this to spin in an endless loop...
+ ** 0 5 10 15 20 25 30 35 40 45 50 55 60 67 70 75
+ ** |....|....|....|....|....|....|....|....|....|....|....|....|....|....|....|....
+ ** +--------------------------------------------------------------------------------+
+ ** 17-|--------------------------------------------------------------------------------|
+ ** 18-| Port address : 0x0000 Memory address : 0x00000 Conflict allowed |
+ ** 19-| IRQ number : 00 Memory size : 0x0000 |
+ ** 20-| Flags : 0x0000 DRQ number : 00 |
+ ** 21-|--------------------------------------------------------------------------------|
+ **
+ ** The "intelligence" in this function that hops around based on the directional
+ ** returns from editval isn't very smart, and depends on the layout above.
+ **/
+static void
+editparams(DEV_LIST *dev)
+{
+ int ret;
+ char buf[16]; /* needs to fit the device name */
+
+ putxy(2,17,"!bParameters!n-!bfor!n-!bdevice!n-");
+ sprintf(buf,"!b%s",dev->dev);
+ putxy(24,17,buf);
+
+ erase(1,22,80,1);
+ for (;;)
+ {
+ ep_iobase:
+ if (dev->iobase > 0)
+ {
+#ifdef PC98
+ puthelp(" IO Port address (Hexadecimal, 0x1-0xffff)");
+ ret = editval(18,18,5,1,0x1,0xffff,&(dev->iobase),(dev->attrib & FLG_FIXIOBASE));
+#else
+ puthelp(" IO Port address (Hexadecimal, 0x1-0x2000)");
+ ret = editval(18,18,5,1,0x1,0x2000,&(dev->iobase),(dev->attrib & FLG_FIXIOBASE));
+#endif
+ switch(ret)
+ {
+ case KEY_EXIT:
+ goto ep_exit;
+
+ case KEY_RIGHT:
+ if (dev->maddr > 0)
+ goto ep_maddr;
+ break;
+
+ case KEY_TAB:
+ case KEY_DOWN:
+ goto ep_irq;
+ }
+ goto ep_iobase;
+ }
+ ep_irq:
+ if (dev->irq > 0)
+ {
+ puthelp(" Interrupt number (Decimal, 1-15)");
+ ret = editval(16,19,3,0,1,15,&(dev->irq),(dev->attrib & FLG_FIXIRQ));
+ switch(ret)
+ {
+ case KEY_EXIT:
+ goto ep_exit;
+
+ case KEY_RIGHT:
+ if (dev->msize > 0)
+ goto ep_msize;
+ break;
+
+ case KEY_UP:
+ if (dev->iobase > 0)
+ goto ep_iobase;
+ break;
+
+ case KEY_TAB:
+ case KEY_DOWN:
+ goto ep_flags;
+ }
+ goto ep_irq;
+ }
+ ep_flags:
+ puthelp(" Device-specific flag values.");
+ ret = editval(18,20,5,1,0x0,0xffff,&(dev->flags),0);
+ switch(ret)
+ {
+ case KEY_EXIT:
+ goto ep_exit;
+
+ case KEY_RIGHT:
+ if (dev->drq > 0)
+ goto ep_drq;
+ break;
+
+ case KEY_UP:
+ if (dev->irq > 0)
+ goto ep_irq;
+ if (dev->iobase > 0)
+ goto ep_iobase;
+ break;
+
+ case KEY_DOWN:
+ if (dev->maddr > 0)
+ goto ep_maddr;
+ if (dev->msize > 0)
+ goto ep_msize;
+ if (dev->drq > 0)
+ goto ep_drq;
+ break;
+
+ case KEY_TAB:
+ goto ep_maddr;
+ }
+ goto ep_flags;
+ ep_maddr:
+ if (dev->maddr > 0)
+ {
+ puthelp(" Device memory start address (Hexadecimal, 0x1-0xfffff)");
+ ret = editval(45,18,6,1,0x1,0xfffff,&(dev->maddr),(dev->attrib & FLG_FIXMADDR));
+ switch(ret)
+ {
+ case KEY_EXIT:
+ goto ep_exit;
+
+ case KEY_LEFT:
+ if (dev->iobase > 0)
+ goto ep_iobase;
+ break;
+
+ case KEY_UP:
+ goto ep_flags;
+
+ case KEY_DOWN:
+ if (dev->msize > 0)
+ goto ep_msize;
+ if (dev->drq > 0)
+ goto ep_drq;
+ break;
+
+ case KEY_TAB:
+ goto ep_msize;
+ }
+ goto ep_maddr;
+ }
+ ep_msize:
+ if (dev->msize > 0)
+ {
+ puthelp(" Device memory size (Hexadecimal, 0x1-0x10000)");
+ ret = editval(45,19,5,1,0x1,0x10000,&(dev->msize),(dev->attrib & FLG_FIXMSIZE));
+ switch(ret)
+ {
+ case KEY_EXIT:
+ goto ep_exit;
+
+ case KEY_LEFT:
+ if (dev->irq > 0)
+ goto ep_irq;
+ break;
+
+ case KEY_UP:
+ if (dev->maddr > 0)
+ goto ep_maddr;
+ goto ep_flags;
+
+ case KEY_DOWN:
+ if (dev->drq > 0)
+ goto ep_drq;
+ break;
+
+ case KEY_TAB:
+ goto ep_drq;
+ }
+ goto ep_msize;
+ }
+ ep_drq:
+ if (dev->drq > 0)
+ {
+ puthelp(" Device DMA request number (Decimal, 1-7)");
+ ret = editval(43,20,2,0,1,7,&(dev->drq),(dev->attrib & FLG_FIXDRQ));
+ switch(ret)
+ {
+ case KEY_EXIT:
+ goto ep_exit;
+
+ case KEY_LEFT:
+ goto ep_flags;
+
+ case KEY_UP:
+ if (dev->msize > 0)
+ goto ep_msize;
+ if (dev->maddr > 0)
+ goto ep_maddr;
+ goto ep_flags;
+
+ case KEY_TAB:
+ goto ep_iobase;
+ }
+ goto ep_drq;
+ }
+ }
+ ep_exit:
+ dev->changed = 1; /* mark as changed */
+}
+
+
+/**
+ ** High-level control functions
+ **/
+
+
+/**
+ ** dolist
+ **
+ ** Handle user movement within (*list) in the region starting at (row) onscreen with
+ ** (num) lines, starting at (*ofs) offset from row onscreen.
+ ** Pass (detail) on to drawing routines.
+ **
+ ** If the user hits a key other than a cursor key, maybe return a code.
+ **
+ ** (*list) points to the device at the top line in the region, (*ofs) is the
+ ** position of the highlight within the region. All routines below
+ ** this take only a device and an absolute row : use ofsent() to find the
+ ** device, and add (*ofs) to (row) to find the absolute row.
+ **/
+static int
+dolist(int row, int num, int detail, int *ofs, DEV_LIST **list, char *dhelp)
+{
+ int extended = 0;
+ int c;
+ DEV_LIST *lp;
+ int delta = 1;
+
+ for(;;)
+ {
+ if (delta)
+ {
+ showparams(ofsent(*ofs,*list)); /* show device parameters */
+ drawline(row+*ofs,detail,ofsent(*ofs,*list),1,dhelp); /* highlight current line */
+ delta = 0;
+ }
+
+ c = getchar(); /* get a character */
+ if ((extended == 2) || (c==588) || (c==596)) /* console gives "alternative" codes */
+ {
+ extended = 0; /* no longer */
+ switch(c)
+ {
+ case 588: /* syscons' idea of 'up' */
+ case 'A': /* up */
+ if (*ofs) /* just a move onscreen */
+ {
+ drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp);/* unhighlight current line */
+ (*ofs)--; /* move up */
+ }else{
+ lp = prevent(*list); /* can we go up? */
+ if (!lp) /* no */
+ break;
+ *list = lp; /* yes, move up list */
+ drawlist(row,num,detail,*list);
+ }
+ delta = 1;
+ break;
+
+ case 596: /* dooby-do */
+ case 'B': /* down */
+ lp = ofsent(*ofs,*list); /* get current item */
+ if (!nextent(lp))
+ break; /* nothing more to move to */
+ drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */
+ if (*ofs < (num-1)) /* room to move onscreen? */
+ {
+ (*ofs)++;
+ }else{
+ *list = nextent(*list); /* scroll region down */
+ drawlist(row,num,detail,*list);
+ }
+ delta = 1;
+ break;
+ }
+ }else{
+ switch(c)
+ {
+ case '\033':
+ extended=1;
+ break;
+
+ case '[': /* cheat : always preceeds cursor move */
+ if (extended==1)
+ extended=2;
+ else
+ extended=0;
+ break;
+
+ case 'Q':
+ case 'q':
+ return(KEY_EXIT); /* user requests exit */
+
+ case '\r':
+ case '\n':
+ return(KEY_DO); /* "do" something */
+
+ case '\b':
+ case '\177':
+ case 599:
+ return(KEY_DEL); /* "delete" response */
+
+ case 'X':
+ case 'x':
+ return(KEY_UNZOOM); /* expand everything */
+
+ case 'C':
+ case 'c':
+ return(KEY_ZOOM); /* collapse everything */
+
+ case '\t':
+ drawline(row+*ofs,detail,ofsent(*ofs,*list),0,dhelp); /* unhighlight current line */
+ return(KEY_TAB); /* "move" response */
+
+ case '\014': /* ^L, redraw */
+ return(KEY_REDRAW);
+ }
+ }
+ }
+}
+
+
+/**
+ ** visuserconfig
+ **
+ ** Do the fullscreen config thang
+ **/
+static int
+visuserconfig(void)
+{
+ int actofs = 0, inactofs = 0, mode = 0, ret = -1, i;
+ DEV_LIST *dp;
+
+ initlist(&active);
+ initlist(&inactive);
+ alist = active;
+ ilist = inactive;
+
+ getdevs();
+
+ conflicts = findconflict(active); /* find conflicts in the active list only */
+
+ redraw();
+
+ for(;;)
+ {
+ switch(mode)
+ {
+ case 0: /* active devices */
+ ret = dolist(1,8,1,&actofs,&alist,
+ " [!bEnter!n] Edit device parameters [!bDEL!n] Disable device");
+ switch(ret)
+ {
+ case KEY_TAB:
+ mode = 1; /* swap lists */
+ break;
+
+ case KEY_REDRAW:
+ redraw();
+ break;
+
+ case KEY_ZOOM:
+ alist = active;
+ actofs = 0;
+ expandlist(active);
+ redrawactive();
+ break;
+
+ case KEY_UNZOOM:
+ alist = active;
+ actofs = 0;
+ collapselist(active);
+ redrawactive();
+ break;
+
+ case KEY_DEL:
+ dp = ofsent(actofs,alist); /* get current device */
+ if (dp) /* paranoia... */
+ {
+ if (dp->attrib & FLG_MANDATORY) /* can't be deleted */
+ break;
+ if (dp == alist) /* moving top item on list? */
+ {
+ if (dp->next)
+ {
+ alist = dp->next; /* point list to non-moving item */
+ }else{
+ alist = dp->prev; /* end of list, go back instead */
+ }
+ }else{
+ if (!dp->next) /* moving last item on list? */
+ actofs--;
+ }
+ dp->conflicts = 0; /* no conflicts on the inactive list */
+ movedev(dp,inactive); /* shift to inactive list */
+ conflicts = findconflict(active); /* update conflict tags */
+ dp->changed = 1;
+ redrawactive(); /* redraw */
+ redrawinactive();
+ }
+ break;
+
+ case KEY_DO: /* edit device parameters */
+ dp = ofsent(actofs,alist); /* get current device */
+ if (dp) /* paranoia... */
+ {
+ if (dp->comment == DEV_DEVICE) /* can't edit comments, zoom? */
+ {
+ masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save device parameters");
+ editparams(dp);
+ masterhelp(" [!bTAB!n] Change fields [!bQ!n] Save and Exit");
+ putxy(0,17,lines);
+ conflicts = findconflict(active); /* update conflict tags */
+
+ }else{ /* DO on comment = zoom */
+ switch(dp->comment) /* Depends on current state */
+ {
+ case DEV_COMMENT: /* not currently zoomed */
+ dp->comment = DEV_ZOOMED;
+ break;
+
+ case DEV_ZOOMED:
+ dp->comment = DEV_COMMENT;
+ break;
+ }
+ }
+ redrawactive();
+ }
+ break;
+ }
+ break;
+
+ case 1: /* inactive devices */
+ ret = dolist(10,7,0,&inactofs,&ilist,
+ " [!bEnter!n] Enable device ");
+ switch(ret)
+ {
+ case KEY_TAB:
+ mode = 0;
+ break;
+
+ case KEY_REDRAW:
+ redraw();
+ break;
+
+ case KEY_ZOOM:
+ ilist = inactive;
+ inactofs = 0;
+ expandlist(inactive);
+ redrawinactive();
+ break;
+
+ case KEY_UNZOOM:
+ ilist = inactive;
+ inactofs = 0;
+ collapselist(inactive);
+ redrawinactive();
+ break;
+
+ case KEY_DO:
+ dp = ofsent(inactofs,ilist); /* get current device */
+ if (dp) /* paranoia... */
+ {
+ if (dp->comment == DEV_DEVICE) /* can't move comments, zoom? */
+ {
+ if (dp == ilist) /* moving top of list? */
+ {
+ if (dp->next)
+ {
+ ilist = dp->next; /* point list to non-moving item */
+ }else{
+ ilist = dp->prev; /* can't go down, go up instead */
+ }
+ }else{
+ if (!dp->next) /* last entry on list? */
+ inactofs--; /* shift cursor up one */
+ }
+
+ movedev(dp,active); /* shift to active list */
+ conflicts = findconflict(active); /* update conflict tags */
+ dp->changed = 1;
+ alist = dp; /* put at top and current */
+ actofs = 0;
+ while(dp->comment == DEV_DEVICE)
+ dp = dp->prev; /* forcibly unzoom section */
+ dp ->comment = DEV_COMMENT;
+ mode = 0; /* and swap modes to follow it */
+
+ }else{ /* DO on comment = zoom */
+ switch(dp->comment) /* Depends on current state */
+ {
+ case DEV_COMMENT: /* not currently zoomed */
+ dp->comment = DEV_ZOOMED;
+ break;
+
+ case DEV_ZOOMED:
+ dp->comment = DEV_COMMENT;
+ break;
+ }
+ }
+ redrawactive(); /* redraw */
+ redrawinactive();
+ }
+ break;
+
+ default: /* nothing else relevant here */
+ break;
+ }
+ break;
+ default:
+ mode = 0; /* shouldn't happen... */
+ }
+ if (ret == KEY_EXIT)
+ {
+ i = yesnocancel(" Save these parameters before exiting? (Yes/No/Cancel) ");
+ switch(i)
+ {
+ case 2: /* cancel */
+ redraw();
+ break;
+
+ case 1: /* save and exit */
+ savelist(active,1);
+ savelist(inactive,0);
+
+ case 0: /* exit */
+ nukelist(active); /* clean up after ourselves */
+ nukelist(inactive);
+ normal();
+ clear();
+ return(1);
+ }
+ }
+ }
+}
+
+/*
+ * Copyright (c) 1991 Regents of the University of California.
+ * All rights reserved.
+ * Copyright (c) 1994 Jordan K. Hubbard
+ * All rights reserved.
+ * Copyright (c) 1994 David Greenman
+ * All rights reserved.
+ *
+ * Many additional changes by Bruce Evans
+ *
+ * This code is derived from software contributed by the
+ * University of California Berkeley, Jordan K. Hubbard,
+ * David Greenman and Bruce Evans.
+ *
+ * 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.
+ *
+ * $Id: userconfig.c,v 1.42 1996/04/13 18:33:04 bde Exp $
+ */
+
+#include "scbus.h"
+
+#include <scsi/scsiconf.h>
+
+#define PARM_DEVSPEC 0x1
+#define PARM_INT 0x2
+#define PARM_ADDR 0x3
+
+typedef struct _cmdparm {
+ int type;
+ union {
+#ifdef PC98
+ struct pc98_device *dparm;
+#else
+ struct isa_device *dparm;
+#endif
+ int iparm;
+ void *aparm;
+ } parm;
+} CmdParm;
+
+typedef int (*CmdFunc)(CmdParm *);
+
+typedef struct _cmd {
+ char *name;
+ CmdFunc handler;
+ CmdParm *parms;
+} Cmd;
+
+
+#if NSCBUS > 0
+static void lsscsi(void);
+static int list_scsi(CmdParm *);
+#endif
+
+#ifdef PC98
+static void lsdevtab(struct pc98_device *);
+static struct pc98_device *find_device(char *, int);
+static struct pc98_device *search_devtable(struct pc98_device *, char *, int);
+#else
+static void lsdevtab(struct isa_device *);
+static struct isa_device *find_device(char *, int);
+static struct isa_device *search_devtable(struct isa_device *, char *, int);
+#endif
+static void cngets(char *, int);
+static Cmd *parse_cmd(char *);
+static int parse_args(char *, CmdParm *);
+static unsigned long strtoul(const char *, char **, int);
+#ifdef PC98
+static int save_dev(struct pc98_device *);
+#else
+static int save_dev(struct isa_device *);
+#endif
+
+static int list_devices(CmdParm *);
+static int set_device_ioaddr(CmdParm *);
+static int set_device_irq(CmdParm *);
+static int set_device_drq(CmdParm *);
+static int set_device_iosize(CmdParm *);
+static int set_device_mem(CmdParm *);
+static int set_device_flags(CmdParm *);
+static int set_device_enable(CmdParm *);
+static int set_device_disable(CmdParm *);
+static int quitfunc(CmdParm *);
+static int helpfunc(CmdParm *);
+
+static int lineno;
+
+static CmdParm addr_parms[] = {
+ { PARM_DEVSPEC, {} },
+ { PARM_ADDR, {} },
+ { -1, {} },
+};
+
+static CmdParm int_parms[] = {
+ { PARM_DEVSPEC, {} },
+ { PARM_INT, {} },
+ { -1, {} },
+};
+
+static CmdParm dev_parms[] = {
+ { PARM_DEVSPEC, {} },
+ { -1, {} },
+};
+
+static Cmd CmdList[] = {
+ { "?", helpfunc, NULL }, /* ? (help) */
+ { "di", set_device_disable, dev_parms }, /* disable dev */
+ { "dr", set_device_drq, int_parms }, /* drq dev # */
+ { "en", set_device_enable, dev_parms }, /* enable dev */
+ { "ex", quitfunc, NULL }, /* exit (quit) */
+ { "f", set_device_flags, int_parms }, /* flags dev mask */
+ { "h", helpfunc, NULL }, /* help */
+ { "iom", set_device_mem, addr_parms }, /* iomem dev addr */
+ { "ios", set_device_iosize, int_parms }, /* iosize dev size */
+ { "ir", set_device_irq, int_parms }, /* irq dev # */
+ { "l", list_devices, NULL }, /* ls, list */
+ { "po", set_device_ioaddr, int_parms }, /* port dev addr */
+ { "res", (CmdFunc)cpu_reset, NULL }, /* reset CPU */
+ { "q", quitfunc, NULL }, /* quit */
+#if NSCBUS > 0
+ { "s", list_scsi, NULL }, /* scsi */
+#endif
+ { "v", (CmdFunc)visuserconfig, NULL }, /* visual mode */
+ { NULL, NULL, NULL },
+};
+
+void
+userconfig(void)
+{
+ char input[80];
+ int rval;
+ Cmd *cmd;
+
+#ifdef PC98
+ printf("\nFreeBSD(98) Kernel Configuration Utility - Version 1.0\n"
+ " Type \"help\" for help or \"visual\" to go to the visual\n"
+ " configuration interface.\n");
+#else
+ printf("\nFreeBSD Kernel Configuration Utility - Version 1.0\n"
+ " Type \"help\" for help or \"visual\" to go to the visual\n"
+ " configuration interface (requires MGA/VGA display or\n"
+ " serial terminal capable of displaying ANSI graphics).\n");
+#endif
+
+ while (1) {
+ printf("config> ");
+ cngets(input, 80);
+ if (input[0] == '\0')
+ continue;
+ cmd = parse_cmd(input);
+ if (!cmd) {
+ printf("Invalid command or syntax. Type `?' for help.\n");
+ continue;
+ }
+ rval = (*cmd->handler)(cmd->parms);
+ if (rval)
+ return;
+ }
+}
+
+static Cmd *
+parse_cmd(char *cmd)
+{
+ Cmd *cp;
+
+ for (cp = CmdList; cp->name; cp++) {
+ int len = strlen(cp->name);
+
+ if (!strncmp(cp->name, cmd, len)) {
+ while (*cmd && *cmd != ' ' && *cmd != '\t')
+ ++cmd;
+ if (parse_args(cmd, cp->parms))
+ return NULL;
+ else
+ return cp;
+ }
+ }
+ return NULL;
+}
+
+static int
+parse_args(char *cmd, CmdParm *parms)
+{
+ while (1) {
+ char *ptr;
+
+ if (*cmd == ' ' || *cmd == '\t') {
+ ++cmd;
+ continue;
+ }
+ if (parms == NULL || parms->type == -1) {
+ if (*cmd == '\0')
+ return 0;
+ printf("Extra arg(s): %s\n", cmd);
+ return 1;
+ }
+ if (parms->type == PARM_DEVSPEC) {
+ int i = 0;
+ char devname[64];
+ int unit = 0;
+
+ while (*cmd && !(*cmd == ' ' || *cmd == '\t' ||
+ (*cmd >= '0' && *cmd <= '9')))
+ devname[i++] = *(cmd++);
+ devname[i] = '\0';
+ if (*cmd >= '0' && *cmd <= '9') {
+ unit = strtoul(cmd, &ptr, 10);
+ if (cmd == ptr) {
+ printf("Invalid device number\n");
+ /* XXX should print invalid token here and elsewhere. */
+ return 1;
+ }
+ /* XXX else should require end of token. */
+ cmd = ptr;
+ }
+ if ((parms->parm.dparm = find_device(devname, unit)) == NULL) {
+ printf("No such device: %s%d\n", devname, unit);
+ return 1;
+ }
+ ++parms;
+ continue;
+ }
+ if (parms->type == PARM_INT) {
+ parms->parm.iparm = strtoul(cmd, &ptr, 0);
+ if (cmd == ptr) {
+ printf("Invalid numeric argument\n");
+ return 1;
+ }
+ cmd = ptr;
+ ++parms;
+ continue;
+ }
+ if (parms->type == PARM_ADDR) {
+ parms->parm.aparm = (void *)strtoul(cmd, &ptr, 0);
+ if (cmd == ptr) {
+ printf("Invalid address argument\n");
+ return 1;
+ }
+ cmd = ptr;
+ ++parms;
+ continue;
+ }
+ }
+ return 0;
+}
+
+static int
+list_devices(CmdParm *parms)
+{
+ lineno = 0;
+#ifdef PC98
+ lsdevtab(&pc98_devtab_bio[0]);
+ lsdevtab(&pc98_devtab_tty[0]);
+ lsdevtab(&pc98_devtab_net[0]);
+ lsdevtab(&pc98_devtab_null[0]);
+#else
+ lsdevtab(&isa_devtab_bio[0]);
+ lsdevtab(&isa_devtab_tty[0]);
+ lsdevtab(&isa_devtab_net[0]);
+ lsdevtab(&isa_devtab_null[0]);
+#endif
+ return 0;
+}
+
+static int
+set_device_ioaddr(CmdParm *parms)
+{
+ parms[0].parm.dparm->id_iobase = parms[1].parm.iparm;
+ save_dev(parms[0].parm.dparm);
+ return 0;
+}
+
+static int
+set_device_irq(CmdParm *parms)
+{
+ unsigned irq;
+
+ irq = parms[1].parm.iparm;
+#ifndef PC98
+ if (irq == 2) {
+ printf("Warning: Remapping IRQ 2 to IRQ 9 - see config(8)\n");
+ irq = 9;
+ }
+ else if (irq != -1 && irq > 15) {
+#else
+ if (irq != -1 && irq > 15) {
+#endif
+ printf("An IRQ > 15 would be invalid.\n");
+ return 0;
+ }
+ parms[0].parm.dparm->id_irq = (irq < 16 ? 1 << irq : 0);
+ save_dev(parms[0].parm.dparm);
+ return 0;
+}
+
+static int
+set_device_drq(CmdParm *parms)
+{
+ unsigned drq;
+
+ /*
+ * The bounds checking is just to ensure that the value can be printed
+ * in 5 characters. 32768 gets converted to -32768 and doesn't fit.
+ */
+ drq = parms[1].parm.iparm;
+ parms[0].parm.dparm->id_drq = (drq < 32768 ? drq : -1);
+ save_dev(parms[0].parm.dparm);
+ return 0;
+}
+
+static int
+set_device_iosize(CmdParm *parms)
+{
+ parms[0].parm.dparm->id_msize = parms[1].parm.iparm;
+ save_dev(parms[0].parm.dparm);
+ return 0;
+}
+
+static int
+set_device_mem(CmdParm *parms)
+{
+ parms[0].parm.dparm->id_maddr = parms[1].parm.aparm;
+ save_dev(parms[0].parm.dparm);
+ return 0;
+}
+
+static int
+set_device_flags(CmdParm *parms)
+{
+ parms[0].parm.dparm->id_flags = parms[1].parm.iparm;
+ save_dev(parms[0].parm.dparm);
+ return 0;
+}
+
+static int
+set_device_enable(CmdParm *parms)
+{
+ parms[0].parm.dparm->id_enabled = TRUE;
+ save_dev(parms[0].parm.dparm);
+ return 0;
+}
+
+static int
+set_device_disable(CmdParm *parms)
+{
+ parms[0].parm.dparm->id_enabled = FALSE;
+ save_dev(parms[0].parm.dparm);
+ return 0;
+}
+
+static int
+quitfunc(CmdParm *parms)
+{
+ return 1;
+}
+
+static int
+helpfunc(CmdParm *parms)
+{
+ printf("Command\t\t\tDescription\n");
+ printf("-------\t\t\t-----------\n");
+ printf("ls\t\t\tList currently configured devices\n");
+ printf("port <devname> <addr>\tSet device port (i/o address)\n");
+ printf("irq <devname> <number>\tSet device irq\n");
+ printf("drq <devname> <number>\tSet device drq\n");
+ printf("iomem <devname> <addr>\tSet device maddr (memory address)\n");
+ printf("iosize <devname> <size>\tSet device memory size\n");
+ printf("flags <devname> <mask>\tSet device flags\n");
+ printf("enable <devname>\tEnable device\n");
+ printf("disable <devname>\tDisable device (will not be probed)\n");
+ printf("quit\t\t\tExit this configuration utility\n");
+ printf("reset\t\t\tReset CPU\n");
+ printf("visual\t\t\tGo to fullscreen mode.\n");
+ printf("help\t\t\tThis message\n\n");
+ printf("Commands may be abbreviated to a unique prefix\n");
+ return 0;
+}
+
+static void
+#ifdef PC98
+lsdevtab(struct pc98_device *dt)
+#else
+lsdevtab(struct isa_device *dt)
+#endif
+{
+ for (; dt->id_id != 0; dt++) {
+ int i;
+ char line[80];
+
+ if (lineno >= 23) {
+ printf("<More> ");
+ (void)cngetc();
+ printf("\n");
+ lineno = 0;
+ }
+ if (lineno == 0) {
+ printf(
+"Device port irq drq iomem iosize unit flags enabled\n");
+ ++lineno;
+ }
+ /*
+ * printf() doesn't support %#, %- or even field widths for strings,
+ * so formatting is not straightforward.
+ */
+ bzero(line, sizeof line);
+ sprintf(line, "%s%d", dt->id_driver->name, dt->id_unit);
+ /* Missing: id_id (don't need it). */
+ /* Missing: id_driver (useful if we could show it by name). */
+ sprintf(line + 9, "0x%x", dt->id_iobase);
+ sprintf(line + 20, "%d", ffs(dt->id_irq) - 1);
+ sprintf(line + 26, "%d", dt->id_drq);
+ sprintf(line + 32, "0x%x", dt->id_maddr);
+ sprintf(line + 40, "%d", dt->id_msize);
+ /* Missing: id_msize (0 at start, useful if we can get here later). */
+ /* Missing: id_intr (useful if we could show it by name). */
+ /* Display only: id_unit. */
+ sprintf(line + 49, "%d", dt->id_unit);
+ sprintf(line + 55, "0x%x", dt->id_flags);
+ /* Missing: id_scsiid, id_alive, id_ri_flags, id_reconfig (0 now...) */
+ sprintf(line + 66, "%s", dt->id_enabled ? "Yes" : "No");
+ for (i = 0; i < 66; ++i)
+ if (line[i] == '\0')
+ line[i] = ' ';
+ printf("%s\n", line);
+ ++lineno;
+ }
+}
+
+#ifdef PC98
+static struct pc98_device *
+find_device(char *devname, int unit)
+{
+ struct pc98_device *ret;
+
+ if ((ret = search_devtable(&pc98_devtab_bio[0], devname, unit)) != NULL)
+ return ret;
+ if ((ret = search_devtable(&pc98_devtab_tty[0], devname, unit)) != NULL)
+ return ret;
+ if ((ret = search_devtable(&pc98_devtab_net[0], devname, unit)) != NULL)
+ return ret;
+ if ((ret = search_devtable(&pc98_devtab_null[0], devname, unit)) != NULL)
+ return ret;
+ return NULL;
+}
+#else
+static struct isa_device *
+find_device(char *devname, int unit)
+{
+ struct isa_device *ret;
+
+ if ((ret = search_devtable(&isa_devtab_bio[0], devname, unit)) != NULL)
+ return ret;
+ if ((ret = search_devtable(&isa_devtab_tty[0], devname, unit)) != NULL)
+ return ret;
+ if ((ret = search_devtable(&isa_devtab_net[0], devname, unit)) != NULL)
+ return ret;
+ if ((ret = search_devtable(&isa_devtab_null[0], devname, unit)) != NULL)
+ return ret;
+ return NULL;
+}
+#endif
+
+#ifdef PC98
+static struct pc98_device *
+search_devtable(struct pc98_device *dt, char *devname, int unit)
+#else
+static struct isa_device *
+search_devtable(struct isa_device *dt, char *devname, int unit)
+#endif
+{
+ int i;
+
+ for (i = 0; dt->id_id != 0; dt++)
+ if (!strcmp(dt->id_driver->name, devname) && dt->id_unit == unit)
+ return dt;
+ return NULL;
+}
+
+static void
+cngets(char *input, int maxin)
+{
+ int c, nchars = 0;
+
+ while (1) {
+ c = cngetc();
+ /* Treat ^H or ^? as backspace */
+ if ((c == '\010' || c == '\177')) {
+ if (nchars) {
+ printf("\010 \010");
+ *--input = '\0', --nchars;
+ }
+ continue;
+ }
+ /* Treat ^U or ^X as kill line */
+ else if ((c == '\025' || c == '\030')) {
+ while (nchars) {
+ printf("\010 \010");
+ *--input = '\0', --nchars;
+ }
+ continue;
+ }
+ printf("%c", c);
+ if ((++nchars == maxin) || (c == '\n') || (c == '\r')) {
+ *input = '\0';
+ break;
+ }
+ *input++ = (u_char)c;
+ }
+}
+
+
+/*
+ * Kludges to get the library sources of strtoul.c to work in our
+ * environment. isdigit() and isspace() could be used above too.
+ */
+#define isalpha(c) (((c) >= 'A' && (c) <= 'Z') \
+ || ((c) >= 'a' && (c) <= 'z')) /* unsafe */
+#define isdigit(c) ((unsigned)((c) - '0') <= '9' - '0')
+#define isspace(c) ((c) == ' ' || (c) == '\t') /* unsafe */
+#define isupper(c) ((unsigned)((c) - 'A') <= 'Z' - 'A')
+
+static int errno;
+
+/*
+ * The following should be identical with the library sources for strtoul.c.
+ */
+
+/*
+ * Convert a string to an unsigned long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+static unsigned long
+strtoul(nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ register int base;
+{
+ register const char *s = nptr;
+ register unsigned long acc;
+ register int c;
+ register unsigned long cutoff;
+ register int neg = 0, any, cutlim;
+
+ /*
+ * See strtol for comments as to the logic used.
+ */
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
+ cutlim = (unsigned long)ULONG_MAX % (unsigned long)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = ULONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
+
+#if NSCBUS > 0
+/* scsi: Support for displaying configured SCSI devices.
+ * There is no way to edit them, and this is inconsistent
+ * with the ISA method. This is here as a basis for further work.
+ */
+static char *
+type_text(char *name) /* XXX: This is bogus */
+{
+ if (strcmp(name, "sd") == 0)
+ return "disk";
+
+ if (strcmp(name, "st") == 0)
+ return "tape";
+
+ return "device";
+}
+
+static void
+id_put(char *desc, int id)
+{
+ if (id != SCCONF_UNSPEC)
+ {
+ if (desc)
+ printf("%s", desc);
+
+ if (id == SCCONF_ANY)
+ printf("?");
+ else
+ printf("%d", id);
+ }
+}
+
+static void
+lsscsi(void)
+{
+ int i;
+
+ printf("scsi: (can't be edited):\n");
+
+ for (i = 0; scsi_cinit[i].driver; i++)
+ {
+ id_put("controller scbus", scsi_cinit[i].bus);
+
+ if (scsi_cinit[i].unit != -1)
+ {
+ printf(" at ");
+ id_put(scsi_cinit[i].driver, scsi_cinit[i].unit);
+ }
+
+ printf("\n");
+ }
+
+ for (i = 0; scsi_dinit[i].name; i++)
+ {
+ printf("%s ", type_text(scsi_dinit[i].name));
+
+ id_put(scsi_dinit[i].name, scsi_dinit[i].unit);
+ id_put(" at scbus", scsi_dinit[i].cunit);
+ id_put(" target ", scsi_dinit[i].target);
+ id_put(" lun ", scsi_dinit[i].lun);
+
+ if (scsi_dinit[i].flags)
+ printf("flags 0x%x\n", scsi_dinit[i].flags);
+
+ printf("\n");
+ }
+}
+
+static int
+list_scsi(CmdParm *parms)
+{
+ lineno = 0;
+ lsscsi();
+ return 0;
+}
+#endif
+
+static int
+save_dev(idev)
+#ifdef PC98
+struct pc98_device *idev;
+#else
+struct isa_device *idev;
+#endif
+{
+#ifdef PC98
+ struct pc98_device *id_p,*id_pn;
+#else
+ struct isa_device *id_p,*id_pn;
+#endif
+
+ for (id_p=isa_devlist;
+ id_p;
+ id_p=id_p->id_next) {
+ if (id_p->id_id == idev->id_id) {
+ id_pn = id_p->id_next;
+#ifdef PC98
+ bcopy(idev,id_p,sizeof(struct pc98_device));
+#else
+ bcopy(idev,id_p,sizeof(struct isa_device));
+#endif
+ id_p->id_next = id_pn;
+ return 1;
+ }
+ }
+#ifdef PC98
+ id_pn = malloc(sizeof(struct pc98_device),M_DEVL,M_WAITOK);
+ bcopy(idev,id_pn,sizeof(struct pc98_device));
+#else
+ id_pn = malloc(sizeof(struct isa_device),M_DEVL,M_WAITOK);
+ bcopy(idev,id_pn,sizeof(struct isa_device));
+#endif
+ id_pn->id_next = isa_devlist;
+ isa_devlist = id_pn;
+ return 0;
+}
+
+
diff --git a/sys/pc98/i386/vm_machdep.c b/sys/pc98/i386/vm_machdep.c
new file mode 100644
index 0000000..98610a8
--- /dev/null
+++ b/sys/pc98/i386/vm_machdep.c
@@ -0,0 +1,897 @@
+/*-
+ * Copyright (c) 1982, 1986 The Regents of the University of California.
+ * Copyright (c) 1989, 1990 William Jolitz
+ * Copyright (c) 1994 John Dyson
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department, and 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: @(#)vm_machdep.c 7.3 (Berkeley) 5/13/91
+ * Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
+ * $Id: vm_machdep.c,v 1.63 1996/05/18 03:36:22 dyson Exp $
+ */
+
+#include "npx.h"
+#include "opt_bounce.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/vmmeter.h>
+
+#include <machine/clock.h>
+#include <machine/md_var.h>
+
+#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_page.h>
+#include <vm/vm_map.h>
+#include <vm/vm_extern.h>
+
+#include <sys/user.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#else
+#include <i386/isa/isa.h>
+#endif
+
+#ifdef BOUNCE_BUFFERS
+static vm_offset_t
+ vm_bounce_kva __P((int size, int waitok));
+static void vm_bounce_kva_free __P((vm_offset_t addr, vm_offset_t size,
+ int now));
+static vm_offset_t
+ vm_bounce_page_find __P((int count));
+static void vm_bounce_page_free __P((vm_offset_t pa, int count));
+
+static volatile int kvasfreecnt;
+
+caddr_t bouncememory;
+int bouncepages;
+static int bpwait;
+static vm_offset_t *bouncepa;
+static int bmwait, bmfreeing;
+
+#define BITS_IN_UNSIGNED (8*sizeof(unsigned))
+static int bounceallocarraysize;
+static unsigned *bounceallocarray;
+static int bouncefree;
+
+#if defined(PC98) && defined (EPSON_BOUNCEDMA)
+#define SIXTEENMEG (3840*4096) /* 15MB boundary */
+#else
+#define SIXTEENMEG (4096*4096)
+#endif
+#define MAXBKVA 1024
+int maxbkva = MAXBKVA*PAGE_SIZE;
+
+/* special list that can be used at interrupt time for eventual kva free */
+static struct kvasfree {
+ vm_offset_t addr;
+ vm_offset_t size;
+} kvaf[MAXBKVA];
+
+/*
+ * get bounce buffer pages (count physically contiguous)
+ * (only 1 inplemented now)
+ */
+static vm_offset_t
+vm_bounce_page_find(count)
+ int count;
+{
+ int bit;
+ int s,i;
+
+ if (count != 1)
+ panic("vm_bounce_page_find -- no support for > 1 page yet!!!");
+
+ s = splbio();
+retry:
+ for (i = 0; i < bounceallocarraysize; i++) {
+ if (bounceallocarray[i] != 0xffffffff) {
+ bit = ffs(~bounceallocarray[i]);
+ if (bit) {
+ bounceallocarray[i] |= 1 << (bit - 1) ;
+ bouncefree -= count;
+ splx(s);
+ return bouncepa[(i * BITS_IN_UNSIGNED + (bit - 1))];
+ }
+ }
+ }
+ bpwait = 1;
+ tsleep((caddr_t) &bounceallocarray, PRIBIO, "bncwai", 0);
+ goto retry;
+}
+
+static void
+vm_bounce_kva_free(addr, size, now)
+ vm_offset_t addr;
+ vm_offset_t size;
+ int now;
+{
+ int s = splbio();
+ kvaf[kvasfreecnt].addr = addr;
+ kvaf[kvasfreecnt].size = size;
+ ++kvasfreecnt;
+ if( now) {
+ /*
+ * this will do wakeups
+ */
+ vm_bounce_kva(0,0);
+ } else {
+ if (bmwait) {
+ /*
+ * if anyone is waiting on the bounce-map, then wakeup
+ */
+ wakeup((caddr_t) io_map);
+ bmwait = 0;
+ }
+ }
+ splx(s);
+}
+
+/*
+ * free count bounce buffer pages
+ */
+static void
+vm_bounce_page_free(pa, count)
+ vm_offset_t pa;
+ int count;
+{
+ int allocindex;
+ int index;
+ int bit;
+
+ if (count != 1)
+ panic("vm_bounce_page_free -- no support for > 1 page yet!!!");
+
+ for(index=0;index<bouncepages;index++) {
+ if( pa == bouncepa[index])
+ break;
+ }
+
+ if( index == bouncepages)
+ panic("vm_bounce_page_free: invalid bounce buffer");
+
+ allocindex = index / BITS_IN_UNSIGNED;
+ bit = index % BITS_IN_UNSIGNED;
+
+ bounceallocarray[allocindex] &= ~(1 << bit);
+
+ bouncefree += count;
+ if (bpwait) {
+ bpwait = 0;
+ wakeup((caddr_t) &bounceallocarray);
+ }
+}
+
+/*
+ * allocate count bounce buffer kva pages
+ */
+static vm_offset_t
+vm_bounce_kva(size, waitok)
+ int size;
+ int waitok;
+{
+ int i;
+ vm_offset_t kva = 0;
+ vm_offset_t off;
+ int s = splbio();
+more:
+ if (!bmfreeing && kvasfreecnt) {
+ bmfreeing = 1;
+ for (i = 0; i < kvasfreecnt; i++) {
+ for(off=0;off<kvaf[i].size;off+=PAGE_SIZE) {
+ pmap_kremove( kvaf[i].addr + off);
+ }
+ kmem_free_wakeup(io_map, kvaf[i].addr,
+ kvaf[i].size);
+ }
+ kvasfreecnt = 0;
+ bmfreeing = 0;
+ if( bmwait) {
+ bmwait = 0;
+ wakeup( (caddr_t) io_map);
+ }
+ }
+
+ if( size == 0) {
+ splx(s);
+ return NULL;
+ }
+
+ if ((kva = kmem_alloc_pageable(io_map, size)) == 0) {
+ if( !waitok) {
+ splx(s);
+ return NULL;
+ }
+ bmwait = 1;
+ tsleep((caddr_t) io_map, PRIBIO, "bmwait", 0);
+ goto more;
+ }
+ splx(s);
+ return kva;
+}
+
+/*
+ * same as vm_bounce_kva -- but really allocate (but takes pages as arg)
+ */
+vm_offset_t
+vm_bounce_kva_alloc(count)
+int count;
+{
+ int i;
+ vm_offset_t kva;
+ vm_offset_t pa;
+ if( bouncepages == 0) {
+ kva = (vm_offset_t) malloc(count*PAGE_SIZE, M_TEMP, M_WAITOK);
+ return kva;
+ }
+ kva = vm_bounce_kva(count*PAGE_SIZE, 1);
+ for(i=0;i<count;i++) {
+ pa = vm_bounce_page_find(1);
+ pmap_kenter(kva + i * PAGE_SIZE, pa);
+ }
+ return kva;
+}
+
+/*
+ * same as vm_bounce_kva_free -- but really free
+ */
+void
+vm_bounce_kva_alloc_free(kva, count)
+ vm_offset_t kva;
+ int count;
+{
+ int i;
+ vm_offset_t pa;
+ if( bouncepages == 0) {
+ free((caddr_t) kva, M_TEMP);
+ return;
+ }
+ for(i = 0; i < count; i++) {
+ pa = pmap_kextract(kva + i * PAGE_SIZE);
+ vm_bounce_page_free(pa, 1);
+ }
+ vm_bounce_kva_free(kva, count*PAGE_SIZE, 0);
+}
+
+/*
+ * do the things necessary to the struct buf to implement
+ * bounce buffers... inserted before the disk sort
+ */
+void
+vm_bounce_alloc(bp)
+ struct buf *bp;
+{
+ int countvmpg;
+ vm_offset_t vastart, vaend;
+ vm_offset_t vapstart, vapend;
+ vm_offset_t va, kva;
+ vm_offset_t pa;
+ int dobounceflag = 0;
+ int i;
+
+ if (bouncepages == 0)
+ return;
+
+ if (bp->b_flags & B_BOUNCE) {
+ printf("vm_bounce_alloc: called recursively???\n");
+ return;
+ }
+
+ if (bp->b_bufsize < bp->b_bcount) {
+ printf(
+ "vm_bounce_alloc: b_bufsize(0x%lx) < b_bcount(0x%lx) !!\n",
+ bp->b_bufsize, bp->b_bcount);
+ panic("vm_bounce_alloc");
+ }
+
+/*
+ * This is not really necessary
+ * if( bp->b_bufsize != bp->b_bcount) {
+ * printf("size: %d, count: %d\n", bp->b_bufsize, bp->b_bcount);
+ * }
+ */
+
+
+ vastart = (vm_offset_t) bp->b_data;
+ vaend = (vm_offset_t) bp->b_data + bp->b_bufsize;
+
+ vapstart = trunc_page(vastart);
+ vapend = round_page(vaend);
+ countvmpg = (vapend - vapstart) / PAGE_SIZE;
+
+/*
+ * if any page is above 16MB, then go into bounce-buffer mode
+ */
+ va = vapstart;
+ for (i = 0; i < countvmpg; i++) {
+ pa = pmap_kextract(va);
+ if (pa >= SIXTEENMEG)
+ ++dobounceflag;
+ if( pa == 0)
+ panic("vm_bounce_alloc: Unmapped page");
+ va += PAGE_SIZE;
+ }
+ if (dobounceflag == 0)
+ return;
+
+ if (bouncepages < dobounceflag)
+ panic("Not enough bounce buffers!!!");
+
+/*
+ * allocate a replacement kva for b_addr
+ */
+ kva = vm_bounce_kva(countvmpg*PAGE_SIZE, 1);
+#if 0
+ printf("%s: vapstart: %x, vapend: %x, countvmpg: %d, kva: %x ",
+ (bp->b_flags & B_READ) ? "read":"write",
+ vapstart, vapend, countvmpg, kva);
+#endif
+ va = vapstart;
+ for (i = 0; i < countvmpg; i++) {
+ pa = pmap_kextract(va);
+ if (pa >= SIXTEENMEG) {
+ /*
+ * allocate a replacement page
+ */
+ vm_offset_t bpa = vm_bounce_page_find(1);
+ pmap_kenter(kva + (PAGE_SIZE * i), bpa);
+#if 0
+ printf("r(%d): (%x,%x,%x) ", i, va, pa, bpa);
+#endif
+ /*
+ * if we are writing, the copy the data into the page
+ */
+ if ((bp->b_flags & B_READ) == 0) {
+ bcopy((caddr_t) va, (caddr_t) kva + (PAGE_SIZE * i), PAGE_SIZE);
+ }
+ } else {
+ /*
+ * use original page
+ */
+ pmap_kenter(kva + (PAGE_SIZE * i), pa);
+ }
+ va += PAGE_SIZE;
+ }
+
+/*
+ * flag the buffer as being bounced
+ */
+ bp->b_flags |= B_BOUNCE;
+/*
+ * save the original buffer kva
+ */
+ bp->b_savekva = bp->b_data;
+/*
+ * put our new kva into the buffer (offset by original offset)
+ */
+ bp->b_data = (caddr_t) (((vm_offset_t) kva) |
+ ((vm_offset_t) bp->b_savekva & PAGE_MASK));
+#if 0
+ printf("b_savekva: %x, newva: %x\n", bp->b_savekva, bp->b_data);
+#endif
+ return;
+}
+
+/*
+ * hook into biodone to free bounce buffer
+ */
+void
+vm_bounce_free(bp)
+ struct buf *bp;
+{
+ int i;
+ vm_offset_t origkva, bouncekva, bouncekvaend;
+
+/*
+ * if this isn't a bounced buffer, then just return
+ */
+ if ((bp->b_flags & B_BOUNCE) == 0)
+ return;
+
+/*
+ * This check is not necessary
+ * if (bp->b_bufsize != bp->b_bcount) {
+ * printf("vm_bounce_free: b_bufsize=%d, b_bcount=%d\n",
+ * bp->b_bufsize, bp->b_bcount);
+ * }
+ */
+
+ origkva = (vm_offset_t) bp->b_savekva;
+ bouncekva = (vm_offset_t) bp->b_data;
+/*
+ printf("free: %d ", bp->b_bufsize);
+*/
+
+/*
+ * check every page in the kva space for b_addr
+ */
+ for (i = 0; i < bp->b_bufsize; ) {
+ vm_offset_t mybouncepa;
+ vm_offset_t copycount;
+
+ copycount = round_page(bouncekva + 1) - bouncekva;
+ mybouncepa = pmap_kextract(trunc_page(bouncekva));
+
+/*
+ * if this is a bounced pa, then process as one
+ */
+ if ( mybouncepa != pmap_kextract( trunc_page( origkva))) {
+ vm_offset_t tocopy = copycount;
+ if (i + tocopy > bp->b_bufsize)
+ tocopy = bp->b_bufsize - i;
+/*
+ * if this is a read, then copy from bounce buffer into original buffer
+ */
+ if (bp->b_flags & B_READ)
+ bcopy((caddr_t) bouncekva, (caddr_t) origkva, tocopy);
+/*
+ * free the bounce allocation
+ */
+
+/*
+ printf("(kva: %x, pa: %x)", bouncekva, mybouncepa);
+*/
+ vm_bounce_page_free(mybouncepa, 1);
+ }
+
+ origkva += copycount;
+ bouncekva += copycount;
+ i += copycount;
+ }
+
+/*
+ printf("\n");
+*/
+/*
+ * add the old kva into the "to free" list
+ */
+
+ bouncekva= trunc_page((vm_offset_t) bp->b_data);
+ bouncekvaend= round_page((vm_offset_t)bp->b_data + bp->b_bufsize);
+
+/*
+ printf("freeva: %d\n", (bouncekvaend - bouncekva) / PAGE_SIZE);
+*/
+ vm_bounce_kva_free( bouncekva, (bouncekvaend - bouncekva), 0);
+ bp->b_data = bp->b_savekva;
+ bp->b_savekva = 0;
+ bp->b_flags &= ~B_BOUNCE;
+
+ return;
+}
+
+
+/*
+ * init the bounce buffer system
+ */
+void
+vm_bounce_init()
+{
+ int i;
+
+ kvasfreecnt = 0;
+
+ if (bouncepages == 0)
+ return;
+
+ bounceallocarraysize = (bouncepages + BITS_IN_UNSIGNED - 1) / BITS_IN_UNSIGNED;
+ bounceallocarray = malloc(bounceallocarraysize * sizeof(unsigned), M_TEMP, M_NOWAIT);
+
+ if (!bounceallocarray)
+ panic("Cannot allocate bounce resource array");
+
+ bouncepa = malloc(bouncepages * sizeof(vm_offset_t), M_TEMP, M_NOWAIT);
+ if (!bouncepa)
+ panic("Cannot allocate physical memory array");
+
+ for(i=0;i<bounceallocarraysize;i++) {
+ bounceallocarray[i] = 0xffffffff;
+ }
+
+ for(i=0;i<bouncepages;i++) {
+ vm_offset_t pa;
+ if( (pa = pmap_kextract((vm_offset_t) bouncememory + i * PAGE_SIZE)) >= SIXTEENMEG)
+ panic("bounce memory out of range");
+ if( pa == 0)
+ panic("bounce memory not resident");
+ bouncepa[i] = pa;
+ bounceallocarray[i/(8*sizeof(int))] &= ~(1<<(i%(8*sizeof(int))));
+ }
+ bouncefree = bouncepages;
+
+}
+#endif /* BOUNCE_BUFFERS */
+
+/*
+ * quick version of vm_fault
+ */
+void
+vm_fault_quick(v, prot)
+ caddr_t v;
+ int prot;
+{
+ if (prot & VM_PROT_WRITE)
+ subyte(v, fubyte(v));
+ else
+ fubyte(v);
+}
+
+/*
+ * Finish a fork operation, with process p2 nearly set up.
+ * Copy and update the kernel stack and pcb, making the child
+ * ready to run, and marking it so that it can return differently
+ * than the parent. Returns 1 in the child process, 0 in the parent.
+ * We currently double-map the user area so that the stack is at the same
+ * address in each process; in the future we will probably relocate
+ * the frame pointers on the stack after copying.
+ */
+int
+cpu_fork(p1, p2)
+ register struct proc *p1, *p2;
+{
+ struct pcb *pcb2 = &p2->p_addr->u_pcb;
+ int sp, offset;
+ volatile int retval;
+
+ /*
+ * Copy pcb and stack from proc p1 to p2.
+ * We do this as cheaply as possible, copying only the active
+ * part of the stack. The stack and pcb need to agree;
+ * this is tricky, as the final pcb is constructed by savectx,
+ * but its frame isn't yet on the stack when the stack is copied.
+ * This should be done differently, with a single call
+ * that copies and updates the pcb+stack,
+ * replacing the bcopy and savectx.
+ */
+
+ __asm __volatile("movl %%esp,%0" : "=r" (sp));
+ offset = sp - (int)kstack;
+
+ retval = 1; /* return 1 in child */
+ bcopy((caddr_t)kstack + offset, (caddr_t)p2->p_addr + offset,
+ (unsigned) ctob(UPAGES) - offset);
+ p2->p_md.md_regs = p1->p_md.md_regs;
+
+ *pcb2 = p1->p_addr->u_pcb;
+ pcb2->pcb_cr3 = vtophys(p2->p_vmspace->vm_pmap.pm_pdir);
+
+ retval = 0; /* return 0 in parent */
+ savectx(pcb2);
+ return (retval);
+}
+
+void
+cpu_exit(p)
+ register struct proc *p;
+{
+#ifdef USER_LDT
+ struct pcb *pcb;
+#endif
+
+#if NNPX > 0
+ npxexit(p);
+#endif /* NNPX */
+#ifdef USER_LDT
+ pcb = &p->p_addr->u_pcb;
+ if (pcb->pcb_ldt != 0) {
+ 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
+ cnt.v_swtch++;
+ cpu_switch(p);
+ panic("cpu_exit");
+}
+
+void
+cpu_wait(p)
+ struct proc *p;
+{
+ /* drop per-process resources */
+ pmap_qremove((vm_offset_t) p->p_addr, UPAGES);
+ kmem_free(u_map, (vm_offset_t)p->p_addr, ctob(UPAGES));
+ vmspace_free(p->p_vmspace);
+}
+
+/*
+ * Dump the machine specific header information at the start of a core dump.
+ */
+int
+cpu_coredump(p, vp, cred)
+ struct proc *p;
+ struct vnode *vp;
+ struct ucred *cred;
+{
+
+ return (vn_rdwr(UIO_WRITE, vp, (caddr_t) p->p_addr, ctob(UPAGES),
+ (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, (int *)NULL,
+ p));
+}
+
+#ifdef notyet
+static void
+setredzone(pte, vaddr)
+ u_short *pte;
+ caddr_t vaddr;
+{
+/* eventually do this by setting up an expand-down stack segment
+ for ss0: selector, allowing stack access down to top of u.
+ this means though that protection violations need to be handled
+ thru a double fault exception that must do an integral task
+ switch to a known good context, within which a dump can be
+ taken. a sensible scheme might be to save the initial context
+ used by sched (that has physical memory mapped 1:1 at bottom)
+ and take the dump while still in mapped mode */
+}
+#endif
+
+/*
+ * Convert kernel VA to physical address
+ */
+u_long
+kvtop(void *addr)
+{
+ vm_offset_t va;
+
+ va = pmap_kextract((vm_offset_t)addr);
+ if (va == 0)
+ panic("kvtop: zero page frame");
+ return((int)va);
+}
+
+/*
+ * Map an IO request into kernel virtual address space.
+ *
+ * All requests are (re)mapped into kernel VA space.
+ * Notice that we use b_bufsize for the size of the buffer
+ * to be mapped. b_bcount might be modified by the driver.
+ */
+void
+vmapbuf(bp)
+ register struct buf *bp;
+{
+ register int npf;
+ register caddr_t addr;
+ int off;
+ vm_offset_t kva;
+ vm_offset_t pa;
+
+ if ((bp->b_flags & B_PHYS) == 0)
+ panic("vmapbuf");
+
+ /*
+ * this is the kva that is to be used for
+ * the temporary kernel mapping
+ */
+ kva = (vm_offset_t) bp->b_saveaddr;
+
+ for (addr = (caddr_t)trunc_page(bp->b_data);
+ addr < bp->b_data + bp->b_bufsize;
+ addr += PAGE_SIZE) {
+
+/*
+ * do the vm_fault if needed, do the copy-on-write thing when
+ * reading stuff off device into memory.
+ */
+ vm_fault_quick(addr,
+ (bp->b_flags&B_READ)?(VM_PROT_READ|VM_PROT_WRITE):VM_PROT_READ);
+ pa = pmap_kextract((vm_offset_t) addr);
+ if (pa == 0)
+ panic("vmapbuf: page not present");
+/*
+ * hold the data page
+ */
+#ifdef DIAGNOSTIC
+ if( VM_PAGE_TO_PHYS(PHYS_TO_VM_PAGE(pa)) != pa)
+ panic("vmapbuf: confused PHYS_TO_VM_PAGE mapping");
+#endif
+ vm_page_hold(PHYS_TO_VM_PAGE(pa));
+ }
+
+ addr = bp->b_saveaddr = bp->b_data;
+ off = (int)addr & PAGE_MASK;
+ npf = btoc(round_page(bp->b_bufsize + off));
+ bp->b_data = (caddr_t) (kva + off);
+ while (npf--) {
+ pa = pmap_kextract((vm_offset_t)addr);
+ if (pa == 0)
+ panic("vmapbuf: null page frame");
+ pmap_kenter(kva, trunc_page(pa));
+ addr += PAGE_SIZE;
+ kva += PAGE_SIZE;
+ }
+}
+
+/*
+ * Free the io map PTEs associated with this IO operation.
+ * We also invalidate the TLB entries and restore the original b_addr.
+ */
+void
+vunmapbuf(bp)
+ register struct buf *bp;
+{
+ register caddr_t addr;
+ vm_offset_t pa;
+
+ if ((bp->b_flags & B_PHYS) == 0)
+ panic("vunmapbuf");
+
+ for (addr = (caddr_t)trunc_page((vm_offset_t) bp->b_data);
+ addr < bp->b_data + bp->b_bufsize;
+ addr += PAGE_SIZE)
+ pmap_kremove((vm_offset_t) addr);
+
+ bp->b_data = bp->b_saveaddr;
+ bp->b_saveaddr = NULL;
+
+/*
+ * unhold the pde, and data pages
+ */
+ for (addr = (caddr_t)trunc_page((vm_offset_t) bp->b_data);
+ addr < bp->b_data + bp->b_bufsize;
+ addr += PAGE_SIZE) {
+ /*
+ * release the data page
+ */
+ pa = pmap_kextract((vm_offset_t) addr);
+ vm_page_unhold(PHYS_TO_VM_PAGE(pa));
+ }
+}
+
+/*
+ * Force reset the processor by invalidating the entire address space!
+ */
+void
+cpu_reset() {
+#ifdef PC98
+ asm(" cli ");
+ outb(0x37, 0x0f); /* SHUT 0 = 0 */
+ outb(0x37, 0x0b); /* SHUT 1 = 0 */
+ if ((pc98_machine_type & M_EPSON_PC98)
+ && (epson_machine_id == 0x20 /*note A*/)) {
+ epson_outb(0xc17, epson_inb(0xc17) | 0x40);
+ /* reset port for NOTE_A */
+ }
+ outb(0xf0, 0x00); /* reset port */
+#else /* IBM-PC */
+
+ /*
+ * Attempt to do a CPU reset via the keyboard controller,
+ * do not turn of the GateA20, as any machine that fails
+ * to do the reset here would then end up in no man's land.
+ */
+
+#ifndef BROKEN_KEYBOARD_RESET
+ outb(IO_KBD + 4, 0xFE);
+ DELAY(500000); /* wait 0.5 sec to see if that did it */
+ printf("Keyboard reset did not work, attempting CPU shutdown\n");
+ DELAY(1000000); /* wait 1 sec for printf to complete */
+#endif
+
+ /* force a shutdown by unmapping entire address space ! */
+ bzero((caddr_t) PTD, PAGE_SIZE);
+
+ /* "good night, sweet prince .... <THUNK!>" */
+ pmap_update();
+#endif
+ /* NOTREACHED */
+ while(1);
+}
+
+/*
+ * Grow the user stack to allow for 'sp'. This version grows the stack in
+ * chunks of SGROWSIZ.
+ */
+int
+grow(p, sp)
+ struct proc *p;
+ u_int sp;
+{
+ unsigned int nss;
+ caddr_t v;
+ struct vmspace *vm = p->p_vmspace;
+
+ if ((caddr_t)sp <= vm->vm_maxsaddr || (unsigned)sp >= (unsigned)USRSTACK)
+ return (1);
+
+ nss = roundup(USRSTACK - (unsigned)sp, PAGE_SIZE);
+
+ if (nss > p->p_rlimit[RLIMIT_STACK].rlim_cur)
+ return (0);
+
+ if (vm->vm_ssize && roundup(vm->vm_ssize << PAGE_SHIFT,
+ SGROWSIZ) < nss) {
+ int grow_amount;
+ /*
+ * If necessary, grow the VM that the stack occupies
+ * to allow for the rlimit. This allows us to not have
+ * to allocate all of the VM up-front in execve (which
+ * is expensive).
+ * Grow the VM by the amount requested rounded up to
+ * the nearest SGROWSIZ to provide for some hysteresis.
+ */
+ grow_amount = roundup((nss - (vm->vm_ssize << PAGE_SHIFT)), SGROWSIZ);
+ v = (char *)USRSTACK - roundup(vm->vm_ssize << PAGE_SHIFT,
+ SGROWSIZ) - grow_amount;
+ /*
+ * If there isn't enough room to extend by SGROWSIZ, then
+ * just extend to the maximum size
+ */
+ if (v < vm->vm_maxsaddr) {
+ v = vm->vm_maxsaddr;
+ grow_amount = MAXSSIZ - (vm->vm_ssize << PAGE_SHIFT);
+ }
+ if ((grow_amount == 0) || (vm_map_find(&vm->vm_map, NULL, 0, (vm_offset_t *)&v,
+ grow_amount, FALSE, VM_PROT_ALL, VM_PROT_ALL, 0) != KERN_SUCCESS)) {
+ return (0);
+ }
+ vm->vm_ssize += grow_amount >> PAGE_SHIFT;
+ }
+
+ return (1);
+}
+
+/*
+ * prototype routine to implement the pre-zeroed page mechanism
+ * this routine is called from the idle loop.
+ */
+int
+vm_page_zero_idle() {
+ vm_page_t m;
+ if ((cnt.v_free_count > cnt.v_interrupt_free_min) &&
+ (m = TAILQ_FIRST(&vm_page_queue_free))) {
+ TAILQ_REMOVE(&vm_page_queue_free, m, pageq);
+ enable_intr();
+ pmap_zero_page(VM_PAGE_TO_PHYS(m));
+ disable_intr();
+ TAILQ_INSERT_HEAD(&vm_page_queue_zero, m, pageq);
+ m->queue = PQ_ZERO;
+ ++vm_page_zero_count;
+ return 1;
+ }
+ return 0;
+}
diff --git a/sys/pc98/pc98/30line.h b/sys/pc98/pc98/30line.h
new file mode 100644
index 0000000..a6b7b7c
--- /dev/null
+++ b/sys/pc98/pc98/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/pc98/aic6360.c b/sys/pc98/pc98/aic6360.c
new file mode 100644
index 0000000..b12a16c
--- /dev/null
+++ b/sys/pc98/pc98/aic6360.c
@@ -0,0 +1,2518 @@
+/*
+ * Copyright (c) 1994 Charles Hannum.
+ * Copyright (c) 1994 Jarle Greipsland
+ * 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 Jarle Greipsland
+ * 4. 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.
+ */
+
+/*
+ * $Id: aic6360.c,v 1.21 1996/05/02 10:43:08 phk Exp $
+ *
+ * Acknowledgements: Many of the algorithms used in this driver are
+ * inspired by the work of Julian Elischer (julian@tfs.com) and
+ * Charles Hannum (mycroft@duality.gnu.ai.mit.edu). Thanks a million!
+ *
+ * Converted from NetBSD to FreeBSD by Jim Babb
+ */
+
+/* TODO list:
+ * 1) Get the DMA stuff working.
+ * 2) Get the iov/uio stuff working. Is this a good thing ???
+ * 3) Get the synch stuff working.
+ * 4) Rewrite it to use malloc for the acb structs instead of static alloc.?
+ */
+
+/*
+ * PC-9801-100/AHA-1030P support by URATA S.
+ */
+
+/*
+ * A few customizable items:
+ */
+
+/* The SCSI ID of the host adapter/computer */
+#ifndef AIC_SCSI_HOSTID
+#define AIC_SCSI_HOSTID 7
+#endif
+
+/* Use doubleword transfers to/from SCSI chip. Note: This requires
+ * motherboard support. Basicly, some motherboard chipsets are able to
+ * split a 32 bit I/O operation into two 16 bit I/O operations,
+ * transparently to the processor. This speeds up some things, notably long
+ * data transfers.
+ */
+#define AIC_USE_DWORDS 0
+
+/* Allow disconnects? Was mainly used in an early phase of the driver when
+ * the message system was very flaky. Should go away soon.
+ */
+#define AIC_ALLOW_DISCONNECT 1
+
+/* Synchronous data transfers? (does not work yet!) XXX */
+#define AIC_USE_SYNCHRONOUS 0 /* Enable/disable (1/0) */
+#define AIC_SYNC_PERIOD 200
+#define AIC_SYNC_REQ_ACK_OFS 8
+
+/* Max attempts made to transmit a message */
+#define AIC_MSG_MAX_ATTEMPT 3 /* Not used now XXX */
+
+/* Use DMA (else we do programmed I/O using string instructions) (not yet!)*/
+#define AIC_USE_EISA_DMA 0
+#define AIC_USE_ISA_DMA 0
+
+/* How to behave on the (E)ISA bus when/if DMAing (on<<4) + off in us */
+#define EISA_BRST_TIM ((15<<4) + 1) /* 15us on, 1us off */
+
+/* Some spin loop parameters (essentially how long to wait some places)
+ * The problem(?) is that sometimes we expect either to be able to transmit a
+ * byte or to get a new one from the SCSI bus pretty soon. In order to avoid
+ * returning from the interrupt just to get yanked back for the next byte we
+ * may spin in the interrupt routine waiting for this byte to come. How long?
+ * This is really (SCSI) device and processor dependent. Tuneable, I guess.
+ */
+#define AIC_MSGI_SPIN 1 /* Will spinwait upto ?ms for a new msg byte */
+#define AIC_MSGO_SPIN 1
+
+/* Include debug functions? At the end of this file there are a bunch of
+ * functions that will print out various information regarding queued SCSI
+ * commands, driver state and chip contents. You can call them from the
+ * kernel debugger. If you set AIC_DEBUG to 0 they are not included (the
+ * kernel uses less memory) but you lose the debugging facilities.
+ */
+#define AIC_DEBUG 0
+
+/* End of customizable parameters */
+
+#if AIC_USE_EISA_DMA || AIC_USE_ISA_DMA
+#error "I said not yet! Start paying attention... grumble"
+#endif
+
+#include "opt_ddb.h"
+#include <aic.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#include <sys/devconf.h>
+#include <machine/clock.h>
+#ifdef PC98
+#include <pc98/pc98/pc98_device.h>
+#else
+#include <i386/isa/isa_device.h>
+#endif
+
+#include <sys/kernel.h>
+
+/* Definitions, most of them has turned out to be unneccesary, but here they
+ * are anyway.
+ */
+
+/*
+ * Generic SCSI messages. For now we reject most of them.
+ */
+/* Messages (1 byte) */ /* I/T M(andatory) or (O)ptional */
+#define MSG_CMDCOMPLETE 0x00 /* M/M */
+#define MSG_EXTENDED 0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER 0x02 /* O/O */
+#define MSG_RESTOREPOINTERS 0x03 /* O/O */
+#define MSG_DISCONNECT 0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR 0x05 /* M/M */
+#define MSG_ABORT 0x06 /* O/M */
+#define MSG_MESSAGE_REJECT 0x07 /* M/M */
+#define MSG_NOOP 0x08 /* M/M */
+#define MSG_PARITY_ERR 0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE 0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF 0x0b /* O/O */
+#define MSG_BUS_DEV_RESET 0x0c /* O/M */
+#define MSG_ABORT_TAG 0x0d /* O/O */
+#define MSG_CLEAR_QUEUE 0x0e /* O/O */
+#define MSG_INIT_RECOVERY 0x0f /* O/O */
+#define MSG_REL_RECOVERY 0x10 /* O/O */
+#define MSG_TERM_IO_PROC 0x11 /* O/O */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */
+#define MSG_HEAD_OF_Q_TAG 0x21 /* O/O */
+#define MSG_ORDERED_Q_TAG 0x22 /* O/O */
+#define MSG_IGN_WIDE_RESIDUE 0x23 /* O/O */
+
+/* Identify message */
+#define MSG_IDENTIFY(lun) ((AIC_ALLOW_DISCONNECT ? 0xc0 : 0x80)|((lun) & 0x7))
+#define MSG_ISIDENT(m) ((m) & 0x80)
+
+/* Extended messages (opcode) */
+#define MSG_EXT_SDTR 0x01
+
+/* SCSI Status codes */
+#define ST_GOOD 0x00
+#define ST_CHKCOND 0x02
+#define ST_CONDMET 0x04
+#define ST_BUSY 0x08
+#define ST_INTERMED 0x10
+#define ST_INTERMED_CONDMET 0x14
+#define ST_RESERVATION_CONFLICT 0x18
+#define ST_CMD_TERM 0x22
+#define ST_QUEUE_FULL 0x28
+
+#define ST_MASK 0x3e /* bit 0,6,7 is reserved */
+
+/* AIC6360 definitions */
+#ifdef PC98
+#define SCSISEQ (iobase + 0x00) /* SCSI sequence control */
+#define SXFRCTL0 (iobase + 0x02) /* SCSI transfer control 0 */
+#define SXFRCTL1 (iobase + 0x04) /* SCSI transfer control 1 */
+#define SCSISIGI (iobase + 0x06) /* SCSI signal in */
+#define SCSISIGO (iobase + 0x06) /* SCSI signal out */
+#define SCSIRATE (iobase + 0x08) /* SCSI rate control */
+#define SCSIID (iobase + 0x0a) /* SCSI ID */
+#define SELID (iobase + 0x0a) /* Selection/Reselection ID */
+#define SCSIDAT (iobase + 0x0c) /* SCSI Latched Data */
+#define SCSIBUS (iobase + 0x0e) /* SCSI Data Bus*/
+#define STCNT0 (iobase + 0x10) /* SCSI transfer count */
+#define STCNT1 (iobase + 0x12)
+#define STCNT2 (iobase + 0x14)
+#define CLRSINT0 (iobase + 0x16) /* Clear SCSI interrupts 0 */
+#define SSTAT0 (iobase + 0x16) /* SCSI interrupt status 0 */
+#define CLRSINT1 (iobase + 0x18) /* Clear SCSI interrupts 1 */
+#define SSTAT1 (iobase + 0x18) /* SCSI status 1 */
+#define SSTAT2 (iobase + 0x1a) /* SCSI status 2 */
+#define SCSITEST (iobase + 0x1c) /* SCSI test control */
+#define SSTAT3 (iobase + 0x1c) /* SCSI status 3 */
+#define CLRSERR (iobase + 0x1e) /* Clear SCSI errors */
+#define SSTAT4 (iobase + 0x1e) /* SCSI status 4 */
+#define SIMODE0 (iobase + 0x20) /* SCSI interrupt mode 0 */
+#define SIMODE1 (iobase + 0x22) /* SCSI interrupt mode 1 */
+#define DMACNTRL0 (iobase + 0x24) /* DMA control 0 */
+#define DMACNTRL1 (iobase + 0x26) /* DMA control 1 */
+#define DMASTAT (iobase + 0x28) /* DMA status */
+#define FIFOSTAT (iobase + 0x2a) /* FIFO status */
+#define DMADATA (iobase + 0x2c) /* DMA data */
+#define DMADATAL (iobase + 0x2c) /* DMA data low byte */
+#define DMADATAH (iobase + 0x2e) /* DMA data high byte */
+#define BRSTCNTRL (iobase + 0x30) /* Burst Control */
+#define DMADATALONG (iobase + 0x30)
+#define PORTA (iobase + 0x34) /* Port A */
+#define PORTB (iobase + 0x36) /* Port B */
+#define REV (iobase + 0x38) /* Revision (001 for 6360) */
+#define STACK (iobase + 0x3a) /* Stack */
+#define TEST (iobase + 0x3c) /* Test register */
+#define ID (iobase + 0x3e) /* ID register */
+#else
+#define SCSISEQ (iobase + 0x00) /* SCSI sequence control */
+#define SXFRCTL0 (iobase + 0x01) /* SCSI transfer control 0 */
+#define SXFRCTL1 (iobase + 0x02) /* SCSI transfer control 1 */
+#define SCSISIGI (iobase + 0x03) /* SCSI signal in */
+#define SCSISIGO (iobase + 0x03) /* SCSI signal out */
+#define SCSIRATE (iobase + 0x04) /* SCSI rate control */
+#define SCSIID (iobase + 0x05) /* SCSI ID */
+#define SELID (iobase + 0x05) /* Selection/Reselection ID */
+#define SCSIDAT (iobase + 0x06) /* SCSI Latched Data */
+#define SCSIBUS (iobase + 0x07) /* SCSI Data Bus*/
+#define STCNT0 (iobase + 0x08) /* SCSI transfer count */
+#define STCNT1 (iobase + 0x09)
+#define STCNT2 (iobase + 0x0a)
+#define CLRSINT0 (iobase + 0x0b) /* Clear SCSI interrupts 0 */
+#define SSTAT0 (iobase + 0x0b) /* SCSI interrupt status 0 */
+#define CLRSINT1 (iobase + 0x0c) /* Clear SCSI interrupts 1 */
+#define SSTAT1 (iobase + 0x0c) /* SCSI status 1 */
+#define SSTAT2 (iobase + 0x0d) /* SCSI status 2 */
+#define SCSITEST (iobase + 0x0e) /* SCSI test control */
+#define SSTAT3 (iobase + 0x0e) /* SCSI status 3 */
+#define CLRSERR (iobase + 0x0f) /* Clear SCSI errors */
+#define SSTAT4 (iobase + 0x0f) /* SCSI status 4 */
+#define SIMODE0 (iobase + 0x10) /* SCSI interrupt mode 0 */
+#define SIMODE1 (iobase + 0x11) /* SCSI interrupt mode 1 */
+#define DMACNTRL0 (iobase + 0x12) /* DMA control 0 */
+#define DMACNTRL1 (iobase + 0x13) /* DMA control 1 */
+#define DMASTAT (iobase + 0x14) /* DMA status */
+#define FIFOSTAT (iobase + 0x15) /* FIFO status */
+#define DMADATA (iobase + 0x16) /* DMA data */
+#define DMADATAL (iobase + 0x16) /* DMA data low byte */
+#define DMADATAH (iobase + 0x17) /* DMA data high byte */
+#define BRSTCNTRL (iobase + 0x18) /* Burst Control */
+#define DMADATALONG (iobase + 0x18)
+#define PORTA (iobase + 0x1a) /* Port A */
+#define PORTB (iobase + 0x1b) /* Port B */
+#define REV (iobase + 0x1c) /* Revision (001 for 6360) */
+#define STACK (iobase + 0x1d) /* Stack */
+#define TEST (iobase + 0x1e) /* Test register */
+#define ID (iobase + 0x1f) /* ID register */
+#endif
+
+#define IDSTRING "(C)1991ADAPTECAIC6360 "
+
+/* What all the bits do */
+
+/* SCSISEQ */
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRESELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+/* SXFRCTL0 */
+#define SCSIEN 0x80
+#define DMAEN 0x40
+#define CHEN 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define CLRCH 0x02
+
+/* SXFRCTL1 */
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL1 0x10
+#define STIMESEL0 0x08
+#define STIMO_256ms 0x00
+#define STIMO_128ms 0x08
+#define STIMO_64ms 0x10
+#define STIMO_32ms 0x18
+#define ENSTIMER 0x04
+#define BYTEALIGN 0x02
+
+/* SCSISIGI */
+#define CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+/* Important! The 3 most significant bits of this register, in initiator mode,
+ * represents the "expected" SCSI bus phase and can be used to trigger phase
+ * mismatch and phase change interrupts. But more important: If there is a
+ * phase mismatch the chip will not transfer any data! This is actually a nice
+ * feature as it gives us a bit more control over what is happening when we are
+ * bursting data (in) through the FIFOs and the phase suddenly changes from
+ * DATA IN to STATUS or MESSAGE IN. The transfer will stop and wait for the
+ * proper phase to be set in this register instead of dumping the bits into the
+ * FIFOs.
+ */
+/* SCSISIGO */
+#define CDO 0x80
+#define CDEXP (CDO)
+#define IOO 0x40
+#define IOEXP (IOO)
+#define MSGO 0x20
+#define MSGEXP (MSGO)
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+/* Information transfer phases */
+#define PH_DOUT (0)
+#define PH_DIN (IOI)
+#define PH_CMD (CDI)
+#define PH_STAT (CDI|IOI)
+#define PH_MSGO (MSGI|CDI)
+#define PH_MSGI (MSGI|CDI|IOI)
+
+#define PH_MASK 0xe0
+
+/* Some pseudo phases for getphase()*/
+#define PH_BUSFREE 0x100 /* (Re)Selection no longer valid */
+#define PH_INVALID 0x101 /* (Re)Selection valid, but no REQ yet */
+#define PH_PSBIT 0x100 /* "pseudo" bit */
+
+/* SCSIRATE */
+#define SXFR2 0x40
+#define SXFR1 0x20
+#define SXFR0 0x10
+#define SOFS3 0x08
+#define SOFS2 0x04
+#define SOFS1 0x02
+#define SOFS0 0x01
+
+/* SCSI ID */
+#define OID2 0x40
+#define OID1 0x20
+#define OID0 0x10
+#define OID_S 4 /* shift value */
+#define TID2 0x04
+#define TID1 0x02
+#define TID0 0x01
+#define SCSI_ID_MASK 0x7
+
+/* SCSI selection/reselection ID (both target *and* initiator) */
+#define SELID7 0x80
+#define SELID6 0x40
+#define SELID5 0x20
+#define SELID4 0x10
+#define SELID3 0x08
+#define SELID2 0x04
+#define SELID1 0x02
+#define SELID0 0x01
+
+/* CLRSINT0 Clears what? (interrupt and/or status bit) */
+#define SETSDONE 0x80
+#define CLRSELDO 0x40 /* I */
+#define CLRSELDI 0x20 /* I+ */
+#define CLRSELINGO 0x10 /* I */
+#define CLRSWRAP 0x08 /* I+S */
+#define CLRSDONE 0x04 /* I+S */
+#define CLRSPIORDY 0x02 /* I */
+#define CLRDMADONE 0x01 /* I */
+
+/* SSTAT0 Howto clear */
+#define TARGET 0x80
+#define SELDO 0x40 /* Selfclearing */
+#define SELDI 0x20 /* Selfclearing when CLRSELDI is set */
+#define SELINGO 0x10 /* Selfclearing */
+#define SWRAP 0x08 /* CLRSWAP */
+#define SDONE 0x04 /* Not used in initiator mode */
+#define SPIORDY 0x02 /* Selfclearing (op on SCSIDAT) */
+#define DMADONE 0x01 /* Selfclearing (all FIFOs empty & T/C */
+
+/* CLRSINT1 Clears what? */
+#define CLRSELTIMO 0x80 /* I+S */
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20 /* I+S */
+#define CLRBUSFREE 0x08 /* I+S */
+#define CLRSCSIPERR 0x04 /* I+S */
+#define CLRPHASECHG 0x02 /* I+S */
+#define CLRREQINIT 0x01 /* I+S */
+
+/* SSTAT1 How to clear? When set?*/
+#define SELTO 0x80 /* C select out timeout */
+#define ATNTARG 0x40 /* Not used in initiator mode */
+#define SCSIRSTI 0x20 /* C RST asserted */
+#define PHASEMIS 0x10 /* Selfclearing */
+#define BUSFREE 0x08 /* C bus free condition */
+#define SCSIPERR 0x04 /* C parity error on inbound data */
+#define PHASECHG 0x02 /* C phase in SCSISIGI doesn't match */
+#define REQINIT 0x01 /* C or ACK asserting edge of REQ */
+
+/* SSTAT2 */
+#define SOFFSET 0x20
+#define SEMPTY 0x10
+#define SFULL 0x08
+#define SFCNT2 0x04
+#define SFCNT1 0x02
+#define SFCNT0 0x01
+
+/* SCSITEST */
+#define SCTESTU 0x08
+#define SCTESTD 0x04
+#define STCTEST 0x01
+
+/* SSTAT3 */
+#define SCSICNT3 0x80
+#define SCSICNT2 0x40
+#define SCSICNT1 0x20
+#define SCSICNT0 0x10
+#define OFFCNT3 0x08
+#define OFFCNT2 0x04
+#define OFFCNT1 0x02
+#define OFFCNT0 0x01
+
+/* CLRSERR */
+#define CLRSYNCERR 0x04
+#define CLRFWERR 0x02
+#define CLRFRERR 0x01
+
+/* SSTAT4 */
+#define SYNCERR 0x04
+#define FWERR 0x02
+#define FRERR 0x01
+
+/* SIMODE0 */
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+/* SIMODE1 */
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+/* DMACNTRL0 */
+#define ENDMA 0x80
+#define B8MODE 0x40
+#define DMA 0x20
+#define DWORDPIO 0x10
+#define WRITE 0x08
+#define INTEN 0x04
+#define RSTFIFO 0x02
+#define SWINT 0x01
+
+/* DMACNTRL1 */
+#define PWRDWN 0x80
+#define ENSTK32 0x40
+#define STK4 0x10
+#define STK3 0x08
+#define STK2 0x04
+#define STK1 0x02
+#define STK0 0x01
+
+/* DMASTAT */
+#define ATDONE 0x80
+#define WORDRDY 0x40
+#define INTSTAT 0x20
+#define DFIFOFULL 0x10
+#define DFIFOEMP 0x08
+#define DFIFOHF 0x04
+#define DWORDRDY 0x02
+
+/* BRSTCNTRL */
+#define BON3 0x80
+#define BON2 0x40
+#define BON1 0x20
+#define BON0 0x10
+#define BOFF3 0x08
+#define BOFF2 0x04
+#define BOFF1 0x02
+#define BOFF0 0x01
+
+/* TEST */
+#define BOFFTMR 0x40
+#define BONTMR 0x20
+#define STCNTH 0x10
+#define STCNTM 0x08
+#define STCNTL 0x04
+#define SCSIBLK 0x02
+#define DMABLK 0x01
+
+
+#define orreg(reg, val) outb((reg), inb(reg)| (val))
+#define andreg(reg, val) outb((reg), inb(reg)& (val))
+#define nandreg(reg, val) outb((reg), inb(reg)&~(val))
+
+
+
+#ifdef DDB
+#define fatal_if_no_DDB()
+#else
+#define fatal_if_no_DDB() panic("panic for historical reasons")
+#endif
+
+typedef u_long physaddr;
+
+struct aic_dma_seg {
+ physaddr addr;
+ long len;
+};
+
+#define DELAYCOUNT 16
+
+#define FUDGE(X) ((X)>>1) /* get 1 ms spincount */
+#define MINIFUDGE(X) ((X)>>4) /* get (approx) 125us spincount */
+#define AIC_NSEG 16
+#define NUM_CONCURRENT 7 /* Only one per target for now */
+
+/*
+ * ACB. Holds additional information for each SCSI command Comments: We
+ * need a separate scsi command block because we may need to overwrite it
+ * with a request sense command. Basicly, we refrain from fiddling with
+ * the scsi_xfer struct (except do the expected updating of return values).
+ * We'll generally update: xs->{flags,resid,error,sense,status} and
+ * occasionally xs->retries.
+ */
+
+struct acb {
+ TAILQ_ENTRY(acb) chain;
+ struct scsi_xfer *xs; /* SCSI xfer ctrl block from above */
+ int flags; /* Status */
+#define ACB_FREE 0x00
+#define ACB_ACTIVE 0x01
+#define ACB_DONE 0x04
+#define ACB_CHKSENSE 0x08
+/* struct aic_dma_seg dma[AIC_NSEG]; */ /* Physical addresses+len */
+ struct scsi_generic cmd; /* SCSI command block */
+ int clen;
+ char *daddr; /* Saved data pointer */
+ int dleft; /* Residue */
+ int stat; /* SCSI status byte */
+};
+
+/*
+ * Some info about each (possible) target on the SCSI bus. This should
+ * probably have been a "per target+lunit" structure, but we'll leave it at
+ * this for now. Is there a way to reliably hook it up to sc->fordriver??
+ */
+struct aic_tinfo {
+ int cmds; /* #commands processed */
+ int dconns; /* #disconnects */
+ int touts; /* #timeouts */
+ int perrs; /* #parity errors */
+ int senses; /* #request sense commands sent */
+ ushort lubusy; /* What local units/subr. are busy? */
+ u_char flags;
+#define NEED_TO_RESET 0x01 /* Should send a BUS_DEV_RESET */
+#define DO_NEGOTIATE 0x02 /* (Re)Negotiate synchronous options */
+#define TARGET_BUSY 0x04 /* Target is busy, i.e. cmd in progress */
+ u_char persgst; /* Period suggestion */
+ u_char offsgst; /* Offset suggestion */
+ u_char syncdata; /* True negotiated synch parameters */
+};
+
+/* Register a linenumber (for debugging) */
+#if AIC_DEBUG
+#define LOGLINE(p) \
+ do { \
+ p->history[p->hp] = __LINE__; \
+ p->hp = ++p->hp % AIC_HSIZE; \
+ } while (0)
+#else
+#define LOGLINE(p)
+#endif
+
+static struct aic_data { /* One of these per adapter */
+ u_short iobase; /* Base I/O port */
+ struct scsi_link sc_link; /* prototype for subdevs */
+ int aic_int; /* IRQ on the EISA bus */
+ int aic_dma; /* DRQ on the EISA bus */
+ /* Lists of command blocks */
+ TAILQ_HEAD(acb_list, acb) free_list, ready_list, nexus_list;
+ struct acb *nexus; /* current command */
+ /* Command blocks and target info */
+ struct acb acb[NUM_CONCURRENT];
+ struct aic_tinfo tinfo[8];
+ /* Data about the current nexus (updated for every cmd switch) */
+ u_char *dp; /* Current data pointer */
+ int dleft; /* Data left to transfer */
+ /* Adapter state */
+ short phase; /* Copy of what bus phase we are in */
+ short prevphase; /* Copy of what bus phase we were in */
+ short state; /* State applicable to the adapter */
+#define AIC_IDLE 0x01
+#define AIC_TMP_UNAVAIL 0x02 /* Don't accept SCSI commands */
+#define AIC_SELECTING 0x03 /* SCSI command is arbiting */
+#define AIC_RESELECTED 0x04 /* Has been reselected */
+#define AIC_HASNEXUS 0x05 /* Actively using the SCSI bus */
+#define AIC_CLEANING 0x06
+ short flags;
+#define AIC_DROP_MSGI 0x01 /* Discard all msgs (parity err detected) */
+#define AIC_DOINGDMA 0x02 /* The FIFO data path is active! */
+#define AIC_BUSFREE_OK 0x04 /* Bus free phase is OK. */
+#define AIC_SYNCHNEGO 0x08 /* Synch negotiation in progress. */
+#define AIC_BLOCKED 0x10 /* Don't schedule new scsi bus operations */
+ /* Debugging stuff */
+#define AIC_HSIZE 8
+ short history[AIC_HSIZE]; /* Store line numbers here. */
+ short hp;
+ u_char progress; /* Set if interrupt has achieved progress */
+ /* Message stuff */
+ u_char msgpriq; /* One or more messages to send (encoded) */
+ u_char msgout; /* What message is on its way out? */
+#define SEND_DEV_RESET 0x01
+#define SEND_PARITY_ERROR 0x02
+#define SEND_ABORT 0x04
+#define SEND_REJECT 0x08
+#define SEND_INIT_DET_ERR 0x10
+#define SEND_IDENTIFY 0x20
+#define SEND_SDTR 0x40
+#define AIC_MAX_MSG_LEN 8
+ u_char omess[AIC_MAX_MSG_LEN]; /* Scratch area for messages */
+ u_char *omp; /* Message pointer (for multibyte messages) */
+ u_char omlen;
+ u_char imess[AIC_MAX_MSG_LEN + 1];
+ u_char *imp; /* Message pointer (for multibyte messages) */
+ u_char imlen;
+} *aicdata[NAIC];
+
+#define AIC_SHOWACBS 0x01
+#define AIC_SHOWINTS 0x02
+#define AIC_SHOWCMDS 0x04
+#define AIC_SHOWMISC 0x08
+#define AIC_SHOWTRAC 0x10
+#define AIC_SHOWSTART 0x20
+static int aic_debug = 0; /* AIC_SHOWSTART|AIC_SHOWMISC|AIC_SHOWTRAC; */
+
+#if AIC_DEBUG
+#define AIC_ACBS(str) do {if (aic_debug & AIC_SHOWACBS) printf str;} while (0)
+#define AIC_MISC(str) do {if (aic_debug & AIC_SHOWMISC) printf str;} while (0)
+#define AIC_INTS(str) do {if (aic_debug & AIC_SHOWINTS) printf str;} while (0)
+#define AIC_TRACE(str) do {if (aic_debug & AIC_SHOWTRAC) printf str;} while (0)
+#define AIC_CMDS(str) do {if (aic_debug & AIC_SHOWCMDS) printf str;} while (0)
+#define AIC_START(str) do {if (aic_debug & AIC_SHOWSTART) printf str;}while (0)
+#else
+#define AIC_ACBS(str)
+#define AIC_MISC(str)
+#define AIC_INTS(str)
+#define AIC_TRACE(str)
+#define AIC_CMDS(str)
+#define AIC_START(str)
+#endif
+
+#ifdef PC98
+static int aicprobe __P((struct pc98_device *));
+static int aicattach __P((struct pc98_device *));
+#else
+static int aicprobe __P((struct isa_device *));
+static int aicattach __P((struct isa_device *));
+#endif
+static void aic_minphys __P((struct buf *));
+static u_int32_t aic_adapter_info __P((int));
+static void aic_init __P((struct aic_data *));
+static int aic_find __P((struct aic_data *));
+static void aic_done __P((struct acb *));
+static void aic_dataout __P((struct aic_data *aic));
+static void aic_datain __P((struct aic_data *aic));
+static int32_t aic_scsi_cmd __P((struct scsi_xfer *));
+static int aic_poll __P((struct aic_data *aic, struct acb *));
+void aic_add_timeout __P((struct acb *, int));
+void aic_remove_timeout __P((struct acb *));
+static void aic6360_reset __P((struct aic_data *aic));
+static u_short aicphase __P((struct aic_data *aic));
+static void aic_msgin __P((struct aic_data *aic));
+static void aic_msgout __P((struct aic_data *aic));
+static timeout_t aic_timeout;
+static void aic_sched __P((struct aic_data *));
+static void aic_scsi_reset __P((struct aic_data *));
+#if AIC_DEBUG
+void aic_print_active_acb __P((void));
+void aic_dump6360 __P((void));
+void aic_dump_driver __P((void));
+#endif
+
+/* Linkup to the rest of the kernel */
+#ifdef PC98
+struct pc98_driver aicdriver = {
+#else
+struct isa_driver aicdriver = {
+#endif
+ aicprobe, aicattach, "aic"
+};
+
+static int aicunit = 0;
+
+static struct scsi_adapter aic_switch = {
+ aic_scsi_cmd,
+ aic_minphys,
+ 0,
+ 0,
+ aic_adapter_info,
+ "aic"
+ ,0 , 0
+};
+
+static struct scsi_device aic_dev = {
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "aic",
+ 0
+};
+
+static struct kern_devconf kdc_aic[NAIC] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ "aic", 0, { MDDT_PC98, 0, "bio" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "aic", 0, { MDDT_ISA, 0, "bio" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* start out in unconfig state */
+ "Adaptec AIC-6360 SCSI host adapter chipset",
+ DC_CLS_MISC /* host adapters aren't special */
+} };
+
+static inline void
+#ifdef PC98
+aic_registerdev(struct pc98_device *id)
+#else
+aic_registerdev(struct isa_device *id)
+#endif
+{
+ if(id->id_unit)
+ kdc_aic[id->id_unit] = kdc_aic[0];
+ kdc_aic[id->id_unit].kdc_unit = id->id_unit;
+ kdc_aic[id->id_unit].kdc_parentdata = id;
+ dev_attach(&kdc_aic[id->id_unit]);
+}
+
+/*
+ * INITIALIZATION ROUTINES (probe, attach ++)
+ */
+
+/*
+ * aicprobe: probe for AIC6360 SCSI-controller
+ * returns non-zero value if a controller is found.
+ */
+static int
+aicprobe(dev)
+#ifdef PC98
+ struct pc98_device *dev;
+#else
+ struct isa_device *dev;
+#endif
+{
+ int unit = aicunit;
+ struct aic_data *aic;
+
+ if (unit >= NAIC) {
+ printf("aic%d: unit number too high\n", unit);
+ return 0;
+ }
+ dev->id_unit = unit;
+ /*
+ * Allocate a storage area for us
+ */
+ if (aicdata[unit]) {
+ printf("aic%d: memory already allocated\n", unit);
+ return 0;
+ }
+ aic = malloc(sizeof(struct aic_data), M_TEMP, M_NOWAIT);
+ if (!aic) {
+ printf("aic%d: cannot malloc!\n", unit);
+ return 0;
+ }
+ bzero(aic, sizeof(struct aic_data));
+ aicdata[unit] = aic;
+ aic->iobase = dev->id_iobase;
+#ifndef DEV_LKM
+ aic_registerdev(dev);
+#endif /* not DEV_LKM */
+
+ if (aic_find(aic) != 0) {
+ aicdata[unit] = NULL;
+ free(aic, M_TEMP);
+ return 0;
+ }
+ aicunit++;
+#ifdef PC98
+ return 0x40;
+#else
+ return 0x20;
+#endif
+}
+
+/* Do the real search-for-device.
+ * Prerequisite: aic->iobase should be set to the proper value
+ */
+static int
+aic_find(aic)
+ struct aic_data *aic;
+{
+ u_short iobase = aic->iobase;
+ char chip_id[sizeof(IDSTRING)]; /* For chips that support it */
+ int i;
+
+ /* Remove aic6360 from possible powerdown mode */
+ outb(DMACNTRL0, 0);
+
+ /* Thanks to mark@aggregate.com for the new method for detecting
+ * whether the chip is present or not. Bonus: may also work for
+ * the AIC-6260!
+ */
+ AIC_TRACE(("aic: probing for aic-chip at port 0x%x\n",(int)iobase));
+ /*
+ * Linux also init's the stack to 1-16 and then clears it,
+ * 6260's don't appear to have an ID reg - mpg
+ */
+ /* Push the sequence 0,1,..,15 on the stack */
+#define STSIZE 16
+ outb(DMACNTRL1, 0); /* Reset stack pointer */
+ for (i = 0; i < STSIZE; i++)
+ outb(STACK, i);
+
+ /* See if we can pull out the same sequence */
+ outb(DMACNTRL1, 0);
+ for (i = 0; i < STSIZE && inb(STACK) == i; i++)
+ ;
+ if (i != STSIZE) {
+ AIC_START(("STACK futzed at %d.\n", i));
+ return ENXIO;
+ }
+
+ /* See if we can pull the id string out of the ID register,
+ * now only used for informational purposes.
+ */
+ bzero(chip_id, sizeof(chip_id));
+ insb(ID, chip_id, sizeof(IDSTRING)-1);
+ AIC_START(("AIC found at 0x%x ", (int)aic->iobase));
+ AIC_START(("ID: %s ",chip_id));
+ AIC_START(("chip revision %d\n",(int)inb(REV)));
+ return 0;
+}
+
+
+/*
+ * Attach the AIC6360, fill out some high and low level data structures
+ */
+static int
+aicattach(dev)
+#ifdef PC98
+ struct pc98_device *dev;
+#else
+ struct isa_device *dev;
+#endif
+{
+ int unit = dev->id_unit;
+ struct aic_data *aic = aicdata[unit];
+ struct scsibus_data *scbus;
+
+ AIC_TRACE(("aicattach\n"));
+ aic->state = 0;
+ aic_scsi_reset(aic);
+ aic_init(aic); /* Init chip and driver */
+
+ /*
+ * Fill in the prototype scsi_link
+ */
+ aic->sc_link.adapter_unit = unit;
+ aic->sc_link.adapter_targ = AIC_SCSI_HOSTID;
+ aic->sc_link.adapter_softc = aic;
+ aic->sc_link.adapter = &aic_switch;
+ aic->sc_link.device = &aic_dev;
+
+ /*
+ * Prepare the scsibus_data area for the upperlevel
+ * scsi code.
+ */
+ scbus = scsi_alloc_bus();
+ if(!scbus)
+ return 0;
+ scbus->adapter_link = &aic->sc_link;
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ kdc_aic[unit].kdc_state = DC_BUSY; /* host adapters are always busy */
+ scsi_attachdevs(scbus);
+
+ return 1;
+}
+
+
+/* Initialize AIC6360 chip itself
+ * The following conditions should hold:
+ * aicprobe should have succeeded, i.e. the iobase address in aic_data must
+ * be valid.
+ */
+static void
+aic6360_reset(aic)
+ struct aic_data *aic;
+{
+ u_short iobase = aic->iobase;
+
+ outb(SCSITEST, 0); /* Doc. recommends to clear these two */
+ outb(TEST, 0); /* registers before operations commence */
+
+ /* Reset SCSI-FIFO and abort any transfers */
+ outb(SXFRCTL0, CHEN|CLRCH|CLRSTCNT);
+
+ /* Reset DMA-FIFO */
+ outb(DMACNTRL0, RSTFIFO);
+ outb(DMACNTRL1, 0);
+
+ outb(SCSISEQ, 0); /* Disable all selection features */
+ outb(SXFRCTL1, 0);
+
+ outb(SIMODE0, 0x00); /* Disable some interrupts */
+ outb(CLRSINT0, 0x7f); /* Clear a slew of interrupts */
+
+ outb(SIMODE1, 0x00); /* Disable some more interrupts */
+ outb(CLRSINT1, 0xef); /* Clear another slew of interrupts */
+
+ outb(SCSIRATE, 0); /* Disable synchronous transfers */
+
+ outb(CLRSERR, 0x07); /* Haven't seen ant errors (yet) */
+
+ outb(SCSIID, AIC_SCSI_HOSTID << OID_S); /* Set our SCSI-ID */
+ outb(BRSTCNTRL, EISA_BRST_TIM);
+}
+
+/* Pull the SCSI RST line for 500 us */
+static void
+aic_scsi_reset(aic)
+ struct aic_data *aic;
+{
+ u_short iobase = aic->iobase;
+
+ outb(SCSISEQ, SCSIRSTO);
+ DELAY(500);
+ outb(SCSISEQ, 0);
+ DELAY(50);
+}
+
+/*
+ * Initialize aic SCSI driver, also (conditonally) reset the SCSI bus.
+ * The reinitialization is still buggy (e.g. on SCSI resets).
+ */
+static void
+aic_init(aic)
+ struct aic_data *aic;
+{
+ u_short iobase = aic->iobase;
+ struct acb *acb;
+ int r;
+
+ /* Reset the SCSI-bus itself */
+ aic_scsi_reset(aic);
+
+ aic6360_reset(aic); /* Clean up our own hardware */
+
+/*XXX*/ /* If not the first time (probably a reset condition),
+ * we should clean queues with active commands
+ */
+ if (aic->state == 0) { /* First time through */
+ TAILQ_INIT(&aic->ready_list);
+ TAILQ_INIT(&aic->nexus_list);
+ TAILQ_INIT(&aic->free_list);
+ aic->nexus = 0;
+ acb = aic->acb;
+ bzero(acb, sizeof(aic->acb));
+ for (r = 0; r < sizeof(aic->acb) / sizeof(*acb); r++) {
+ TAILQ_INSERT_TAIL(&aic->free_list, acb, chain);
+ acb++;
+ }
+ bzero(&aic->tinfo, sizeof(aic->tinfo));
+ } else {
+ aic->state = AIC_CLEANING;
+ if (aic->nexus != NULL) {
+ aic->nexus->xs->error = XS_DRIVER_STUFFUP;
+ untimeout(aic_timeout, (caddr_t)aic->nexus);
+ aic_done(aic->nexus);
+ }
+ aic->nexus = NULL;
+ while (acb = aic->nexus_list.tqh_first) {
+ acb->xs->error = XS_DRIVER_STUFFUP;
+ untimeout(aic_timeout, (caddr_t)acb);
+ aic_done(acb);
+ }
+ }
+
+ aic->phase = aic->prevphase = PH_INVALID;
+ aic->hp = 0;
+ for (r = 0; r < 7; r++) {
+ struct aic_tinfo *tp = &aic->tinfo[r];
+ tp->flags = AIC_USE_SYNCHRONOUS ? DO_NEGOTIATE : 0;
+ tp->flags |= NEED_TO_RESET;
+ tp->persgst = AIC_SYNC_PERIOD;
+ tp->offsgst = AIC_SYNC_REQ_ACK_OFS;
+ tp->syncdata = 0;
+ }
+ aic->state = AIC_IDLE;
+ outb(DMACNTRL0, INTEN);
+ return;
+}
+
+/*
+ * DRIVER FUNCTIONS CALLABLE FROM HIGHER LEVEL DRIVERS
+ */
+
+/*
+ * Expected sequence:
+ * 1) Command inserted into ready list
+ * 2) Command selected for execution
+ * 3) Command won arbitration and has selected target device
+ * 4) Send message out (identify message, eventually also sync.negotiations)
+ * 5) Send command
+ * 5a) Receive disconnect message, disconnect.
+ * 5b) Reselected by target
+ * 5c) Receive identify message from target.
+ * 6) Send or receive data
+ * 7) Receive status
+ * 8) Receive message (command complete etc.)
+ * 9) If status == SCSI_CHECK construct a synthetic request sense SCSI cmd.
+ * Repeat 2-8 (no disconnects please...)
+ */
+
+/*
+ * Start a SCSI-command
+ * This function is called by the higher level SCSI-driver to queue/run
+ * SCSI-commands.
+ */
+static int32_t
+aic_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *sc = xs->sc_link;
+ struct aic_data *aic;
+ struct acb *acb;
+ int s = 0;
+ int flags;
+
+ aic = (struct aic_data *)sc->adapter_softc;
+ SC_DEBUG(sc, SDEV_DB2, ("aic_scsi_cmd\n"));
+ AIC_TRACE(("aic_scsi_cmd\n"));
+ AIC_MISC(("[0x%x, %d]->%d ", (int)xs->cmd->opcode, xs->cmdlen,
+ sc->target));
+
+ flags = xs->flags;
+
+ /* Get a aic command block */
+ if (!(flags & SCSI_NOMASK)) {
+ /* Critical region */
+ s = splbio();
+ acb = aic->free_list.tqh_first;
+ if (acb) {
+ TAILQ_REMOVE(&aic->free_list, acb, chain);
+ }
+ splx(s);
+ } else {
+ acb = aic->free_list.tqh_first;
+ if (acb) {
+ TAILQ_REMOVE(&aic->free_list, acb, chain);
+ }
+ }
+
+ if (acb == NULL) {
+ xs->error = XS_DRIVER_STUFFUP;
+ AIC_MISC(("TRY_AGAIN_LATER"));
+ return TRY_AGAIN_LATER;
+ }
+
+ /* Initialize acb */
+ acb->flags = ACB_ACTIVE;
+ acb->xs = xs;
+ bcopy(xs->cmd, &acb->cmd, xs->cmdlen);
+ acb->clen = xs->cmdlen;
+ acb->daddr = xs->data;
+ acb->dleft = xs->datalen;
+ acb->stat = 0;
+
+ if (!(flags & SCSI_NOMASK))
+ s = splbio();
+
+ TAILQ_INSERT_TAIL(&aic->ready_list, acb, chain);
+ timeout(aic_timeout, (caddr_t)acb, (xs->timeout*hz)/1000);
+
+ if (aic->state == AIC_IDLE)
+ aic_sched(aic);
+
+ if (!(flags & SCSI_NOMASK)) { /* Almost done. Wait outside */
+ splx(s);
+ AIC_MISC(("SUCCESSFULLY_QUEUED"));
+ return SUCCESSFULLY_QUEUED;
+ }
+
+ /* Not allowed to use interrupts, use polling instead */
+ return aic_poll(aic, acb);
+}
+
+/*
+ * Adjust transfer size in buffer structure
+ */
+static void
+aic_minphys(bp)
+ struct buf *bp;
+{
+
+ AIC_TRACE(("aic_minphys\n"));
+ if (bp->b_bcount > (AIC_NSEG << PAGE_SHIFT))
+ bp->b_bcount = (AIC_NSEG << PAGE_SHIFT);
+}
+
+
+static u_int32_t
+aic_adapter_info(unit)
+ int unit;
+{
+
+ AIC_TRACE(("aic_adapter_info\n"));
+ return (2); /* One outstanding command per target */
+}
+
+/*
+ * Used when interrupt driven I/O isn't allowed, e.g. during boot.
+ */
+static int
+aic_poll(aic, acb)
+ struct aic_data *aic;
+ struct acb *acb;
+{
+ register u_short iobase = aic->iobase;
+ struct scsi_xfer *xs = acb->xs;
+ int count = xs->timeout * 10;
+
+ AIC_TRACE(("aic_poll\n"));
+ while (count) {
+ if (inb(DMASTAT) & INTSTAT)
+ aicintr(xs->sc_link->adapter_unit);
+ if (xs->flags & ITSDONE)
+ break;
+ DELAY(100);
+ count--;
+ }
+ if (count == 0) {
+ AIC_MISC(("aic_poll: timeout"));
+ aic_timeout((caddr_t)acb);
+ }
+ if (xs->error)
+ return HAD_ERROR;
+ return COMPLETE;
+}
+
+/* LOW LEVEL SCSI UTILITIES */
+
+/* Determine the SCSI bus phase, return either a real SCSI bus phase or some
+ * pseudo phase we use to detect certain exceptions. This one is a bit tricky.
+ * The bits we peek at:
+ * CDI, MSGI and DI is the 3 SCSI signals determining the bus phase.
+ * These should be qualified by REQI high and ACKI low.
+ * Also peek at SSTAT0[SELDO|SELDI] to detect a passing BUSFREE condition.
+ * No longer detect SCSI RESET or PERR here. They are tested for separately
+ * in the interrupt handler.
+ * Note: If an exception occur at some critical time during the phase
+ * determination we'll most likely return something wildly erronous....
+ */
+static inline u_short
+aicphase(aic)
+ struct aic_data *aic;
+{
+ register u_short iobase = aic->iobase;
+ register u_char sstat0, sstat1, scsisig;
+
+ sstat1 = inb(SSTAT1); /* Look for REQINIT (REQ asserted) */
+ scsisig = inb(SCSISIGI); /* Get the SCSI bus signals */
+ sstat0 = inb(SSTAT0); /* Get the selection valid status bits */
+
+ if (!(inb(SSTAT0) & (SELDO|SELDI))) /* Selection became invalid? */
+ return PH_BUSFREE;
+
+ /* Selection is still valid */
+ if (!(sstat1 & REQINIT)) /* REQ not asserted ? */
+ return PH_INVALID;
+
+ /* REQ is asserted, (and ACK is not) */
+ return scsisig & PH_MASK;
+}
+
+
+/* Schedule a scsi operation. This has now been pulled out of the interrupt
+ * handler so that we may call it from aic_scsi_cmd and aic_done. This may
+ * save us an unecessary interrupt just to get things going. Should only be
+ * called when state == AIC_IDLE and at bio pl.
+ */
+static void
+aic_sched(aic)
+ register struct aic_data *aic;
+{
+ struct scsi_link *sc;
+ struct acb *acb;
+ u_short iobase = aic->iobase;
+ int t;
+ u_char simode0, simode1, scsiseq;
+
+ AIC_TRACE(("aic_sched\n"));
+ simode0 = ENSELDI;
+ simode1 = ENSCSIRST|ENSCSIPERR|ENREQINIT;
+ scsiseq = ENRESELI;
+ /*
+ * Find first acb in rdy queue that is for a target/lunit
+ * combinations that is not busy.
+ */
+ outb(CLRSINT1, CLRSELTIMO|CLRBUSFREE|CLRSCSIPERR);
+ for (acb = aic->ready_list.tqh_first; acb; acb = acb->chain.tqe_next) {
+ sc = acb->xs->sc_link;
+ t = sc->target;
+ if (!(aic->tinfo[t].lubusy & (1 << sc->lun))) {
+ TAILQ_REMOVE(&aic->ready_list, acb, chain);
+ aic->nexus = acb;
+ aic->state = AIC_SELECTING;
+ /*
+ * Start selection process. Always enable
+ * reselections. Note: we don't have a nexus yet, so
+ * cannot set aic->state = AIC_HASNEXUS.
+ */
+ simode0 = ENSELDI|ENSELDO;
+ simode1 = ENSCSIRST|ENSCSIPERR|
+ ENREQINIT|ENSELTIMO;
+ scsiseq = ENRESELI|ENSELO|ENAUTOATNO;
+ outb(SCSIID, AIC_SCSI_HOSTID << OID_S | t);
+ outb(SXFRCTL1, STIMO_256ms|ENSTIMER);
+ outb(CLRSINT0, CLRSELDO);
+ break;
+ }
+#if AIC_DEBUG
+ else
+ AIC_MISC(("%d:%d busy\n", t, sc->lun));
+#endif
+ }
+ AIC_MISC(("%sselecting\n",scsiseq&ENSELO?"":"re"));
+ outb(SIMODE0, simode0);
+ outb(SIMODE1, simode1);
+ outb(SCSISEQ, scsiseq);
+}
+
+
+/*
+ * POST PROCESSING OF SCSI_CMD (usually current)
+ */
+static void
+aic_done(acb)
+ struct acb *acb;
+{
+ struct scsi_xfer *xs = acb->xs;
+ struct scsi_link *sc = xs->sc_link;
+ struct aic_data *aic = (struct aic_data *)sc->adapter_softc;
+
+ AIC_TRACE(("aic_done "));
+
+ /*
+ * Now, if we've come here with no error code, i.e. we've kept the
+ * initial XS_NOERROR, and the status code signals that we should
+ * check sense, we'll need to set up a request sense cmd block and
+ * push the command back into the ready queue *before* any other
+ * commands for this target/lunit, else we lose the sense info.
+ * We don't support chk sense conditions for the request sense cmd.
+ */
+ if (xs->error == XS_NOERROR && !(acb->flags & ACB_CHKSENSE)) {
+ if ((acb->stat & ST_MASK)==SCSI_CHECK) {
+ struct scsi_sense *ss = (void *)&acb->cmd;
+ AIC_MISC(("requesting sense "));
+ /* First, save the return values */
+ xs->resid = acb->dleft;
+ xs->status = acb->stat;
+ /* Next, setup a request sense command block */
+ bzero(ss, sizeof(*ss));
+ ss->op_code = REQUEST_SENSE;
+ ss->byte2 = sc->lun << 5;
+ ss->length = sizeof(struct scsi_sense_data);
+ acb->clen = sizeof(*ss);
+ acb->daddr = (char *)&xs->sense;
+ acb->dleft = sizeof(struct scsi_sense_data);
+ acb->flags = ACB_ACTIVE|ACB_CHKSENSE;
+ TAILQ_INSERT_HEAD(&aic->ready_list, acb, chain);
+ aic->tinfo[sc->target].lubusy &= ~(1<<sc->lun);
+ aic->tinfo[sc->target].senses++;
+ if (aic->nexus == acb) {
+ aic->nexus = NULL;
+ aic->state = AIC_IDLE;
+ aic_sched(aic);
+ }
+ return;
+ }
+ }
+
+ if (xs->flags & SCSI_ERR_OK) {
+ xs->resid = 0;
+ xs->error = XS_NOERROR;
+ } else if (xs->error == XS_NOERROR && (acb->flags & ACB_CHKSENSE)) {
+ xs->error = XS_SENSE;
+ } else {
+ xs->resid = acb->dleft;
+ }
+ xs->flags |= ITSDONE;
+
+#if AIC_DEBUG
+ if (aic_debug & AIC_SHOWMISC) {
+ printf("err=0x%02x ",xs->error);
+ if (xs->error == XS_SENSE)
+ printf("sense=%2x\n", xs->sense.error_code);
+ }
+ if ((xs->resid || xs->error > XS_SENSE) && aic_debug & AIC_SHOWMISC) {
+ if (xs->resid)
+ printf("aic_done: resid=%d\n", xs->resid);
+ if (xs->error)
+ printf("aic_done: error=%d\n", xs->error);
+ }
+#endif
+
+ /*
+ * Remove the ACB from whatever queue it's on. We have to do a bit of
+ * a hack to figure out which queue it's on. Note that it is *not*
+ * necessary to cdr down the ready queue, but we must cdr down the
+ * nexus queue and see if it's there, so we can mark the unit as no
+ * longer busy. This code is sickening, but it works.
+ */
+ if (acb == aic->nexus) {
+ aic->state = AIC_IDLE;
+ aic->tinfo[sc->target].lubusy &= ~(1<<sc->lun);
+ aic_sched(aic);
+ } else if (aic->ready_list.tqh_last == &acb->chain.tqe_next) {
+ TAILQ_REMOVE(&aic->ready_list, acb, chain);
+ } else {
+ register struct acb *acb2;
+ for (acb2 = aic->nexus_list.tqh_first; acb2;
+ acb2 = acb2->chain.tqe_next)
+ if (acb2 == acb) {
+ TAILQ_REMOVE(&aic->nexus_list, acb, chain);
+ aic->tinfo[sc->target].lubusy &= ~(1<<sc->lun);
+ /* XXXX Should we call aic_sched() here? */
+ break;
+ }
+ if (acb2)
+ ;
+ else if (acb->chain.tqe_next) {
+ TAILQ_REMOVE(&aic->ready_list, acb, chain);
+ } else {
+ printf("aic%d: can't find matching acb\n",
+ xs->sc_link->adapter_unit);
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ }
+ }
+ /* Put it on the free list. */
+ acb->flags = ACB_FREE;
+ TAILQ_INSERT_HEAD(&aic->free_list, acb, chain);
+
+ aic->tinfo[sc->target].cmds++;
+ scsi_done(xs);
+ return;
+}
+
+/*
+ * INTERRUPT/PROTOCOL ENGINE
+ */
+
+/* The message system:
+ * This is a revamped message system that now should easier accomodate new
+ * messages, if necessary.
+ * Currently we accept these messages:
+ * IDENTIFY (when reselecting)
+ * COMMAND COMPLETE # (expect bus free after messages marked #)
+ * NOOP
+ * MESSAGE REJECT
+ * SYNCHRONOUS DATA TRANSFER REQUEST
+ * SAVE DATA POINTER
+ * RESTORE POINTERS
+ * DISCONNECT #
+ *
+ * We may send these messages in prioritized order:
+ * BUS DEVICE RESET # if SCSI_RESET & xs->flags (or in weird sits.)
+ * MESSAGE PARITY ERROR par. err. during MSGI
+ * MESSAGE REJECT If we get a message we don't know how to handle
+ * ABORT # send on errors
+ * INITIATOR DETECTED ERROR also on errors (SCSI2) (during info xfer)
+ * IDENTIFY At the start of each transfer
+ * SYNCHRONOUS DATA TRANSFER REQUEST if appropriate
+ * NOOP if nothing else fits the bill ...
+ */
+
+#define aic_sched_msgout(m) \
+ do { \
+ orreg(SCSISIGO, ATNO); \
+ aic->msgpriq |= (m); \
+ } while (0)
+
+#define IS1BYTEMSG(m) (((m) != 1 && (m) < 0x20) || (m) >= 0x80)
+#define IS2BYTEMSG(m) (((m) & 0xf0) == 0x20)
+#define ISEXTMSG(m) ((m) == 1)
+/* Precondition:
+ * The SCSI bus is already in the MSGI phase and there is a message byte
+ * on the bus, along with an asserted REQ signal.
+ */
+static void
+aic_msgin(aic)
+ register struct aic_data *aic;
+{
+ register u_short iobase = aic->iobase;
+ int spincount, extlen;
+ u_char sstat1;
+
+ AIC_TRACE(("aic_msgin "));
+ outb(SCSISIGO, PH_MSGI);
+ /* Prepare for a new message. A message should (according to the SCSI
+ * standard) be transmitted in one single message_in phase.
+ * If we have been in some other phase, then this is a new message.
+ */
+ if (aic->prevphase != PH_MSGI) {
+ aic->flags &= ~AIC_DROP_MSGI;
+ aic->imlen = 0;
+ }
+ /*
+ * Read a whole message but the last byte. If we shall reject the
+ * message, we shall have to do it, by asserting ATNO, during the
+ * message transfer phase itself.
+ */
+ for (;;) {
+ sstat1 = inb(SSTAT1);
+ /* If parity errors just dump everything on the floor, also
+ * a parity error automatically sets ATNO
+ */
+ if (sstat1 & SCSIPERR) {
+ aic_sched_msgout(SEND_PARITY_ERROR);
+ aic->flags |= AIC_DROP_MSGI;
+ }
+ /*
+ * If we're going to reject the message, don't bother storing
+ * the incoming bytes. But still, we need to ACK them.
+ */
+ if (!(aic->flags & AIC_DROP_MSGI)) {
+ /* Get next message byte */
+ aic->imess[aic->imlen] = inb(SCSIDAT);
+ /*
+ * This testing is suboptimal, but most messages will
+ * be of the one byte variety, so it should not effect
+ * performance significantly.
+ */
+ if (IS1BYTEMSG(aic->imess[0]))
+ break;
+ if (IS2BYTEMSG(aic->imess[0]) && aic->imlen == 1)
+ break;
+ if (ISEXTMSG(aic->imess[0]) && aic->imlen > 0) {
+ if (aic->imlen == AIC_MAX_MSG_LEN) {
+ aic->flags |= AIC_DROP_MSGI;
+ aic_sched_msgout(SEND_REJECT);
+ }
+ extlen = aic->imess[1] ? aic->imess[1] : 256;
+ if (aic->imlen == extlen + 2)
+ break; /* Got it all */
+ }
+ }
+ /* If we reach this spot we're either:
+ * a) in the middle of a multi-byte message or
+ * b) we're dropping bytes
+ */
+ outb(SXFRCTL0, CHEN|SPIOEN);
+ inb(SCSIDAT); /* Really read it (ACK it, that is) */
+ outb(SXFRCTL0, CHEN);
+ aic->imlen++;
+
+ /*
+ * We expect the bytes in a multibyte message to arrive
+ * relatively close in time, a few microseconds apart.
+ * Therefore we will spinwait for some small amount of time
+ * waiting for the next byte.
+ */
+ spincount = DELAYCOUNT * AIC_MSGI_SPIN;
+ LOGLINE(aic);
+ while (spincount-- && !((sstat1 = inb(SSTAT1)) & REQINIT))
+ ;
+ if (spincount == -1 || sstat1 & (PHASEMIS|BUSFREE))
+ return;
+ }
+ /* Now we should have a complete message (1 byte, 2 byte and moderately
+ * long extended messages). We only handle extended messages which
+ * total length is shorter than AIC_MAX_MSG_LEN. Longer messages will
+ * be amputated. (Return XS_BOBBITT ?)
+ */
+ if (aic->state == AIC_HASNEXUS) {
+ struct acb *acb = aic->nexus;
+ struct aic_tinfo *ti = &aic->tinfo[acb->xs->sc_link->target];
+ int offs, per, rate;
+
+ outb(SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE|ENSCSIPERR);
+ switch (aic->imess[0]) {
+ case MSG_CMDCOMPLETE:
+ if (!acb) {
+ aic_sched_msgout(SEND_ABORT);
+ printf("aic: CMDCOMPLETE but no command?\n");
+ break;
+ }
+ if (aic->dleft < 0) {
+ struct scsi_link *sc = acb->xs->sc_link;
+ printf("aic: %d extra bytes from %d:%d\n",
+ -aic->dleft, sc->target, sc->lun);
+ acb->dleft = 0;
+ }
+ acb->xs->resid = acb->dleft = aic->dleft;
+ aic->flags |= AIC_BUSFREE_OK;
+ untimeout(aic_timeout, (caddr_t)acb);
+ aic_done(acb);
+ break;
+ case MSG_MESSAGE_REJECT:
+ if (aic_debug & AIC_SHOWMISC)
+ printf("aic: our msg rejected by target\n");
+ if (aic->flags & AIC_SYNCHNEGO) {
+ ti->syncdata = 0;
+ ti->persgst = ti->offsgst = 0;
+ aic->flags &= ~AIC_SYNCHNEGO;
+ ti->flags &= ~DO_NEGOTIATE;
+ }
+ /* Not all targets understand INITIATOR_DETECTED_ERR */
+ if (aic->msgout == SEND_INIT_DET_ERR)
+ aic_sched_msgout(SEND_ABORT);
+ break;
+ case MSG_NOOP: /* Will do! Immediately, sir!*/
+ break; /* Hah, that was easy! */
+ case MSG_DISCONNECT:
+ if (!acb) {
+ aic_sched_msgout(SEND_ABORT);
+ printf("aic: nothing to DISCONNECT\n");
+ break;
+ }
+ ti->dconns++;
+ TAILQ_INSERT_HEAD(&aic->nexus_list, acb, chain);
+ acb = aic->nexus = NULL;
+ aic->state = AIC_IDLE;
+ aic->flags |= AIC_BUSFREE_OK;
+ break;
+ case MSG_SAVEDATAPOINTER:
+ if (!acb) {
+ aic_sched_msgout(SEND_ABORT);
+ printf("aic: no DATAPOINTERs to save\n");
+ break;
+ }
+ acb->dleft = aic->dleft;
+ acb->daddr = aic->dp;
+ break;
+ case MSG_RESTOREPOINTERS:
+ if (!acb) {
+ aic_sched_msgout(SEND_ABORT);
+ printf("aic: no DATAPOINTERs to restore\n");
+ break;
+ }
+ aic->dp = acb->daddr;
+ aic->dleft = acb->dleft;
+ break;
+ case MSG_EXTENDED:
+ switch (aic->imess[2]) {
+ case MSG_EXT_SDTR:
+ per = aic->imess[3] * 4;
+ rate = (per + 49 - 100)/50;
+ offs = aic->imess[4];
+ if (offs == 0)
+ ti->syncdata = 0;
+ else if (rate > 7) {
+ /* Too slow for aic6360. Do asynch
+ * instead. Renegotiate the deal.
+ */
+ ti->persgst = 0;
+ ti->offsgst = 0;
+ aic_sched_msgout(SEND_SDTR);
+ } else {
+ rate = rate<<4 | offs;
+ ti->syncdata = rate;
+ }
+ break;
+ default: /* Extended messages we don't handle */
+ aic_sched_msgout(SEND_REJECT);
+ break;
+ }
+ break;
+ default:
+ aic_sched_msgout(SEND_REJECT);
+ break;
+ }
+ } else if (aic->state == AIC_RESELECTED) {
+ struct scsi_link *sc;
+ struct acb *acb;
+ u_char selid, lunit;
+ /*
+ * Which target is reselecting us? (The ID bit really)
+ */
+ selid = inb(SELID) & ~(1<<AIC_SCSI_HOSTID);
+ if (MSG_ISIDENT(aic->imess[0])) { /* Identify? */
+ AIC_MISC(("searching "));
+ /* Search wait queue for disconnected cmd
+ * The list should be short, so I haven't bothered with
+ * any more sophisticated structures than a simple
+ * singly linked list.
+ */
+ lunit = aic->imess[0] & 0x07;
+ for (acb = aic->nexus_list.tqh_first; acb;
+ acb = acb->chain.tqe_next) {
+ sc = acb->xs->sc_link;
+ if (sc->lun == lunit &&
+ selid == (1<<sc->target)) {
+ TAILQ_REMOVE(&aic->nexus_list, acb,
+ chain);
+ break;
+ }
+ }
+ if (!acb) { /* Invalid reselection! */
+ aic_sched_msgout(SEND_ABORT);
+ printf("aic: invalid reselect (idbit=0x%2x)\n",
+ selid);
+ } else { /* Reestablish nexus */
+ /* Setup driver data structures and
+ * do an implicit RESTORE POINTERS
+ */
+ aic->nexus = acb;
+ aic->dp = acb->daddr;
+ aic->dleft = acb->dleft;
+ aic->tinfo[sc->target].lubusy |= (1<<sc->lun);
+ outb(SCSIRATE,aic->tinfo[sc->target].syncdata);
+ AIC_MISC(("... found acb"));
+ aic->state = AIC_HASNEXUS;
+ }
+ } else {
+ printf("aic: bogus reselect (no IDENTIFY) %0x2x\n",
+ selid);
+ aic_sched_msgout(SEND_DEV_RESET);
+ }
+ } else { /* Neither AIC_HASNEXUS nor AIC_RESELECTED! */
+ printf("aic: unexpected message in; will send DEV_RESET\n");
+ aic_sched_msgout(SEND_DEV_RESET);
+ }
+ /* Must not forget to ACK the last message byte ... */
+ outb(SXFRCTL0, CHEN|SPIOEN);
+ inb(SCSIDAT);
+ outb(SXFRCTL0, CHEN);
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+}
+
+
+/* The message out (and in) stuff is a bit complicated:
+ * If the target requests another message (sequence) without
+ * having changed phase in between it really asks for a
+ * retransmit, probably due to parity error(s).
+ * The following messages can be sent:
+ * IDENTIFY @ These 3 stems from scsi command activity
+ * BUS_DEV_RESET @
+ * IDENTIFY + SDTR @
+ * MESSAGE_REJECT if MSGI doesn't make sense
+ * MESSAGE_PARITY_ERROR if MSGI spots a parity error
+ * NOOP if asked for a message and there's nothing to send
+ */
+static void
+aic_msgout(aic)
+ register struct aic_data *aic;
+{
+ register u_short iobase = aic->iobase;
+ struct aic_tinfo *ti;
+ struct acb *acb;
+
+ /* First determine what to send. If we haven't seen a
+ * phasechange this is a retransmission request.
+ */
+ outb(SCSISIGO, PH_MSGO);
+ if (aic->prevphase != PH_MSGO) { /* NOT a retransmit */
+ /* Pick up highest priority message */
+ aic->msgout = aic->msgpriq & -aic->msgpriq; /* What message? */
+ aic->omlen = 1; /* "Default" message len */
+ switch (aic->msgout) {
+ case SEND_SDTR: /* Also implies an IDENTIFY message */
+ acb = aic->nexus;
+ ti = &aic->tinfo[acb->xs->sc_link->target];
+ aic->omess[1] = MSG_EXTENDED;
+ aic->omess[2] = 3;
+ aic->omess[3] = MSG_EXT_SDTR;
+ aic->omess[4] = ti->persgst >> 2;
+ aic->omess[5] = ti->offsgst;
+ aic->omlen = 6;
+ /* Fallthrough! */
+ case SEND_IDENTIFY:
+ if (aic->state != AIC_HASNEXUS) {
+ printf("aic at line %d: no nexus", __LINE__);
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ }
+ acb = aic->nexus;
+ aic->omess[0] = MSG_IDENTIFY(acb->xs->sc_link->lun);
+ break;
+ case SEND_DEV_RESET:
+ aic->omess[0] = MSG_BUS_DEV_RESET;
+ aic->flags |= AIC_BUSFREE_OK;
+ break;
+ case SEND_PARITY_ERROR:
+ aic->omess[0] = MSG_PARITY_ERR;
+ break;
+ case SEND_ABORT:
+ aic->omess[0] = MSG_ABORT;
+ aic->flags |= AIC_BUSFREE_OK;
+ break;
+ case SEND_INIT_DET_ERR:
+ aic->omess[0] = MSG_INITIATOR_DET_ERR;
+ break;
+ case SEND_REJECT:
+ aic->omess[0] = MSG_MESSAGE_REJECT;
+ break;
+ default:
+ aic->omess[0] = MSG_NOOP;
+ break;
+ }
+ aic->omp = aic->omess;
+ } else if (aic->omp == &aic->omess[aic->omlen]) {
+ /* Have sent the message at least once, this is a retransmit.
+ */
+ AIC_MISC(("retransmitting "));
+ if (aic->omlen > 1)
+ outb(SCSISIGO, PH_MSGO|ATNO);
+ }
+ /* else, we're in the middle of a multi-byte message */
+ outb(SXFRCTL0, CHEN|SPIOEN);
+ outb(DMACNTRL0, INTEN|RSTFIFO);
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+ do {
+ LOGLINE(aic);
+ do {
+ aic->phase = aicphase(aic);
+ } while (aic->phase == PH_INVALID);
+ if (aic->phase != PH_MSGO)
+ /* Target left MSGO, possibly to reject our
+ * message
+ */
+ break;
+ /* Clear ATN before last byte */
+ if (aic->omp == &aic->omess[aic->omlen-1])
+ outb(CLRSINT1, CLRATNO);
+ outb(SCSIDAT, *aic->omp++); /* Send MSG */
+ LOGLINE(aic);
+ while (inb(SCSISIGI) & ACKO)
+ ;
+ } while (aic->omp != &aic->omess[aic->omlen]);
+ aic->progress = aic->omp != aic->omess;
+ /* We get here in two ways:
+ * a) phase != MSGO. Target is probably going to reject our message
+ * b) aic->omp == &aic->omess[aic->omlen], i.e. the message has been
+ * transmitted correctly and accepted by the target.
+ */
+ if (aic->phase == PH_MSGO) { /* Message accepted by target! */
+ aic->msgpriq &= ~aic->msgout;
+ aic->msgout = 0;
+ }
+ outb(SXFRCTL0, CHEN); /* Disable SPIO */
+ outb(SIMODE0, 0); /* Setup interrupts before leaving */
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+ /* Enabled ints: SCSIPERR, SCSIRSTI (unexpected)
+ * REQINIT (expected) BUSFREE (possibly expected)
+ */
+}
+
+/* aic_dataout: perform a data transfer using the FIFO datapath in the aic6360
+ * Precondition: The SCSI bus should be in the DOUT phase, with REQ asserted
+ * and ACK deasserted (i.e. waiting for a data byte)
+ * This new revision has been optimized (I tried) to make the common case fast,
+ * and the rarer cases (as a result) somewhat more comlex
+ */
+static void
+aic_dataout(aic)
+ register struct aic_data *aic;
+{
+ register u_short iobase = aic->iobase;
+ register u_char dmastat;
+ int amount, olddleft = aic->dleft;
+#define DOUTAMOUNT 128 /* Full FIFO */
+
+ /* Enable DATA OUT transfers */
+ outb(SCSISIGO, PH_DOUT);
+ outb(CLRSINT1, CLRPHASECHG);
+ /* Clear FIFOs and counters */
+ outb(SXFRCTL0, CHEN|CLRSTCNT|CLRCH);
+ outb(DMACNTRL0, WRITE|INTEN|RSTFIFO);
+ /* Enable FIFOs */
+ outb(SXFRCTL0, SCSIEN|DMAEN|CHEN);
+ outb(DMACNTRL0, ENDMA|DWORDPIO|WRITE|INTEN);
+
+ /* Setup to detect:
+ * PHASEMIS & PHASECHG: target has left the DOUT phase
+ * SCSIRST: something just pulled the RST line.
+ * BUSFREE: target has unexpectedly left the DOUT phase
+ */
+ outb(SIMODE1, ENPHASEMIS|ENSCSIRST|ENBUSFREE|ENPHASECHG);
+
+ /* I have tried to make the main loop as tight as possible. This
+ * means that some of the code following the loop is a bit more
+ * complex than otherwise.
+ */
+ while (aic->dleft) {
+ int xfer;
+
+ LOGLINE(aic);
+
+ for (;;) {
+ dmastat = inb(DMASTAT);
+ if (dmastat & DFIFOEMP)
+ break;
+ if (dmastat & INTSTAT)
+ goto phasechange;
+ }
+
+ xfer = min(DOUTAMOUNT, aic->dleft);
+
+#if AIC_USE_DWORDS
+ if (xfer >= 12) {
+ outsl(DMADATALONG, aic->dp, xfer/4);
+ aic->dleft -= xfer & ~3;
+ aic->dp += xfer & ~3;
+ xfer &= 3;
+ }
+#else
+ if (xfer >= 8) {
+ outsw(DMADATA, aic->dp, xfer/2);
+ aic->dleft -= xfer & ~1;
+ aic->dp += xfer & ~1;
+ xfer &= 1;
+ }
+#endif
+
+ if (xfer) {
+ outb(DMACNTRL0, ENDMA|B8MODE|INTEN);
+ outsb(DMADATA, aic->dp, xfer);
+ aic->dleft -= xfer;
+ aic->dp += xfer;
+ outb(DMACNTRL0, ENDMA|DWORDPIO|INTEN);
+ }
+ }
+
+ /* See the bytes off chip */
+ for (;;) {
+ dmastat = inb(DMASTAT);
+ if ((dmastat & DFIFOEMP) && (inb(SSTAT2) & SEMPTY))
+ break;
+ if (dmastat & INTSTAT)
+ goto phasechange;
+ }
+
+phasechange:
+ /* We now have the data off chip. */
+ outb(SXFRCTL0, CHEN);
+
+ if (dmastat & INTSTAT) { /* Some sort of phasechange */
+ register u_char sstat2;
+ /* Stop transfers, do some accounting */
+ amount = inb(FIFOSTAT);
+ sstat2 = inb(SSTAT2);
+ if ((sstat2 & 7) == 0)
+ amount += sstat2 & SFULL ? 8 : 0;
+ else
+ amount += sstat2 & 7;
+ aic->dleft += amount;
+ aic->dp -= amount;
+ AIC_MISC(("+%d ", amount));
+ }
+
+ outb(DMACNTRL0, RSTFIFO|INTEN);
+ LOGLINE(aic);
+ while (inb(SXFRCTL0) & SCSIEN)
+ ;
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+ /* Enabled ints: BUSFREE, SCSIPERR, SCSIRSTI (unexpected)
+ * REQINIT (expected)
+ */
+ aic->progress = olddleft != aic->dleft;
+ return;
+}
+
+/* aic_datain: perform data transfers using the FIFO datapath in the aic6360
+ * Precondition: The SCSI bus should be in the DIN phase, with REQ asserted
+ * and ACK deasserted (i.e. at least one byte is ready).
+ * For now, uses a pretty dumb algorithm, hangs around until all data has been
+ * transferred. This, is OK for fast targets, but not so smart for slow
+ * targets which don't disconnect or for huge transfers.
+ */
+static void
+aic_datain(aic)
+ register struct aic_data *aic;
+{
+ register u_short iobase = aic->iobase;
+ register u_char dmastat;
+ int olddleft = aic->dleft;
+#define DINAMOUNT 128 /* Default amount of data to transfer */
+
+ /* Enable DATA IN transfers */
+ outb(SCSISIGO, PH_DIN);
+ outb(CLRSINT1, CLRPHASECHG);
+ /* Clear FIFOs and counters */
+ outb(SXFRCTL0, CHEN|CLRSTCNT|CLRCH);
+ outb(DMACNTRL0, INTEN|RSTFIFO);
+ /* Enable FIFOs */
+ outb(SXFRCTL0, SCSIEN|DMAEN|CHEN);
+ outb(DMACNTRL0, ENDMA|DWORDPIO|INTEN);
+
+ outb(SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE|ENPHASECHG);
+
+ /* We leave this loop if one or more of the following is true:
+ * a) phase != PH_DIN && FIFOs are empty
+ * b) SCSIRSTI is set (a reset has occurred) or busfree is detected.
+ */
+ while (aic->dleft) {
+ int done = 0;
+ int xfer;
+
+ LOGLINE(aic);
+
+ /* Wait for fifo half full or phase mismatch */
+ for (;;) {
+ dmastat = inb(DMASTAT);
+ if (dmastat & (DFIFOFULL|INTSTAT))
+ break;
+ }
+
+ if (dmastat & DFIFOFULL)
+ xfer = DINAMOUNT;
+ else {
+ while ((inb(SSTAT2) & SEMPTY) == 0)
+ ;
+ xfer = inb(FIFOSTAT);
+ done = 1;
+ }
+
+ xfer = min(xfer, aic->dleft);
+
+#if AIC_USE_DWORDS
+ if (xfer >= 12) {
+ insl(DMADATALONG, aic->dp, xfer/4);
+ aic->dleft -= xfer & ~3;
+ aic->dp += xfer & ~3;
+ xfer &= 3;
+ }
+#else
+ if (xfer >= 8) {
+ insw(DMADATA, aic->dp, xfer/2);
+ aic->dleft -= xfer & ~1;
+ aic->dp += xfer & ~1;
+ xfer &= 1;
+ }
+#endif
+
+ if (xfer) {
+ outb(DMACNTRL0, ENDMA|B8MODE|INTEN);
+ insb(DMADATA, aic->dp, xfer);
+ aic->dleft -= xfer;
+ aic->dp += xfer;
+ outb(DMACNTRL0, ENDMA|DWORDPIO|INTEN);
+ }
+
+ if (done)
+ break;
+ }
+
+#if 0
+ if (aic->dleft)
+ printf("residual of %d\n", aic->dleft);
+#endif
+
+ aic->progress = olddleft != aic->dleft;
+ /* Some SCSI-devices are rude enough to transfer more data than what
+ * was requested, e.g. 2048 bytes from a CD-ROM instead of the
+ * requested 512. Test for progress, i.e. real transfers. If no real
+ * transfers have been performed (acb->dleft is probably already zero)
+ * and the FIFO is not empty, waste some bytes....
+ */
+ if (!aic->progress) {
+ int extra = 0;
+ LOGLINE(aic);
+
+ for (;;) {
+ dmastat = inb(DMASTAT);
+ if (dmastat & DFIFOEMP)
+ break;
+ (void) inb(DMADATA); /* Throw it away */
+ extra++;
+ }
+
+ AIC_MISC(("aic: %d extra bytes from %d:%d\n", extra,
+ acb->xs->sc_link->target, acb->xs->sc_link->lun));
+ aic->progress = extra;
+ }
+
+ /* Stop the FIFO data path */
+ outb(SXFRCTL0, CHEN);
+
+ outb(DMACNTRL0, RSTFIFO|INTEN);
+ /* Come back when REQ is set again */
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+ LOGLINE(aic);
+}
+
+
+/*
+ * This is the workhorse routine of the driver.
+ * Deficiencies (for now):
+ * 1) always uses programmed I/O
+ * 2) doesn't support synchronous transfers properly (yet)
+ */
+
+void
+aicintr(int unit)
+{
+ struct aic_data *aic = aicdata[unit];
+ register struct acb *acb;
+ register struct scsi_link *sc;
+ register u_short iobase = aic->iobase;
+ struct aic_tinfo *ti;
+ u_char sstat0, sstat1, sstat2, sxfrctl0;
+
+
+ LOGLINE(aic);
+ /* Clear INTEN. This is important if we're running with edge
+ * triggered interrupts as we don't guarantee that all interrupts will
+ * be served during one single invocation of this routine, i.e. we may
+ * need another edge.
+ */
+ outb(DMACNTRL0, 0);
+ AIC_TRACE(("aicintr\n"));
+
+ /*
+ * 1st check for abnormal conditions, such as reset or parity errors
+ */
+ sstat1 = inb(SSTAT1);
+ AIC_MISC(("s1:0x%02x ", sstat1));
+ if (sstat1 & (SCSIRSTI|SCSIPERR)) {
+ if (sstat1 & SCSIRSTI) {
+ printf("aic: reset in -- reinitializing....\n");
+ aic_init(aic); /* Restart everything */
+ LOGLINE(aic);
+ outb(DMACNTRL0, INTEN);
+ return;
+ } else {
+ printf("aic: SCSI bus parity error\n");
+ outb(CLRSINT1, CLRSCSIPERR);
+ if (aic->prevphase == PH_MSGI)
+ aic_sched_msgout(SEND_PARITY_ERROR);
+ else
+ aic_sched_msgout(SEND_INIT_DET_ERR);
+ }
+ }
+
+ /*
+ * If we're not already busy doing something test for the following
+ * conditions:
+ * 1) We have been reselected by something
+ * 2) We have selected something successfully
+ * 3) Our selection process has timed out
+ * 4) This is really a bus free interrupt just to get a new command
+ * going?
+ * 5) Spurious interrupt?
+ */
+ sstat0 = inb(SSTAT0);
+ AIC_MISC(("s0:0x%02x ", sstat0));
+ if (aic->state != AIC_HASNEXUS) { /* No nexus yet */
+ if (sstat0 & SELDI) {
+ LOGLINE(aic);
+ /* We have been reselected. Things to do:
+ * a) If we're trying to select something ourselves
+ * back off the current command.
+ * b) "Wait" for a message in phase (IDENTIFY)
+ * c) Call aic_msgin() to get the identify message and
+ * retrieve the disconnected command from the wait
+ * queue.
+ */
+ AIC_MISC(("reselect "));
+ /* If we're trying to select a target ourselves,
+ * push our command back into the rdy list.
+ */
+ if (aic->state == AIC_SELECTING) {
+ AIC_MISC(("backoff selector "));
+ TAILQ_INSERT_HEAD(&aic->ready_list, aic->nexus,
+ chain);
+ aic->nexus = NULL;
+ }
+ aic->state = AIC_RESELECTED;
+ /* Clear interrupts, disable future selection stuff
+ * including select interrupts and timeouts
+ */
+ outb(CLRSINT0, CLRSELDI);
+ outb(SCSISEQ, 0);
+ outb(SIMODE0, 0);
+ /* Setup chip so we may detect spurious busfree
+ * conditions later.
+ */
+ outb(CLRSINT1, CLRBUSFREE);
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|
+ ENSCSIPERR|ENREQINIT);
+ /* Now, we're expecting an IDENTIFY message. */
+ aic->phase = aicphase(aic);
+ if (aic->phase & PH_PSBIT) {
+ LOGLINE(aic);
+ outb(DMACNTRL0, INTEN);
+ return; /* Come back when REQ is set */
+ }
+ if (aic->phase == PH_MSGI)
+ aic_msgin(aic); /* Handle identify message */
+ else {
+ /* Things are seriously fucked up.
+ * Pull the brakes, i.e. RST
+ */
+ printf("aic at line %d: target didn't identify\n", __LINE__);
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ aic_init(aic);
+ return;
+ }
+ if (aic->state != AIC_HASNEXUS) {/* IDENTIFY fail?! */
+ printf("aic at line %d: identify failed\n",
+ __LINE__);
+ aic_init(aic);
+ return;
+ } else {
+ outb(SIMODE1,
+ ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+ /* Fallthrough to HASNEXUS part of aicintr */
+ }
+ } else if (sstat0 & SELDO) {
+ LOGLINE(aic);
+ /* We have selected a target. Things to do:
+ * a) Determine what message(s) to send.
+ * b) Verify that we're still selecting the target.
+ * c) Mark device as busy.
+ */
+ acb = aic->nexus;
+ if (!acb) {
+ printf("aic at line %d: missing acb", __LINE__);
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ }
+ sc = acb->xs->sc_link;
+ ti = &aic->tinfo[sc->target];
+ if (acb->xs->flags & SCSI_RESET)
+ aic->msgpriq = SEND_DEV_RESET;
+ else if (ti->flags & DO_NEGOTIATE)
+ aic->msgpriq = SEND_IDENTIFY|SEND_SDTR;
+ else
+ aic->msgpriq = SEND_IDENTIFY;
+ /* Setup chip to enable later testing for busfree
+ * conditions
+ */
+ outb(CLRSINT1, CLRBUSFREE);
+ outb(SCSISEQ, 0); /* Stop selection stuff */
+ nandreg(SIMODE0, ENSELDO); /* No more selectout ints */
+ sstat0 = inb(SSTAT0);
+ if (sstat0 & SELDO) { /* Still selected!? */
+ outb(SIMODE0, 0);
+ outb(SIMODE1, ENSCSIRST|ENSCSIPERR|
+ ENBUSFREE|ENREQINIT);
+ aic->state = AIC_HASNEXUS;
+ aic->flags = 0;
+ aic->prevphase = PH_INVALID;
+ aic->dp = acb->daddr;
+ aic->dleft = acb->dleft;
+ ti->lubusy |= (1<<sc->lun);
+ AIC_MISC(("select ok "));
+ } else {
+ /* Has seen busfree since selection, i.e.
+ * a "spurious" selection. Shouldn't happen.
+ */
+ printf("aic: unexpected busfree\n");
+ acb->xs->error = XS_DRIVER_STUFFUP;
+ untimeout(aic_timeout, (caddr_t)acb);
+ aic_done(acb);
+ }
+ LOGLINE(aic);
+ outb(DMACNTRL0, INTEN);
+ return;
+ } else if (sstat1 & SELTO) {
+ /* Selection timed out. What to do:
+ * Disable selections out and fail the command with
+ * code XS_TIMEOUT.
+ */
+ acb = aic->nexus;
+ if (!acb) {
+ printf("aic at line %d: missing acb", __LINE__);
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ }
+ outb(SCSISEQ, ENRESELI|ENAUTOATNP);
+ outb(SXFRCTL1, 0);
+ outb(CLRSINT1, CLRSELTIMO);
+ aic->state = AIC_IDLE;
+ acb->xs->error = XS_TIMEOUT;
+ untimeout(aic_timeout, (caddr_t)acb);
+ aic_done(acb);
+ LOGLINE(aic);
+ outb(DMACNTRL0, INTEN);
+ return;
+ } else {
+ /* Assume a bus free interrupt. What to do:
+ * Start selecting.
+ */
+ if (aic->state == AIC_IDLE)
+ aic_sched(aic);
+#if AIC_DEBUG
+ else
+ AIC_MISC(("Extra aic6360 interrupt."));
+#endif
+ LOGLINE(aic);
+ outb(DMACNTRL0, INTEN);
+ return;
+ }
+ }
+ /* Driver is now in state AIC_HASNEXUS, i.e. we have a current command
+ * working the SCSI bus.
+ */
+ acb = aic->nexus;
+ if (aic->state != AIC_HASNEXUS || acb == NULL) {
+ printf("aic: no nexus!!\n");
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ }
+
+ /* What sort of transfer does the bus signal? */
+ aic->phase = aicphase(aic);
+ if (!(aic->phase & PH_PSBIT)) /* not a pseudo phase */
+ outb(SCSISIGO, aic->phase);
+ outb(CLRSINT1, CLRPHASECHG);
+ /* These interrupts are enabled by default:
+ * SCSIRSTI, SCSIPERR, BUSFREE, REQINIT
+ */
+ switch (aic->phase) {
+ case PH_MSGO:
+ LOGLINE(aic);
+ if (aic_debug & AIC_SHOWMISC)
+ printf("PH_MSGO ");
+ aic_msgout(aic);
+ aic->prevphase = PH_MSGO;
+ /* Setup interrupts before leaving */
+ outb(SIMODE0, 0);
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+ /* Enabled ints: SCSIPERR, SCSIRSTI (unexpected)
+ * REQINIT (expected) BUSFREE (possibly expected)
+ */
+ break;
+ case PH_CMD: /* CMD phase & REQ asserted */
+ LOGLINE(aic);
+ if (aic_debug & AIC_SHOWMISC)
+ printf("PH_CMD 0x%02x (%d) ",
+ acb->cmd.opcode, acb->clen);
+ outb(SCSISIGO, PH_CMD);
+ /* Use FIFO for CMDs. Assumes that no cmd > 128 bytes. OK? */
+ /* Clear hostFIFO and enable EISA-hostFIFO transfers */
+ outb(DMACNTRL0, WRITE|RSTFIFO|INTEN); /* 3(4) */
+ /* Clear scsiFIFO and enable SCSI-interface
+ & hostFIFO-scsiFIFO transfers */
+ outb(SXFRCTL0, CHEN|CLRCH|CLRSTCNT); /* 4 */
+ outb(SXFRCTL0, SCSIEN|DMAEN|CHEN); /* 5 */
+ outb(DMACNTRL0, ENDMA|WRITE|INTEN); /* 3+6 */
+ /* What (polled) interrupts to enable */
+ outb(SIMODE1, ENPHASEMIS|ENSCSIRST|ENBUSFREE|ENSCSIPERR);
+ /* DFIFOEMP is set, FIFO (128 byte) is always big enough */
+ outsw(DMADATA, (short *)&acb->cmd, acb->clen>>1);
+
+ /* Wait for SCSI FIFO to drain */
+ LOGLINE(aic);
+ do {
+ sstat2 = inb(SSTAT2);
+ } while (!(sstat2 & SEMPTY) && !(inb(DMASTAT) & INTSTAT));
+ if (!(inb(SSTAT2) & SEMPTY)) {
+ printf("aic at line %d: SCSI-FIFO didn't drain\n",
+ __LINE__);
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ acb->xs->error = XS_DRIVER_STUFFUP;
+ untimeout(aic_timeout, (caddr_t)acb);
+ aic_done(acb);
+ aic_init(aic);
+ return;
+ }
+ outb(SXFRCTL0, CHEN); /* Clear SCSIEN & DMAEN */
+ outb(SIMODE0, 0);
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR);
+ LOGLINE(aic);
+ do {
+ sxfrctl0 = inb(SXFRCTL0);
+ } while (sxfrctl0 & SCSIEN && !(inb(DMASTAT) & INTSTAT));
+ if (sxfrctl0 & SCSIEN) {
+ printf("aic at line %d: scsi xfer never finished\n",
+ __LINE__);
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ acb->xs->error = XS_DRIVER_STUFFUP;
+ untimeout(aic_timeout, (caddr_t)acb);
+ aic_done(acb);
+ aic_init(aic);
+ return;
+ }
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+ /* Enabled ints: BUSFREE, SCSIPERR, SCSIRSTI (unexpected)
+ * REQINIT (expected)
+ */
+ aic->prevphase = PH_CMD;
+ break;
+ case PH_DOUT:
+ LOGLINE(aic);
+ AIC_MISC(("PH_DOUT [%d] ",aic->dleft));
+ aic_dataout(aic);
+ aic->prevphase = PH_DOUT;
+ break;
+ case PH_MSGI:
+ LOGLINE(aic);
+ if (aic_debug & AIC_SHOWMISC)
+ printf("PH_MSGI ");
+ aic_msgin(aic);
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+ aic->prevphase = PH_MSGI;
+ break;
+ case PH_DIN:
+ LOGLINE(aic);
+ if (aic_debug & AIC_SHOWMISC)
+ printf("PH_DIN ");
+ aic_datain(aic);
+ aic->prevphase = PH_DIN;
+ break;
+ case PH_STAT:
+ LOGLINE(aic);
+ if (aic_debug & AIC_SHOWMISC)
+ printf("PH_STAT ");
+ outb(SCSISIGO, PH_STAT);
+ outb(SXFRCTL0, CHEN|SPIOEN);
+ outb(DMACNTRL0, RSTFIFO|INTEN);
+ outb(SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE|ENSCSIPERR);
+ acb->stat = inb(SCSIDAT);
+ outb(SXFRCTL0, CHEN);
+ if (aic_debug & AIC_SHOWMISC)
+ printf("0x%02x ", acb->stat);
+ outb(SIMODE1, ENSCSIRST|ENBUSFREE|ENSCSIPERR|ENREQINIT);
+ aic->prevphase = PH_STAT;
+ break;
+ case PH_INVALID:
+ LOGLINE(aic);
+ break;
+ case PH_BUSFREE:
+ LOGLINE(aic);
+ if (aic->flags & AIC_BUSFREE_OK) { /*It's fun the 1st time.. */
+ aic->flags &= ~AIC_BUSFREE_OK;
+ } else {
+ printf("aic at line %d: unexpected busfree phase\n",
+ __LINE__);
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ }
+ break;
+ default:
+ printf("aic at line %d: bogus bus phase\n", __LINE__);
+ Debugger("aic6360");
+ fatal_if_no_DDB();
+ break;
+ }
+ LOGLINE(aic);
+ outb(DMACNTRL0, INTEN);
+ return;
+}
+
+static void
+aic_timeout(void *arg1) {
+ int s = splbio();
+ struct acb *acb = (struct acb *)arg1;
+ int unit;
+ struct aic_data *aic;
+
+ unit = acb->xs->sc_link->adapter_unit;
+ aic = aicdata[unit];
+ sc_print_addr(acb->xs->sc_link);
+ acb->xs->error = XS_TIMEOUT;
+ printf("timed out\n");
+
+ aic_done(acb);
+ splx(s);
+}
+
+#if AIC_DEBUG
+/*
+ * The following functions are mostly used for debugging purposes, either
+ * directly called from the driver or from the kernel debugger.
+ */
+
+void
+aic_show_scsi_cmd(acb)
+ struct acb *acb;
+{
+ u_char *b = (u_char *)&acb->cmd;
+ struct scsi_link *sc = acb->xs->sc_link;
+ int i;
+
+ sc_print_addr(sc);
+ if (!(acb->xs->flags & SCSI_RESET)) {
+ for (i = 0; i < acb->clen; i++) {
+ if (i)
+ printf(",");
+ printf("%x", b[i]);
+ }
+ printf("\n");
+ } else
+ printf("RESET\n");
+}
+
+void
+aic_print_acb(acb)
+ struct acb *acb;
+{
+
+ printf("acb@%x xs=%x flags=%x", acb, acb->xs, acb->flags);
+ printf(" daddr=%x dleft=%d stat=%x\n",
+ (long)acb->daddr, acb->dleft, acb->stat);
+ aic_show_scsi_cmd(acb);
+}
+
+void
+aic_print_active_acb()
+{
+ struct acb *acb;
+ struct aic_data *aic = aicdata[0];
+
+ printf("ready list:\n");
+ for (acb = aic->ready_list.tqh_first; acb; acb = acb->chain.tqe_next)
+ aic_print_acb(acb);
+ printf("nexus:\n");
+ if (aic->nexus)
+ aic_print_acb(aic->nexus);
+ printf("nexus list:\n");
+ for (acb = aic->nexus_list.tqh_first; acb; acb = acb->chain.tqe_next)
+ aic_print_acb(acb);
+}
+
+void
+aic_dump6360()
+{
+ u_short iobase = 0x340;
+
+ printf("aic6360: SCSISEQ=%x SXFRCTL0=%x SXFRCTL1=%x SCSISIGI=%x\n",
+ inb(SCSISEQ), inb(SXFRCTL0), inb(SXFRCTL1), inb(SCSISIGI));
+ printf(" SSTAT0=%x SSTAT1=%x SSTAT2=%x SSTAT3=%x SSTAT4=%x\n",
+ inb(SSTAT0), inb(SSTAT1), inb(SSTAT2), inb(SSTAT3), inb(SSTAT4));
+ printf(" SIMODE0=%x SIMODE1=%x DMACNTRL0=%x DMACNTRL1=%x DMASTAT=%x\n",
+ inb(SIMODE0), inb(SIMODE1), inb(DMACNTRL0), inb(DMACNTRL1),
+ inb(DMASTAT));
+ printf(" FIFOSTAT=%d SCSIBUS=0x%x\n",
+ inb(FIFOSTAT), inb(SCSIBUS));
+}
+
+void
+aic_dump_driver()
+{
+ struct aic_data *aic = aicdata[0];
+ struct aic_tinfo *ti;
+ int i;
+
+ printf("nexus=%x phase=%x prevphase=%x\n", aic->nexus, aic->phase,
+ aic->prevphase);
+ printf("state=%x msgin=%x msgpriq=%x msgout=%x imlen=%d omlen=%d\n",
+ aic->state, aic->imess[0], aic->msgpriq, aic->msgout, aic->imlen,
+ aic->omlen);
+ printf("history:");
+ i = aic->hp;
+ do {
+ printf(" %d", aic->history[i]);
+ i = (i + 1) % AIC_HSIZE;
+ } while (i != aic->hp);
+ printf("*\n");
+ for (i = 0; i < 7; i++) {
+ ti = &aic->tinfo[i];
+ printf("tinfo%d: %d cmds %d disconnects %d timeouts",
+ i, ti->cmds, ti->dconns, ti->touts);
+ printf(" %d senses flags=%x\n", ti->senses, ti->flags);
+ }
+}
+#endif
diff --git a/sys/pc98/pc98/atapi.c b/sys/pc98/pc98/atapi.c
new file mode 100644
index 0000000..f6bf628
--- /dev/null
+++ b/sys/pc98/pc98/atapi.c
@@ -0,0 +1,1074 @@
+/*
+ * Device-independent level for ATAPI drivers.
+ *
+ * Copyright (C) 1995 Cronyx Ltd.
+ * Author Serge Vakulenko, <vak@cronyx.ru>
+ *
+ * This software is distributed with NO WARRANTIES, not even the implied
+ * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Authors grant any other persons or organisations permission to use
+ * or modify this software as long as this message is kept with the software,
+ * all derivative works or modified versions.
+ *
+ * Version 1.9, Mon Oct 9 22:34:47 MSK 1995
+ */
+
+/*
+ * The ATAPI level is implemented as a machine-dependent layer
+ * between the device driver and the IDE controller.
+ * All the machine- and controller dependency is isolated inside
+ * the ATAPI level, while all the device dependency is located
+ * in the device subdriver.
+ *
+ * It seems that an ATAPI bus will became popular for medium-speed
+ * storage devices such as CD-ROMs, magneto-optical disks, tape streamers etc.
+ *
+ * To ease the development of new ATAPI drivers, the subdriver
+ * interface was designed to be as simple as possible.
+ *
+ * Three routines are available for the subdriver to access the device:
+ *
+ * struct atapires atapi_request_wait (ata, unit, cmd, a1, a2, a3, a4, a5,
+ * a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, addr, count);
+ * struct atapi *ata; -- atapi controller descriptor
+ * int unit; -- device unit number on the IDE bus
+ * u_char cmd; -- ATAPI command code
+ * u_char a1..a15; -- ATAPI command arguments
+ * char *addr; -- address of the data buffer for i/o
+ * int count; -- data length, >0 for read ops, <0 for write ops
+ *
+ * The atapi_request_wait() function puts the op in the queue of ATAPI
+ * commands for the IDE controller, starts the controller, the waits for
+ * operation to be completed (using tsleep).
+ * The function should be called from the user phase only (open(), close(),
+ * ioctl() etc).
+ * Ata and unit args are the values which the subdriver gets from the ATAPI
+ * level via attach() call.
+ * Buffer pointed to by *addr should be placed in core memory, static
+ * or dynamic, but not in stack.
+ * The function returns the error code structure, which consists of:
+ * - atapi driver code value
+ * - controller status port value
+ * - controller error port value
+ *
+ * struct atapires atapi_request_immediate (ata, unit, cmd, a1, a2, a3,
+ * a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
+ * addr, count);
+ *
+ * The atapi_request_immediate() function is similar to atapi_request_wait(),
+ * but it does not use interrupts for performing the request.
+ * It should be used during an attach phase to get parameters from the device.
+ *
+ * void atapi_request_callback (ata, unit, cmd, a1, a2, a3, a4, a5,
+ * a6, a7, a8, a9, a10, a11, a12, a13, a14, a15,
+ * addr, count, done, x, y);
+ * struct atapi *ata; -- atapi controller descriptor
+ * int unit; -- device unit number on the IDE bus
+ * u_char cmd; -- ATAPI command code
+ * u_char a1..a15; -- ATAPI command arguments
+ * char *addr; -- address of the data buffer for i/o
+ * int count; -- data length, >0 for read ops, <0 for write ops
+ * void (*done)(); -- function to call when op finished
+ * void *x, *y; -- arguments for done() function
+ *
+ * The atapi_request_callback() function puts the op in the queue of ATAPI
+ * commands for the IDE controller, starts the controller, then returns.
+ * When the operation finishes, then the callback function done()
+ * will be called on the interrupt level.
+ * The function is designed to be callable from the interrupt phase.
+ * The done() functions is called with the following arguments:
+ * (void) (*done) (x, y, count, errcode)
+ * void *x, *y; -- arguments from the atapi_request_callback()
+ * int count; -- the data residual count
+ * struct atapires errcode; -- error code structure, see above
+ *
+ * The new driver could be added in three steps:
+ * 1. Add entries for the new driver to bdevsw and cdevsw tables in conf.c.
+ * You will need to make at least three routines: open(), close(),
+ * strategy() and possibly ioctl().
+ * 2. Make attach() routine, which should allocate all the needed data
+ * structures and print the device description string (see wcdattach()).
+ * 3. Add an appropriate case to the switch in atapi_attach() routine,
+ * call attach() routine of the new driver here. Add the appropriate
+ * #include line at the top of attach.c.
+ * That's all!
+ *
+ * Use #define DEBUG in atapi.c to enable tracing of all i/o operations
+ * on the IDE bus.
+ */
+#undef DEBUG
+
+#include "wdc.h"
+#ifndef ATAPI_MODULE
+# include "wcd.h"
+/* # include "wmt.h" -- add your driver here */
+/* # include "wmd.h" -- add your driver here */
+#endif
+
+#if NWDC > 0 && defined (ATAPI)
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+
+#include <machine/clock.h>
+#include <machine/cpufunc.h>
+
+#ifdef ATAPI_MODULE
+# define ATAPI_STATIC
+#endif
+
+#ifdef PC98
+#include <pc98/pc98/atapi.h>
+#else
+#include <i386/isa/atapi.h>
+#endif
+
+#ifndef ATAPI_STATIC
+/* this code is compiled as part of the kernel if options ATAPI */
+/*
+ * In the case of loadable ATAPI driver we need to store
+ * the probe info for delayed attaching.
+ */
+struct atapidrv atapi_drvtab[4];
+int atapi_ndrv;
+struct atapi *atapi_tab;
+
+int atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
+{
+ atapi_drvtab[atapi_ndrv].ctlr = ctlr;
+ atapi_drvtab[atapi_ndrv].unit = unit;
+ atapi_drvtab[atapi_ndrv].port = port;
+ atapi_drvtab[atapi_ndrv].parent = parent;
+ atapi_drvtab[atapi_ndrv].attached = 0;
+ ++atapi_ndrv;
+ return (1);
+}
+#else /* ATAPI_STATIC */
+/* this code is compiled part of the module */
+
+#ifdef DEBUG
+# define print(s) printf s
+#else
+# define print(s) {/*void*/}
+#endif
+
+/*
+ * ATAPI packet command phase.
+ */
+#define PHASE_CMDOUT (ARS_DRQ | ARI_CMD)
+#define PHASE_DATAIN (ARS_DRQ | ARI_IN)
+#define PHASE_DATAOUT ARS_DRQ
+#define PHASE_COMPLETED (ARI_IN | ARI_CMD)
+#define PHASE_ABORTED 0 /* nonstandard - for NEC 260 */
+
+struct atapi atapitab[NWDC];
+
+static struct atapi_params *atapi_probe (int port, int unit);
+static int atapi_wait (int port, u_char bits_wanted);
+static void atapi_send_cmd (struct atapi *ata, struct atapicmd *ac);
+static int atapi_io (struct atapi *ata, struct atapicmd *ac);
+static int atapi_start_cmd (struct atapi *ata, struct atapicmd *ac);
+static int atapi_wait_cmd (struct atapi *ata, struct atapicmd *ac);
+
+extern int wdstart (int ctrlr);
+extern int wcdattach(struct atapi*, int, struct atapi_params*, int, struct kern_devconf*);
+
+/*
+ * Probe the ATAPI device at IDE controller `ctlr', drive `unit'.
+ * Called at splbio().
+ */
+#ifdef ATAPI_MODULE
+static
+#endif
+int atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
+{
+ struct atapi *ata = atapitab + ctlr;
+ struct atapi_params *ap;
+ char buf [sizeof(ap->model) + 1];
+ char revbuf [sizeof(ap->revision) + 1];
+ struct atapicmd *ac;
+
+ print (("atapi%d.%d at 0x%x: attach called\n", ctlr, unit, port));
+ ap = atapi_probe (port, unit);
+ if (! ap)
+ return (0);
+
+ bcopy (ap->model, buf, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = 0;
+
+ bcopy (ap->revision, revbuf, sizeof(revbuf)-1);
+ revbuf[sizeof(revbuf)-1] = 0;
+
+ printf ("wdc%d: unit %d (atapi): <%s/%s>", ctlr, unit, buf, revbuf);
+
+ /* device is removable */
+ if (ap->removable)
+ printf (", removable");
+
+ /* packet command size */
+ switch (ap->cmdsz) {
+ case AT_PSIZE_12: break;
+ case AT_PSIZE_16: printf (", cmd16"); ata->cmd16 = 1; break;
+ default: printf (", cmd%d", ap->cmdsz);
+ }
+
+ /* DRQ type */
+ switch (ap->drqtype) {
+ case AT_DRQT_MPROC: ata->slow = 1; break;
+ case AT_DRQT_INTR: printf (", intr"); ata->intrcmd = 1; break;
+ case AT_DRQT_ACCEL: printf (", accel"); break;
+ default: printf (", drq%d", ap->drqtype);
+ }
+
+ /* overlap operation supported */
+ if (ap->ovlapflag)
+ printf (", ovlap");
+
+ /* interleaved DMA supported */
+ if (ap->idmaflag)
+ printf (", idma");
+ /* DMA supported */
+ else if (ap->dmaflag)
+ printf (", dma");
+
+ /* IORDY can be disabled */
+ if (ap->iordydis)
+ printf (", iordis");
+ /* IORDY supported */
+ else if (ap->iordyflag)
+ printf (", iordy");
+
+ printf ("\n");
+
+ ata->port = port;
+ ata->ctrlr = ctlr;
+ ata->parent = parent;
+ ata->attached[unit] = 0;
+#ifdef DEBUG
+ ata->debug = 1;
+#else
+ ata->debug = 0;
+#endif
+ /* Initialize free queue. */
+ ata->cmdrq[15].next = 0;
+ for (ac = ata->cmdrq+14; ac >= ata->cmdrq; --ac)
+ ac->next = ac+1;
+ ata->free = ata->cmdrq;
+
+ if (ap->proto != AT_PROTO_ATAPI) {
+ printf ("wdc%d: unit %d: unknown ATAPI protocol=%d\n",
+ ctlr, unit, ap->proto);
+ free (ap, M_TEMP);
+ return (0);
+ }
+#ifdef ATAPI_MODULE
+ ata->params[unit] = ap;
+ return (1);
+#else
+ switch (ap->devtype) {
+ default:
+ /* unknown ATAPI device */
+ printf ("wdc%d: unit %d: unknown ATAPI type=%d\n",
+ ctlr, unit, ap->devtype);
+ break;
+
+ case AT_TYPE_DIRECT: /* direct-access */
+ case AT_TYPE_CDROM: /* CD-ROM device */
+#if NWCD > 0
+ /* ATAPI CD-ROM */
+ if (wcdattach (ata, unit, ap, ata->debug, parent) < 0)
+ break;
+ /* Device attached successfully. */
+ ata->attached[unit] = 1;
+ return (1);
+#else
+ printf ("wdc%d: ATAPI CD-ROMs not configured\n", ctlr);
+ break;
+#endif
+
+ case AT_TYPE_TAPE: /* streaming tape (QIC-121 model) */
+#if NWMT > 0
+ /* Add your driver here */
+#else
+ printf ("wdc%d: ATAPI streaming tapes not supported yet\n", ctlr);
+#endif
+ break;
+
+ case AT_TYPE_OPTICAL: /* optical disk */
+#if NWMD > 0
+ /* Add your driver here */
+#else
+ printf ("wdc%d: ATAPI optical disks not supported yet\n", ctlr);
+#endif
+ break;
+ }
+ /* Attach failed. */
+ free (ap, M_TEMP);
+ return (0);
+#endif /* ATAPI_MODULE */
+}
+
+static char *cmdname (u_char cmd)
+{
+ static char buf[8];
+
+ switch (cmd) {
+ case 0x00: return ("TEST_UNIT_READY");
+ case 0x03: return ("REQUEST_SENSE");
+ case 0x1b: return ("START_STOP");
+ case 0x1e: return ("PREVENT_ALLOW");
+ case 0x25: return ("READ_CAPACITY");
+ case 0x28: return ("READ_BIG");
+ case 0x43: return ("READ_TOC");
+ case 0x42: return ("READ_SUBCHANNEL");
+ case 0x55: return ("MODE_SELECT_BIG");
+ case 0x5a: return ("MODE_SENSE");
+ case 0xb4: return ("PLAY_CD");
+ case 0x47: return ("PLAY_MSF");
+ case 0x4b: return ("PAUSE");
+ case 0x48: return ("PLAY_TRACK");
+ case 0xa5: return ("PLAY_BIG");
+ }
+ sprintf (buf, "[0x%x]", cmd);
+ return (buf);
+}
+
+static void bswap (char *buf, int len)
+{
+ u_short *p = (u_short*) (buf + len);
+ while (--p >= (u_short*) buf)
+ *p = ntohs (*p);
+}
+
+static void btrim (char *buf, int len)
+{
+ char *p;
+
+ /* Remove the trailing spaces. */
+ for (p=buf; p<buf+len; ++p)
+ if (! *p)
+ *p = ' ';
+ for (p=buf+len-1; p>=buf && *p==' '; --p)
+ *p = 0;
+}
+
+/*
+ * Issue IDENTIFY command to ATAPI drive to ask it what it is.
+ */
+static struct atapi_params *atapi_probe (int port, int unit)
+{
+ struct atapi_params *ap;
+ char tb [DEV_BSIZE];
+ int cnt;
+
+#ifdef PC98
+ outb(0x432,unit%2);
+ print(("unit = %d,select %d\n",unit,unit%2));
+#endif
+ /* Wait for controller not busy. */
+ outb (port + AR_DRIVE, unit ? ARD_DRIVE1 : ARD_DRIVE0);
+ if (atapi_wait (port, 0) < 0) {
+ print (("atapiX.%d at 0x%x: controller busy, status=%b\n",
+ unit, port, inb (port + AR_STATUS), ARS_BITS));
+ return (0);
+ }
+
+ /* Issue ATAPI IDENTIFY command. */
+#ifdef PC98
+ outb (port + AR_DRIVE, unit/2 ? ARD_DRIVE1 : ARD_DRIVE0);
+
+ /* Wait for DRQ deassert. */
+ for (cnt=2000; cnt>0; --cnt)
+ if (! (inb (0x640 + AR_STATUS) & ARS_DRQ))
+ break;
+
+ outb (port + AR_COMMAND, ATAPIC_IDENTIFY);
+ DELAY(500);
+#else
+ outb (port + AR_DRIVE, unit ? ARD_DRIVE1 : ARD_DRIVE0);
+ outb (port + AR_COMMAND, ATAPIC_IDENTIFY);
+#endif
+
+ /* Check that device is present. */
+ if (inb (port + AR_STATUS) == 0xff) {
+ print (("atapiX.%d at 0x%x: no device\n", unit, port));
+ if (unit == 1)
+ /* Select unit 0. */
+ outb (port + AR_DRIVE, ARD_DRIVE0);
+ return (0);
+ }
+
+ /* Wait for data ready. */
+ if (atapi_wait (port, ARS_DRQ) != 0) {
+ print (("atapiX.%d at 0x%x: identify not ready, status=%b\n",
+ unit, port, inb (port + AR_STATUS), ARS_BITS));
+ if (unit == 1)
+ /* Select unit 0. */
+ outb (port + AR_DRIVE, ARD_DRIVE0);
+ return (0);
+ }
+
+ /* Obtain parameters. */
+ insw (port + AR_DATA, tb, sizeof(tb) / sizeof(short));
+
+ ap = malloc (sizeof *ap, M_TEMP, M_NOWAIT);
+ if (! ap)
+ return (0);
+ bcopy (tb, ap, sizeof *ap);
+
+ /*
+ * Shuffle string byte order.
+ * Mitsumi and NEC drives don't need this.
+ */
+ if (! ((ap->model[0] == 'N' && ap->model[1] == 'E') ||
+ (ap->model[0] == 'F' && ap->model[1] == 'X')))
+ bswap (ap->model, sizeof(ap->model));
+ bswap (ap->serial, sizeof(ap->serial));
+ bswap (ap->revision, sizeof(ap->revision));
+
+ /* Clean up the model name, serial and revision numbers. */
+ btrim (ap->model, sizeof(ap->model));
+ btrim (ap->serial, sizeof(ap->serial));
+ btrim (ap->revision, sizeof(ap->revision));
+ return (ap);
+}
+
+/*
+ * Wait uninterruptibly until controller is not busy and certain
+ * status bits are set.
+ * The wait is usually short unless it is for the controller to process
+ * an entire critical command.
+ * Return 1 for (possibly stale) controller errors, -1 for timeout errors,
+ * or 0 for no errors.
+ */
+static int atapi_wait (int port, u_char bits_wanted)
+{
+ int cnt;
+ u_char s;
+
+ /* Wait 5 sec for BUSY deassert. */
+ for (cnt=500000; cnt>0; --cnt) {
+ s = inb (port + AR_STATUS);
+ if (! (s & ARS_BSY))
+ break;
+ DELAY (10);
+ }
+ if (cnt <= 0)
+ return (-1);
+ if (! bits_wanted)
+ return (s & ARS_CHECK);
+
+ /* Wait 50 msec for bits wanted. */
+ for (cnt=5000; cnt>0; --cnt) {
+ s = inb (port + AR_STATUS);
+ if ((s & bits_wanted) == bits_wanted)
+ return (s & ARS_CHECK);
+ DELAY (10);
+ }
+ return (-1);
+}
+
+void atapi_debug (struct atapi *ata, int on)
+{
+ ata->debug = on;
+}
+
+static struct atapicmd *atapi_alloc (struct atapi *ata)
+{
+ struct atapicmd *ac;
+
+ while (! ata->free)
+ tsleep ((caddr_t)ata, PRIBIO, "atacmd", 0);
+ ac = ata->free;
+ ata->free = ac->next;
+ ac->busy = 1;
+ return (ac);
+}
+
+static void atapi_free (struct atapi *ata, struct atapicmd *ac)
+{
+ if (! ata->free)
+ wakeup ((caddr_t)&ata);
+ ac->busy = 0;
+ ac->next = ata->free;
+ ata->free = ac;
+}
+
+/*
+ * Add new command request to the end of the queue.
+ */
+static void atapi_enqueue (struct atapi *ata, struct atapicmd *ac)
+{
+ ac->next = 0;
+ if (ata->tail)
+ ata->tail->next = ac;
+ else
+ ata->queue = ac;
+ ata->tail = ac;
+}
+
+static void atapi_done (struct atapi *ata)
+{
+ struct atapicmd *ac = ata->queue;
+
+ if (! ac)
+ return; /* cannot happen */
+
+ ata->queue = ac->next;
+ if (! ata->queue)
+ ata->tail = 0;
+
+ if (ac->callback) {
+ (*ac->callback) (ac->cbarg1, ac->cbarg2, ac->count, ac->result);
+ atapi_free (ata, ac);
+ } else
+ wakeup ((caddr_t)ac);
+}
+
+/*
+ * Start new packet op. Called from wdstart().
+ * Return 1 if op started, and we are waiting for interrupt.
+ * Return 0 when idle.
+ */
+int atapi_start (int ctrlr)
+{
+ struct atapi *ata = atapitab + ctrlr;
+ struct atapicmd *ac;
+again:
+ ac = ata->queue;
+ if (! ac)
+ return (0);
+
+ /* Start packet command. */
+ if (atapi_start_cmd (ata, ac) < 0) {
+ atapi_done (ata);
+ goto again;
+ }
+
+ if (ata->intrcmd)
+ /* Wait for interrupt before sending packet command */
+ return (1);
+
+ /* Wait for DRQ. */
+ if (atapi_wait_cmd (ata, ac) < 0) {
+ atapi_done (ata);
+ goto again;
+ }
+
+ /* Send packet command. */
+ atapi_send_cmd (ata, ac);
+ return (1);
+}
+
+/*
+ * Start new packet op. Returns -1 on errors.
+ */
+int atapi_start_cmd (struct atapi *ata, struct atapicmd *ac)
+{
+ ac->result.error = 0;
+ ac->result.status = 0;
+
+#ifdef PC98
+ outb(0x432,(ac->unit)%2);
+ print(("(ac->unit) = %d,select %d (2) \n",(ac->unit),(ac->unit)%2));
+ outb (ata->port + AR_DRIVE, (ac->unit)/2 ? ARD_DRIVE1 : ARD_DRIVE0);
+#else
+ outb (ata->port + AR_DRIVE, ac->unit ? ARD_DRIVE1 : ARD_DRIVE0);
+#endif
+ if (atapi_wait (ata->port, 0) < 0) {
+ printf ("atapi%d.%d: controller not ready for cmd\n",
+ ata->ctrlr, ac->unit);
+ ac->result.code = RES_NOTRDY;
+ return (-1);
+ }
+
+ /* Set up the controller registers. */
+ outb (ata->port + AR_FEATURES, 0);
+ outb (ata->port + AR_IREASON, 0);
+ outb (ata->port + AR_TAG, 0);
+ outb (ata->port + AR_CNTLO, ac->count & 0xff);
+ outb (ata->port + AR_CNTHI, ac->count >> 8);
+ outb (ata->port + AR_COMMAND, ATAPIC_PACKET);
+
+ if (ata->debug)
+ printf ("atapi%d.%d: start\n", ata->ctrlr, ac->unit);
+ return (0);
+}
+
+/*
+ * Wait for DRQ before sending packet cmd. Returns -1 on errors.
+ */
+int atapi_wait_cmd (struct atapi *ata, struct atapicmd *ac)
+{
+ /* Wait for DRQ from 50 usec to 3 msec for slow devices */
+ int cnt = ata->intrcmd ? 10000 : ata->slow ? 3000 : 50;
+ int ireason = 0, phase = 0;
+
+ /* Wait for command phase. */
+ for (; cnt>0; cnt-=10) {
+ ireason = inb (ata->port + AR_IREASON);
+ ac->result.status = inb (ata->port + AR_STATUS);
+ phase = (ireason & (ARI_CMD | ARI_IN)) |
+ (ac->result.status & ARS_DRQ);
+ if (phase == PHASE_CMDOUT)
+ break;
+ DELAY (10);
+ }
+
+ if (phase != PHASE_CMDOUT) {
+ ac->result.code = RES_NODRQ;
+ ac->result.error = inb (ata->port + AR_ERROR);
+ printf ("atapi%d.%d: invalid command phase, ireason=0x%x, status=%b, error=%b\n",
+ ata->ctrlr, ac->unit, ireason,
+ ac->result.status, ARS_BITS,
+ ac->result.error, AER_BITS);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Send packet cmd.
+ */
+void atapi_send_cmd (struct atapi *ata, struct atapicmd *ac)
+{
+ outsw (ata->port + AR_DATA, ac->cmd, ata->cmd16 ? 8 : 6);
+ if (ata->debug)
+ printf ("atapi%d.%d: send cmd %s %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x\n",
+ ata->ctrlr, ac->unit, cmdname (ac->cmd[0]), ac->cmd[0],
+ ac->cmd[1], ac->cmd[2], ac->cmd[3], ac->cmd[4],
+ ac->cmd[5], ac->cmd[6], ac->cmd[7], ac->cmd[8],
+ ac->cmd[9], ac->cmd[10], ac->cmd[11], ac->cmd[12],
+ ac->cmd[13], ac->cmd[14], ac->cmd[15]);
+}
+
+/*
+ * Interrupt routine for the controller. Called from wdintr().
+ * Finish the started op, wakeup wait-type commands,
+ * run callbacks for callback-type commands, then return.
+ * Do not start new op here, it will be done by wdstart,
+ * which is called just after us.
+ * Return 1 if op continues, and we are waiting for new interrupt.
+ * Return 0 when idle.
+ */
+int atapi_intr (int ctrlr)
+{
+ struct atapi *ata = atapitab + ctrlr;
+ struct atapicmd *ac = ata->queue;
+
+#ifdef PC98
+ outb(0x432,(ac->unit)%2);
+ print(("atapi_intr:(ac->unit)= %d,select %d\n",ac->unit,(ac->unit)%2));
+#endif
+
+ if (! ac) {
+ printf ("atapi%d: stray interrupt\n", ata->ctrlr);
+ return (0);
+ }
+ if (atapi_io (ata, ac) > 0)
+ return (1);
+ atapi_done (ata);
+ return (0);
+}
+
+/*
+ * Process the i/o phase, transferring the command/data to/from the device.
+ * Return 1 if op continues, and we are waiting for new interrupt.
+ * Return 0 when idle.
+ */
+int atapi_io (struct atapi *ata, struct atapicmd *ac)
+{
+ u_char ireason;
+ u_short len, i;
+
+ if (atapi_wait (ata->port, 0) < 0) {
+ ac->result.status = inb (ata->port + AR_STATUS);
+ ac->result.error = inb (ata->port + AR_ERROR);
+ ac->result.code = RES_NOTRDY;
+ printf ("atapi%d.%d: controller not ready, status=%b, error=%b\n",
+ ata->ctrlr, ac->unit, ac->result.status, ARS_BITS,
+ ac->result.error, AER_BITS);
+ return (0);
+ }
+
+ ac->result.status = inb (ata->port + AR_STATUS);
+ ac->result.error = inb (ata->port + AR_ERROR);
+ len = inb (ata->port + AR_CNTLO);
+ len |= inb (ata->port + AR_CNTHI) << 8;
+ ireason = inb (ata->port + AR_IREASON);
+
+ if (ata->debug) {
+ printf ("atapi%d.%d: intr ireason=0x%x, len=%d, status=%b, error=%b\n",
+ ata->ctrlr, ac->unit, ireason, len,
+ ac->result.status, ARS_BITS,
+ ac->result.error, AER_BITS);
+ }
+ switch ((ireason & (ARI_CMD | ARI_IN)) | (ac->result.status & ARS_DRQ)) {
+ default:
+ printf ("atapi%d.%d: unknown phase\n", ata->ctrlr, ac->unit);
+ ac->result.code = RES_ERR;
+ break;
+
+ case PHASE_CMDOUT:
+ /* Send packet command. */
+ if (! (ac->result.status & ARS_DRQ)) {
+ printf ("atapi%d.%d: no cmd drq\n",
+ ata->ctrlr, ac->unit);
+ ac->result.code = RES_NODRQ;
+ break;
+ }
+ atapi_send_cmd (ata, ac);
+ return (1);
+
+ case PHASE_DATAOUT:
+ /* Write data */
+ if (ac->count > 0) {
+ printf ("atapi%d.%d: invalid data direction\n",
+ ata->ctrlr, ac->unit);
+ ac->result.code = RES_INVDIR;
+ break;
+ }
+ if (-ac->count < len) {
+ print (("atapi%d.%d: send data underrun, %d bytes left\n",
+ ata->ctrlr, ac->unit, -ac->count));
+ ac->result.code = RES_UNDERRUN;
+ outsw (ata->port + AR_DATA, ac->addr,
+ -ac->count / sizeof(short));
+ for (i= -ac->count; i<len; i+=sizeof(short))
+ outw (ata->port + AR_DATA, 0);
+ } else
+ outsw (ata->port + AR_DATA, ac->addr,
+ len / sizeof(short));
+ ac->addr += len;
+ ac->count += len;
+ return (1);
+
+ case PHASE_DATAIN:
+ /* Read data */
+ if (ac->count < 0) {
+ printf ("atapi%d.%d: invalid data direction\n",
+ ata->ctrlr, ac->unit);
+ ac->result.code = RES_INVDIR;
+ break;
+ }
+ if (ac->count < len) {
+ print (("atapi%d.%d: recv data overrun, %d bytes left\n",
+ ata->ctrlr, ac->unit, ac->count));
+ ac->result.code = RES_OVERRUN;
+ insw (ata->port + AR_DATA, ac->addr,
+ ac->count / sizeof(short));
+ for (i=ac->count; i<len; i+=sizeof(short))
+ inw (ata->port + AR_DATA);
+ } else
+ insw (ata->port + AR_DATA, ac->addr,
+ len / sizeof(short));
+ ac->addr += len;
+ ac->count -= len;
+ return (1);
+
+ case PHASE_ABORTED:
+ case PHASE_COMPLETED:
+ if (ac->result.status & (ARS_CHECK | ARS_DF))
+ ac->result.code = RES_ERR;
+ else if (ac->count < 0) {
+ print (("atapi%d.%d: send data overrun, %d bytes left\n",
+ ata->ctrlr, ac->unit, -ac->count));
+ ac->result.code = RES_OVERRUN;
+ } else if (ac->count > 0) {
+ print (("atapi%d.%d: recv data underrun, %d bytes left\n",
+ ata->ctrlr, ac->unit, ac->count));
+ ac->result.code = RES_UNDERRUN;
+ bzero (ac->addr, ac->count);
+ } else
+ ac->result.code = RES_OK;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * Queue new packet request, then call wdstart().
+ * Called on splbio().
+ */
+void atapi_request_callback (struct atapi *ata, int unit,
+ u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4,
+ u_char a5, u_char a6, u_char a7, u_char a8, u_char a9,
+ u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15,
+ char *addr, int count, atapi_callback_t *done, void *x, void *y)
+{
+ struct atapicmd *ac;
+
+ ac = atapi_alloc (ata);
+ ac->cmd[0] = cmd; ac->cmd[1] = a1;
+ ac->cmd[2] = a2; ac->cmd[3] = a3;
+ ac->cmd[4] = a4; ac->cmd[5] = a5;
+ ac->cmd[6] = a6; ac->cmd[7] = a7;
+ ac->cmd[8] = a8; ac->cmd[9] = a9;
+ ac->cmd[10] = a10; ac->cmd[11] = a11;
+ ac->cmd[12] = a12; ac->cmd[13] = a13;
+ ac->cmd[14] = a14; ac->cmd[15] = a15;
+ ac->unit = unit;
+ ac->addr = addr;
+ ac->count = count;
+ ac->callback = done;
+ ac->cbarg1 = x;
+ ac->cbarg2 = y;
+
+ if (ata->debug)
+ printf ("atapi%d.%d: req cb %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x len=%d\n",
+ ata->ctrlr, ac->unit, ac->cmd[0], ac->cmd[1],
+ ac->cmd[2], ac->cmd[3], ac->cmd[4], ac->cmd[5],
+ ac->cmd[6], ac->cmd[7], ac->cmd[8], ac->cmd[9],
+ ac->cmd[10], ac->cmd[11], ac->cmd[12],
+ ac->cmd[13], ac->cmd[14], ac->cmd[15], count);
+ atapi_enqueue (ata, ac);
+ wdstart (ata->ctrlr);
+}
+
+/*
+ * Queue new packet request, then call wdstart().
+ * Wait until the request is finished.
+ * Called on spl0().
+ * Return atapi error.
+ * Buffer pointed to by *addr should be placed in core memory, not in stack!
+ */
+struct atapires atapi_request_wait (struct atapi *ata, int unit,
+ u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4,
+ u_char a5, u_char a6, u_char a7, u_char a8, u_char a9,
+ u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15,
+ char *addr, int count)
+{
+ struct atapicmd *ac;
+ int x = splbio ();
+ struct atapires result;
+
+ ac = atapi_alloc (ata);
+ ac->cmd[0] = cmd; ac->cmd[1] = a1;
+ ac->cmd[2] = a2; ac->cmd[3] = a3;
+ ac->cmd[4] = a4; ac->cmd[5] = a5;
+ ac->cmd[6] = a6; ac->cmd[7] = a7;
+ ac->cmd[8] = a8; ac->cmd[9] = a9;
+ ac->cmd[10] = a10; ac->cmd[11] = a11;
+ ac->cmd[12] = a12; ac->cmd[13] = a13;
+ ac->cmd[14] = a14; ac->cmd[15] = a15;
+ ac->unit = unit;
+ ac->addr = addr;
+ ac->count = count;
+ ac->callback = 0;
+ ac->cbarg1 = 0;
+ ac->cbarg2 = 0;
+
+ if (ata->debug)
+ printf ("atapi%d.%d: req w %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x len=%d\n",
+ ata->ctrlr, ac->unit, ac->cmd[0], ac->cmd[1],
+ ac->cmd[2], ac->cmd[3], ac->cmd[4], ac->cmd[5],
+ ac->cmd[6], ac->cmd[7], ac->cmd[8], ac->cmd[9],
+ ac->cmd[10], ac->cmd[11], ac->cmd[12],
+ ac->cmd[13], ac->cmd[14], ac->cmd[15], count);
+ atapi_enqueue (ata, ac);
+ wdstart (ata->ctrlr);
+ tsleep ((caddr_t)ac, PRIBIO, "atareq", 0);
+
+ result = ac->result;
+ atapi_free (ata, ac);
+ splx (x);
+ return (result);
+}
+
+/*
+ * Perform a packet command on the device.
+ * Should be called on splbio().
+ * Return atapi error.
+ */
+struct atapires atapi_request_immediate (struct atapi *ata, int unit,
+ u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4,
+ u_char a5, u_char a6, u_char a7, u_char a8, u_char a9,
+ u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15,
+ char *addr, int count)
+{
+ struct atapicmd cmdbuf, *ac = &cmdbuf;
+ int cnt;
+
+ ac->cmd[0] = cmd; ac->cmd[1] = a1;
+ ac->cmd[2] = a2; ac->cmd[3] = a3;
+ ac->cmd[4] = a4; ac->cmd[5] = a5;
+ ac->cmd[6] = a6; ac->cmd[7] = a7;
+ ac->cmd[8] = a8; ac->cmd[9] = a9;
+ ac->cmd[10] = a10; ac->cmd[11] = a11;
+ ac->cmd[12] = a12; ac->cmd[13] = a13;
+ ac->cmd[14] = a14; ac->cmd[15] = a15;
+ ac->unit = unit;
+ ac->addr = addr;
+ ac->count = count;
+ ac->callback = 0;
+ ac->cbarg1 = 0;
+ ac->cbarg2 = 0;
+
+ if (ata->debug)
+ printf ("atapi%d.%d: req im %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x len=%d\n",
+ ata->ctrlr, ac->unit, ac->cmd[0], ac->cmd[1],
+ ac->cmd[2], ac->cmd[3], ac->cmd[4], ac->cmd[5],
+ ac->cmd[6], ac->cmd[7], ac->cmd[8], ac->cmd[9],
+ ac->cmd[10], ac->cmd[11], ac->cmd[12],
+ ac->cmd[13], ac->cmd[14], ac->cmd[15], count);
+
+ /* Start packet command, wait for DRQ. */
+ if (atapi_start_cmd (ata, ac) >= 0 && atapi_wait_cmd (ata, ac) >= 0) {
+ /* Send packet command. */
+ atapi_send_cmd (ata, ac);
+
+ /* Wait for data i/o phase. */
+ for (cnt=20000; cnt>0; --cnt)
+ if (((inb (ata->port + AR_IREASON) & (ARI_CMD | ARI_IN)) |
+ (inb (ata->port + AR_STATUS) & ARS_DRQ)) != PHASE_CMDOUT)
+ break;
+
+ /* Do all needed i/o. */
+ while (atapi_io (ata, ac))
+ /* Wait for DRQ deassert. */
+ for (cnt=2000; cnt>0; --cnt)
+ if (! (inb (ata->port + AR_STATUS) & ARS_DRQ))
+ break;
+ }
+ return (ac->result);
+}
+#endif /* ATAPI_STATIC */
+
+#if defined (ATAPI_MODULE) || !defined(ATAPI_STATIC)
+int (*atapi_start_ptr) (int ctrlr);
+int (*atapi_intr_ptr) (int ctrlr);
+void (*atapi_debug_ptr) (struct atapi *ata, int on);
+struct atapires (*atapi_request_wait_ptr) (struct atapi *ata, int unit,
+ u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4,
+ u_char a5, u_char a6, u_char a7, u_char a8, u_char a9,
+ u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15,
+ char *addr, int count);
+void (*atapi_request_callback_ptr) (struct atapi *ata, int unit,
+ u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4,
+ u_char a5, u_char a6, u_char a7, u_char a8, u_char a9,
+ u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15,
+ char *addr, int count, atapi_callback_t *done, void *x, void *y);
+struct atapires (*atapi_request_immediate_ptr) (struct atapi *ata, int unit,
+ u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4,
+ u_char a5, u_char a6, u_char a7, u_char a8, u_char a9,
+ u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15,
+ char *addr, int count);
+#endif
+
+#ifdef ATAPI_MODULE
+/*
+ * ATAPI loadable driver stubs.
+ */
+#include <sys/exec.h>
+#include <sys/conf.h>
+#include <sys/sysent.h>
+#include <sys/lkm.h>
+
+extern int atapi_lock (int ctlr);
+/*
+ * XXX "ioconf.h" is not included by <sys/conf.h> for lkms, so we need this
+ * misplaced declaration.
+ */
+extern void wdintr (int);
+
+/*
+ * Construct lkm_misc structure (see lkm.h).
+ */
+MOD_MISC(atapi);
+
+int atapi_locked;
+
+int atapi_lock (int ctlr)
+{
+ atapi_locked = 1;
+ wakeup (&atapi_locked);
+ return (1);
+}
+
+/*
+ * Function called when loading the driver.
+ */
+static int atapi_load (struct lkm_table *lkmtp, int cmd)
+{
+ struct atapidrv *d;
+ int n, x;
+
+ /*
+ * Probe all free IDE units, searching for ATAPI drives.
+ */
+ n = 0;
+ for (d=atapi_drvtab; d<atapi_drvtab+atapi_ndrv && d->port; ++d) {
+ /* Lock the controller. */
+ x = splbio ();
+ atapi_locked = 0;
+ atapi_start_ptr = atapi_lock;
+ wdstart (d->ctlr);
+ while (! atapi_locked)
+ tsleep (&atapi_locked, PRIBIO, "atach", 0);
+
+ /* Probe the drive. */
+ if (atapi_attach (d->ctlr, d->unit, d->port, d->parent)) {
+ d->attached = 1;
+ ++n;
+ }
+
+ /* Unlock the controller. */
+ atapi_start_ptr = 0;
+ wdintr (d->ctlr);
+ splx (x);
+ }
+ if (! n)
+ return ENXIO;
+ atapi_start_ptr = atapi_start;
+ atapi_intr_ptr = atapi_intr;
+ atapi_debug_ptr = atapi_debug;
+ atapi_request_wait_ptr = atapi_request_wait;
+ atapi_request_callback_ptr = atapi_request_callback;
+ atapi_request_immediate_ptr = atapi_request_immediate;
+ atapi_tab = atapitab;
+ return 0;
+}
+
+/*
+ * Function called when unloading the driver.
+ */
+static int atapi_unload (struct lkm_table *lkmtp, int cmd)
+{
+ struct atapi *ata;
+ int u;
+
+ for (ata=atapi_tab; ata<atapi_tab+2; ++ata)
+ if (ata->port)
+ for (u=0; u<2; ++u)
+ if (ata->attached[u])
+ return EBUSY;
+ for (ata=atapi_tab; ata<atapi_tab+2; ++ata)
+ if (ata->port)
+ for (u=0; u<2; ++u)
+ if (ata->params[u]) {
+ free (ata->params[u], M_TEMP);
+ ata->params[u] = 0;
+ }
+ atapi_start_ptr = 0;
+ atapi_intr_ptr = 0;
+ atapi_debug_ptr = 0;
+ atapi_request_wait_ptr = 0;
+ atapi_request_callback_ptr = 0;
+ atapi_request_immediate_ptr = 0;
+ atapi_tab = 0;
+ return 0;
+}
+
+/*
+ * Dispatcher function for the module (load/unload/stat).
+ */
+int atapi_mod (struct lkm_table *lkmtp, int cmd, int ver)
+{
+ DISPATCH (lkmtp, cmd, ver, atapi_load, atapi_unload, lkm_nullcmd);
+}
+#endif /* ATAPI_MODULE */
+
+#endif /* NWDC && ATAPI */
diff --git a/sys/pc98/pc98/atcompat_diskslice.c b/sys/pc98/pc98/atcompat_diskslice.c
new file mode 100644
index 0000000..33db0b1
--- /dev/null
+++ b/sys/pc98/pc98/atcompat_diskslice.c
@@ -0,0 +1,474 @@
+/*-
+ * Copyright (c) 1994 Bruce D. Evans.
+ * All rights reserved.
+ *
+ * Copyright (c) 1982, 1986, 1988 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: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91
+ * from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $
+ * $Id: diskslice_machdep.c,v 1.20 1996/04/07 17:32:09 bde Exp $
+ */
+
+/*
+ * atcompat_diskslice.c by KATO Takenori
+ * This file is created from diskslice_machdpe.c to support IBM-PC's HDD
+ * for FreeBSD(98).
+ */
+
+#include <stddef.h>
+#include <sys/param.h>
+#include <sys/buf.h>
+#ifdef PC98
+#undef PC98
+#endif
+#include <sys/disklabel.h>
+#define DOSPTYP_EXTENDED 5
+#define DOSPTYP_ONTRACK 84
+#define PC98
+#include <sys/diskslice.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+
+#define TRACE(str) do { if (dsi_debug) printf str; } while (0)
+
+static volatile u_char dsi_debug;
+
+static struct dos_partition historical_bogus_partition_table[NDOSPART] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 0x80, 0, 1, 0, DOSPTYP_386BSD, 255, 255, 255, 0, 50000, },
+};
+
+static int check_part __P((char *sname, struct dos_partition *dp,
+ u_long offset, int nsectors, int ntracks,
+ u_long mbr_offset));
+static void atcompat_extended __P((char *dname, dev_t dev,
+ d_strategy_t *strat,
+ struct disklabel *lp, struct diskslices *ssp,
+ u_long ext_offset, u_long ext_size,
+ u_long base_ext_offset, int nsectors, int ntracks,
+ u_long mbr_offset));
+
+static int
+check_part(sname, dp, offset, nsectors, ntracks, mbr_offset )
+ char *sname;
+ struct dos_partition *dp;
+ u_long offset;
+ int nsectors;
+ int ntracks;
+ u_long mbr_offset;
+{
+ int chs_ecyl;
+ int chs_esect;
+ int chs_scyl;
+ int chs_ssect;
+ int error;
+ u_long esector;
+ u_long esector1;
+ u_long secpercyl;
+ u_long ssector;
+ u_long ssector1;
+
+ secpercyl = (u_long)nsectors * ntracks;
+ chs_scyl = DPCYL(dp->dp_scyl, dp->dp_ssect);
+ chs_ssect = DPSECT(dp->dp_ssect);
+ ssector = chs_ssect - 1 + dp->dp_shd * nsectors + chs_scyl * secpercyl
+ + mbr_offset;
+ ssector1 = offset + dp->dp_start;
+
+ /*
+ * If ssector1 is on a cylinder >= 1024, then ssector can't be right.
+ * Allow the C/H/S for it to be 1023/ntracks-1/nsectors, or correct
+ * apart from the cylinder being reduced modulo 1024.
+ */
+ if (ssector < ssector1
+ && ((chs_ssect == nsectors && dp->dp_shd == ntracks - 1
+ && chs_scyl == 1023)
+ || (ssector1 - ssector) % (1024 * secpercyl) == 0)
+ || (dp->dp_scyl == 255 && dp->dp_shd == 255
+ && dp->dp_ssect == 255)) {
+ TRACE(("%s: C/H/S start %d/%d/%d, start %lu: allow\n",
+ sname, chs_scyl, dp->dp_shd, chs_ssect, ssector1));
+ ssector = ssector1;
+ }
+
+ chs_ecyl = DPCYL(dp->dp_ecyl, dp->dp_esect);
+ chs_esect = DPSECT(dp->dp_esect);
+ esector = chs_esect - 1 + dp->dp_ehd * nsectors + chs_ecyl * secpercyl
+ + mbr_offset;
+ esector1 = ssector1 + dp->dp_size - 1;
+
+ /* Allow certain bogus C/H/S values for esector, as above. */
+ if (esector < esector1
+ && ((chs_esect == nsectors && dp->dp_ehd == ntracks - 1
+ && chs_ecyl == 1023)
+ || (esector1 - esector) % (1024 * secpercyl) == 0)
+ || (dp->dp_ecyl == 255 && dp->dp_ehd == 255
+ && dp->dp_esect == 255)) {
+ TRACE(("%s: C/H/S end %d/%d/%d, end %lu: allow\n",
+ sname, chs_ecyl, dp->dp_ehd, chs_esect, esector1));
+ esector = esector1;
+ }
+
+ error = (ssector == ssector1 && esector == esector1) ? 0 : EINVAL;
+ if (bootverbose)
+ printf("%s: type 0x%x, start %lu, end = %lu, size %lu %s\n",
+ sname, dp->dp_typ, ssector1, esector1, dp->dp_size,
+ error ? "" : ": OK");
+ if (ssector != ssector1 && bootverbose)
+ printf("%s: C/H/S start %d/%d/%d (%lu) != start %lu: invalid\n",
+ sname, chs_scyl, dp->dp_shd, chs_ssect,
+ ssector, ssector1);
+ if (esector != esector1 && bootverbose)
+ printf("%s: C/H/S end %d/%d/%d (%lu) != end %lu: invalid\n",
+ sname, chs_ecyl, dp->dp_ehd, chs_esect,
+ esector, esector1);
+ return (error);
+}
+
+int atcompat_dsinit __P((char *dname, dev_t dev, d_strategy_t *strat,
+ struct disklabel *lp, struct diskslices **sspp));
+int
+atcompat_dsinit(dname, dev, strat, lp, sspp)
+ char *dname;
+ dev_t dev;
+ d_strategy_t *strat;
+ struct disklabel *lp;
+ struct diskslices **sspp;
+{
+ struct buf *bp;
+ u_char *cp;
+ int dospart;
+ struct dos_partition *dp;
+ struct dos_partition *dp0;
+ int error;
+ int max_ncyls;
+ int max_nsectors;
+ int max_ntracks;
+ u_long mbr_offset;
+ char partname[2];
+ u_long secpercyl;
+ char *sname;
+ struct diskslice *sp;
+ struct diskslices *ssp;
+
+ /*
+ * Allocate a dummy slices "struct" and initialize it to contain
+ * only an empty compatibility slice (pointing to itself) and a
+ * whole disk slice (covering the disk as described by the label).
+ * If there is an error, then the dummy struct becomes final.
+ */
+ ssp = malloc(offsetof(struct diskslices, dss_slices)
+ + BASE_SLICE * sizeof *sp, M_DEVBUF, M_WAITOK);
+ *sspp = ssp;
+ ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
+ ssp->dss_nslices = BASE_SLICE;
+ sp = &ssp->dss_slices[0];
+ bzero(sp, BASE_SLICE * sizeof *sp);
+ sp[WHOLE_DISK_SLICE].ds_size = lp->d_secperunit;
+
+ mbr_offset = DOSBBSECTOR;
+reread_mbr:
+ /* Read master boot record. */
+ bp = geteblk((int)lp->d_secsize);
+ bp->b_dev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
+ bp->b_blkno = mbr_offset;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags |= B_BUSY | B_READ;
+ (*strat)(bp);
+ if (biowait(bp) != 0) {
+ diskerr(bp, dname, "error reading primary partition table",
+ LOG_PRINTF, 0, lp);
+ printf("\n");
+ error = EIO;
+ goto done;
+ }
+
+ /* Weakly verify it. */
+ cp = bp->b_un.b_addr;
+ sname = dsname(dname, dkunit(dev), WHOLE_DISK_SLICE, RAW_PART,
+ partname);
+ if (cp[0x1FE] != 0x55 || cp[0x1FF] != 0xAA) {
+ printf("%s: invalid primary partition table: no magic\n",
+ sname);
+ error = EINVAL;
+ goto done;
+ }
+ dp0 = (struct dos_partition *)(cp + DOSPARTOFF);
+
+ /* Check for "Ontrack Diskmanager". */
+ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
+ if (dp->dp_typ == DOSPTYP_ONTRACK) {
+ if (bootverbose)
+ printf(
+ "%s: Found \"Ontrack Disk Manager\" on this disk.\n", sname);
+ bp->b_flags |= B_INVAL | B_AGE;
+ brelse(bp);
+ mbr_offset = 63;
+ goto reread_mbr;
+ }
+ }
+
+ if (bcmp(dp0, historical_bogus_partition_table,
+ sizeof historical_bogus_partition_table) == 0) {
+ TRACE(("%s: invalid primary partition table: historical\n",
+ sname));
+ error = EINVAL;
+ goto done;
+ }
+
+ /* Guess the geometry. */
+ /*
+ * TODO:
+ * Perhaps skip entries with 0 size.
+ * Perhaps only look at entries of type DOSPTYP_386BSD.
+ */
+ max_ncyls = 0;
+ max_nsectors = 0;
+ max_ntracks = 0;
+ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
+ int nsectors;
+ int ntracks;
+
+ max_ncyls = DPCYL(dp->dp_ecyl, dp->dp_esect);
+ if (max_ncyls < max_ncyls)
+ max_ncyls = max_ncyls;
+ nsectors = DPSECT(dp->dp_esect);
+ if (max_nsectors < nsectors)
+ max_nsectors = nsectors;
+ ntracks = dp->dp_ehd + 1;
+ if (max_ntracks < ntracks)
+ max_ntracks = ntracks;
+ }
+
+ /* Check the geometry. */
+ /*
+ * TODO:
+ * As above.
+ * Check for overlaps.
+ * Check against d_secperunit if the latter is reliable.
+ */
+ error = 0;
+ secpercyl = (u_long)max_nsectors * max_ntracks;
+ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
+ if (dp->dp_scyl == 0 && dp->dp_shd == 0 && dp->dp_ssect == 0
+ && dp->dp_start == 0 && dp->dp_size == 0)
+ continue;
+ sname = dsname(dname, dkunit(dev), BASE_SLICE + dospart,
+ RAW_PART, partname);
+
+ /*
+ * Temporarily ignore errors from this check. We could
+ * simplify things by accepting the table eariler if we
+ * always ignore errors here. Perhaps we should always
+ * accept the table if the magic is right but not let
+ * bad entries affect the geometry.
+ */
+ check_part(sname, dp, mbr_offset, max_nsectors, max_ntracks,
+ mbr_offset);
+ }
+ if (error != 0)
+ goto done;
+
+ /*
+ * Accept the DOS partition table.
+ * First adjust the label (we have been careful not to change it
+ * before we can guarantee success).
+ */
+ if (secpercyl != 0) {
+ u_long secperunit;
+
+ lp->d_nsectors = max_nsectors;
+ lp->d_ntracks = max_ntracks;
+ lp->d_secpercyl = secpercyl;
+ secperunit = secpercyl * max_ncyls;
+ if (lp->d_secperunit < secperunit)
+ lp->d_secperunit = secperunit;
+ lp->d_ncylinders = lp->d_secperunit / secpercyl;
+ }
+
+ /*
+ * Free the dummy slices "struct" and allocate a real new one.
+ * Initialize special slices as above.
+ */
+ free(ssp, M_DEVBUF);
+ ssp = malloc(offsetof(struct diskslices, dss_slices)
+#define MAX_SLICES_SUPPORTED MAX_SLICES /* was (BASE_SLICE + NDOSPART) */
+ + MAX_SLICES_SUPPORTED * sizeof *sp, M_DEVBUF, M_WAITOK);
+ *sspp = ssp;
+ ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
+ sp = &ssp->dss_slices[0];
+ bzero(sp, MAX_SLICES_SUPPORTED * sizeof *sp);
+ sp[WHOLE_DISK_SLICE].ds_size = lp->d_secperunit;
+
+ /* Initialize normal slices. */
+ sp += BASE_SLICE;
+ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++, sp++) {
+ sp->ds_offset = mbr_offset + dp->dp_start;
+ sp->ds_size = dp->dp_size;
+ sp->ds_type = dp->dp_typ;
+#ifdef PC98
+ /* fake up FreeBSD(98) */
+ if (sp->ds_type == DOSPTYP_386BSD)
+ sp->ds_type = 0x94;
+#endif
+#if 0
+ lp->d_subtype |= (lp->d_subtype & 3) | dospart
+ | DSTYPE_INDOSPART;
+#endif
+ }
+ ssp->dss_nslices = BASE_SLICE + NDOSPART;
+
+ /* Handle extended partitions. */
+ sp -= NDOSPART;
+ for (dospart = 0; dospart < NDOSPART; dospart++, sp++)
+ if (sp->ds_type == DOSPTYP_EXTENDED)
+ atcompat_extended(dname, bp->b_dev, strat, lp, ssp,
+ sp->ds_offset, sp->ds_size, sp->ds_offset,
+ max_nsectors, max_ntracks, mbr_offset);
+
+done:
+ bp->b_flags |= B_INVAL | B_AGE;
+ brelse(bp);
+ if (error == EINVAL)
+ error = 0;
+ return (error);
+}
+
+static void
+atcompat_extended(dname, dev, strat, lp, ssp, ext_offset, ext_size,
+ base_ext_offset, nsectors, ntracks, mbr_offset)
+ char *dname;
+ dev_t dev;
+ struct disklabel *lp;
+ d_strategy_t *strat;
+ struct diskslices *ssp;
+ u_long ext_offset;
+ u_long ext_size;
+ u_long base_ext_offset;
+ int nsectors;
+ int ntracks;
+ u_long mbr_offset;
+{
+ struct buf *bp;
+ u_char *cp;
+ int dospart;
+ struct dos_partition *dp;
+ u_long ext_offsets[NDOSPART];
+ u_long ext_sizes[NDOSPART];
+ char partname[2];
+ int slice;
+ char *sname;
+ struct diskslice *sp;
+
+ /* Read extended boot record. */
+ bp = geteblk((int)lp->d_secsize);
+ bp->b_dev = dev;
+ bp->b_blkno = ext_offset;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags |= B_BUSY | B_READ;
+ (*strat)(bp);
+ if (biowait(bp) != 0) {
+ diskerr(bp, dname, "error reading extended partition table",
+ LOG_PRINTF, 0, lp);
+ printf("\n");
+ goto done;
+ }
+
+ /* Weakly verify it. */
+ cp = bp->b_un.b_addr;
+ if (cp[0x1FE] != 0x55 || cp[0x1FF] != 0xAA) {
+ sname = dsname(dname, dkunit(dev), WHOLE_DISK_SLICE, RAW_PART,
+ partname);
+ printf("%s: invalid extended partition table: no magic\n",
+ sname);
+ goto done;
+ }
+
+ for (dospart = 0,
+ dp = (struct dos_partition *)(bp->b_un.b_addr + DOSPARTOFF),
+ slice = ssp->dss_nslices, sp = &ssp->dss_slices[slice];
+ dospart < NDOSPART; dospart++, dp++) {
+ ext_sizes[dospart] = 0;
+ if (dp->dp_scyl == 0 && dp->dp_shd == 0 && dp->dp_ssect == 0
+ && dp->dp_start == 0 && dp->dp_size == 0)
+ continue;
+ if (dp->dp_typ == DOSPTYP_EXTENDED) {
+ char buf[32];
+
+ sname = dsname(dname, dkunit(dev), WHOLE_DISK_SLICE,
+ RAW_PART, partname);
+ strcpy(buf, sname);
+ if (strlen(buf) < sizeof buf - 11)
+ strcat(buf, "<extended>");
+ check_part(buf, dp, base_ext_offset, nsectors,
+ ntracks, mbr_offset);
+ ext_offsets[dospart] = base_ext_offset + dp->dp_start;
+ ext_sizes[dospart] = dp->dp_size;
+ } else {
+ sname = dsname(dname, dkunit(dev), slice, RAW_PART,
+ partname);
+ check_part(sname, dp, ext_offset, nsectors, ntracks,
+ mbr_offset);
+ if (slice >= MAX_SLICES) {
+ printf("%s: too many slices\n", sname);
+ slice++;
+ continue;
+ }
+ sp->ds_offset = ext_offset + dp->dp_start;
+ sp->ds_size = dp->dp_size;
+ sp->ds_type = dp->dp_typ;
+#ifdef PC98
+ /* fake up FreeBSD(98) */
+ if (sp->ds_type == DOSPTYP_386BSD)
+ sp->ds_type = 0x94;
+#endif
+ ssp->dss_nslices++;
+ slice++;
+ sp++;
+ }
+ }
+
+ /* If we found any more slices, recursively find all the subslices. */
+ for (dospart = 0; dospart < NDOSPART; dospart++)
+ if (ext_sizes[dospart] != 0)
+ atcompat_extended(dname, dev, strat, lp, ssp,
+ ext_offsets[dospart], ext_sizes[dospart],
+ base_ext_offset, nsectors, ntracks,
+ mbr_offset);
+
+done:
+ bp->b_flags |= B_INVAL | B_AGE;
+ brelse(bp);
+}
diff --git a/sys/pc98/pc98/clock.c b/sys/pc98/pc98/clock.c
new file mode 100644
index 0000000..b2befbf
--- /dev/null
+++ b/sys/pc98/pc98/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/pc98/diskslice_machdep.c b/sys/pc98/pc98/diskslice_machdep.c
new file mode 100644
index 0000000..e8370db
--- /dev/null
+++ b/sys/pc98/pc98/diskslice_machdep.c
@@ -0,0 +1,652 @@
+/*-
+ * Copyright (c) 1994 Bruce D. Evans.
+ * All rights reserved.
+ *
+ * Copyright (c) 1982, 1986, 1988 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: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91
+ * from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $
+ * $Id: diskslice_machdep.c,v 1.20 1996/04/07 17:32:09 bde Exp $
+ */
+
+/*
+ * PC9801 port by KATO Takenor <kato@eclogite.eps.nagoya-u.ac.jp>
+ */
+
+#include <stddef.h>
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/disklabel.h>
+#ifndef PC98
+#define DOSPTYP_EXTENDED 5
+#define DOSPTYP_ONTRACK 84
+#endif
+#include <sys/diskslice.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+
+#define TRACE(str) do { if (dsi_debug) printf str; } while (0)
+
+static volatile u_char dsi_debug;
+
+#ifndef PC98
+static struct dos_partition historical_bogus_partition_table[NDOSPART] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ { 0x80, 0, 1, 0, DOSPTYP_386BSD, 255, 255, 255, 0, 50000, },
+};
+#endif
+
+static int check_part __P((char *sname, struct dos_partition *dp,
+ u_long offset, int nsectors, int ntracks,
+ u_long mbr_offset));
+static void extended __P((char *dname, dev_t dev, d_strategy_t *strat,
+ struct disklabel *lp, struct diskslices *ssp,
+ u_long ext_offset, u_long ext_size,
+ u_long base_ext_offset, int nsectors, int ntracks,
+ u_long mbr_offset));
+
+#ifdef PC98
+#define DPBLKNO(cyl,hd,sect) ((cyl)*(lp->d_secpercyl))
+#ifdef COMPAT_ATDISK
+int atcompat_dsinit __P((char *dname, dev_t dev, d_strategy_t *strat,
+ struct disklabel *lp, struct diskslices **sspp));
+#endif
+#endif
+
+static int
+check_part(sname, dp, offset, nsectors, ntracks, mbr_offset )
+ char *sname;
+ struct dos_partition *dp;
+ u_long offset;
+ int nsectors;
+ int ntracks;
+ u_long mbr_offset;
+{
+ int chs_ecyl;
+ int chs_esect;
+ int chs_scyl;
+ int chs_ssect;
+ int error;
+ u_long esector;
+ u_long esector1;
+ u_long secpercyl;
+ u_long ssector;
+ u_long ssector1;
+#ifdef PC98
+ u_long pc98_start;
+ u_long pc98_size;
+#endif
+
+ secpercyl = (u_long)nsectors * ntracks;
+#ifdef PC98
+ chs_scyl = dp->dp_scyl;
+ chs_ssect = dp->dp_ssect;
+ ssector = chs_ssect + dp->dp_shd * nsectors +
+ chs_scyl * secpercyl + mbr_offset;
+#else
+ chs_scyl = DPCYL(dp->dp_scyl, dp->dp_ssect);
+ chs_ssect = DPSECT(dp->dp_ssect);
+ ssector = chs_ssect - 1 + dp->dp_shd * nsectors + chs_scyl * secpercyl
+ + mbr_offset;
+#endif
+#ifdef PC98
+ pc98_start = dp->dp_scyl * secpercyl;
+ pc98_size = dp->dp_ecyl ?
+ (dp->dp_ecyl + 1) * secpercyl - pc98_start : 0;
+ ssector1 = offset + pc98_start;
+#else
+ ssector1 = offset + dp->dp_start;
+#endif
+
+ /*
+ * If ssector1 is on a cylinder >= 1024, then ssector can't be right.
+ * Allow the C/H/S for it to be 1023/ntracks-1/nsectors, or correct
+ * apart from the cylinder being reduced modulo 1024.
+ */
+ if (ssector < ssector1
+ && ((chs_ssect == nsectors && dp->dp_shd == ntracks - 1
+ && chs_scyl == 1023)
+ || (ssector1 - ssector) % (1024 * secpercyl) == 0)
+ || (dp->dp_scyl == 255 && dp->dp_shd == 255
+ && dp->dp_ssect == 255)) {
+ TRACE(("%s: C/H/S start %d/%d/%d, start %lu: allow\n",
+ sname, chs_scyl, dp->dp_shd, chs_ssect, ssector1));
+ ssector = ssector1;
+ }
+
+#ifdef PC98
+ chs_ecyl = dp->dp_ecyl;
+ chs_esect = nsectors - 1;
+ esector = chs_esect + (ntracks - 1) * nsectors +
+ chs_ecyl * secpercyl + mbr_offset;
+ esector1 = ssector1 + pc98_size - 1;
+#else
+ chs_ecyl = DPCYL(dp->dp_ecyl, dp->dp_esect);
+ chs_esect = DPSECT(dp->dp_esect);
+ esector = chs_esect - 1 + dp->dp_ehd * nsectors + chs_ecyl * secpercyl
+ + mbr_offset;
+ esector1 = ssector1 + dp->dp_size - 1;
+#endif
+
+ /* Allow certain bogus C/H/S values for esector, as above. */
+ if (esector < esector1
+ && ((chs_esect == nsectors && dp->dp_ehd == ntracks - 1
+ && chs_ecyl == 1023)
+ || (esector1 - esector) % (1024 * secpercyl) == 0)
+ || (dp->dp_ecyl == 255 && dp->dp_ehd == 255
+ && dp->dp_esect == 255)) {
+ TRACE(("%s: C/H/S end %d/%d/%d, end %lu: allow\n",
+ sname, chs_ecyl, dp->dp_ehd, chs_esect, esector1));
+ esector = esector1;
+ }
+
+ error = (ssector == ssector1 && esector == esector1) ? 0 : EINVAL;
+ if (bootverbose)
+#ifdef PC98
+ printf("%s: mid 0x%x, start %lu, end = %lu, size %lu%s\n",
+ sname, dp->dp_mid, ssector1, esector1, pc98_size,
+ error ? "" : ": OK");
+#else
+ printf("%s: type 0x%x, start %lu, end = %lu, size %lu %s\n",
+ sname, dp->dp_typ, ssector1, esector1, dp->dp_size,
+ error ? "" : ": OK");
+#endif
+ if (ssector != ssector1 && bootverbose)
+ printf("%s: C/H/S start %d/%d/%d (%lu) != start %lu: invalid\n",
+ sname, chs_scyl, dp->dp_shd, chs_ssect,
+ ssector, ssector1);
+ if (esector != esector1 && bootverbose)
+ printf("%s: C/H/S end %d/%d/%d (%lu) != end %lu: invalid\n",
+ sname, chs_ecyl, dp->dp_ehd, chs_esect,
+ esector, esector1);
+ return (error);
+}
+
+int
+dsinit(dname, dev, strat, lp, sspp)
+ char *dname;
+ dev_t dev;
+ d_strategy_t *strat;
+ struct disklabel *lp;
+ struct diskslices **sspp;
+{
+ struct buf *bp;
+ u_char *cp;
+ int dospart;
+ struct dos_partition *dp;
+ struct dos_partition *dp0;
+ int error;
+ int max_ncyls;
+ int max_nsectors;
+ int max_ntracks;
+ u_long mbr_offset;
+ char partname[2];
+ u_long secpercyl;
+ char *sname;
+ struct diskslice *sp;
+ struct diskslices *ssp;
+#ifdef PC98
+ u_long pc98_start;
+ u_long pc98_size;
+#endif
+
+ /*
+ * Allocate a dummy slices "struct" and initialize it to contain
+ * only an empty compatibility slice (pointing to itself) and a
+ * whole disk slice (covering the disk as described by the label).
+ * If there is an error, then the dummy struct becomes final.
+ */
+ ssp = malloc(offsetof(struct diskslices, dss_slices)
+ + BASE_SLICE * sizeof *sp, M_DEVBUF, M_WAITOK);
+ *sspp = ssp;
+ ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
+ ssp->dss_nslices = BASE_SLICE;
+ sp = &ssp->dss_slices[0];
+ bzero(sp, BASE_SLICE * sizeof *sp);
+ sp[WHOLE_DISK_SLICE].ds_size = lp->d_secperunit;
+
+ mbr_offset = DOSBBSECTOR;
+reread_mbr:
+ /* Read master boot record. */
+#ifdef PC98
+ if ((int)lp->d_secsize < 1024)
+ bp = geteblk((int)1024);
+ else
+ bp = geteblk((int)lp->d_secsize);
+#else
+ bp = geteblk((int)lp->d_secsize);
+#endif
+ bp->b_dev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
+ bp->b_blkno = mbr_offset;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags |= B_BUSY | B_READ;
+#ifdef PC98
+ if (bp->b_bcount < 1024)
+ bp->b_bcount = 1024;
+#endif
+ (*strat)(bp);
+ if (biowait(bp) != 0) {
+ diskerr(bp, dname, "error reading primary partition table",
+ LOG_PRINTF, 0, lp);
+ printf("\n");
+ error = EIO;
+ goto done;
+ }
+
+ /* Weakly verify it. */
+ cp = bp->b_un.b_addr;
+ sname = dsname(dname, dkunit(dev), WHOLE_DISK_SLICE, RAW_PART,
+ partname);
+ if (cp[0x1FE] != 0x55 || cp[0x1FF] != 0xAA) {
+ printf("%s: invalid primary partition table: no magic\n",
+ sname);
+ error = EINVAL;
+ goto done;
+ }
+#ifdef PC98
+ /*
+ * entire disk for FreeBSD
+ */
+ if ((*(cp + 512) == 0x57) && (*(cp + 513) == 0x45) &&
+ (*(cp + 514) == 0x56) && (*(cp + 515) == 0x82)) {
+ sname = dsname(dname, dkunit(dev), BASE_SLICE,
+ RAW_PART, partname);
+ free(ssp, M_DEVBUF);
+ ssp = malloc(offsetof(struct diskslices, dss_slices)
+#define MAX_SLICES_SUPPORTED MAX_SLICES /* was (BASE_SLICE + NDOSPART) */
+ + MAX_SLICES_SUPPORTED * sizeof *sp, M_DEVBUF, M_WAITOK);
+ *sspp = ssp;
+ ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
+ sp = &ssp->dss_slices[0];
+ bzero(sp, MAX_SLICES_SUPPORTED * sizeof *sp);
+ sp[WHOLE_DISK_SLICE].ds_size = lp->d_secperunit;
+
+ /* Initialize normal slices. */
+ sp += BASE_SLICE;
+ sp->ds_offset = 0;
+ sp->ds_size = lp->d_secperunit;
+ sp->ds_type = DOSPTYP_386BSD;
+ sp->ds_subtype = 0xc4;
+ error = 0;
+ ssp->dss_nslices = BASE_SLICE + 1;
+ goto done;
+ }
+
+ /*
+ * XXX --- MS-DOG MO
+ */
+ if ((*(cp + 0x0e) == 1) && (*(cp + 0x15) == 0xf0) &&
+ (*(cp + 0x1c) == 0x0) &&
+ ((*(cp + 512) == 0xf0) || (*(cp + 512) == 0xf8)) &&
+ (*(cp + 513) == 0xff) && (*(cp + 514) == 0xff)) {
+ sname = dsname(dname, dkunit(dev), BASE_SLICE,
+ RAW_PART, partname);
+ free(ssp, M_DEVBUF);
+ ssp = malloc(offsetof(struct diskslices, dss_slices)
+#define MAX_SLICES_SUPPORTED MAX_SLICES /* was (BASE_SLICE + NDOSPART) */
+ + MAX_SLICES_SUPPORTED * sizeof *sp, M_DEVBUF, M_WAITOK);
+ *sspp = ssp;
+ ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
+ sp = &ssp->dss_slices[0];
+ bzero(sp, MAX_SLICES_SUPPORTED * sizeof *sp);
+ sp[WHOLE_DISK_SLICE].ds_size = lp->d_secperunit;
+
+ /* Initialize normal slices. */
+ sp += BASE_SLICE;
+ sp->ds_offset = 0;
+ sp->ds_size = lp->d_secperunit;
+ sp->ds_type = 0xa0; /* XXX */
+ sp->ds_subtype = 0x81; /* XXX */
+ error = 0;
+ ssp->dss_nslices = BASE_SLICE + 1;
+ goto done;
+ }
+#ifdef COMPAT_ATDISK
+ /*
+ * Check magic number of 'extended format' for PC-9801.
+ * If no magic, it may be formatted on IBM-PC.
+ */
+ if (((cp[4] != 'I') || (cp[5] != 'P') || (cp[6] != 'L') ||
+ (cp[7] != '1')) &&
+ ((strncmp(dname, "sd", 2) == 0) || (strncmp(dname, "wd", 2) == 0))) {
+ /* IBM-PC HDD */
+ bp->b_flags = B_INVAL | B_AGE;
+ brelse(bp);
+ free(ssp, M_DEVBUF);
+ return atcompat_dsinit(dname, dev, strat, lp, sspp);
+ }
+#endif
+ dp0 = (struct dos_partition *)(cp + 512);
+#else
+ dp0 = (struct dos_partition *)(cp + DOSPARTOFF);
+
+ /* Check for "Ontrack Diskmanager". */
+ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
+ if (dp->dp_typ == DOSPTYP_ONTRACK) {
+ if (bootverbose)
+ printf(
+ "%s: Found \"Ontrack Disk Manager\" on this disk.\n", sname);
+ bp->b_flags |= B_INVAL | B_AGE;
+ brelse(bp);
+ mbr_offset = 63;
+ goto reread_mbr;
+ }
+ }
+
+ if (bcmp(dp0, historical_bogus_partition_table,
+ sizeof historical_bogus_partition_table) == 0) {
+ TRACE(("%s: invalid primary partition table: historical\n",
+ sname));
+ error = EINVAL;
+ goto done;
+ }
+#endif
+
+ /* Guess the geometry. */
+ /*
+ * TODO:
+ * Perhaps skip entries with 0 size.
+ * Perhaps only look at entries of type DOSPTYP_386BSD.
+ */
+ max_ncyls = 0;
+ max_nsectors = 0;
+ max_ntracks = 0;
+ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
+ int nsectors;
+ int ntracks;
+
+
+#ifdef PC98
+ max_ncyls = lp->d_secpercyl;
+#else
+ max_ncyls = DPCYL(dp->dp_ecyl, dp->dp_esect);
+#endif
+ if (max_ncyls < max_ncyls)
+ max_ncyls = max_ncyls;
+#ifdef PC98
+ nsectors = lp->d_nsectors;
+#else
+ nsectors = DPSECT(dp->dp_esect);
+#endif
+ if (max_nsectors < nsectors)
+ max_nsectors = nsectors;
+#ifdef PC98
+ ntracks = lp->d_ntracks;
+#else
+ ntracks = dp->dp_ehd + 1;
+#endif
+ if (max_ntracks < ntracks)
+ max_ntracks = ntracks;
+ }
+
+ /* Check the geometry. */
+ /*
+ * TODO:
+ * As above.
+ * Check for overlaps.
+ * Check against d_secperunit if the latter is reliable.
+ */
+ error = 0;
+ secpercyl = (u_long)max_nsectors * max_ntracks;
+#ifdef PC98
+ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
+ if (dp->dp_scyl == 0 && dp->dp_shd == 0 && dp->dp_ssect == 0)
+ continue;
+ sname = dsname(dname, dkunit(dev), BASE_SLICE + dospart,
+ RAW_PART, partname);
+#else
+ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++) {
+ if (dp->dp_scyl == 0 && dp->dp_shd == 0 && dp->dp_ssect == 0
+ && dp->dp_start == 0 && dp->dp_size == 0)
+ continue;
+ sname = dsname(dname, dkunit(dev), BASE_SLICE + dospart,
+ RAW_PART, partname);
+#endif
+ /*
+ * Temporarily ignore errors from this check. We could
+ * simplify things by accepting the table eariler if we
+ * always ignore errors here. Perhaps we should always
+ * accept the table if the magic is right but not let
+ * bad entries affect the geometry.
+ */
+ check_part(sname, dp, mbr_offset, max_nsectors, max_ntracks,
+ mbr_offset);
+ }
+ if (error != 0)
+ goto done;
+
+ /*
+ * Accept the DOS partition table.
+ * First adjust the label (we have been careful not to change it
+ * before we can guarantee success).
+ */
+ if (secpercyl != 0) {
+ u_long secperunit;
+
+ lp->d_nsectors = max_nsectors;
+ lp->d_ntracks = max_ntracks;
+ lp->d_secpercyl = secpercyl;
+ secperunit = secpercyl * max_ncyls;
+ if (lp->d_secperunit < secperunit)
+ lp->d_secperunit = secperunit;
+ lp->d_ncylinders = lp->d_secperunit / secpercyl;
+ }
+
+ /*
+ * Free the dummy slices "struct" and allocate a real new one.
+ * Initialize special slices as above.
+ */
+ free(ssp, M_DEVBUF);
+ ssp = malloc(offsetof(struct diskslices, dss_slices)
+#define MAX_SLICES_SUPPORTED MAX_SLICES /* was (BASE_SLICE + NDOSPART) */
+ + MAX_SLICES_SUPPORTED * sizeof *sp, M_DEVBUF, M_WAITOK);
+ *sspp = ssp;
+ ssp->dss_first_bsd_slice = COMPATIBILITY_SLICE;
+ sp = &ssp->dss_slices[0];
+ bzero(sp, MAX_SLICES_SUPPORTED * sizeof *sp);
+ sp[WHOLE_DISK_SLICE].ds_size = lp->d_secperunit;
+
+ /* Initialize normal slices. */
+ sp += BASE_SLICE;
+ for (dospart = 0, dp = dp0; dospart < NDOSPART; dospart++, dp++, sp++) {
+#ifdef PC98
+ pc98_start = DPBLKNO(dp->dp_scyl,dp->dp_shd,dp->dp_ssect);
+ pc98_size = dp->dp_ecyl ? DPBLKNO(dp->dp_ecyl+1,dp->dp_ehd,dp->dp_esect) - pc98_start : 0;
+ sp->ds_offset = pc98_start;
+ sp->ds_size = pc98_size;
+ sp->ds_type = dp->dp_mid;
+ sp->ds_subtype = dp->dp_sid;
+ strncpy(sp->ds_name, dp->dp_name, 16);
+#else
+ sp->ds_offset = mbr_offset + dp->dp_start;
+ sp->ds_size = dp->dp_size;
+ sp->ds_type = dp->dp_typ;
+#endif
+#if 0
+ lp->d_subtype |= (lp->d_subtype & 3) | dospart
+ | DSTYPE_INDOSPART;
+#endif
+ }
+ ssp->dss_nslices = BASE_SLICE + NDOSPART;
+
+#ifndef PC98
+ /* Handle extended partitions. */
+ sp -= NDOSPART;
+ for (dospart = 0; dospart < NDOSPART; dospart++, sp++)
+ if (sp->ds_type == DOSPTYP_EXTENDED)
+ extended(dname, bp->b_dev, strat, lp, ssp,
+ sp->ds_offset, sp->ds_size, sp->ds_offset,
+ max_nsectors, max_ntracks, mbr_offset);
+#endif
+
+done:
+ bp->b_flags |= B_INVAL | B_AGE;
+ brelse(bp);
+ if (error == EINVAL)
+ error = 0;
+ return (error);
+}
+
+/* PC98 does not use this function */
+void
+extended(dname, dev, strat, lp, ssp, ext_offset, ext_size, base_ext_offset,
+ nsectors, ntracks, mbr_offset)
+ char *dname;
+ dev_t dev;
+ struct disklabel *lp;
+ d_strategy_t *strat;
+ struct diskslices *ssp;
+ u_long ext_offset;
+ u_long ext_size;
+ u_long base_ext_offset;
+ int nsectors;
+ int ntracks;
+ u_long mbr_offset;
+{
+ struct buf *bp;
+ u_char *cp;
+ int dospart;
+ struct dos_partition *dp;
+ u_long ext_offsets[NDOSPART];
+ u_long ext_sizes[NDOSPART];
+ char partname[2];
+ int slice;
+ char *sname;
+ struct diskslice *sp;
+#ifdef PC98
+ int pc98_start;
+ int pc98_size;
+#endif
+
+ /* Read extended boot record. */
+ bp = geteblk((int)lp->d_secsize);
+ bp->b_dev = dev;
+ bp->b_blkno = ext_offset;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags |= B_BUSY | B_READ;
+ (*strat)(bp);
+ if (biowait(bp) != 0) {
+ diskerr(bp, dname, "error reading extended partition table",
+ LOG_PRINTF, 0, lp);
+ printf("\n");
+ goto done;
+ }
+
+ /* Weakly verify it. */
+ cp = bp->b_un.b_addr;
+ if (cp[0x1FE] != 0x55 || cp[0x1FF] != 0xAA) {
+ sname = dsname(dname, dkunit(dev), WHOLE_DISK_SLICE, RAW_PART,
+ partname);
+ printf("%s: invalid extended partition table: no magic\n",
+ sname);
+ goto done;
+ }
+
+ for (dospart = 0,
+ dp = (struct dos_partition *)(bp->b_un.b_addr + DOSPARTOFF),
+ slice = ssp->dss_nslices, sp = &ssp->dss_slices[slice];
+ dospart < NDOSPART; dospart++, dp++) {
+ ext_sizes[dospart] = 0;
+#ifdef PC98
+ if (dp->dp_scyl == 0 && dp->dp_shd == 0 && dp->dp_ssect == 0)
+#else
+ if (dp->dp_scyl == 0 && dp->dp_shd == 0 && dp->dp_ssect == 0
+ && dp->dp_start == 0 && dp->dp_size == 0)
+#endif
+ continue;
+#ifdef PC98
+ if (dp->dp_mid == 0xff) { /* XXX */
+#else
+ if (dp->dp_typ == DOSPTYP_EXTENDED) {
+#endif
+ char buf[32];
+
+ sname = dsname(dname, dkunit(dev), WHOLE_DISK_SLICE,
+ RAW_PART, partname);
+ strcpy(buf, sname);
+ if (strlen(buf) < sizeof buf - 11)
+ strcat(buf, "<extended>");
+ check_part(buf, dp, base_ext_offset, nsectors,
+ ntracks, mbr_offset);
+#ifdef PC98
+ pc98_start = DPBLKNO(dp->dp_scyl,dp->dp_shd,dp->dp_ssect);
+ ext_offsets[dospart] = pc98_start;
+ ext_sizes[dospart] = DPBLKNO(dp->dp_ecyl+1,dp->dp_ehd,dp->dp_esect)
+ - pc98_start;
+#else
+ ext_offsets[dospart] = base_ext_offset + dp->dp_start;
+ ext_sizes[dospart] = dp->dp_size;
+#endif
+ } else {
+ sname = dsname(dname, dkunit(dev), slice, RAW_PART,
+ partname);
+ check_part(sname, dp, ext_offset, nsectors, ntracks,
+ mbr_offset);
+ if (slice >= MAX_SLICES) {
+ printf("%s: too many slices\n", sname);
+ slice++;
+ continue;
+ }
+#ifdef PC98
+ pc98_start = DPBLKNO(dp->dp_scyl,dp->dp_shd,dp->dp_ssect);
+ pc98_size = dp->dp_ecyl ? DPBLKNO(dp->dp_ecyl+1,dp->dp_ehd,dp->dp_esect) - pc98_start : 0;
+ sp->ds_offset = ext_offset + pc98_start;
+ sp->ds_size = pc98_size;
+ sp->ds_type = dp->dp_mid;
+ sp->ds_subtype = dp->dp_sid;
+ strncpy(sp->ds_name, dp->dp_name, 16);
+#else
+ sp->ds_offset = ext_offset + dp->dp_start;
+ sp->ds_size = dp->dp_size;
+ sp->ds_type = dp->dp_typ;
+#endif
+ ssp->dss_nslices++;
+ slice++;
+ sp++;
+ }
+ }
+
+ /* If we found any more slices, recursively find all the subslices. */
+ for (dospart = 0; dospart < NDOSPART; dospart++)
+ if (ext_sizes[dospart] != 0)
+ extended(dname, dev, strat, lp, ssp,
+ ext_offsets[dospart], ext_sizes[dospart],
+ base_ext_offset, nsectors, ntracks,
+ mbr_offset);
+
+done:
+ bp->b_flags |= B_INVAL | B_AGE;
+ brelse(bp);
+}
diff --git a/sys/pc98/pc98/fd.c b/sys/pc98/pc98/fd.c
new file mode 100644
index 0000000..55a8354
--- /dev/null
+++ b/sys/pc98/pc98/fd.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/pc98/fd.c.new b/sys/pc98/pc98/fd.c.new
new file mode 100644
index 0000000..121cf8b
--- /dev/null
+++ b/sys/pc98/pc98/fd.c.new
@@ -0,0 +1,2680 @@
+/*
+ * 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.87 1996/04/08 19:40:56 smpatel 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>
+#include <machine/stdarg.h>
+#if NFT > 0
+#include <sys/ftape.h>
+#include <pc98/pc98/ftreg.h>
+#endif
+#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>
+#include <machine/stdarg.h>
+#if NFT > 0
+#include <sys/ftape.h>
+#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
+#ifdef PC98
+#define B_XXX3 0x08000000
+#define B_READID B_XXX3
+#endif
+
+/*
+ * 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 14
+#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
+#define FD_997 6
+#define FD_988 7
+#define FD_250 8
+#define FD_640_256 9
+#define FD_800 10
+#define FD_360 11
+#define FD_320 12
+#define FD_320_256 13
+#define FD_400 14
+#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 }, /* 1200K in 3/5 2HD (512/sec) */
+{ 8,3,0xFF,0x35,77,1232,1,0,2,0x74,1 }, /* 1232K in 3/5/8 2HD (1024/sec) */
+{ 9,2,0xFF,0x20,80,1440,1,1,2,0x50,1 }, /* 720K in 3/5 2DD (512/sec) */
+{ 8,2,0xFF,0x2A,80,1280,1,1,2,0x50,1 }, /* 640K in 3/5 2DD (512/sec) */
+{ 18,2,0xFF,0x1B,80,2880,1,2,2,0x6C,1 }, /* 1440K in 3 2HD (512/sec) */
+{ 26,1,0xFF,0x0E,77,3991,1,0,2,0x36,1 }, /* 1M in 3/5/8 2HD (256/sec) */
+{ 26,1,0xFF,0x0E,76,3952,1,0,2,0x36,1 }, /* 1M in 3/5/8 2HD (256/sec) */
+{ 26,0,0xFF,0x07,77,2002,1,0,1,0x1B,1 }, /* 250K in 8 1S (128/sec) */
+{ 16,1,0xFF,0x0E,80,2560,1,1,2,0x36,1 }, /* 640K in 3/5 2DD (256/sec) */
+{ 5,3,0xFF,0x35,80, 800,1,1,2,0x74,1 }, /* 800K in 3/5 2DD (1024/sec) */
+{ 9,2,0xFF,0x23,40, 720,2,1,2,0x50,1 }, /* 360K in 5 2D (512/sec) */
+{ 8,2,0xFF,0x2A,40, 640,2,1,2,0x50,1 }, /* 320K in 5 2D (512/sec) */
+{ 16,1,0xFF,0x0E,40,1280,2,1,2,0x36,1 }, /* 320K in 5 2D (256/sec) */
+{ 5,3,0xFF,0x35,40, 400,2,1,2,0x74,1 }, /* 400K in 5 2D (1024/sec) */
+#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];
+
+#ifdef PC98
+struct fd_id {
+ u_char cyl;
+ u_char head;
+ u_char sec;
+ u_char secsize;
+};
+#endif
+
+/***********************************************************************\
+* 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 */
+#ifdef PC98
+#define FD_READ_ID_DONE 0x10 /* read id done */
+#endif
+ 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 PC98
+ struct fd_id id; /* sector id data */
+#endif
+#ifdef DEVFS
+ void *bdevs[1 + NUMDENS + MAXPARTITIONS];
+ void *cdevs[1 + NUMDENS + MAXPARTITIONS];
+#endif
+} fd_data[NFD];
+
+/***********************************************************************\
+* 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;
+
+ 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;
+
+ 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;
+
+ ret = fd_in(fdc->fdcu, &status);
+ fdc->status[i] = status;
+ if (ret != 0)
+ break;
+ }
+
+ 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 int pc98_unit_type;
+
+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;
+{
+#if 0
+ if (pc98_trans == 2 && pc98_trans_prev < 2) {
+ outb(0x4be, (fdu << 5) || 0x11);
+ } else if (pc98_trans < 2 && pc98_trans_prev == 2) {
+ outb(0x4be, (fdu << 5) || 0x10);
+ }
+ DELAY(100);
+#endif
+
+ /* always motor on */
+ outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0) | FDP_PORTEXC);
+ 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;
+
+ while (retry++ < 30000) {
+ set_motor(fdcu, fd->fdsu, TURNON);
+ out_fdc(fdcu, NE7CMD_SENSED); /* Sense Drive Status */
+ out_fdc(fdcu, fdu); /* Drive number */
+ if ((in_fdc(fdcu) & NE7_ST3_RD)){
+ outb(fdc_data[fdcu].baseport + FDOUT,
+ FDO_DMAE | FDO_MTON);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int pc98_read_id( dev, type)
+ dev_t dev;
+ int type;
+{
+ fdu_t fdu = FDUNIT(minor(dev));
+ fd_p fd = &fd_data[fdu];
+ struct buf *bp;
+ int s;
+ int rv = 0;
+ int cyl = 2;
+ size_t fdblk;
+
+ fd->type = type;
+ fd->ft = fd_types + type - 1;
+ fdblk = 128 << fd->ft->secsize;
+
+ if ((bp = (struct buf*)malloc(
+ sizeof(struct buf), M_TEMP, M_NOWAIT)) == 0) {
+ return( ENOBUFS);
+ }
+ bzero((void*)bp, sizeof(struct buf));
+ bp->b_flags = B_BUSY | B_READ | B_READID;
+ bp->b_dev = dev;
+ bp->b_blkno = cyl * fd->ft->sectrac * fd->ft->heads * fdblk / DEV_BSIZE;
+ bp->b_bcount = sizeof(struct fd_id);
+ bp->b_un.b_addr = (caddr_t)&fd->id;
+
+ fdstrategy(bp);
+ s = splbio();
+ while (!(bp->b_flags & B_DONE)) {
+ if( (rv = tsleep( (caddr_t)bp,
+ PRIBIO, "fdreadid", 4 * hz)) == EWOULDBLOCK) {
+ break;
+ }
+ }
+ splx(s);
+
+ if (rv == EWOULDBLOCK) {
+ rv = EIO;
+ }
+ if (bp->b_flags & B_ERROR) {
+ rv = bp->b_error;
+ }
+ biodone(bp);
+ free(bp, M_TEMP);
+ return(rv);
+}
+#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 */
+ if (fd_cmd(fdcu,
+#ifdef PC98
+ 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0),
+#else
+ 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
+#endif
+ 0))
+ {
+ 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
+ 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);
+ }
+ 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
+
+ /* 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;
+ else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fdu) & 0x01)
+ fdt = FDT_12M;
+ else fdt = FDT_NONE;
+ pc98_unit_type = fdt;
+ 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:
+ printf("1M/640K FDD\n");
+ fd->type = FD_1200;
+ kdc_fd[fdu].kdc_description =
+ "1M/640K floppy disk drive";
+ break;
+ case FDT_144M:
+ printf("1.44M FDD\n");
+ fd->type = FD_1200;
+ kdc_fd[fdu].kdc_description =
+ "1.44MB (1440K) 3.5in floppy disk drive";
+ 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) {
+#ifdef PC98
+ case FD_1200:
+ if (i != FD_1200)
+ continue;
+ break;
+ case FD_1440:
+ if (i != FD_1200 && i != FD_1440)
+ continue;
+ break;
+#else
+ 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;
+#endif
+ }
+ typemynor = mynor | i;
+ typesize = fd_types[i - 1].size / 2;
+ /*
+ * XXX all these conversions give bloated code and
+ * confusing names.
+ */
+#ifndef PC98
+ if (typesize == 1476)
+ typesize = 1480;
+ if (typesize == 1722)
+ typesize = 1720;
+#endif
+ 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);
+}
+
+#ifdef PC98
+int
+fdsize(dev_t dev)
+{
+ return(0);
+}
+#endif
+
+/****************************************************************************/
+/* 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);
+ 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);
+ 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
+ */
+ (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
+#ifdef PC98
+ NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0),
+#else
+ NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
+#endif
+ 0);
+ }
+}
+
+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);
+
+#ifndef PC98
+ /*
+ * 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;
+ }
+#endif
+
+ 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(100);
+ outb(fdc->baseport + FDOUT, 0x18);
+#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 */
+ (void)fd_cmd(fdcu, 3, NE7CMD_SPECIFY,
+#ifdef PC98
+ NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
+#else
+ NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0),
+#endif
+ 0);
+}
+
+/****************************************************************************/
+/* fdc in/out */
+/****************************************************************************/
+int
+in_fdc(fdcu_t fdcu)
+{
+ int baseport = fdc_data[fdcu].baseport;
+ int i, j = 100000;
+ 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 = 100000;
+ 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);
+ *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 = 100000;
+ while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0);
+ if (i <= 0) return fdc_err(fdcu, "direction bit not set\n");
+
+ /* Check that the floppy controller is ready for a command */
+ i = 100000;
+ while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0);
+ if (i <= 0)
+ return fdc_err(fdcu, bootverbose? "output ready timeout\n": 0);
+
+ /* Send the command and return */
+ outb(baseport+FDDATA, x);
+ 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)
+#ifdef PC98
+ {
+ struct fd_id *id = &fd_data[fdu].id;
+
+ type = FD_1200;
+ if (pc98_read_id(dev, FD_720) == 0) {
+ if (id->secsize == 1) {
+ type = (id->cyl == 1) ? FD_320_256 : FD_640_256;
+ }
+ else if (id->secsize == 2) {
+ type = (id->cyl == 1) ? FD_360 : FD_720;
+ }
+ else if (id->secsize == 3) {
+ type = (id->cyl == 1) ? FD_400 : FD_800;
+ }
+ if (id->cyl == 1) {
+ fd_data[fdu].track = -2;
+ }
+ }
+ else if (pc98_read_id(dev, FD_1200) == 0) {
+ if (id->secsize == 1) {
+ type = FD_997;
+ }
+ else if (id->secsize == 3) {
+ type = FD_1232;
+ }
+ }
+ else if (pc98_unit_type == FDT_144M
+ && pc98_read_id(dev, FD_1440) == 0) {
+ if (id->secsize == 2) {
+ type = FD_1440;
+ }
+ }
+ else if (pc98_read_id(dev, FD_250) == 0) {
+ if (id->secsize == 0) {
+ type = FD_250;
+ }
+ }
+ }
+#else
+ type = fd_data[fdu].type;
+ 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
+
+#ifdef PC98
+ fd_data[fdu].type = type;
+#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)
+{
+ register struct buf *dp;
+ 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);
+#ifdef PC98
+ if (!(bp->b_flags & (B_FORMAT | B_READID))) {
+#else
+ if (!(bp->b_flags & B_FORMAT)) {
+#endif
+ 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;
+ }
+#ifdef PC98
+ if ((bp->b_blkno * DEV_BSIZE) % fdblk) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+#endif
+ 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;
+ }
+ if (fdblk < DEV_BSIZE) {
+ nblocks -= nblocks % (DEV_BSIZE / fdblk);
+ }
+ if (blknum == nblocks) {
+ bp->b_resid = bp->b_bcount;
+ goto bad;
+ }
+ else if (blknum > nblocks) {
+ bp->b_error = ENOSPC;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+ if (fd->type == FD_997) {
+ if (blknum < fd->ft->size - (fd->ft->sectrac >> 1)) {
+ blknum += fd->ft->sectrac;
+ }
+ else {
+ blknum = 0;
+ }
+ }
+ else if (fd->type == FD_988) {
+ blknum += fd->ft->sectrac * fd->ft->heads;
+ }
+#else
+ 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;
+ }
+#endif
+ bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads);
+ bp->b_pblkno = bp->b_blkno;
+ dp = &(fdc->head);
+ s = splbio();
+ disksort(dp, 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 *dp, *bp;
+ int s;
+
+ dp = &fdc_data[fdcu].head;
+ bp = dp->b_actf;
+
+ /*
+ * 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)
+ {
+ 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 *dp, *bp;
+ struct fd_formb *finfo = NULL;
+ size_t fdblk;
+#ifdef PC98
+ int readid;
+ struct fd_id *id = NULL;
+#endif
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+ 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;
+#ifdef PC98
+ sectrac = fd->ft->sectrac;
+ if (readid = bp->b_flags & B_READID) {
+ id = (struct fd_id*)bp->b_un.b_addr;
+ }
+#endif
+ 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);
+ }
+ pc98_trans_prev = 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 *
+ \*******************************************************/
+ 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);
+ }
+ 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;
+ 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));
+ }
+ }
+
+ 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;
+#ifdef PC98
+ blknum = (unsigned long)bp->b_blkno * DEV_BSIZE + fd->skip;
+ if (fd->type == FD_997) {
+ long size = fdblk * (fd->ft->size - (sectrac >> 1));
+
+ if (blknum < size) {
+ blknum /= fdblk;
+ blknum += sectrac;
+ }
+ else {
+ blknum -= size;
+ blknum /= (fdblk >>= 1);
+ }
+ }
+ else if (fd->type == FD_988) {
+ blknum /= fdblk;
+ blknum += sectrac * fd->ft->heads;
+ }
+ else {
+ blknum /= fdblk;
+ }
+#endif
+ if(format)
+ fd->skip = (char *)&(finfo->fd_formb_cylno(0))
+ - (char *)finfo;
+#ifdef PC98
+ if (!readid)
+ 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);
+#ifndef PC98
+ blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk
+ + fd->skip/fdblk;
+ sectrac = fd->ft->sectrac;
+#endif
+ 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 */
+#ifdef PC98
+ int mask = ~0; /* MFM mode */
+ int gaplen = finfo->fd_formb_gaplen;
+
+ if (fd->type == FD_997 && blknum < sectrac) {
+ gaplen = fd_types[FD_250-1].f_gap;
+ mask = ~0x40; /* FM mode */
+ }
+ else if (fd->ft->heads == 1) {
+ mask = ~0x40; /* FM mode */
+ }
+ if(fd_cmd(fdcu, 6,
+ NE7CMD_FORMAT & mask,
+ head << 2 | fdu,
+ finfo->fd_formb_secshift,
+ finfo->fd_formb_nsecs,
+ gaplen,
+ finfo->fd_formb_fillbyte,
+ 0))
+#else
+ 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))
+#endif
+ {
+ /* controller fell over */
+ fdc->retry = 6;
+ return(retrier(fdcu));
+ }
+ }
+#ifdef PC98
+ else if (readid) {
+ int mask = ~0; /* MFM mode */
+
+ if( fd->ft->heads == 1) {
+ mask = ~0x40; /* FM mode */
+ }
+ if( out_fdc( fdcu, NE7CMD_READID & mask) < 0) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ bp->b_resid = bp->b_bcount;
+ dp->b_actf = bp->b_actf;
+ biodone( bp);
+ fdc->state = FINDWORK;
+ fdc->fd = (fd_p)0;
+ fdc->fdu = -1;
+ return( 1);
+ }
+ out_fdc( fdcu, head << 2 | fdu);
+ }
+#endif
+ else
+ {
+#ifdef PC98
+ int mask = ~0; /* MFM mode */
+ struct fd_type *ft = fd->ft;
+
+ if (fd->type == FD_997 && blknum < sectrac) {
+ ft = fd_types + FD_250 - 1;
+ }
+ if (ft->heads == 1) {
+ mask = ~0x40; /* FM mode */
+ }
+ if ((read && out_fdc(fdcu, NE7CMD_READ & mask) < 0)
+ || (!read /* i.e., write */
+ && out_fdc(fdcu, NE7CMD_WRITE & mask) < 0))
+ if (fd_cmd(fdcu, 9,
+ (read ? NE7CMD_READ &mask :
+ NE7CMD_WRITE & mask),
+ head << 2 | fdu, /* head & unit */
+ fd->track, /* track */
+ head,
+ sec, /* sector + 1 */
+ ft->secsize, /* sector size */
+ sectrac, /* sectors/track */
+ ft->gap, /* gap size */
+ ft->datalen, /* data length */
+ 0))
+#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))
+#endif
+ {
+ /* 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 */
+ case IOCOMPLETE: /* IO DONE, post-analyze */
+ untimeout(fd_timeout, (caddr_t)fdcu);
+
+ if (fd_read_status(fdc, fd->fdsu))
+ {
+ if (fdc->retry < 6)
+ fdc->retry = 6; /* force a reset */
+ return retrier(fdcu);
+ }
+
+#ifdef PC98
+ if (readid) {
+ id->cyl = fdc->status[3];
+ id->head = fdc->status[4];
+ id->sec = fdc->status[5];
+ id->secsize = fdc->status[6];
+ }
+#endif
+ fdc->state = IOTIMEDOUT;
+
+ /* FALLTHROUGH */
+
+ case IOTIMEDOUT:
+#ifdef PC98
+ blknum = (unsigned long)bp->b_blkno * DEV_BSIZE + fd->skip;
+ if (fd->type == FD_997 && blknum
+ >= fdblk * (fd->ft->size - (sectrac >> 1))) {
+ fdblk >>= 1;
+ }
+ if (!readid)
+ 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 PC98
+ if (!readid && fdc->status[0] & NE7_ST0_IC)
+#else
+ if (fdc->status[0] & NE7_ST0_IC)
+#endif
+ {
+ 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;
+#ifdef PC98
+ fdblk = 128 << fd->ft->secsize;
+ blknum = (unsigned long)bp->b_blkno * DEV_BSIZE + fd->skip;
+ if (!(format | readid) && fd->skip < bp->b_bcount
+ && blknum < fdblk * fd->ft->size) {
+ if (fd->type == FD_997) {
+ long size = fdblk *
+ (fd->ft->size - (sectrac >> 1));
+
+ if (blknum < size) {
+ blknum /= fdblk;
+ blknum += sectrac;
+ }
+ else {
+ blknum -= size;
+ blknum /= (fdblk >>= 1);
+ }
+ }
+ else if (fd->type == FD_988) {
+ blknum /= fdblk;
+ blknum += sectrac * fd->ft->heads;
+ }
+ else {
+ blknum /= fdblk;
+ }
+#else
+ if (!format && fd->skip < bp->b_bcount)
+ {
+ /* set up next transfer */
+ blknum = (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 */
+#ifdef PC98
+ if (readid) {
+ if (fdc->status[0] & NE7_ST0_IC) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ bp->b_resid = bp->b_bcount;
+ }
+ else {
+ bp->b_resid = 0;
+ }
+ }
+ if (format) {
+ bp->b_resid = 0;
+ }
+ else {
+ bp->b_resid = bp->b_bcount - fd->skip;
+ }
+ fd->skip = 0;
+ if (readid) {
+ fd->flags |= FD_READ_ID_DONE;
+ fdc->state = STARTRECAL;
+ }
+ else {
+ dp->b_actf = bp->b_actf;
+ biodone(bp);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ fdc->state = FINDWORK;
+ }
+#else
+ fd->skip = 0;
+ bp->b_resid = 0;
+ dp->b_actf = bp->b_actf;
+ biodone(bp);
+ fdc->fd = (fd_p) 0;
+ fdc->fdu = -1;
+ fdc->state = FINDWORK;
+#endif
+ }
+ 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);
+ 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 */
+#ifdef PC98
+ if (readid && fd->flags & FD_READ_ID_DONE) {
+ fd->flags &= ~FD_READ_ID_DONE;
+ dp->b_actf = bp->b_actf;
+ biodone(bp);
+ fdc->fd = (fd_p)0;
+ fdc->fdu = -1;
+ fdc->state = FINDWORK;
+ }
+ else {
+ fdc->state = DOSEEK;
+ }
+#else
+ fdc->state = DOSEEK;
+#endif
+ 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 *dp, *bp;
+
+ dp = &(fdc->head);
+ bp = dp->b_actf;
+
+ 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;
+ dp->b_actf = bp->b_actf;
+ 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;
+#ifdef PC98
+ int cyl, head;
+#endif
+
+ 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
+ */
+#ifdef PC98
+ cyl = finfo->cyl;
+ head = finfo->head;
+ if (fd->type == FD_997) {
+ if (cyl == 0 && head == 0) {
+ cyl = fd->ft->tracks - 1;
+ head++;
+ }
+ else {
+ int track = cyl * fd->ft->heads + head - 1;
+
+ cyl = track / fd->ft->heads;
+ head = track % fd->ft->heads;
+ }
+ }
+ else if (fd->type == FD_988 && cyl > 0) {
+ cyl--;
+ }
+ bp->b_blkno = ((cyl * fd->ft->sectrac * fd->ft->heads
+ + head * fd->ft->sectrac) * fdblk - 1) / DEV_BSIZE + 1;
+#else
+ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads)
+ + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE;
+#endif
+
+ 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;
+#ifdef PC98
+ dl->d_secpercyl = fdt->size;
+ if (fd_data[FDUNIT(minor(dev))].type == FD_997) {
+ dl->d_secpercyl += (fdt->sectrac >> 1);
+ }
+ dl->d_secpercyl /= fdt->tracks;
+#else
+ dl->d_secpercyl = fdt->size / fdt->tracks;
+#endif
+ 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/pc98/fdc.h b/sys/pc98/pc98/fdc.h
new file mode 100644
index 0000000..b763b1c
--- /dev/null
+++ b/sys/pc98/pc98/fdc.h
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 1990 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: @(#)fd.c 7.4 (Berkeley) 5/25/91
+ * $Id: fdc.h,v 1.6 1996/05/03 14:57:22 phk Exp $
+ *
+ */
+
+enum fdc_type
+{
+ FDC_NE765, FDC_I82077, FDC_NE72065, FDC_UNKNOWN = -1
+};
+
+
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+struct fdc_data
+{
+ int fdcu; /* our unit number */
+ int baseport;
+ int dmachan;
+ int flags;
+#define FDC_ATTACHED 0x01
+#define FDC_HASFTAPE 0x02
+#define FDC_TAPE_BUSY 0x04
+#define FDC_STAT_VALID 0x08
+ struct fd_data *fd;
+ int fdu; /* the active drive */
+ int state;
+ int retry;
+ int fdout; /* mirror of the w/o digital output reg */
+ u_long status[7]; /* copy of the registers */
+ enum fdc_type fdct; /* chip version of FDC */
+ int fdc_errs; /* number of logged errors */
+ struct buf_queue_head head; /* Head of buf chain */
+};
+
+/***********************************************************************\
+* 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) *
+\***********************************************************************/
+typedef int fdu_t;
+typedef int fdcu_t;
+typedef int fdsu_t;
+typedef struct fd_data *fd_p;
+typedef struct fdc_data *fdc_p;
+typedef enum fdc_type fdc_t;
+
+#define FDUNIT(s) (((s)>>6)&03)
+#define FDTYPE(s) ((s)&077)
diff --git a/sys/pc98/pc98/fdreg.h b/sys/pc98/pc98/fdreg.h
new file mode 100644
index 0000000..7b10161
--- /dev/null
+++ b/sys/pc98/pc98/fdreg.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/pc98/ft.c b/sys/pc98/pc98/ft.c
new file mode 100644
index 0000000..61d4723
--- /dev/null
+++ b/sys/pc98/pc98/ft.c
@@ -0,0 +1,2695 @@
+/*
+ * Copyright (c) 1993, 1994 Steve Gerakines
+ *
+ * This is freely redistributable software. You may do anything you
+ * wish with it, so long as the above notice stays intact.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * ft.c - QIC-40/80 floppy tape driver
+ * $Id: ft.c,v 1.25 1995/12/15 00:53:58 bde Exp $
+ *
+ * 01/19/95 ++sg
+ * Cleaned up recalibrate/seek code at attach time for FreeBSD 2.x.
+ *
+ * 06/07/94 v0.9 ++sg
+ * Tape stuck on segment problem should be gone. Re-wrote buffering
+ * scheme. Added support for drives that do not automatically perform
+ * seek load point. Can handle more wakeup types now and should correctly
+ * report most manufacturer names. Fixed places where unit 0 was being
+ * sent to the fdc instead of the actual unit number. Added ioctl support
+ * for an in-core badmap.
+ *
+ * 01/26/94 v0.3b - Jim Babb
+ * Got rid of the hard coded device selection. Moved (some of) the
+ * static variables into a structure for support of multiple devices.
+ * ( still has a way to go for 2 controllers - but closer )
+ * Changed the interface with fd.c so we no longer 'steal' it's
+ * driver routine vectors.
+ *
+ * 10/30/93 v0.3
+ * Fixed a couple more bugs. Reading was sometimes looping when an
+ * an error such as address-mark-missing was encountered. Both
+ * reading and writing was having more backup-and-retries than was
+ * necessary. Added support to get hardware info. Updated for use
+ * with FreeBSD.
+ *
+ * 09/15/93 v0.2 pl01
+ * Fixed a bunch of bugs: extra isa_dmadone() in async_write() (shouldn't
+ * matter), fixed double buffering in async_req(), changed tape_end() in
+ * set_fdcmode() to reduce unexpected interrupts, changed end of track
+ * processing in async_req(), protected more of ftreq_rw() with an
+ * splbio(). Changed some of the ftreq_*() functions so that they wait
+ * for inactivity and then go, instead of aborting immediately.
+ *
+ * 08/07/93 v0.2 release
+ * Shifted from ftstrat to ioctl support for I/O. Streaming is now much
+ * more reliable. Added internal support for error correction, QIC-40,
+ * and variable length tapes. Random access of segments greatly
+ * improved. Formatting and verification support is close but still
+ * incomplete.
+ *
+ * 06/03/93 v0.1 Alpha release
+ * Hopefully the last re-write. Many bugs fixed, many remain.
+ */
+
+#include "ft.h"
+#if NFT > 0
+#include "fd.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/disklabel.h> /* temp. for dkunit() in fdc.h */
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/ftape.h>
+#include <sys/devconf.h>
+
+#include <machine/clock.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/fdreg.h>
+#include <pc98/pc98/fdc.h>
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/ftreg.h>
+#else
+#include <i386/isa/isa_device.h>
+#include <i386/isa/fdreg.h>
+#include <i386/isa/fdc.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/rtc.h>
+#include <i386/isa/ftreg.h>
+#endif
+
+extern int ftintr __P((ftu_t ftu));
+
+/* Enable or disable debugging messages. */
+#define FTDBGALL 0 /* 1 if you want everything */
+/*#define DPRT(a) printf a */
+#define DPRT(a)
+
+/* Constants private to the driver */
+#define FTPRI (PRIBIO) /* sleep priority */
+#define FTNBUFF 9 /* 8 for buffering, 1 for header */
+
+/* The following items are needed from the fd driver. */
+extern int in_fdc(int); /* read fdc registers */
+extern int out_fdc(int, int); /* write fdc registers */
+
+extern int hz; /* system clock rate */
+
+/* Flags in isadev struct */
+#define FT_PROBE 0x1 /* allow for "dangerous" tape probes */
+
+/* Type of tape attached */
+/* use numbers that don't interfere with the possible floppy types */
+#define NO_TYPE 0 /* (same as NO_TYPE in fd.c) */
+
+/* F_TAPE_TYPE must match value in fd.c */
+#define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */
+#define FT_NONE (F_TAPE_TYPE | 0) /* no method required */
+#define FT_MOUNTAIN (F_TAPE_TYPE | 1) /* mountain */
+#define FT_COLORADO (F_TAPE_TYPE | 2) /* colorado */
+#define FT_INSIGHT (F_TAPE_TYPE | 3) /* insight */
+
+/* Mode FDC is currently in: tape or disk */
+enum { FDC_TAPE_MODE, FDC_DISK_MODE };
+
+/* Command we are awaiting completion of */
+enum { FTCMD_NONE, FTCMD_RESET, FTCMD_RECAL, FTCMD_SEEK, FTCMD_READID };
+
+/* Tape interrupt status of current request */
+enum { FTSTS_NONE, FTSTS_SNOOZE, FTSTS_INTERRUPT, FTSTS_TIMEOUT };
+
+/* Tape I/O status */
+enum {
+ FTIO_READY, /* No I/O activity */
+ FTIO_READING, /* Currently reading blocks */
+ FTIO_RDAHEAD, /* Currently reading ahead */
+ FTIO_WRITING /* Buffers are being written */
+};
+
+/* Current tape mode */
+enum {
+ FTM_PRIMARY, /* Primary mode */
+ FTM_VERIFY, /* Verify mode */
+ FTM_FORMAT, /* Format mode */
+ FTM_DIAG1, /* Diagnostic mode 1 */
+ FTM_DIAG2 /* Diagnostic mode 2 */
+};
+
+/* Tape geometries table */
+static QIC_Geom ftgtbl[] = {
+ { 0, 0, "Unformatted", "Unknown", 0, 0, 0, 0, 0 }, /* XXX */
+ { 1, 1, "QIC-40", "205/550", 20, 68, 2176, 128, 21760 },
+ { 1, 2, "QIC-40", "307.5/550", 20, 102, 3264, 128, 32640 },
+ { 1, 3, "QIC-40", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 1, 4, "QIC-40", "1100/550", 20, 365, 11680, 128, 32512 },
+ { 1, 5, "QIC-40", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 2, 1, "QIC-80", "205/550", 28, 100, 3200, 128, 19200 },
+ { 2, 2, "QIC-80", "307.5/550", 28, 150, 4800, 128, 19200 },
+ { 2, 3, "QIC-80", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 2, 4, "QIC-80", "1100/550", 28, 537, 17184, 128, 32512 },
+ { 2, 5, "QIC-80", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 1, "QIC-500", "205/550", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 2, "QIC-500", "307.5/550", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 3, "QIC-500", "295/900", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 4, "QIC-500", "1100/550", 0, 0, 0, 0, 0 }, /* ??? */
+ { 3, 5, "QIC-500", "1100/900", 0, 0, 0, 0, 0 } /* ??? */
+};
+#define NGEOM (sizeof(ftgtbl) / sizeof(QIC_Geom))
+
+static QIC_Geom *ftg = NULL; /* Current tape's geometry */
+
+/*
+ * things relating to asynchronous commands
+ */
+static int awr_state; /* state of async write */
+static int ard_state; /* state of async read */
+static int arq_state; /* state of async request */
+static int async_retries; /* retries, one per invocation */
+static int async_func; /* function to perform */
+static int async_state; /* state current function is at */
+static int async_arg0; /* up to 3 arguments for async cmds */
+static int async_arg1; /**/
+static int async_arg2; /**/
+static int async_ret; /* return value */
+static struct _astk {
+ int over_func;
+ int over_state;
+ int over_retries;
+ int over_arg0;
+ int over_arg1;
+ int over_arg2;
+} astk[10];
+static struct _astk *astk_ptr = &astk[0]; /* Pointer to stack position */
+
+/* List of valid async (interrupt driven) tape support functions. */
+enum {
+ ACMD_NONE, /* no command */
+ ACMD_SEEK, /* command seek */
+ ACMD_STATUS, /* report status */
+ ACMD_STATE, /* wait for state bits to be true */
+ ACMD_SEEKSTS, /* perform command and wait for status */
+ ACMD_READID, /* read id */
+ ACMD_RUNBLK /* ready tape for I/O on the given block */
+};
+
+/* Call another asyncronous command from within async_cmd(). */
+#define CALL_ACMD(r,f,a,b,c) \
+ astk_ptr->over_retries = async_retries; \
+ astk_ptr->over_func = async_func; \
+ astk_ptr->over_state = (r); \
+ astk_ptr->over_arg0 = async_arg0; \
+ astk_ptr->over_arg1 = async_arg1; \
+ astk_ptr->over_arg2 = async_arg2; \
+ async_func = (f); async_state = 0; async_retries = 0; \
+ async_arg0=(a); async_arg1=(b); async_arg2=(c); \
+ astk_ptr++; \
+ goto restate
+
+/* Perform an asyncronous command from outside async_cmd(). */
+#define ACMD_FUNC(r,f,a,b,c) over_async = (r); astk_ptr = &astk[0]; \
+ async_func = (f); async_state = 0; async_retries = 0; \
+ async_arg0=(a); async_arg1=(b); async_arg2=(c); \
+ async_cmd(ftu); \
+ return
+
+/* Various wait channels */
+static char *wc_buff_avail = "bavail";
+static char *wc_buff_done = "bdone";
+static char *wc_iosts_change = "iochg";
+static char *wc_long_delay = "ldelay";
+static char *wc_intr_wait = "intrw";
+#define ftsleep(wc,to) tsleep((caddr_t)(wc),FTPRI,(wc),(to))
+
+/***********************************************************************\
+* Per controller structure. *
+\***********************************************************************/
+extern struct fdc_data fdc_data[NFDC];
+
+/***********************************************************************\
+* Per tape drive structure. *
+\***********************************************************************/
+static struct ft_data {
+ struct fdc_data *fdc; /* pointer to controller structure */
+ int ftsu; /* this units number on this controller */
+ int type; /* Drive type (Mountain, Colorado) */
+/* QIC_Geom *ftg; */ /* pointer to Current tape's geometry */
+ int flags;
+ int cmd_wait; /* Command we are awaiting completion of */
+ int sts_wait; /* Tape interrupt status of current request */
+ int io_sts; /* Tape I/O status */
+ int mode;
+ int pcn; /* present cylinder number */
+ int attaching; /* true when ft is attaching */
+ unsigned char *xptr; /* pointer to buffer blk to xfer */
+ int xcnt; /* transfer count */
+ int xblk; /* block number to transfer */
+ int xseg; /* segment being transferred */
+ SegReq *segh; /* Current I/O request */
+ SegReq *segt; /* Tail of queued I/O requests */
+ SegReq *doneh; /* Completed I/O request queue */
+ SegReq *donet; /* Completed I/O request tail */
+ SegReq *segfree; /* Free segments */
+ SegReq *hdr; /* Current tape header */
+ int nsegq; /* Segments on request queue */
+ int ndoneq; /* Segments on completed queue */
+ int nfreelist; /* Segments on free list */
+
+ /* the next 3 should be defines in 'flags' */
+ int active; /* TRUE if transfer is active */
+ int rdonly; /* TRUE if tape is read-only */
+ int newcart; /* TRUE if new cartridge detected */
+ int laststs; /* last reported status code */
+ int lastcfg; /* last reported QIC config */
+ int lasterr; /* last QIC error code */
+ int lastpos; /* last known segment number */
+ int moving; /* TRUE if tape is moving */
+ int rid[7]; /* read_id return values */
+
+} ft_data[NFT];
+
+/***********************************************************************\
+* Throughout this file the following conventions will be used: *
+* ft is a pointer to the ft_data struct for the drive in question *
+* fdc is a pointer to the fdc_data struct for the controller *
+* ftu is the tape drive unit number *
+* fdcu is the floppy controller unit number *
+* ftsu is the tape drive unit number on that controller. (sub-unit) *
+\***********************************************************************/
+
+
+
+#define id_physid id_scsiid /* this biotab field doubles as a field */
+ /* for the physical unit number on the controller */
+
+int ftopen(dev_t, int);
+int ftclose(dev_t, int);
+int ftioctl(dev_t, int, caddr_t, int, struct proc *);
+#ifdef PC98
+int ftattach(struct pc98_device *, struct pc98_device *, int);
+#else
+int ftattach(struct isa_device *, struct isa_device *, int);
+#endif
+static timeout_t ft_timeout;
+static void async_cmd(ftu_t);
+static void async_req(ftu_t, int);
+static void async_read(ftu_t, int);
+static void async_write(ftu_t, int);
+static void tape_start(ftu_t, int);
+static void tape_end(ftu_t);
+static void tape_inactive(ftu_t);
+static int tape_cmd(ftu_t, int);
+static int tape_status(ftu_t);
+static int qic_status(ftu_t, int, int);
+static int ftreq_rewind(ftu_t);
+static int ftreq_hwinfo(ftu_t, QIC_HWInfo *);
+
+/*****************************************************************************/
+
+
+/*
+ * Allocate a segment I/O buffer from the free list.
+ */
+static SegReq *
+segio_alloc(ft_p ft)
+{
+ SegReq *r;
+
+ /* Grab first item from free list */
+ if ((r = ft->segfree) != NULL) {
+ ft->segfree = ft->segfree->next;
+ ft->nfreelist--;
+ }
+ DPRT(("segio_alloc: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
+ return(r);
+}
+
+
+/*
+ * Queue a segment I/O request.
+ */
+static void
+segio_queue(ft_p ft, SegReq *sp)
+{
+ /* Put request on in process queue. */
+ if (ft->segt == NULL)
+ ft->segh = sp;
+ else
+ ft->segt->next = sp;
+ sp->next = NULL;
+ ft->segt = sp;
+ ft->nsegq++;
+ DPRT(("segio_queue: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
+}
+
+
+/*
+ * Segment I/O completed, place on correct queue.
+ */
+static void
+segio_done(ft_p ft, SegReq *sp)
+{
+ /* First remove from current I/O queue */
+ ft->segh = sp->next;
+ if (ft->segh == NULL) ft->segt = NULL;
+ ft->nsegq--;
+
+ if (sp->reqtype == FTIO_WRITING) {
+ /* Place on free list */
+ sp->next = ft->segfree;
+ ft->segfree = sp;
+ ft->nfreelist++;
+ wakeup((caddr_t)wc_buff_avail);
+ DPRT(("segio_done: (w) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
+ } else {
+ /* Put on completed I/O queue */
+ if (ft->donet == NULL)
+ ft->doneh = sp;
+ else
+ ft->donet->next = sp;
+ sp->next = NULL;
+ ft->donet = sp;
+ ft->ndoneq++;
+ wakeup((caddr_t)wc_buff_done);
+ DPRT(("segio_done: (r) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
+ }
+}
+
+
+/*
+ * Take I/O request from finished queue to free queue.
+ */
+static void
+segio_free(ft_p ft, SegReq *sp)
+{
+ /* First remove from done queue */
+ ft->doneh = sp->next;
+ if (ft->doneh == NULL) ft->donet = NULL;
+ ft->ndoneq--;
+
+ /* Place on free list */
+ sp->next = ft->segfree;
+ ft->segfree = sp;
+ ft->nfreelist++;
+ wakeup((caddr_t)wc_buff_avail);
+ DPRT(("segio_free: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq));
+}
+
+static int ft_externalize(struct kern_devconf *, struct sysctl_req *);
+
+extern struct kern_devconf kdc_fdc[];
+static struct kern_devconf kdc_ft[NFT] = { {
+ 0, 0, 0, /* filled in by kern_devconf.c */
+ "ft", 0, { MDDT_DISK, 0 },
+ ft_externalize, 0, 0, DISK_EXTERNALLEN,
+ 0, /* parent */
+ 0, /* parentdata */
+ DC_IDLE, /* state */
+ "floppy tape",
+ DC_CLS_TAPE /* class */
+} };
+
+static inline void
+ft_registerdev(int ctlr, int unit)
+{
+ if(unit != 0)
+ kdc_ft[unit] = kdc_ft[0];
+
+ kdc_ft[unit].kdc_unit = unit;
+ kdc_ft[unit].kdc_parent = &kdc_fdc[ctlr];
+ kdc_ft[unit].kdc_parentdata = 0;
+ dev_attach(&kdc_ft[unit]);
+}
+
+
+static int
+ft_externalize(struct kern_devconf *kdc, struct sysctl_req *req)
+{
+ return disk_externalize(ft_data[kdc->kdc_unit].ftsu, req);
+}
+
+/*
+ * Probe/attach floppy tapes.
+ */
+int
+ftattach(isadev, fdup, unithasfd)
+#ifdef PC98
+ struct pc98_device *isadev, *fdup;
+#else
+ struct isa_device *isadev, *fdup;
+#endif
+ int unithasfd;
+{
+ fdcu_t fdcu = isadev->id_unit; /* fdc active unit */
+ fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */
+ ftu_t ftu = fdup->id_unit;
+ ft_p ft;
+ ftsu_t ftsu = fdup->id_physid;
+ QIC_HWInfo hw;
+ char *manu;
+
+ if (ftu >= NFT) return 0;
+ ft = &ft_data[ftu];
+
+ /* Probe for tape */
+ ft->attaching = 1;
+ ft->type = NO_TYPE;
+ ft->fdc = fdc;
+ ft->ftsu = ftsu;
+
+ /*
+ * FT_NONE - no method, just do it
+ */
+ tape_start(ftu, 0);
+ if (tape_status(ftu) >= 0) {
+ ft->type = FT_NONE;
+ ftreq_hwinfo(ftu, &hw);
+ goto out;
+ }
+
+ /*
+ * FT_COLORADO - colorado style
+ */
+ tape_start(ftu, 0);
+ tape_cmd(ftu, QC_COL_ENABLE1);
+ tape_cmd(ftu, QC_COL_ENABLE2 + ftu);
+ if (tape_status(ftu) >= 0) {
+ ft->type = FT_COLORADO;
+ ftreq_hwinfo(ftu, &hw);
+ tape_cmd(ftu, QC_COL_DISABLE);
+ goto out;
+ }
+
+ /*
+ * FT_MOUNTAIN - mountain style
+ */
+ tape_start(ftu, 0);
+ tape_cmd(ftu, QC_MTN_ENABLE1);
+ tape_cmd(ftu, QC_MTN_ENABLE2);
+ if (tape_status(ftu) >= 0) {
+ ft->type = FT_MOUNTAIN;
+ ftreq_hwinfo(ftu, &hw);
+ tape_cmd(ftu, QC_MTN_DISABLE);
+ goto out;
+ }
+
+ if(isadev->id_flags & FT_PROBE) {
+ /*
+ * Insight probe is dangerous, since it requires the motor being
+ * enabled and therefore risks attached floppy disk drives to jam.
+ * Probe only if explicitly requested by a flag 0x1 from config
+ */
+
+ /*
+ * FT_INSIGHT - insight style
+ *
+ * Since insight requires turning the drive motor on, we will not
+ * perform this probe if a floppy drive was already found with the
+ * the given unit and controller.
+ */
+ if (unithasfd) goto out;
+ tape_start(ftu, 1);
+ if (tape_status(ftu) >= 0) {
+ ft->type = FT_INSIGHT;
+ ftreq_hwinfo(ftu, &hw);
+ goto out;
+ }
+ }
+
+out:
+ tape_end(ftu);
+ if (ft->type != NO_TYPE) {
+ fdc->flags |= FDC_HASFTAPE;
+ ft_registerdev(fdcu, ftu);
+ switch(hw.hw_make) {
+ case 0x0000:
+ if (ft->type == FT_COLORADO) {
+ manu = "Colorado";
+ kdc_ft[ftu].kdc_description = "Colorado floppy tape";
+ } else if (ft->type == FT_INSIGHT) {
+ manu = "Insight";
+ kdc_ft[ftu].kdc_description = "Insight floppy tape";
+ } else if (ft->type == FT_MOUNTAIN && hw.hw_model == 0x05) {
+ manu = "Archive";
+ kdc_ft[ftu].kdc_description = "Archive floppy tape";
+ } else if (ft->type == FT_MOUNTAIN) {
+ manu = "Mountain";
+ kdc_ft[ftu].kdc_description = "Mountain floppy tape";
+ } else {
+ manu = "Unknown";
+ }
+ break;
+ case 0x0001:
+ manu = "Colorado";
+ kdc_ft[ftu].kdc_description = "Colorado floppy tape";
+ break;
+ case 0x0005:
+ if (hw.hw_model >= 0x09) {
+ manu = "Conner";
+ kdc_ft[ftu].kdc_description = "Conner floppy tape";
+ } else {
+ manu = "Archive";
+ kdc_ft[ftu].kdc_description = "Archive floppy tape";
+ }
+ break;
+ case 0x0006:
+ manu = "Mountain";
+ kdc_ft[ftu].kdc_description = "Mountain floppy tape";
+ break;
+ case 0x0007:
+ manu = "Wangtek";
+ kdc_ft[ftu].kdc_description = "Wangtek floppy tape";
+ break;
+ case 0x0222:
+ manu = "IOMega";
+ kdc_ft[ftu].kdc_description = "IOMega floppy tape";
+ break;
+ default:
+ manu = "Unknown";
+ break;
+ }
+ printf("ft%d: %s tape\n", fdup->id_unit, manu);
+ }
+ ft->attaching = 0;
+ return(ft->type);
+}
+
+
+/*
+ * Perform common commands asynchronously.
+ */
+static void
+async_cmd(ftu_t ftu) {
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu;
+ int cmd, i, st0, st3, pcn;
+ static int bitn, retval, retpos, nbits, newcn;
+ static int wanttrk, wantblk, wantdir;
+ static int curtrk, curblk, curdir, curdiff;
+ static int errcnt = 0;
+
+restate:
+#if FTDBGALL
+ DPRT(("async_cmd state: func: %d state: %d\n", async_func, async_state));
+#endif
+ switch(async_func) {
+ case ACMD_SEEK:
+ /*
+ * Arguments:
+ * 0 - command to perform
+ */
+ switch (async_state) {
+ case 0:
+ cmd = async_arg0;
+#if FTDBGALL
+ DPRT(("===>async_seek cmd = %d\n", cmd));
+#endif
+ newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
+ async_state = 1;
+ i = 0;
+ if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1;
+#ifdef PC98
+ if (!i && out_fdc(fdcu, 3) < 0) i = 1;
+#else
+ if (!i && out_fdc(fdcu, ftu) < 0) i = 1;
+#endif
+ if (!i && out_fdc(fdcu, newcn) < 0) i = 1;
+ if (i) {
+ if (++async_retries >= 10) {
+ DPRT(("ft%d: async_cmd command seek failed!!\n", ftu));
+ goto complete;
+ }
+ DPRT(("ft%d: async_cmd command seek retry...\n",ftu));
+ async_state = 0;
+ goto restate;
+ }
+ break;
+ case 1:
+ out_fdc(fdcu, NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ pcn = in_fdc(fdcu);
+ if (st0 < 0 || pcn < 0 || newcn != pcn) {
+ if (++async_retries >= 10) {
+ DPRT(("ft%d: async_cmd seek retries exceeded\n",ftu));
+ goto complete;
+ }
+ DPRT(("ft%d: async_cmd command bad st0=$%02x pcn=$%02x\n",
+ ftu, st0, pcn));
+ async_state = 0;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10);
+ break;
+ }
+ if (st0 & 0x20) { /* seek done */
+ ft->pcn = pcn;
+ }
+#if FTDBGALL
+ else
+ DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n",
+ ftu, st0, pcn));
+#endif
+ if (async_arg1) goto complete;
+ async_state = 2;
+ timeout(ft_timeout, (caddr_t)ftu, hz/50);
+ break;
+ case 2:
+ goto complete;
+ /* NOTREACHED */
+ }
+ break;
+
+ case ACMD_STATUS:
+ /*
+ * Arguments:
+ * 0 - command to issue report from
+ * 1 - number of bits
+ * modifies: bitn, retval, st3
+ */
+ switch (async_state) {
+ case 0:
+ bitn = 0;
+ retval = 0;
+ cmd = async_arg0;
+ nbits = async_arg1;
+ DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits));
+ CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0);
+ /* NOTREACHED */
+ case 1:
+ out_fdc(fdcu, NE7CMD_SENSED);
+#ifdef PC98
+ out_fdc(fdcu, 3);
+#else
+ out_fdc(fdcu, ftu);
+#endif
+ st3 = in_fdc(fdcu);
+ if (st3 < 0) {
+ DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n",
+ ftu,bitn,retval));
+ async_ret = -1;
+ goto complete;
+ }
+ if ((st3 & 0x10) != 0) retval |= (1 << bitn);
+ bitn++;
+ if (bitn >= (nbits+2)) {
+ if ((retval & 1) && (retval & (1 << (nbits+1)))) {
+ async_ret = (retval & ~(1<<(nbits+1))) >> 1;
+ if (async_arg0 == QC_STATUS && async_arg2 == 0 &&
+ (async_ret & (QS_ERROR|QS_NEWCART))) {
+ async_state = 2;
+ goto restate;
+ }
+ DPRT(("async status got $%04x ($%04x)\n", async_ret,retval));
+ } else {
+ DPRT(("ft%d: async_status failed: retval=$%04x nbits=%d\n",
+ ftu, retval,nbits));
+ async_ret = -2;
+ }
+ goto complete;
+ }
+ CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0);
+ /* NOTREACHED */
+ case 2:
+ if (async_ret & QS_NEWCART) ft->newcart = 1;
+ CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1);
+ case 3:
+ ft->lasterr = async_ret;
+ if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) {
+ DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
+ ftu, ft->lasterr & 0xff, ft->lasterr >> 8));
+ }
+ cmd = async_arg0;
+ nbits = async_arg1;
+ CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1);
+ case 4:
+ goto complete;
+ case 5:
+ CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0);
+ case 6:
+ CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0);
+ case 7:
+ CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0);
+ case 8:
+ cmd = async_arg0;
+ CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0);
+ }
+ break;
+
+ case ACMD_STATE:
+ /*
+ * Arguments:
+ * 0 - status bits to check
+ */
+ switch(async_state) {
+ case 0:
+ CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0);
+ case 1:
+ if ((async_ret & async_arg0) != 0) goto complete;
+ async_state = 0;
+ if (++async_retries == 360) { /* 90 secs. */
+ DPRT(("ft%d: acmd_state exceeded retry count\n", ftu));
+ goto complete;
+ }
+ timeout(ft_timeout, (caddr_t)ftu, hz/4);
+ break;
+ }
+ break;
+
+ case ACMD_SEEKSTS:
+ /*
+ * Arguments:
+ * 0 - command to perform
+ * 1 - status bits to check
+ * 2 - (optional) seconds to wait until completion
+ */
+ switch(async_state) {
+ case 0:
+ cmd = async_arg0;
+ async_retries = (async_arg2) ? (async_arg2 * 4) : 10;
+ CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0);
+ case 1:
+ CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0);
+ case 2:
+ if ((async_ret & async_arg1) != 0) goto complete;
+ if (--async_retries == 0) {
+ DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu));
+ goto complete;
+ }
+ async_state = 1;
+ timeout(ft_timeout, (caddr_t)ftu, hz/4);
+ break;
+ }
+ break;
+
+ case ACMD_READID:
+ /*
+ * Arguments: (none)
+ */
+ switch(async_state) {
+ case 0:
+ if (!ft->moving) {
+ CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
+ /* NOTREACHED */
+ }
+ async_state = 1;
+ out_fdc(fdcu, 0x4a); /* READ_ID */
+#ifdef PC98
+ out_fdc(fdcu, 3);
+#else
+ out_fdc(fdcu, ftu);
+#endif
+ break;
+ case 1:
+ for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
+ async_ret = (ft->rid[3]*ftg->g_fdtrk) +
+ (ft->rid[4]*ftg->g_fdside) + ft->rid[5] - 1;
+ DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n",
+ ft->rid[0], ft->rid[1], ft->rid[2], ft->rid[3],
+ ft->rid[4], ft->rid[5], async_ret));
+ if ((ft->rid[0] & 0xc0) != 0 || async_ret < 0) {
+ /*
+ * Method for retry:
+ * errcnt == 1 regular retry
+ * 2 microstep head 1
+ * 3 microstep head 2
+ * 4 microstep head back to 0
+ * 5 fail
+ */
+ if (++errcnt >= 5) {
+ DPRT(("ft%d: acmd_readid errcnt exceeded\n", fdcu));
+ async_ret = -2;
+ errcnt = 0;
+ goto complete;
+ }
+ if (errcnt == 1) {
+ ft->moving = 0;
+ CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
+ } else {
+ ft->moving = 0;
+ CALL_ACMD(4, ACMD_SEEKSTS, QC_STPAUSE, QS_READY, 0);
+ }
+ DPRT(("readid retry %d...\n", errcnt));
+ async_state = 0;
+ goto restate;
+ }
+ if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) {
+ DPRT(("acmd_readid detected last block on track\n"));
+ retpos = async_ret;
+ CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0);
+ /* NOTREACHED */
+ }
+ ft->lastpos = async_ret;
+ errcnt = 0;
+ goto complete;
+ /* NOTREACHED */
+ case 2:
+ CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0);
+ case 3:
+ ft->moving = 0;
+ async_ret = retpos+1;
+ goto complete;
+ case 4:
+ CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0);
+ case 5:
+ ft->moving = 1;
+ async_state = 0;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+ }
+ break;
+
+ case ACMD_RUNBLK:
+ /*
+ * Arguments:
+ * 0 - block number I/O will be performed on
+ *
+ * modifies: curpos
+ */
+ switch (async_state) {
+ case 0:
+ wanttrk = async_arg0 / ftg->g_blktrk;
+ wantblk = async_arg0 % ftg->g_blktrk;
+ wantdir = wanttrk & 1;
+ ft->moving = 0;
+ CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
+ case 1:
+ curtrk = wanttrk;
+ curdir = curtrk & 1;
+ DPRT(("Changing to track %d\n", wanttrk));
+ CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0);
+ case 2:
+ cmd = wanttrk+2;
+ CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0);
+ case 3:
+ CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0);
+ case 4:
+ ft->laststs = async_ret;
+ if (wantblk == 0) {
+ curblk = 0;
+ cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART;
+ CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90);
+ }
+ if (ft->laststs & QS_BOT) {
+ DPRT(("Tape is at BOT\n"));
+ curblk = (wantdir) ? 4800 : 0;
+ async_state = 6;
+ goto restate;
+ }
+ if (ft->laststs & QS_EOT) {
+ DPRT(("Tape is at EOT\n"));
+ curblk = (wantdir) ? 0 : 4800;
+ async_state = 6;
+ goto restate;
+ }
+ CALL_ACMD(5, ACMD_READID, 0, 0, 0);
+ case 5:
+ if (async_ret < 0) {
+ ft->moving = 0;
+ ft->lastpos = -2;
+ if (async_ret == -2) {
+ CALL_ACMD(9, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
+ }
+ CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
+ }
+ curtrk = (async_ret+1) / ftg->g_blktrk;
+ curblk = (async_ret+1) % ftg->g_blktrk;
+ DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n",
+ curtrk, wanttrk, curblk, wantblk));
+ if (curtrk != wanttrk) { /* oops! */
+ DPRT(("oops!! wrong track!\n"));
+ CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
+ }
+ async_state = 6;
+ goto restate;
+ case 6:
+ DPRT(("curtrk = %d nextblk = %d\n", curtrk, curblk));
+ if (curblk == wantblk) {
+ ft->lastpos = curblk - 1;
+ async_ret = ft->lastpos;
+ if (ft->moving) goto complete;
+ CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0);
+ }
+ if (curblk > wantblk) { /* passed it */
+ ft->moving = 0;
+ CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
+ }
+ if ((wantblk - curblk) <= 256) { /* approaching it */
+ CALL_ACMD(5, ACMD_READID, 0, 0, 0);
+ }
+ /* way up ahead */
+ ft->moving = 0;
+ CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0);
+ break;
+ case 7:
+ ft->moving = 1;
+ CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0);
+ break;
+ case 8:
+ async_state = 9;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+ case 9:
+ goto complete;
+ case 10:
+ curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2;
+ if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1;
+ DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff));
+ CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0);
+ case 11:
+ DPRT(("reverse 1 done\n"));
+ CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0);
+ case 12:
+ DPRT(("reverse 2 done\n"));
+ CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
+ case 13:
+ CALL_ACMD(5, ACMD_READID, 0, 0, 0);
+ case 14:
+ curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2;
+ if (curdiff < 0) curdiff = 0;
+ DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff));
+ CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0);
+ case 15:
+ DPRT(("forward 1 done\n"));
+ CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0);
+ case 16:
+ DPRT(("forward 2 done\n"));
+ CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90);
+ }
+ break;
+ }
+
+ return;
+
+complete:
+ if (astk_ptr != &astk[0]) {
+ astk_ptr--;
+ async_retries = astk_ptr->over_retries;
+ async_func = astk_ptr->over_func;
+ async_state = astk_ptr->over_state;
+ async_arg0 = astk_ptr->over_arg0;
+ async_arg1 = astk_ptr->over_arg1;
+ async_arg2 = astk_ptr->over_arg2;
+ goto restate;
+ }
+ async_func = ACMD_NONE;
+ async_state = 0;
+ switch (ft->io_sts) {
+ case FTIO_READY:
+ async_req(ftu, 2);
+ break;
+ case FTIO_READING:
+ case FTIO_RDAHEAD:
+ async_read(ftu, 2);
+ break;
+ case FTIO_WRITING:
+ async_write(ftu, 2);
+ break;
+ default:
+ DPRT(("ft%d: bad async_cmd ending I/O state!\n", ftu));
+ break;
+ }
+}
+
+
+/*
+ * Entry point for the async request processor.
+ */
+static void
+async_req(ftu_t ftu, int from)
+{
+ ft_p ft = &ft_data[ftu];
+ SegReq *sp;
+ static int over_async, lastreq;
+ int cmd;
+
+ if (from == 2) arq_state = over_async;
+
+restate:
+ switch (arq_state) {
+ case 0: /* Process segment */
+ sp = ft->segh;
+ ft->io_sts = (sp == NULL) ? FTIO_READY : sp->reqtype;
+
+ if (ft->io_sts == FTIO_WRITING)
+ async_write(ftu, from);
+ else
+ async_read(ftu, from);
+ if (ft->io_sts != FTIO_READY) return;
+
+ /* Pull buffer from current I/O queue */
+ if (sp != NULL) {
+ lastreq = sp->reqtype;
+ segio_done(ft, sp);
+
+ /* If I/O cancelled, clear finished queue. */
+ if (sp->reqcan) {
+ while (ft->doneh != NULL)
+ segio_free(ft, ft->doneh);
+ lastreq = FTIO_READY;
+ }
+ } else
+ lastreq = FTIO_READY;
+
+ /* Detect end of track */
+ if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) {
+ ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0);
+ }
+ arq_state = 1;
+ goto restate;
+
+ case 1: /* Next request */
+ /* If we have another request queued, start it running. */
+ if (ft->segh != NULL) {
+ sp = ft->segh;
+ sp->reqcrc = 0;
+ arq_state = ard_state = awr_state = 0;
+ ft->xblk = sp->reqblk;
+ ft->xseg = sp->reqseg;
+ ft->xcnt = 0;
+ ft->xptr = sp->buff;
+ DPRT(("I/O reqblk = %d\n", ft->xblk));
+ goto restate;
+ }
+
+ /* If the last request was reading, do read ahead. */
+ if ((lastreq == FTIO_READING || lastreq == FTIO_RDAHEAD) &&
+ (sp = segio_alloc(ft)) != NULL) {
+ sp->reqtype = FTIO_RDAHEAD;
+ sp->reqblk = ft->xblk;
+ sp->reqseg = ft->xseg+1;
+ sp->reqcrc = 0;
+ sp->reqcan = 0;
+ segio_queue(ft, sp);
+ bzero(sp->buff, QCV_SEGSIZE);
+ arq_state = ard_state = awr_state = 0;
+ ft->xblk = sp->reqblk;
+ ft->xseg = sp->reqseg;
+ ft->xcnt = 0;
+ ft->xptr = sp->buff;
+ DPRT(("Processing readahead reqblk = %d\n", ft->xblk));
+ goto restate;
+ }
+
+ if (ft->moving) {
+ DPRT(("No more I/O.. Stopping.\n"));
+ ft->moving = 0;
+ ACMD_FUNC(7, ACMD_SEEKSTS, QC_PAUSE, QS_READY, 0);
+ break;
+ }
+ arq_state = 7;
+ goto restate;
+
+ case 2: /* End of track */
+ ft->moving = 0;
+ ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
+ break;
+
+ case 3:
+ DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk));
+ ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0);
+ break;
+
+ case 4:
+ cmd = (ft->xblk / ftg->g_blktrk) + 2;
+ if (ft->segh != NULL) {
+ ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0);
+ } else {
+ ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0);
+ }
+ break;
+
+ case 5:
+ ft->moving = 1;
+ ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0);
+ break;
+
+ case 6:
+ arq_state = 1;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+
+ case 7:
+ /* Time to rest. */
+ ft->active = 0;
+ ft->lastpos = -2;
+
+ /* wakeup those who want an i/o chg */
+ wakeup((caddr_t)wc_iosts_change);
+ break;
+ }
+}
+
+
+/*
+ * Entry for async read.
+ */
+static void
+async_read(ftu_t ftu, int from)
+{
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+ int i, rddta[7];
+ int where;
+ static int over_async;
+ static int retries = 0;
+
+ if (from == 2) ard_state = over_async;
+
+restate:
+#if FTDBGALL
+ DPRT(("async_read: state: %d from = %d\n", ard_state, from));
+#endif
+ switch (ard_state) {
+ case 0: /* Start off */
+ /* If tape is not at desired position, stop and locate */
+ if (ft->lastpos != (ft->xblk-1)) {
+ DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
+ ftu, ft->lastpos, ft->xblk));
+ ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0);
+ }
+
+ /* Tape is in position but stopped. */
+ if (!ft->moving) {
+ DPRT(("async_read ******STARTING TAPE\n"));
+ ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
+ }
+ ard_state = 1;
+ goto restate;
+
+ case 1: /* Start DMA */
+ /* Tape is now moving and in position-- start DMA now! */
+#ifdef PC98
+ pc98_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2);
+#else
+ isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2);
+#endif
+ out_fdc(fdcu, 0x66); /* read */
+#ifdef PC98
+ out_fdc(fdcu, 3);
+#else
+ out_fdc(fdcu, ftu); /* unit */
+#endif
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */
+ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */
+ out_fdc(fdcu, 0x03); /* 1K sectors */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */
+ out_fdc(fdcu, 0x74); /* gap length */
+ out_fdc(fdcu, 0xff); /* transfer size */
+ ard_state = 2;
+ break;
+
+ case 2: /* DMA completed */
+ /* Transfer complete, get status */
+ for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
+#ifdef PC98
+ pc98_dmadone(B_READ, ft->xptr, QCV_BLKSIZE, 2);
+#else
+ isa_dmadone(B_READ, ft->xptr, QCV_BLKSIZE, 2);
+#endif
+
+#if FTDBGALL
+ /* Compute where the controller thinks we are */
+ where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside)
+ + rddta[5]-1;
+ DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
+ rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
+ where, ft->xblk));
+#endif
+
+ /* Check for errors */
+ if ((rddta[0] & 0xc0) != 0x00) {
+#if !FTDBGALL
+ where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside)
+ + rddta[5]-1;
+ DPRT(("xd: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
+ rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
+ where, ft->xblk));
+#endif
+ if ((rddta[1] & 0x04) == 0x04 && retries < 2) {
+ /* Probably wrong position */
+ DPRT(("async_read: doing retry %d\n", retries));
+ ft->lastpos = ft->xblk;
+ ard_state = 0;
+ retries++;
+ goto restate;
+ } else {
+ /* CRC/Address-mark/Data-mark, et. al. */
+ DPRT(("ft%d: CRC error on block %d\n", fdcu, ft->xblk));
+ ft->segh->reqcrc |= (1 << ft->xcnt);
+ }
+ }
+
+ /* Otherwise, transfer completed okay. */
+ retries = 0;
+ ft->lastpos = ft->xblk;
+ ft->xblk++;
+ ft->xcnt++;
+ ft->xptr += QCV_BLKSIZE;
+ if (ft->xcnt < QCV_BLKSEG && ft->segh->reqcan == 0) {
+ ard_state = 0;
+ goto restate;
+ }
+ DPRT(("Read done.. Cancel = %d\n", ft->segh->reqcan));
+ ft->io_sts = FTIO_READY;
+ break;
+
+ case 3:
+ ft->moving = 1;
+ ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
+ break;
+
+ case 4:
+ ard_state = 1;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+
+ default:
+ DPRT(("ft%d: bad async_read state %d!!\n", ftu, ard_state));
+ break;
+ }
+}
+
+
+/*
+ * Entry for async write. If from is 0, this came from the interrupt
+ * routine, if it's 1 then it was a timeout, if it's 2, then an
+ * async_cmd completed.
+ */
+static void
+async_write(ftu_t ftu, int from)
+{
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+ int i, rddta[7];
+ int where;
+ static int over_async;
+ static int retries = 0;
+
+ if (from == 2) awr_state = over_async;
+
+restate:
+#if FTDBGALL
+ DPRT(("async_write: state: %d from = %d\n", awr_state, from));
+#endif
+ switch (awr_state) {
+ case 0: /* Start off */
+ /* If tape is not at desired position, stop and locate */
+ if (ft->lastpos != (ft->xblk-1)) {
+ DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n",
+ ftu, ft->lastpos, ft->xblk));
+ ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0);
+ }
+
+ /* Tape is in position but stopped. */
+ if (!ft->moving) {
+ DPRT(("async_write ******STARTING TAPE\n"));
+ ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0);
+ }
+ awr_state = 1;
+ goto restate;
+
+ case 1: /* Start DMA */
+ /* Tape is now moving and in position-- start DMA now! */
+#ifdef PC98
+ pc98_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
+#else
+ isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
+#endif
+ out_fdc(fdcu, 0x45); /* write */
+#ifdef PC98
+ out_fdc(fdcu, 3);
+#else
+ out_fdc(fdcu, ftu); /* unit */
+#endif
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cyl */
+ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */
+ out_fdc(fdcu, 0x03); /* 1K sectors */
+ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */
+ out_fdc(fdcu, 0x74); /* gap length */
+ out_fdc(fdcu, 0xff); /* transfer size */
+ awr_state = 2;
+ break;
+
+ case 2: /* DMA completed */
+ /* Transfer complete, get status */
+ for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu);
+#ifdef PC98
+ pc98_dmadone(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
+#else
+ isa_dmadone(B_WRITE, ft->xptr, QCV_BLKSIZE, 2);
+#endif
+
+#if FTDBGALL
+ /* Compute where the controller thinks we are */
+ where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1;
+ DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
+ rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
+ where, ft->xblk));
+#endif
+
+ /* Check for errors */
+ if ((rddta[0] & 0xc0) != 0x00) {
+#if !FTDBGALL
+ where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside)
+ + rddta[5]-1;
+ DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n",
+ rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5],
+ where, ft->xblk));
+#endif
+ if (retries < 3) {
+ /* Something happened -- try again */
+ DPRT(("async_write: doing retry %d\n", retries));
+ ft->lastpos = ft->xblk;
+ awr_state = 0;
+ retries++;
+ goto restate;
+ } else {
+ /*
+ * Retries failed. Note the unrecoverable error.
+ * Marking the block as bad is useless right now.
+ */
+ printf("ft%d: unrecoverable write error on block %d\n",
+ ftu, ft->xblk);
+ ft->segh->reqcrc |= (1 << ft->xcnt);
+ }
+ }
+
+ /* Otherwise, transfer completed okay. */
+ retries = 0;
+ ft->lastpos = ft->xblk;
+ ft->xblk++;
+ ft->xcnt++;
+ ft->xptr += QCV_BLKSIZE;
+ if (ft->xcnt < QCV_BLKSEG) {
+ awr_state = 0; /* next block */
+ goto restate;
+ }
+#if FTDBGALL
+ DPRT(("Write done.\n"));
+#endif
+ ft->io_sts = FTIO_READY;
+ break;
+
+ case 3:
+ ft->moving = 1;
+ ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0);
+ break;
+
+ case 4:
+ awr_state = 1;
+ timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */
+ break;
+
+ default:
+ DPRT(("ft%d: bad async_write state %d!!\n", ftu, awr_state));
+ break;
+ }
+}
+
+
+/*
+ * Interrupt handler for active tape. Bounced off of fdintr().
+ */
+int
+ftintr(ftu_t ftu)
+{
+ int st0, pcn, i;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+ int s = splbio();
+
+ st0 = 0;
+ pcn = 0;
+
+ /* I/O segment transfer completed */
+ if (ft->active) {
+ if (async_func != ACMD_NONE) {
+ async_cmd(ftu);
+ splx(s);
+ return(1);
+ }
+#if FTDBGALL
+ DPRT(("Got request interrupt\n"));
+#endif
+ async_req(ftu, 0);
+ splx(s);
+ return(1);
+ }
+
+ /* Get interrupt status */
+ if (ft->cmd_wait != FTCMD_READID) {
+ out_fdc(fdcu, NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ pcn = in_fdc(fdcu);
+ }
+
+ if (ft->cmd_wait == FTCMD_NONE || ft->sts_wait != FTSTS_SNOOZE) {
+huh_what:
+ printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n",
+ ftu, st0, pcn);
+ splx(s);
+ return(1);
+ }
+
+ switch (ft->cmd_wait) {
+ case FTCMD_RESET:
+ ft->sts_wait = FTSTS_INTERRUPT;
+ wakeup((caddr_t)wc_intr_wait);
+ break;
+ case FTCMD_RECAL:
+ case FTCMD_SEEK:
+ if (st0 & 0x20) { /* seek done */
+ ft->sts_wait = FTSTS_INTERRUPT;
+ ft->pcn = pcn;
+ wakeup((caddr_t)wc_intr_wait);
+ }
+#if FTDBGALL
+ else
+ DPRT(("ft%d: seek error st0 = $%02x pcn = %d\n",
+ ftu, st0, pcn));
+#endif
+ break;
+ case FTCMD_READID:
+ for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu);
+ ft->sts_wait = FTSTS_INTERRUPT;
+ wakeup((caddr_t)wc_intr_wait);
+ break;
+
+ default:
+ goto huh_what;
+ }
+
+ splx(s);
+ return(1);
+}
+
+
+/*
+ * Interrupt timeout routine.
+ */
+static void
+ft_timeout(void *arg1)
+{
+ int s;
+ ftu_t ftu = (ftu_t)arg1;
+ ft_p ft = &ft_data[ftu];
+
+ s = splbio();
+ if (ft->active) {
+ if (async_func != ACMD_NONE) {
+ async_cmd(ftu);
+ splx(s);
+ return;
+ }
+ async_req(ftu, 1);
+ } else {
+ ft->sts_wait = FTSTS_TIMEOUT;
+ wakeup((caddr_t)wc_intr_wait);
+ }
+ splx(s);
+}
+
+
+/*
+ * Wait for a particular interrupt to occur. ftintr() will wake us up
+ * if it sees what we want. Otherwise, time out and return error.
+ * Should always disable ints before trigger is sent and calling here.
+ */
+static int
+ftintr_wait(ftu_t ftu, int cmd, int ticks)
+{
+ int retries, st0, pcn;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+
+ ft->cmd_wait = cmd;
+ ft->sts_wait = FTSTS_SNOOZE;
+
+ /* At attach time, we can't rely on having interrupts serviced */
+ if (ft->attaching) {
+ switch (cmd) {
+ case FTCMD_RESET:
+ DELAY(100);
+ ft->sts_wait = FTSTS_INTERRUPT;
+ goto intrdone;
+ case FTCMD_RECAL:
+ case FTCMD_SEEK:
+ for (retries = 0; retries < 10000; retries++) {
+ DELAY(150);
+ out_fdc(fdcu, NE7CMD_SENSEI);
+ st0 = in_fdc(fdcu);
+ if ((st0 & 0xc0) == 0x80) continue;
+ pcn = in_fdc(fdcu);
+ if (st0 & 0x20) {
+ ft->sts_wait = FTSTS_INTERRUPT;
+ ft->pcn = pcn;
+ goto intrdone;
+ }
+ }
+ break;
+ }
+ ft->sts_wait = FTSTS_TIMEOUT;
+ goto intrdone;
+ }
+
+ ftsleep(wc_intr_wait, ticks);
+
+intrdone:
+ if (ft->sts_wait == FTSTS_TIMEOUT) { /* timeout */
+#if FTDBGALL
+ if (ft->cmd_wait != FTCMD_RESET)
+ DPRT(("ft%d: timeout on command %d\n", ftu, ft->cmd_wait));
+#endif
+ ft->cmd_wait = FTCMD_NONE;
+ ft->sts_wait = FTSTS_NONE;
+ return(1);
+ }
+
+ /* got interrupt */
+ if (ft->attaching == 0 && ticks) untimeout(ft_timeout, (caddr_t)ftu);
+ ft->cmd_wait = FTCMD_NONE;
+ ft->sts_wait = FTSTS_NONE;
+ return(0);
+}
+
+
+/*
+ * Recalibrate tape drive. Parameter totape is true, if we should
+ * recalibrate to tape drive settings.
+ */
+static int
+tape_recal(ftu_t ftu, int totape)
+{
+ int s;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+
+ DPRT(("tape_recal start\n"));
+
+#ifdef PC98
+ outb(0xbe, FDP_FDDEXC | FDP_PORTEXC);
+#endif
+ out_fdc(fdcu, NE7CMD_SPECIFY);
+#ifdef PC98
+ out_fdc(fdcu, (totape) ? 0xEF : 0xCF);
+ out_fdc(fdcu, 0x02);
+#else
+ out_fdc(fdcu, (totape) ? 0xAD : 0xDF);
+ out_fdc(fdcu, 0x02);
+#endif
+
+ s = splbio();
+ out_fdc(fdcu, NE7CMD_RECAL);
+#ifdef PC98
+ out_fdc(fdcu, 3);
+#else
+ out_fdc(fdcu, ftu);
+#endif
+
+ if (ftintr_wait(ftu, FTCMD_RECAL, hz)) {
+ splx(s);
+ DPRT(("ft%d: recalibrate timeout\n", ftu));
+ return(1);
+ }
+ splx(s);
+
+ out_fdc(fdcu, NE7CMD_SPECIFY);
+#ifdef PC98
+ out_fdc(fdcu, (totape) ? 0xEF : 0xCF);
+ out_fdc(fdcu, 0x02);
+#else
+ out_fdc(fdcu, (totape) ? 0xFD : 0xDF);
+ out_fdc(fdcu, 0x02);
+#endif
+
+ DPRT(("tape_recal end\n"));
+ return(0);
+}
+
+/*
+ * Wait for a particular tape status to be met. If all is TRUE, then
+ * all states must be met, otherwise any state can be met.
+ */
+static int
+tape_state(ftu_t ftu, int all, int mask, int seconds)
+{
+ int r, tries, maxtries;
+
+ maxtries = (seconds) ? (4 * seconds) : 1;
+ for (tries = 0; tries < maxtries; tries++) {
+ r = tape_status(ftu);
+ if (r >= 0) {
+ if (all && (r & mask) == mask) return(r);
+ if ((r & mask) != 0) return(r);
+ }
+ if (seconds) ftsleep(wc_long_delay, hz/4);
+ }
+ DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n",
+ ftu, mask, maxtries));
+ return(-1);
+}
+
+
+/*
+ * Send a QIC command to tape drive, wait for completion.
+ */
+static int
+tape_cmd(ftu_t ftu, int cmd)
+{
+ int newcn;
+ int retries = 0;
+ int s;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+
+ DPRT(("===> tape_cmd: %d\n",cmd));
+ newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd;
+
+retry:
+
+ /* Perform seek */
+ s = splbio();
+ out_fdc(fdcu, NE7CMD_SEEK);
+#ifdef PC98
+ out_fdc(fdcu, 3);
+#else
+ out_fdc(fdcu, ftu);
+#endif
+ out_fdc(fdcu, newcn);
+
+ if (ftintr_wait(ftu, FTCMD_SEEK, hz)) {
+ DPRT(("ft%d: tape_cmd seek timeout\n", ftu));
+redo:
+ splx(s);
+ if (++retries < 5) goto retry;
+ DPRT(("ft%d: tape_cmd seek failed!\n", ftu));
+ return(1);
+ }
+ splx(s);
+
+ if (ft->pcn != newcn) {
+ DPRT(("ft%d: bad seek in tape_cmd; pcn = %d newcn = %d\n",
+ ftu, ft->pcn, newcn));
+ goto redo;
+ }
+ DELAY(2500);
+ return(0);
+}
+
+
+/*
+ * Return status of tape drive
+ */
+static int
+tape_status(ftu_t ftu)
+{
+ int r, err, tries;
+ ft_p ft = &ft_data[ftu];
+ int max = (ft->attaching) ? 2 : 3;
+
+ for (r = -1, tries = 0; r < 0 && tries < max; tries++)
+ r = qic_status(ftu, QC_STATUS, 8);
+ if (tries == max) return(-1);
+
+recheck:
+ DPRT(("tape_status got $%04x\n",r));
+ ft->laststs = r;
+
+ if (r & (QS_ERROR|QS_NEWCART)) {
+ err = qic_status(ftu, QC_ERRCODE, 16);
+ ft->lasterr = err;
+ if (r & QS_NEWCART) {
+ ft->newcart = 1;
+ /* If tape not referenced, do a seek load point. */
+ if ((r & QS_FMTOK) == 0 && !ft->attaching) {
+ tape_cmd(ftu, QC_SEEKLP);
+ do {
+ ftsleep(wc_long_delay, hz);
+ } while ((r = qic_status(ftu, QC_STATUS, 8)) < 0 ||
+ (r & (QS_READY|QS_CART)) == QS_CART);
+ goto recheck;
+ }
+ } else if (err && !ft->attaching) {
+ DPRT(("ft%d: QIC error %d occurred on cmd %d\n",
+ ftu, err & 0xff, err >> 8));
+ }
+ r = qic_status(ftu, QC_STATUS, 8);
+ ft->laststs = r;
+ DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r));
+ }
+
+ ft->rdonly = (r & QS_RDONLY);
+ return(r);
+}
+
+
+/*
+ * Transfer control to tape drive.
+ */
+static void
+tape_start(ftu_t ftu, int motor)
+{
+ ft_p ft = &ft_data[ftu];
+ fdc_p fdc = ft->fdc;
+ int s, mbits;
+#ifndef PC98
+ static int mbmotor[] = { FDO_MOEN0, FDO_MOEN1, FDO_MOEN2, FDO_MOEN3 };
+#endif
+
+ s = splbio();
+ DPRT(("tape_start start\n"));
+
+ /* reset, dma disable */
+#ifdef PC98
+ outb(fdc->baseport+FDOUT, FDO_RST | FDO_FRY | FDO_AIE | FDO_MTON);
+#else
+ outb(fdc->baseport+FDOUT, 0x00);
+#endif
+ (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
+
+ /* raise reset, enable DMA, motor on if needed */
+#ifdef PC98
+ outb(fdc->baseport+FDOUT, FDO_DMAE | FDO_MTON);
+#else
+ mbits = ftu & 3;
+ if (motor && ftu < 4)
+ mbits |= mbmotor[ftu];
+
+ outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN | mbits);
+#endif
+ (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
+
+ splx(s);
+
+ tape_recal(ftu, 1);
+
+ /* set transfer speed */
+#ifndef PC98
+ outb(fdc->baseport+FDCTL, FDC_500KBPS);
+ DELAY(10);
+#endif
+
+ DPRT(("tape_start end\n"));
+}
+
+
+/*
+ * Transfer control back to floppy disks.
+ */
+static void
+tape_end(ftu_t ftu)
+{
+ ft_p ft = &ft_data[ftu];
+ fdc_p fdc = ft->fdc;
+ int s;
+
+ DPRT(("tape_end start\n"));
+ tape_recal(ftu, 0);
+
+ s = splbio();
+
+ /* reset, dma disable */
+#ifdef PC98
+ outb(fdc->baseport+FDOUT, FDO_RST | FDO_FRY | FDO_AIE | FDO_MTON);
+#else
+ outb(fdc->baseport+FDOUT, 0x00);
+#endif
+ (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
+
+ /* raise reset, enable DMA */
+#ifdef PC98
+ outb(fdc->baseport+FDOUT, FDO_DMAE | FDO_MTON);
+#else
+ outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN);
+#endif
+ (void)ftintr_wait(ftu, FTCMD_RESET, hz/10);
+
+ splx(s);
+
+ /* set transfer speed */
+#ifndef PC98
+ outb(fdc->baseport+FDCTL, FDC_500KBPS);
+ DELAY(10);
+#endif
+ fdc->flags &= ~FDC_TAPE_BUSY;
+
+ DPRT(("tape_end end\n"));
+}
+
+
+/*
+ * Wait for the driver to go inactive, cancel readahead if necessary.
+ */
+static void
+tape_inactive(ftu_t ftu)
+{
+ ft_p ft = &ft_data[ftu];
+ int s = splbio();
+
+ if (ft->segh != NULL) {
+ if (ft->segh->reqtype == FTIO_RDAHEAD) {
+ /* cancel read-ahead */
+ ft->segh->reqcan = 1;
+ } else if (ft->segh->reqtype == FTIO_WRITING && !ft->active) {
+ /* flush out any remaining writes */
+ DPRT(("Flushing write I/O chain\n"));
+ arq_state = ard_state = awr_state = 0;
+ ft->xblk = ft->segh->reqblk;
+ ft->xseg = ft->segh->reqseg;
+ ft->xcnt = 0;
+ ft->xptr = ft->segh->buff;
+ ft->active = 1;
+ timeout(ft_timeout, (caddr_t)ftu, 1);
+ }
+ }
+ while (ft->active) ftsleep(wc_iosts_change, 0);
+ splx(s);
+}
+
+
+/*
+ * Get the geometry of the tape currently in the drive.
+ */
+static int
+ftgetgeom(ftu_t ftu)
+{
+ int r, i, tries;
+ int cfg, qic80, ext;
+ int sts, fmt, len;
+ ft_p ft = &ft_data[ftu];
+
+ r = tape_status(ftu);
+
+ /* XXX fix me when format mode is finished */
+ if (r < 0 || (r & QS_CART) == 0 || (r & QS_FMTOK) == 0) {
+ DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r));
+ ftg = NULL;
+ ft->newcart = 1;
+ return(0);
+ }
+
+ /* Report drive configuration */
+ for (cfg = -1, tries = 0; cfg < 0 && tries < 3; tries++)
+ cfg = qic_status(ftu, QC_CONFIG, 8);
+ if (tries == 3) {
+ DPRT(("ftgetgeom report config failed\n"));
+ ftg = NULL;
+ return(-1);
+ }
+ DPRT(("ftgetgeom report config got $%04x\n", cfg));
+ ft->lastcfg = cfg;
+
+ qic80 = cfg & QCF_QIC80;
+ ext = cfg & QCF_EXTRA;
+
+/*
+ * XXX - This doesn't seem to work on my Colorado Jumbo 250...
+ * if it works on your drive, I'd sure like to hear about it.
+ */
+#if 0
+ /* Report drive status */
+ for (sts = -1, tries = 0; sts < 0 && tries < 3; tries++)
+ sts = qic_status(ftu, QC_TSTATUS, 8);
+ if (tries == 3) {
+ DPRT(("ftgetgeom report tape status failed\n"));
+ ftg = NULL;
+ return(-1);
+ }
+ DPRT(("ftgetgeom report tape status got $%04x\n", sts));
+#else
+ /*
+ * XXX - Forge a fake tape status based upon the returned
+ * configuration, since the above command or code is broken
+ * for my drive and probably other older drives.
+ */
+ sts = 0;
+ sts = (qic80) ? QTS_QIC80 : QTS_QIC40;
+ sts |= (ext) ? QTS_LEN2 : QTS_LEN1;
+#endif
+
+ fmt = sts & QTS_FMMASK;
+ len = (sts & QTS_LNMASK) >> 4;
+
+ if (fmt > QCV_NFMT) {
+ ftg = NULL;
+ printf("ft%d: unsupported tape format\n", ftu);
+ return(-1);
+ }
+ if (len > QCV_NLEN) {
+ ftg = NULL;
+ printf("ft%d: unsupported tape length\n", ftu);
+ return(-1);
+ }
+
+ /* Look up geometry in the table */
+ for (i = 1; i < NGEOM; i++)
+ if (ftgtbl[i].g_fmtno == fmt && ftgtbl[i].g_lenno == len) break;
+ if (i == NGEOM) {
+ printf("ft%d: unknown tape geometry\n", ftu);
+ ftg = NULL;
+ return(-1);
+ }
+ ftg = &ftgtbl[i];
+ if (!ftg->g_trktape) {
+ printf("ft%d: unsupported format %s w/len %s\n",
+ ftu, ftg->g_fmtdesc, ftg->g_lendesc);
+ ftg = NULL;
+ return(-1);
+ }
+ DPRT(("Tape format is %s, length is %s\n", ftg->g_fmtdesc, ftg->g_lendesc));
+ ft->newcart = 0;
+ return(0);
+}
+
+
+/*
+ * Switch between tape/floppy. This will send the tape enable/disable
+ * codes for this drive's manufacturer.
+ */
+static int
+set_fdcmode(dev_t dev, int newmode)
+{
+ ftu_t ftu = FDUNIT(minor(dev));
+ ft_p ft = &ft_data[ftu];
+ fdc_p fdc = ft->fdc;
+ static int havebufs = 0;
+ int i;
+ SegReq *sp, *rsp;
+
+ if (newmode == FDC_TAPE_MODE) {
+ /* Wake up the tape drive */
+ switch (ft->type) {
+ case NO_TYPE:
+ fdc->flags &= ~FDC_TAPE_BUSY;
+ return(ENXIO);
+ case FT_NONE:
+ tape_start(ftu, 0);
+ break;
+ case FT_COLORADO:
+ tape_start(ftu, 0);
+ if (tape_cmd(ftu, QC_COL_ENABLE1)) {
+ tape_end(ftu);
+ return(EIO);
+ }
+ if (tape_cmd(ftu, QC_COL_ENABLE2 + ftu)) {
+ tape_end(ftu);
+ return(EIO);
+ }
+ break;
+ case FT_MOUNTAIN:
+ tape_start(ftu, 0);
+ if (tape_cmd(ftu, QC_MTN_ENABLE1)) {
+ tape_end(ftu);
+ return(EIO);
+ }
+ if (tape_cmd(ftu, QC_MTN_ENABLE2)) {
+ tape_end(ftu);
+ return(EIO);
+ }
+ break;
+ case FT_INSIGHT:
+ tape_start(ftu, 1);
+ break;
+ default:
+ DPRT(("ft%d: bad tape type\n", ftu));
+ return(ENXIO);
+ }
+ if (tape_status(ftu) < 0) {
+ if (ft->type == FT_COLORADO)
+ tape_cmd(ftu, QC_COL_DISABLE);
+ else if (ft->type == FT_MOUNTAIN)
+ tape_cmd(ftu, QC_MTN_DISABLE);
+ tape_end(ftu);
+ return(EIO);
+ }
+
+ /* Grab buffers from memory. */
+ if (!havebufs) {
+ ft->segh = ft->segt = NULL;
+ ft->doneh = ft->donet = NULL;
+ ft->segfree = NULL;
+ ft->hdr = NULL;
+ ft->nsegq = ft->ndoneq = ft->nfreelist = 0;
+ for (i = 0; i < FTNBUFF; i++) {
+ sp = malloc(sizeof(SegReq), M_DEVBUF, M_WAITOK);
+ if (sp == NULL) {
+ printf("ft%d: not enough memory for buffers\n", ftu);
+ for (sp=ft->segfree; sp != NULL; sp=sp->next)
+ free(sp, M_DEVBUF);
+ if (ft->type == FT_COLORADO)
+ tape_cmd(ftu, QC_COL_DISABLE);
+ else if (ft->type == FT_MOUNTAIN)
+ tape_cmd(ftu, QC_MTN_DISABLE);
+ tape_end(ftu);
+ return(ENOMEM);
+ }
+ sp->reqtype = FTIO_READY;
+ sp->next = ft->segfree;
+ ft->segfree = sp;
+ ft->nfreelist++;
+ }
+ /* take one buffer for header */
+ ft->hdr = ft->segfree;
+ ft->segfree = ft->segfree->next;
+ ft->nfreelist--;
+ havebufs = 1;
+ }
+ ft->io_sts = FTIO_READY; /* tape drive is ready */
+ ft->active = 0; /* interrupt driver not active */
+ ft->moving = 0; /* tape not moving */
+ ft->rdonly = 0; /* tape read only */
+ ft->newcart = 0; /* new cartridge flag */
+ ft->lastpos = -1; /* tape is rewound */
+ async_func = ACMD_NONE; /* No async function */
+ tape_state(ftu, 0, QS_READY, 60);
+ tape_cmd(ftu, QC_RATE);
+ tape_cmd(ftu, QCF_RT500+2); /* 500K bps */
+ tape_state(ftu, 0, QS_READY, 60);
+ ft->mode = FTM_PRIMARY;
+ tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */
+ tape_state(ftu, 0, QS_READY, 60);
+ ftg = NULL; /* No geometry yet */
+ ftgetgeom(ftu); /* Get tape geometry */
+ ftreq_rewind(ftu); /* Make sure tape is rewound */
+ } else {
+ if (ft->type == FT_COLORADO)
+ tape_cmd(ftu, QC_COL_DISABLE);
+ else if (ft->type == FT_MOUNTAIN)
+ tape_cmd(ftu, QC_MTN_DISABLE);
+ tape_end(ftu);
+ ft->newcart = 0; /* clear new cartridge */
+ if (ft->hdr != NULL) free(ft->hdr, M_DEVBUF);
+ if (havebufs) {
+ for (sp = ft->segfree; sp != NULL;) {
+ rsp = sp; sp = sp->next;
+ free(rsp, M_DEVBUF);
+ }
+ for (sp = ft->segh; sp != NULL;) {
+ rsp = sp; sp = sp->next;
+ free(rsp, M_DEVBUF);
+ }
+ for (sp = ft->doneh; sp != NULL;) {
+ rsp = sp; sp = sp->next;
+ free(rsp, M_DEVBUF);
+ }
+ }
+ havebufs = 0;
+ }
+ return(0);
+}
+
+
+/*
+ * Perform a QIC status function.
+ */
+static int
+qic_status(ftu_t ftu, int cmd, int nbits)
+{
+ int st3, r, i;
+ ft_p ft = &ft_data[ftu];
+ fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */
+
+ if (tape_cmd(ftu, cmd)) {
+ DPRT(("ft%d: QIC status timeout\n", ftu));
+ return(-1);
+ }
+
+ /* Sense drive status */
+ out_fdc(fdcu, NE7CMD_SENSED);
+#ifdef PC98
+ out_fdc(fdcu, 3);
+#else
+ out_fdc(fdcu, ftu);
+#endif
+ st3 = in_fdc(fdcu);
+
+ if ((st3 & 0x10) == 0) { /* track 0 */
+ DPRT(("qic_status has dead drive... st3 = $%02x\n", st3));
+ return(-1);
+ }
+
+ for (i = r = 0; i <= nbits; i++) {
+ if (tape_cmd(ftu, QC_NEXTBIT)) {
+ DPRT(("ft%d: QIC status bit timed out on %d\n", ftu, i));
+ return(-1);
+ }
+
+ out_fdc(fdcu, NE7CMD_SENSED);
+#ifdef PC98
+ out_fdc(fdcu, 3);
+#else
+ out_fdc(fdcu, ftu);
+#endif
+ st3 = in_fdc(fdcu);
+ if (st3 < 0) {
+ DPRT(("ft%d: controller timed out on bit %d r=$%02x\n",
+ ftu, i, r));
+ return(-1);
+ }
+
+ r >>= 1;
+ if (i < nbits)
+ r |= ((st3 & 0x10) ? 1 : 0) << nbits;
+ else if ((st3 & 0x10) == 0) {
+ DPRT(("ft%d: qic status stop bit missing at %d, st3=$%02x r=$%04x\n",
+ ftu,i,st3,r));
+ return(-1);
+ }
+ }
+
+ DPRT(("qic_status returned $%02x\n", r));
+ return(r);
+}
+
+
+/*
+ * Open tape drive for use. Bounced off of Fdopen if tape minor is
+ * detected.
+ */
+int
+ftopen(dev_t dev, int arg2) {
+ ftu_t ftu = FDUNIT(minor(dev));
+ fdc_p fdc;
+
+ /* check bounds */
+ if (ftu >= NFT)
+ return(ENXIO);
+ fdc = ft_data[ftu].fdc;
+ if ((fdc == NULL) || (ft_data[ftu].type == NO_TYPE))
+ return(ENXIO);
+ /* check for controller already busy with tape */
+ if (fdc->flags & FDC_TAPE_BUSY)
+ return(EBUSY);
+ /* make sure we found a tape when probed */
+ if (!(fdc->flags & FDC_HASFTAPE))
+ return(ENODEV);
+ fdc->fdu = ftu;
+ fdc->flags |= FDC_TAPE_BUSY;
+ kdc_ft[ftu].kdc_state = DC_BUSY;
+ return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */
+}
+
+
+/*
+ * Close tape and return floppy controller to disk mode.
+ */
+int
+ftclose(dev_t dev, int flags)
+{
+ ftu_t ftu = FDUNIT(minor(dev));
+ ft_p ft = &ft_data[ftu];
+
+
+ /* Wait for any remaining I/O activity to complete. */
+ tape_inactive(ftu);
+
+ ft->mode = FTM_PRIMARY;
+ tape_cmd(ftu, QC_PRIMARY);
+ tape_state(ftu, 0, QS_READY, 60);
+ ftreq_rewind(ftu);
+ kdc_ft[ftu].kdc_state = DC_IDLE;
+ return(set_fdcmode(dev, FDC_DISK_MODE)); /* Otherwise, close tape */
+}
+
+/*
+ * Read or write a segment.
+ */
+static int
+ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p)
+{
+ int r, i;
+ SegReq *sp;
+ int s;
+ long blk, bad, seg;
+ unsigned char *cp, *cp2;
+ ft_p ft = &ft_data[ftu];
+
+ if (!ft->active && ft->segh == NULL) {
+ r = tape_status(ftu);
+ if ((r & QS_CART) == 0)
+ return(ENXIO); /* No cartridge */
+ if ((r & QS_FMTOK) == 0)
+ return(ENXIO); /* Not formatted */
+ tape_state(ftu, 0, QS_READY, 90);
+ }
+
+ if (ftg == NULL || ft->newcart) {
+ tape_inactive(ftu);
+ tape_state(ftu, 0, QS_READY, 90);
+ if (ftgetgeom(ftu) < 0)
+ return(ENXIO);
+ }
+
+ /* Write not allowed on a read-only tape. */
+ if (cmd == QIOWRITE && ft->rdonly)
+ return(EROFS);
+
+ /* Quick check of request and buffer. */
+ if (sr == NULL || sr->sg_data == NULL)
+ return(EINVAL);
+
+ /* Make sure requested track and segment is in range. */
+ if (sr->sg_trk >= ftg->g_trktape || sr->sg_seg >= ftg->g_segtrk)
+ return(EINVAL);
+
+ blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG;
+ seg = sr->sg_trk * ftg->g_segtrk + sr->sg_seg;
+
+ s = splbio();
+ if (cmd == QIOREAD) {
+ /*
+ * See if the driver is reading ahead.
+ */
+ if (ft->doneh != NULL ||
+ (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD)) {
+ /*
+ * Eat the completion queue and see if the request
+ * is already there.
+ */
+ while (ft->doneh != NULL) {
+ if (blk == ft->doneh->reqblk) {
+ sp = ft->doneh;
+ sp->reqtype = FTIO_READING;
+ sp->reqbad = sr->sg_badmap;
+ goto rddone;
+ }
+ segio_free(ft, ft->doneh);
+ }
+
+ /*
+ * Not on the completed queue, in progress maybe?
+ */
+ if (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD &&
+ blk == ft->segh->reqblk) {
+ sp = ft->segh;
+ sp->reqtype = FTIO_READING;
+ sp->reqbad = sr->sg_badmap;
+ goto rdwait;
+ }
+ }
+
+ /* Wait until we're ready. */
+ tape_inactive(ftu);
+
+ /* Set up a new read request. */
+ sp = segio_alloc(ft);
+ sp->reqcrc = 0;
+ sp->reqbad = sr->sg_badmap;
+ sp->reqblk = blk;
+ sp->reqseg = seg;
+ sp->reqcan = 0;
+ sp->reqtype = FTIO_READING;
+ segio_queue(ft, sp);
+
+ /* Start the read request off. */
+ DPRT(("Starting read I/O chain\n"));
+ arq_state = ard_state = awr_state = 0;
+ ft->xblk = sp->reqblk;
+ ft->xseg = sp->reqseg;
+ ft->xcnt = 0;
+ ft->xptr = sp->buff;
+ ft->active = 1;
+ timeout(ft_timeout, (caddr_t)ftu, 1);
+
+rdwait:
+ ftsleep(wc_buff_done, 0);
+
+rddone:
+ bad = sp->reqbad;
+ sr->sg_crcmap = sp->reqcrc & ~bad;
+
+ /* Copy out segment and discard bad mapped blocks. */
+ cp = sp->buff; cp2 = sr->sg_data;
+ for (i = 0; i < QCV_BLKSEG; cp += QCV_BLKSIZE, i++) {
+ if (bad & (1 << i)) continue;
+ copyout(cp, cp2, QCV_BLKSIZE);
+ cp2 += QCV_BLKSIZE;
+ }
+ segio_free(ft, sp);
+ } else {
+ if (ft->segh != NULL && ft->segh->reqtype != FTIO_WRITING)
+ tape_inactive(ftu);
+
+ /* Allocate a buffer and start tape if we're running low. */
+ sp = segio_alloc(ft);
+ if (!ft->active && (sp == NULL || ft->nfreelist <= 1)) {
+ DPRT(("Starting write I/O chain\n"));
+ arq_state = ard_state = awr_state = 0;
+ ft->xblk = ft->segh->reqblk;
+ ft->xseg = ft->segh->reqseg;
+ ft->xcnt = 0;
+ ft->xptr = ft->segh->buff;
+ ft->active = 1;
+ timeout(ft_timeout, (caddr_t)ftu, 1);
+ }
+
+ /* Sleep until a buffer becomes available. */
+ while (sp == NULL) {
+ ftsleep(wc_buff_avail, 0);
+ sp = segio_alloc(ft);
+ }
+
+ /* Copy in segment and expand bad blocks. */
+ bad = sr->sg_badmap;
+ cp = sr->sg_data; cp2 = sp->buff;
+ for (i = 0; i < QCV_BLKSEG; cp2 += QCV_BLKSIZE, i++) {
+ if (bad & (1 << i)) continue;
+ copyin(cp, cp2, QCV_BLKSIZE);
+ cp += QCV_BLKSIZE;
+ }
+ sp->reqblk = blk;
+ sp->reqseg = seg;
+ sp->reqcan = 0;
+ sp->reqtype = FTIO_WRITING;
+ segio_queue(ft, sp);
+ }
+ splx(s);
+ return(0);
+}
+
+
+/*
+ * Rewind to beginning of tape
+ */
+static int
+ftreq_rewind(ftu_t ftu)
+{
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+ tape_cmd(ftu, QC_SEEKSTART);
+ tape_state(ftu, 0, QS_READY, 90);
+ tape_cmd(ftu, QC_SEEKTRACK);
+ tape_cmd(ftu, 2);
+ tape_state(ftu, 0, QS_READY, 90);
+ ft->lastpos = -1;
+ ft->moving = 0;
+ return(0);
+}
+
+
+/*
+ * Move to logical beginning or end of track
+ */
+static int
+ftreq_trkpos(ftu_t ftu, int req)
+{
+ int curtrk, r, cmd;
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+
+ r = tape_status(ftu);
+ if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */
+ if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
+
+ if (ftg == NULL || ft->newcart) {
+ if (ftgetgeom(ftu) < 0) return(ENXIO);
+ }
+
+ curtrk = (ft->lastpos < 0) ? 0 : ft->lastpos / ftg->g_blktrk;
+ if (req == QIOBOT)
+ cmd = (curtrk & 1) ? QC_SEEKEND : QC_SEEKSTART;
+ else
+ cmd = (curtrk & 1) ? QC_SEEKSTART : QC_SEEKEND;
+ tape_cmd(ftu, cmd);
+ tape_state(ftu, 0, QS_READY, 90);
+ return(0);
+}
+
+
+/*
+ * Seek tape head to a particular track.
+ */
+static int
+ftreq_trkset(ftu_t ftu, int *trk)
+{
+ int r;
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+
+ r = tape_status(ftu);
+ if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */
+ if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
+ if (ftg == NULL || ft->newcart) {
+ if (ftgetgeom(ftu) < 0) return(ENXIO);
+ }
+
+ tape_cmd(ftu, QC_SEEKTRACK);
+ tape_cmd(ftu, *trk + 2);
+ tape_state(ftu, 0, QS_READY, 90);
+ return(0);
+}
+
+
+/*
+ * Start tape moving forward.
+ */
+static int
+ftreq_lfwd(ftu_t ftu)
+{
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+ tape_cmd(ftu, QC_FORWARD);
+ ft->moving = 1;
+ return(0);
+}
+
+
+/*
+ * Stop the tape
+ */
+static int
+ftreq_stop(ftu_t ftu)
+{
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ tape_cmd(ftu, QC_STOP);
+ tape_state(ftu, 0, QS_READY, 90);
+ ft->moving = 0;
+ return(0);
+}
+
+
+/*
+ * Set the particular mode the drive should be in.
+ */
+static int
+ftreq_setmode(ftu_t ftu, int cmd)
+{
+ int r;
+ ft_p ft = &ft_data[ftu];
+
+ tape_inactive(ftu);
+ r = tape_status(ftu);
+
+ switch(cmd) {
+ case QIOPRIMARY:
+ ft->mode = FTM_PRIMARY;
+ tape_cmd(ftu, QC_PRIMARY);
+ break;
+ case QIOFORMAT:
+ if (r & QS_RDONLY) return(ENXIO);
+ if ((r & QS_BOT) == 0) return(ENXIO);
+ tape_cmd(ftu, QC_FORMAT);
+ break;
+ case QIOVERIFY:
+ if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */
+ tape_cmd(ftu, QC_VERIFY);
+ break;
+ }
+ tape_state(ftu, 0, QS_READY, 60);
+ return(0);
+}
+
+
+/*
+ * Return drive status bits
+ */
+static int
+ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p)
+{
+ ft_p ft = &ft_data[ftu];
+
+ if (ft->active)
+ *sts = ft->laststs & ~QS_READY;
+ else
+ *sts = tape_status(ftu);
+ return(0);
+}
+
+
+/*
+ * Return drive configuration bits
+ */
+static int
+ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p)
+{
+ int r, tries;
+ ft_p ft = &ft_data[ftu];
+
+ if (ft->active)
+ r = ft->lastcfg;
+ else {
+ for (r = -1, tries = 0; r < 0 && tries < 3; tries++)
+ r = qic_status(ftu, QC_CONFIG, 8);
+ if (tries == 3) return(ENXIO);
+ }
+ *cfg = r;
+ return(0);
+}
+
+
+/*
+ * Return current tape's geometry.
+ */
+static int
+ftreq_geom(ftu_t ftu, QIC_Geom *g)
+{
+ tape_inactive(ftu);
+ if (ftg == NULL && ftgetgeom(ftu) < 0) return(ENXIO);
+ bcopy(ftg, g, sizeof(QIC_Geom));
+ return(0);
+}
+
+
+/*
+ * Return drive hardware information
+ */
+static int
+ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp)
+{
+ int tries;
+ int rom, vend;
+
+ tape_inactive(ftu);
+ bzero(hwp, sizeof(QIC_HWInfo));
+
+ for (rom = -1, tries = 0; rom < 0 && tries < 3; tries++)
+ rom = qic_status(ftu, QC_VERSION, 8);
+ if (rom > 0) {
+ hwp->hw_rombeta = (rom >> 7) & 0x01;
+ hwp->hw_romid = rom & 0x7f;
+ }
+
+ for (vend = -1, tries = 0; vend < 0 && tries < 3; tries++)
+ vend = qic_status(ftu, QC_VENDORID, 16);
+ if (vend > 0) {
+ hwp->hw_make = (vend >> 6) & 0x3ff;
+ hwp->hw_model = vend & 0x3f;
+ }
+
+ return(0);
+}
+
+
+/*
+ * Receive or Send the in-core header segment.
+ */
+static int
+ftreq_hdr(ftu_t ftu, int cmd, QIC_Segment *sp)
+{
+ ft_p ft = &ft_data[ftu];
+ QIC_Header *h = (QIC_Header *)ft->hdr->buff;
+
+ if (sp == NULL || sp->sg_data == NULL) return(EINVAL);
+ if (cmd == QIOSENDHDR) {
+ copyin(sp->sg_data, ft->hdr->buff, QCV_SEGSIZE);
+ } else {
+ if (h->qh_sig != QCV_HDRMAGIC) return(EIO);
+ copyout(ft->hdr->buff, sp->sg_data, QCV_SEGSIZE);
+ }
+ return(0);
+}
+
+/*
+ * I/O functions.
+ */
+int
+ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ ftu_t ftu = FDUNIT(minor(dev));
+
+ switch(cmd) {
+ case QIOREAD: /* Request reading a segment from tape. */
+ case QIOWRITE: /* Request writing a segment to tape. */
+ return(ftreq_rw(ftu, cmd, (QIC_Segment *)data, p));
+
+ case QIOREWIND: /* Rewind tape. */
+ return(ftreq_rewind(ftu));
+
+ case QIOBOT: /* Seek to logical beginning of track. */
+ case QIOEOT: /* Seek to logical end of track. */
+ return(ftreq_trkpos(ftu, cmd));
+
+ case QIOTRACK: /* Seek tape head to specified track. */
+ return(ftreq_trkset(ftu, (int *)data));
+
+ case QIOSEEKLP: /* Seek load point. */
+ goto badreq;
+
+ case QIOFORWARD: /* Move tape in logical forward direction. */
+ return(ftreq_lfwd(ftu));
+
+ case QIOSTOP: /* Causes tape to stop. */
+ return(ftreq_stop(ftu));
+
+ case QIOPRIMARY: /* Enter primary mode. */
+ case QIOFORMAT: /* Enter format mode. */
+ case QIOVERIFY: /* Enter verify mode. */
+ return(ftreq_setmode(ftu, cmd));
+
+ case QIOWRREF: /* Write reference burst. */
+ goto badreq;
+
+ case QIOSTATUS: /* Get drive status. */
+ return(ftreq_status(ftu, cmd, (int *)data, p));
+
+ case QIOCONFIG: /* Get tape configuration. */
+ return(ftreq_config(ftu, cmd, (int *)data, p));
+
+ case QIOGEOM:
+ return(ftreq_geom(ftu, (QIC_Geom *)data));
+
+ case QIOHWINFO:
+ return(ftreq_hwinfo(ftu, (QIC_HWInfo *)data));
+
+ case QIOSENDHDR:
+ case QIORECVHDR:
+ return(ftreq_hdr(ftu, cmd, (QIC_Segment *)data));
+ }
+badreq:
+ DPRT(("ft%d: unknown ioctl(%d) request\n", ftu, cmd));
+ return(ENXIO);
+}
+
+#endif
diff --git a/sys/pc98/pc98/ftreg.h b/sys/pc98/pc98/ftreg.h
new file mode 100644
index 0000000..eea78ec
--- /dev/null
+++ b/sys/pc98/pc98/ftreg.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1993, 1994 Steve Gerakines
+ *
+ * This is freely redistributable software. You may do anything you
+ * wish with it, so long as the above notice stays intact.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * ftreg.h - QIC-40/80 floppy tape driver header
+ * 06/03/94 v0.9
+ * Changed seek load point to QC_SEEKLP, added reqseg to SegReq structure.
+ *
+ * 10/30/93 v0.3
+ * More things will end up here. QC_VENDORID and QC_VERSION now used.
+ *
+ * 08/07/93 v0.2 release
+ * Things that should've been here in the first place were moved.
+ * Tape geometry and segment request types were added.
+ *
+ * 06/03/93 v0.1 Alpha release
+ * Initial revision. Many more things should be moved here.
+ */
+
+/* QIC-117 command set. */
+#define QC_RESET 1 /* reset */
+#define QC_NEXTBIT 2 /* report next bit */
+#define QC_PAUSE 3 /* pause */
+#define QC_STPAUSE 4 /* step pause */
+#define QC_TIMEOUT 5 /* alt timeout */
+#define QC_STATUS 6 /* report status */
+#define QC_ERRCODE 7 /* report error code */
+#define QC_CONFIG 8 /* report config */
+#define QC_VERSION 9 /* report version */
+#define QC_FORWARD 10 /* logical forward */
+#define QC_SEEKSTART 11 /* seek to track start */
+#define QC_SEEKEND 12 /* seek to track end */
+#define QC_SEEKTRACK 13 /* seek head to track */
+#define QC_SEEKLP 14 /* seek load point */
+#define QC_FORMAT 15 /* format mode */
+#define QC_WRITEREF 16 /* write reference */
+#define QC_VERIFY 17 /* verify mode */
+#define QC_STOP 18 /* stop tape */
+#define QC_STEPUP 21 /* step head up */
+#define QC_STEPDOWN 22 /* step head down */
+#define QC_SEEKREV 25 /* seek reverse */
+#define QC_SEEKFWD 26 /* seek forward */
+#define QC_RATE 27 /* select data rate */
+#define QC_DIAG1 28 /* diagnostic mode 1 */
+#define QC_DIAG2 29 /* diagnostic mode 2 */
+#define QC_PRIMARY 30 /* primary mode */
+#define QC_VENDORID 32 /* vendor id */
+#define QC_TSTATUS 33 /* report tape status */
+#define QC_EXTREV 34 /* extended skip reverse */
+#define QC_EXTFWD 35 /* extended skip forward */
+
+/* Colorado enable/disable. */
+#define QC_COL_ENABLE1 46 /* enable */
+#define QC_COL_ENABLE2 2 /* unit+2 */
+#define QC_COL_DISABLE 47 /* disable */
+
+/* Mountain enable/disable. */
+#define QC_MTN_ENABLE1 23 /* enable 1 */
+#define QC_MTN_ENABLE2 20 /* enable 2 */
+#define QC_MTN_DISABLE 24 /* disable */
+
+/* Segment I/O request. */
+typedef struct segq {
+ unsigned char buff[QCV_SEGSIZE];/* Segment data; first for alignment */
+ int reqtype; /* Request type */
+ long reqcrc; /* CRC Errors found */
+ long reqbad; /* Bad sector map */
+ long reqblk; /* Block request starts at */
+ long reqseg; /* Segment request is at */
+ int reqcan; /* Cancel read-ahead */
+ struct segq *next; /* Next request */
+} SegReq;
+
+typedef int ftu_t;
+typedef int ftsu_t;
+typedef struct ft_data *ft_p;
diff --git a/sys/pc98/pc98/ic/i82365.h b/sys/pc98/pc98/ic/i82365.h
new file mode 100644
index 0000000..b86812e
--- /dev/null
+++ b/sys/pc98/pc98/ic/i82365.h
@@ -0,0 +1,190 @@
+#ifndef __83265_H__
+#define __83265_H__
+
+/***********************************************************************
+ * 82365.h -- information necessary for direct manipulation of PCMCIA
+ * cards and controllers
+ *
+ * Support is included for Intel 82365SL PCIC controllers and clones
+ * thereof.
+ *
+ * originally by Barry Jaspan; hacked over by Keith Moore
+ *
+ ***********************************************************************/
+
+/*
+ * PCIC Registers
+ * Each register is given a name, and most of the bits are named too.
+ * I should really name them all.
+ *
+ * Finally, since the banks can be addressed with a regular syntax,
+ * some macros are provided for that purpose.
+ */
+
+#define PCIC_BASE 0x03e0 /* base adddress of pcic register set */
+
+/* First, all the registers */
+#define PCIC_ID_REV 0x00 /* Identification and Revision */
+#define PCIC_STATUS 0x01 /* Interface Status */
+#define PCIC_POWER 0x02 /* Power and RESETDRV control */
+#define PCIC_INT_GEN 0x03 /* Interrupt and General Control */
+#define PCIC_STAT_CHG 0x04 /* Card Status Change */
+#define PCIC_STAT_INT 0x05 /* Card Status Change Interrupt Config */
+#define PCIC_ADDRWINE 0x06 /* Address Window Enable */
+#define PCIC_IOCTL 0x07 /* I/O Control */
+#define PCIC_IO0_STL 0x08 /* I/O Address 0 Start Low Byte */
+#define PCIC_IO0_STH 0x09 /* I/O Address 0 Start High Byte */
+#define PCIC_IO0_SPL 0x0a /* I/O Address 0 Stop Low Byte */
+#define PCIC_IO0_SPH 0x0b /* I/O Address 0 Stop High Byte */
+#define PCIC_IO1_STL 0x0c /* I/O Address 1 Start Low Byte */
+#define PCIC_IO1_STH 0x0d /* I/O Address 1 Start High Byte */
+#define PCIC_IO1_SPL 0x0e /* I/O Address 1 Stop Low Byte */
+#define PCIC_IO1_SPH 0x0f /* I/O Address 1 Stop High Byte */
+#define PCIC_SM0_STL 0x10 /* System Memory Address 0 Mapping Start Low Byte */
+#define PCIC_SM0_STH 0x11 /* System Memory Address 0 Mapping Start High Byte */
+#define PCIC_SM0_SPL 0x12 /* System Memory Address 0 Mapping Stop Low Byte */
+#define PCIC_SM0_SPH 0x13 /* System Memory Address 0 Mapping Stop High Byte */
+#define PCIC_CM0_L 0x14 /* Card Memory Offset Address 0 Low Byte */
+#define PCIC_CM0_H 0x15 /* Card Memory Offset Address 0 High Byte */
+#define PCIC_CDGC 0x16 /* Card Detect and General Control */
+#define PCIC_RES17 0x17 /* Reserved */
+#define PCIC_SM1_STL 0x18 /* System Memory Address 1 Mapping Start Low Byte */
+#define PCIC_SM1_STH 0x19 /* System Memory Address 1 Mapping Start High Byte */
+#define PCIC_SM1_SPL 0x1a /* System Memory Address 1 Mapping Stop Low Byte */
+#define PCIC_SM1_SPH 0x1b /* System Memory Address 1 Mapping Stop High Byte */
+#define PCIC_CM1_L 0x1c /* Card Memory Offset Address 1 Low Byte */
+#define PCIC_CM1_H 0x1d /* Card Memory Offset Address 1 High Byte */
+#define PCIC_GLO_CTRL 0x1e /* Global Control Register */
+#define PCIC_RES1F 0x1f /* Reserved */
+#define PCIC_SM2_STL 0x20 /* System Memory Address 2 Mapping Start Low Byte */
+#define PCIC_SM2_STH 0x21 /* System Memory Address 2 Mapping Start High Byte */
+#define PCIC_SM2_SPL 0x22 /* System Memory Address 2 Mapping Stop Low Byte */
+#define PCIC_SM2_SPH 0x23 /* System Memory Address 2 Mapping Stop High Byte */
+#define PCIC_CM2_L 0x24 /* Card Memory Offset Address 2 Low Byte */
+#define PCIC_CM2_H 0x25 /* Card Memory Offset Address 2 High Byte */
+#define PCIC_RES26 0x26 /* Reserved */
+#define PCIC_RES27 0x27 /* Reserved */
+#define PCIC_SM3_STL 0x28 /* System Memory Address 3 Mapping Start Low Byte */
+#define PCIC_SM3_STH 0x29 /* System Memory Address 3 Mapping Start High Byte */
+#define PCIC_SM3_SPL 0x2a /* System Memory Address 3 Mapping Stop Low Byte */
+#define PCIC_SM3_SPH 0x2b /* System Memory Address 3 Mapping Stop High Byte */
+#define PCIC_CM3_L 0x2c /* Card Memory Offset Address 3 Low Byte */
+#define PCIC_CM3_H 0x2d /* Card Memory Offset Address 3 High Byte */
+#define PCIC_RES2E 0x2e /* Reserved */
+#define PCIC_RES2F 0x2f /* Reserved */
+#define PCIC_SM4_STL 0x30 /* System Memory Address 4 Mapping Start Low Byte */
+#define PCIC_SM4_STH 0x31 /* System Memory Address 4 Mapping Start High Byte */
+#define PCIC_SM4_SPL 0x32 /* System Memory Address 4 Mapping Stop Low Byte */
+#define PCIC_SM4_SPH 0x33 /* System Memory Address 4 Mapping Stop High Byte */
+#define PCIC_CM4_L 0x34 /* Card Memory Offset Address 4 Low Byte */
+#define PCIC_CM4_H 0x35 /* Card Memory Offset Address 4 High Byte */
+#define PCIC_RES36 0x36 /* Reserved */
+#define PCIC_RES37 0x37 /* Reserved */
+#define PCIC_RES38 0x38 /* Reserved */
+#define PCIC_RES39 0x39 /* Reserved */
+#define PCIC_RES3A 0x3a /* Reserved */
+#define PCIC_RES3B 0x3b /* Reserved */
+#define PCIC_RES3C 0x3c /* Reserved */
+#define PCIC_RES3D 0x3d /* Reserved */
+#define PCIC_RES3E 0x3e /* Reserved */
+#define PCIC_RES3F 0x3f /* Reserved */
+
+/* Now register bits, ordered by reg # */
+
+/* For Identification and Revision (PCIC_ID_REV) */
+#define PCIC_INTEL0 0x82 /* Intel 82365SL Rev. 0; Both Memory and I/O */
+#define PCIC_INTEL1 0x83 /* Intel 82365SL Rev. 1; Both Memory and I/O */
+#define PCIC_IBM1 0x88 /* IBM PCIC clone; Both Memory and I/O */
+#define PCIC_IBM2 0x89 /* IBM PCIC clone; Both Memory and I/O */
+
+/* For Interface Status register (PCIC_STATUS) */
+#define PCIC_VPPV 0x80 /* Vpp_valid */
+#define PCIC_POW 0x40 /* PC Card power active */
+#define PCIC_READY 0x20 /* Ready/~Busy */
+#define PCIC_MWP 0x10 /* Memory Write Protect */
+#define PCIC_CD 0x0C /* Both card detect bits */
+#define PCIC_BVD 0x03 /* Both Battery Voltage Detect bits */
+
+/* For the Power and RESETDRV register (PCIC_POWER) */
+#define PCIC_OUTENA 0x80 /* Output Enable */
+#define PCIC_DISRST 0x40 /* Disable RESETDRV */
+#define PCIC_APSENA 0x20 /* Auto Pwer Switch Enable */
+#define PCIC_PCPWRE 0x10 /* PC Card Power Enable */
+
+/* For the Interrupt and General Control register (PCIC_INT_GEN) */
+#define PCIC_CARDTYPE 0x20 /* Card Type 0 = memory, 1 = I/O */
+#define PCIC_IOCARD 0x20
+#define PCIC_MEMCARD 0x00
+#define PCIC_CARDRESET 0x40 /* Card reset 0 = Reset, 1 = Normal */
+
+/* For the Card Status Change register (PCIC_STAT_CHG) */
+#define PCIC_CDTCH 0x08 /* Card Detect Change */
+#define PCIC_RDYCH 0x04 /* Ready Change */
+#define PCIC_BATWRN 0x02 /* Battery Warning */
+#define PCIC_BATDED 0x01 /* Battery Dead */
+
+/* For the Address Window Enable Register (PCIC_ADDRWINE) */
+#define PCIC_SM0_EN 0x01 /* Memory Window 0 Enable */
+#define PCIC_SM1_EN 0x02 /* Memory Window 1 Enable */
+#define PCIC_SM2_EN 0x04 /* Memory Window 2 Enable */
+#define PCIC_SM3_EN 0x08 /* Memory Window 3 Enable */
+#define PCIC_SM4_EN 0x10 /* Memory Window 4 Enable */
+#define PCIC_MEMCS16 0x20 /* ~MEMCS16 Decode A23-A12 */
+#define PCIC_IO0_EN 0x40 /* I/O Window 0 Enable */
+#define PCIC_IO1_EN 0x80 /* I/O Window 1 Enable */
+
+/* For the I/O Control Register (PCIC_IOCTL) */
+#define PCIC_IO0_16BIT 0x01 /* I/O to this segment is 16 bit */
+#define PCIC_IO0_CS16 0x02 /* I/O cs16 source is the card */
+#define PCIC_IO0_0WS 0x04 /* zero wait states added on 8 bit cycles */
+#define PCIC_IO0_WS 0x08 /* Wait states added for 16 bit cycles */
+#define PCIC_IO1_16BIT 0x10 /* I/O to this segment is 16 bit */
+#define PCIC_IO1_CS16 0x20 /* I/O cs16 source is the card */
+#define PCIC_IO1_0WS 0x04 /* zero wait states added on 8 bit cycles */
+#define PCIC_IO1_WS 0x80 /* Wait states added for 16 bit cycles */
+
+/* For the various I/O and Memory windows */
+#define PCIC_ADDR_LOW 0
+#define PCIC_ADDR_HIGH 1
+#define PCIC_START 0x00 /* Start of mapping region */
+#define PCIC_END 0x02 /* End of mapping region */
+#define PCIC_MOFF 0x04 /* Card Memory Mapping region offset */
+#define PCIC_IO0 0x08 /* I/O Address 0 */
+#define PCIC_IO1 0x0c /* I/O Address 1 */
+#define PCIC_SM0 0x10 /* System Memory Address 0 Mapping */
+#define PCIC_SM1 0x18 /* System Memory Address 1 Mapping */
+#define PCIC_SM2 0x20 /* System Memory Address 2 Mapping */
+#define PCIC_SM3 0x28 /* System Memory Address 3 Mapping */
+#define PCIC_SM4 0x30 /* System Memory Address 4 Mapping */
+
+/* For System Memory Window start registers
+ (PCIC_SMx|PCIC_START|PCIC_ADDR_HIGH) */
+#define PCIC_ZEROWS 0x40 /* Zero wait states */
+#define PCIC_DATA16 0x80 /* Data width is 16 bits */
+
+/* For System Memory Window stop registers
+ (PCIC_SMx|PCIC_END|PCIC_ADDR_HIGH) */
+#define PCIC_MW0 0x40 /* Wait state bit 0 */
+#define PCIC_MW1 0x80 /* Wait state bit 1 */
+
+/* For System Memory Window offset registers
+ (PCIC_SMx|PCIC_MOFF|PCIC_ADDR_HIGH) */
+#define PCIC_REG 0x40 /* Attribute/Common select (why called Reg?) */
+#define PCIC_WP 0x80 /* Write-protect this window */
+
+/* For Card Detect and General Control register (PCIC_CDGC) */
+#define PCIC_16_DL_INH 0x01 /* 16-bit memory delay inhibit */
+#define PCIC_CNFG_RST_EN 0x02 /* configuration reset enable */
+#define PCIC_GPI_EN 0x04 /* GPI Enable */
+#define PCIC_GPI_TRANS 0x08 /* GPI Transition Control */
+#define PCIC_CDRES_EN 0x10 /* card detect resume enable */
+#define PCIC_SW_CD_INT 0x20 /* s/w card detect interrupt */
+
+/* For Global Control register (PCIC_GLO_CTRL) */
+#define PCIC_PWR_DOWN 0x01 /* power down */
+#define PCIC_LVL_MODE 0x02 /* level mode interrupt enable */
+#define PCIC_WB_CSCINT 0x04 /* explicit write-back csc intr */
+#define PCIC_IRQ14_PULSE 0x08 /* irq 14 pulse mode enable */
+
+/* DON'T ADD ANYTHING AFTER THIS #endif */
+#endif /* __83265_H__ */
diff --git a/sys/pc98/pc98/ic/i8237.h b/sys/pc98/pc98/ic/i8237.h
new file mode 100644
index 0000000..464e558
--- /dev/null
+++ b/sys/pc98/pc98/ic/i8237.h
@@ -0,0 +1,11 @@
+/*
+ * Intel 8237 DMA Controller
+ *
+ * i8237.h,v 1.3 1994/11/01 17:26:47 ache Exp
+ */
+
+#define DMA37MD_SINGLE 0x40 /* single pass mode */
+#define DMA37MD_CASCADE 0xc0 /* cascade mode */
+#define DMA37MD_AUTO 0x50 /* autoinitialise single pass mode */
+#define DMA37MD_WRITE 0x04 /* read the device, write memory operation */
+#define DMA37MD_READ 0x08 /* write the device, read memory operation */
diff --git a/sys/pc98/pc98/ic/i8251.h b/sys/pc98/pc98/ic/i8251.h
new file mode 100644
index 0000000..f11c916
--- /dev/null
+++ b/sys/pc98/pc98/ic/i8251.h
@@ -0,0 +1,79 @@
+/*-
+ * 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.
+ *
+ * @(#)ns16550.h 7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ * modified for PC9801 by M.Ishii
+ * Kyoto University Microcomputer Club (KMC)
+ */
+
+/* define command and status code */
+#define CMD8251_TxEN 0x01 /* transmit enable */
+#define CMD8251_DTR 0x02 /* assert DTR */
+#define CMD8251_RxEN 0x04 /* receive enable */
+#define CMD8251_SBRK 0x08 /* send break */
+#define CMD8251_ER 0x10 /* error reset */
+#define CMD8251_RTS 0x20 /* assert RTS */
+#define CMD8251_RESET 0x40 /* internal reset */
+#define CMD8251_EH 0x80 /* enter hunt mode (only synchronous mode)*/
+
+#define STS8251_TxRDY 0x01 /* transmit READY */
+#define STS8251_RxRDY 0x02 /* data exists in receive buffer */
+#define STS8251_TxEMP 0x04 /* transmit buffer EMPTY */
+#define STS8251_PE 0x08 /* perity error */
+#define STS8251_OE 0x10 /* overrun error */
+#define STS8251_FE 0x20 /* framing error */
+#define STS8251_BD_SD 0x40 /* break detect (async) / sync detect (sync) */
+#define STS8251_DSR 0x80 /* DSR is asserted */
+
+#define MOD8251_5BITS 0x00
+#define MOD8251_6BITS 0x04
+#define MOD8251_7BITS 0x08
+#define MOD8251_8BITS 0x0c
+#define MOD8251_PDISAB 0x00 /* parity disable */
+#define MOD8251_PODD 0x10 /* parity odd */
+#define MOD8251_PEVEN 0x30 /* parity even */
+#define MOD8251_STOP1 0x40 /* stop bit len = 1bit */
+#define MOD8251_STOP2 0xc0 /* stop bit len = 2bit */
+#define MOD8251_CLKX16 0x02 /* x16 */
+#define MOD8251_CLKX1 0x01 /* x1 */
+
+#define CICSCD_CI 0x80 /* CI */
+#define CICSCD_CS 0x40 /* CS */
+#define CICSCD_CD 0x20 /* CD */
+
+/* interrupt mask control */
+#define IEN_Rx 0x01
+#define IEN_TxEMP 0x02
+#define IEN_Tx 0x04
diff --git a/sys/pc98/pc98/ic/mb86960.h b/sys/pc98/pc98/ic/mb86960.h
new file mode 100644
index 0000000..c87101c
--- /dev/null
+++ b/sys/pc98/pc98/ic/mb86960.h
@@ -0,0 +1,372 @@
+/*
+ * All Rights Reserved, Copyright (C) Fujitsu Limited 1995
+ *
+ * This software may be used, modified, copied, distributed, and sold, in
+ * both source and binary form provided that the above copyright, these
+ * terms and the following disclaimer are retained. The name of the author
+ * and/or the contributor 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 AND THE CONTRIBUTOR ``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 OR THE CONTRIBUTOR 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.
+ */
+
+#define FE_MB86960_H_VERSION "mb86960.h ver. 0.8"
+
+/*
+ * Registers of Fujitsu MB86960A/MB86965A Ethernet controller.
+ * Written and contributed by M.S. <seki@sysrap.cs.fujitsu.co.jp>
+ */
+
+/*
+ * Notes on register naming:
+ *
+ * Fujitsu documents for MB86960A/MB86965A uses no mnemorable names
+ * for their registers. They defined only three names for 32
+ * registers and appended numbers to distinguish registers of
+ * same name. Surprisingly, the numbers represent I/O address
+ * offsets of the registers from the base addresses, and their
+ * names correspond to the "bank" the registers are allocated.
+ * All this means that, for example, to say "read DLCR8" has no more
+ * than to say "read a register at offset 8 on bank DLCR."
+ *
+ * The following definitions may look silly, but that's what Fujitsu
+ * did, and it is necessary to know these names to read Fujitsu
+ * documents..
+ */
+
+/*
+ * Modified for Allied-Telesis RE1000 series.
+ */
+
+#ifdef PC98
+/* Data Link Control Registrs, on invaliant port addresses. */
+#define FE_DLCR0 0
+#define FE_DLCR1 1
+#define FE_DLCR2 0x200
+#define FE_DLCR3 0x201
+#define FE_DLCR4 0x400
+#define FE_DLCR5 0x401
+#define FE_DLCR6 0x600
+#define FE_DLCR7 0x601
+
+/* More DLCRs, on register bank #0. */
+#define FE_DLCR8 0x800
+#define FE_DLCR9 0x801
+#define FE_DLCR10 0xA00
+#define FE_DLCR11 0xA01
+#define FE_DLCR12 0xC00
+#define FE_DLCR13 0xC01
+#define FE_DLCR14 0xE00
+#define FE_DLCR15 0xE01
+
+/* Malticast Address Registers. On register bank #1. */
+#define FE_MAR8 0x800
+#define FE_MAR9 0x801
+#define FE_MAR10 0xA00
+#define FE_MAR11 0xA01
+#define FE_MAR12 0xC00
+#define FE_MAR13 0xC01
+#define FE_MAR14 0xE00
+#define FE_MAR15 0xE01
+
+/* Buffer Memory Port Registers. On register back #2. */
+#define FE_BMPR8 0x800
+#define FE_BMPR9 0x801
+#define FE_BMPR10 0xA00
+#define FE_BMPR11 0xA01
+#define FE_BMPR12 0xC00
+#define FE_BMPR13 0xC01
+#define FE_BMPR14 0xE00
+#define FE_BMPR15 0xE01
+
+/* More BMPRs, only on MB86965A, accessible only when JLI mode. */
+#define FE_BMPR16 0x1000
+#define FE_BMPR17 0x1001
+#define FE_BMPR18 0x1200
+#define FE_BMPR19 0x1201
+#else /* not PC98 */
+/* Data Link Control Registrs, on invaliant port addresses. */
+#define FE_DLCR0 0
+#define FE_DLCR1 1
+#define FE_DLCR2 2
+#define FE_DLCR3 3
+#define FE_DLCR4 4
+#define FE_DLCR5 5
+#define FE_DLCR6 6
+#define FE_DLCR7 7
+
+/* More DLCRs, on register bank #0. */
+#define FE_DLCR8 8
+#define FE_DLCR9 9
+#define FE_DLCR10 10
+#define FE_DLCR11 11
+#define FE_DLCR12 12
+#define FE_DLCR13 13
+#define FE_DLCR14 14
+#define FE_DLCR15 15
+
+/* Malticast Address Registers. On register bank #1. */
+#define FE_MAR8 8
+#define FE_MAR9 9
+#define FE_MAR10 10
+#define FE_MAR11 11
+#define FE_MAR12 12
+#define FE_MAR13 13
+#define FE_MAR14 14
+#define FE_MAR15 15
+
+/* Buffer Memory Port Registers. On register back #2. */
+#define FE_BMPR8 8
+#define FE_BMPR9 9
+#define FE_BMPR10 10
+#define FE_BMPR11 11
+#define FE_BMPR12 12
+#define FE_BMPR13 13
+#define FE_BMPR14 14
+#define FE_BMPR15 15
+
+/* More BMPRs, only on MB86965A, accessible only when JLI mode. */
+#define FE_BMPR16 16
+#define FE_BMPR17 17
+#define FE_BMPR18 18
+#define FE_BMPR19 19
+#endif /* PC98 */
+
+/*
+ * Definitions of registers.
+ * I don't have Fujitsu documents of MB86960A/MB86965A, so I don't
+ * know the official names for each flags and fields. The following
+ * names are assigned by me (the author of this file,) since I cannot
+ * mnemorize hexadecimal constants for all of these functions.
+ * Comments? FIXME.
+ */
+
+/* DLCR0 -- transmitter status */
+#define FE_D0_BUSERR 0x01 /* Bus write error */
+#define FE_D0_COLL16 0x02 /* Collision limit (16) encountered */
+#define FE_D0_COLLID 0x04 /* Collision on last transmission */
+#define FE_D0_JABBER 0x08 /* Jabber */
+#define FE_D0_CRLOST 0x10 /* Carrier lost on last transmission */
+#define FE_D0_PKTRCD 0x20 /* No corrision on last transmission */
+#define FE_D0_NETBSY 0x40 /* Network Busy (Carrier Detected) */
+#define FE_D0_TXDONE 0x80 /* Transmission complete */
+
+/* DLCR1 -- receiver status */
+#define FE_D1_OVRFLO 0x01 /* Receiver buffer overflow */
+#define FE_D1_CRCERR 0x02 /* CRC error on last packet */
+#define FE_D1_ALGERR 0x04 /* Alignment error on last packet */
+#define FE_D1_SRTPKT 0x08 /* Short (RUNT) packet is received */
+#define FE_D1_RMTRST 0x10 /* Remote reset packet (type = 0x0900) */
+#define FE_D1_DMAEOP 0x20 /* Host asserted End of DMA OPeration */
+#define FE_D1_BUSERR 0x40 /* Bus read error */
+#define FE_D1_PKTRDY 0x80 /* Packet(s) ready on receive buffer */
+
+/* DLCR2 -- transmitter interrupt control; same layout as DLCR0 */
+#define FE_D2_BUSERR FE_D0_BUSERR
+#define FE_D2_COLL16 FE_D0_COLL16
+#define FE_D2_COLLID FE_D0_COLLID
+#define FE_D2_JABBER FE_D0_JABBER
+#define FE_D2_TXDONE FE_D0_TXDONE
+
+#define FE_D2_RESERVED 0x70
+
+/* DLCR3 -- receiver interrupt control; same layout as DLCR1 */
+#define FE_D3_OVRFLO FE_D1_OVRFLO
+#define FE_D3_CRCERR FE_D1_CRCERR
+#define FE_D3_ALGERR FE_D1_ALGERR
+#define FE_D3_SRTPKT FE_D1_SRTPKT
+#define FE_D3_RMTRST FE_D1_RMTRST
+#define FE_D3_DMAEOP FE_D1_DMAEOP
+#define FE_D3_BUSERR FE_D1_BUSERR
+#define FE_D3_PKTRDY FE_D1_PKTRDY
+
+/* DLCR4 -- transmitter operation mode */
+#define FE_D4_DSC 0x01 /* Disable carrier sense on trans. */
+#define FE_D4_LBC 0x02 /* Loop back test control */
+#define FE_D4_CNTRL 0x04 /* - ??? */
+#define FE_D4_TEST1 0x08 /* Test output #1 */
+#define FE_D4_COL 0xF0 /* Collision counter */
+
+#define FE_D4_LBC_ENABLE 0x00 /* Perform loop back test */
+#define FE_D4_LBC_DISABLE 0x02 /* Normal operation */
+
+#define FE_D4_COL_SHIFT 4
+
+/* DLCR5 -- receiver operation mode */
+#define FE_D5_AFM0 0x01 /* Receive packets for other stations */
+#define FE_D5_AFM1 0x02 /* Receive packets for this station */
+#define FE_D5_RMTRST 0x04 /* Enable remote reset operation */
+#define FE_D5_SRTPKT 0x08 /* Accept short (RUNT) packets */
+#define FE_D5_SRTADR 0x10 /* Short (16 bits?) MAC address */
+#define FE_D5_BADPKT 0x20 /* Accept packets with error */
+#define FE_D5_BUFEMP 0x40 /* Receive buffer is empty */
+#define FE_D5_TEST2 0x80 /* Test output #2 */
+
+/* DLCR6 -- hardware configuration #0 */
+#define FE_D6_BUFSIZ 0x03 /* Size of NIC buffer SRAM */
+#define FE_D6_TXBSIZ 0x0C /* Size (and config)of trans. buffer */
+#define FE_D6_BBW 0x10 /* Buffer SRAM bus width */
+#define FE_D6_SBW 0x20 /* System bus width */
+#define FE_D6_SRAM 0x40 /* Buffer SRAM access time */
+#define FE_D6_DLC 0x80 /* Disable DLC (recever/transmitter) */
+
+#define FE_D6_BUFSIZ_8KB 0x00 /* The board has 8KB SRAM */
+#define FE_D6_BUFSIZ_16KB 0x01 /* The board has 16KB SRAM */
+#define FE_D6_BUFSIZ_32KB 0x02 /* The board has 32KB SRAM */
+#define FE_D6_BUFSIZ_64KB 0x03 /* The board has 64KB SRAM */
+
+#define FE_D6_TXBSIZ_1x2KB 0x00 /* Single 2KB buffer for trans. */
+#define FE_D6_TXBSIZ_2x2KB 0x04 /* Double 2KB buffers */
+#define FE_D6_TXBSIZ_2x4KB 0x08 /* Double 4KB buffers */
+#define FE_D6_TXBSIZ_2x8KB 0x0C /* Double 8KB buffers */
+
+#define FE_D6_BBW_WORD 0x00 /* SRAM has 16 bit data line */
+#define FE_D6_BBW_BYTE 0x10 /* SRAM has 8 bit data line */
+
+#define FE_D6_SBW_WORD 0x00 /* Access with 16 bit (AT) bus */
+#define FE_D6_SBW_BYTE 0x20 /* Access with 8 bit (XT) bus */
+
+#define FE_D6_SRAM_150ns 0x00 /* The board has slow SRAM */
+#define FE_D6_SRAM_100ns 0x40 /* The board has fast SRAM */
+
+#define FE_D6_DLC_ENABLE 0x00 /* Normal operation */
+#define FE_D6_DLC_DISABLE 0x80 /* Stop sending/receiving */
+
+/* DLC7 -- hardware configuration #1 */
+#define FE_D7_BYTSWP 0x01 /* Host byte order control */
+#define FE_D7_EOPPOL 0x02 /* Polarity of DMA EOP signal */
+#define FE_D7_RBS 0x0C /* Register bank select */
+#define FE_D7_RDYPNS 0x10 /* Senses RDYPNSEL input signal */
+#define FE_D7_POWER 0x20 /* Stand-by (power down) mode control */
+#define FE_D7_IDENT 0xC0 /* Chip identification */
+
+#define FE_D7_BYTSWP_LH 0x00 /* DEC/Intel byte order */
+#define FE_D7_BYTSWP_HL 0x01 /* IBM/Motorolla byte order */
+
+#define FE_D7_RBS_DLCR 0x00 /* Select DLCR8-15 */
+#define FE_D7_RBS_MAR 0x04 /* Select MAR8-15 */
+#define FE_D7_RBS_BMPR 0x08 /* Select BMPR8-15 */
+
+#define FE_D7_POWER_DOWN 0x00 /* Power down (stand-by) mode */
+#define FE_D7_POWER_UP 0x20 /* Normal operation */
+
+#define FE_D7_IDENT_NICE 0x80
+#define FE_D7_IDENT_EC 0xC0
+
+/* DLCR8 thru DLCR13 are for Ethernet station address. */
+
+/* DLCR14 and DLCR15 are for TDR. (BTW, what is TDR? FIXME.) */
+
+/* MAR8 thru MAR15 are for Multicast address filter. */
+
+/* BMPR8 and BMPR9 are for packet data. */
+
+/* BMPR10 -- transmitter start trigger */
+#define FE_B10_START 0x80 /* Start transmitter */
+#define FE_B10_COUNT 0x7F /* Packet count */
+
+/* BMPR11 -- 16 collisions control */
+#define FE_B11_CTRL 0x01 /* Skip or resend errored packets */
+#define FE_B11_MODE1 0x02 /* Restart transmitter after COLL16 */
+#define FE_B11_MODE2 0x04 /* Automatic restart enable */
+
+#define FE_B11_CTRL_RESEND 0x00 /* Re-send the collided packet */
+#define FE_B11_CTRL_SKIP 0x01 /* Skip the collided packet */
+
+/* BMPR12 -- DMA enable */
+#define FE_B12_TXDMA 0x01 /* Enable transmitter DMA */
+#define FE_B12_RXDMA 0x02 /* Enable receiver DMA */
+
+/* BMPR13 -- DMA control */
+#define FE_B13_BSTCTL 0x03 /* DMA burst mode control */
+#define FE_B13_TPTYPE 0x04 /* Twisted pair cable impedance */
+#define FE_B13_PORT 0x18 /* Port (TP/AUI) selection */
+#define FE_B13_LNKTST 0x20 /* Link test enable */
+#define FE_B13_SQTHLD 0x40 /* Lower squelch threshold */
+#define FE_B13_IOUNLK 0x80 /* Change I/O base address */
+
+#define FE_B13_BSTCTL_1 0x00
+#define FE_B13_BSTCTL_4 0x01
+#define FE_B13_BSTCTL_8 0x02
+#define FE_B13_BSTCLT_12 0x03
+
+#define FE_B13_TPTYPE_UTP 0x00 /* Unshielded (standard) cable */
+#define FE_B13_TPTYPE_STP 0x04 /* Shielded (IBM) cable */
+
+#define FE_B13_PORT_AUTO 0x00 /* Auto detected */
+#define FE_B13_PORT_TP 0x08 /* Force TP */
+#define FE_B13_PORT_AUI 0x18 /* Force AUI */
+
+/* BMPR14 -- More receiver control and more transmission interrupts */
+#define FE_B14_FILTER 0x01 /* Filter out self-originated packets */
+#define FE_B14_SQE 0x02 /* SQE interrupt enable */
+#define FE_B14_SKIP 0x04 /* Skip a received packet */
+#define FE_B14_RJAB 0x20 /* RJAB interrupt enable */
+#define FE_B14_LLD 0x40 /* Local-link-down interrupt enable */
+#define FE_B14_RLD 0x80 /* Remote-link-down interrupt enable */
+
+/* BMPR15 -- More transmitter status; basically same layout as BMPR14 */
+#define FE_B15_SQE FE_B14_SQE
+#define FE_B15_RCVPOL 0x08 /* Reversed receive line polarity */
+#define FE_B15_RMTPRT 0x10 /* ??? */
+#define FE_B15_RAJB FE_B14_RJAB
+#define FE_B15_LLD FE_B14_LLD
+#define FE_B15_RLD FE_B14_RLD
+
+/* BMPR16 -- EEPROM control */
+#define FE_B16_DOUT 0x04 /* EEPROM Data in (CPU to EEPROM) */
+#define FE_B16_SELECT 0x20 /* EEPROM chip select */
+#define FE_B16_CLOCK 0x40 /* EEPROM shift clock */
+#define FE_B16_DIN 0x80 /* EEPROM data out (EEPROM to CPU) */
+
+/* BMPR17 -- EEPROM data */
+#define FE_B17_DATA 0x80 /* EEPROM data bit */
+
+/* BMPR18 ??? */
+
+/* BMPR19 -- ISA interface configuration */
+#define FE_B19_IRQ 0xC0
+#define FE_B19_IRQ_SHIFT 6
+
+#define FE_B19_ROM 0x38
+#define FE_B19_ROM_SHIFT 3
+
+#define FE_B19_ADDR 0x07
+#define FE_B19_ADDR_SHIFT 0
+
+/*
+ * EEPROM specification (of JLI mode).
+ */
+
+/* Number of bytes in an EEPROM accessible through 86965. */
+#define FE_EEPROM_SIZE 32
+
+/* Offset for JLI config; automatically copied into BMPR19 at startup. */
+#define FE_EEPROM_CONF 0
+
+/*
+ * Some 86960 specific constants.
+ */
+
+/* Length (in bytes) of a Multicast Address Filter. */
+#define FE_FILTER_LEN 8
+
+/* How many packets we can put in the transmission buffer on NIC memory. */
+#define FE_QUEUEING_MAX 127
+
+/* Length (in bytes) of a "packet length" word in transmission buffer. */
+#define FE_DATA_LEN_LEN 2
+
+/* Special Multicast Address Filter value. */
+#define FE_FILTER_NOTHING { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }
+#define FE_FILTER_ALL { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }
diff --git a/sys/pc98/pc98/ic/nec765.h b/sys/pc98/pc98/ic/nec765.h
new file mode 100644
index 0000000..5cf1e73
--- /dev/null
+++ b/sys/pc98/pc98/ic/nec765.h
@@ -0,0 +1,141 @@
+/*-
+ * 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: @(#)nec765.h 7.1 (Berkeley) 5/9/91
+ * $Id: nec765.h,v 1.4 1995/01/06 15:20:00 joerg Exp $
+ */
+
+/*
+ * Nec 765 floppy disc controller definitions
+ */
+
+/* Main status register */
+#define NE7_DAB 0x01 /* Diskette drive A is seeking, thus busy */
+#define NE7_DBB 0x02 /* Diskette drive B is seeking, thus busy */
+#define NE7_CB 0x10 /* Diskette Controller Busy */
+#define NE7_NDM 0x20 /* Diskette Controller in Non Dma Mode */
+#define NE7_DIO 0x40 /* Diskette Controller Data register I/O */
+#define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */
+
+/* Status register ST0 */
+#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005equ_chck\004drive_notrdy\003top_head"
+
+#define NE7_ST0_IC 0xc0 /* interrupt completion code */
+
+#define NE7_ST0_IC_RC 0xc0 /* terminated due to ready changed, n/a */
+#define NE7_ST0_IC_IV 0x80 /* invalid command; must reset FDC */
+#define NE7_ST0_IC_AT 0x40 /* abnormal termination, check error stat */
+#define NE7_ST0_IC_NT 0x00 /* normal termination */
+
+#define NE7_ST0_SE 0x20 /* seek end */
+#define NE7_ST0_EC 0x10 /* equipment check, recalibrated but no trk0 */
+#define NE7_ST0_NR 0x08 /* not ready (n/a) */
+#define NE7_ST0_HD 0x04 /* upper head selected */
+#define NE7_ST0_DR 0x03 /* drive code */
+
+/* Status register ST1 */
+#define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am"
+
+#define NE7_ST1_EN 0x80 /* end of cylinder, access past last record */
+#define NE7_ST1_DE 0x20 /* data error, CRC fail in ID or data */
+#define NE7_ST1_OR 0x10 /* DMA overrun, DMA failed to do i/o quickly */
+#define NE7_ST1_ND 0x04 /* no data, sector not found or CRC in ID f. */
+#define NE7_ST1_NW 0x02 /* not writeable, attempt to violate WP */
+#define NE7_ST1_MA 0x01 /* missing address mark (in ID or data field)*/
+
+/* Status register ST2 */
+#define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam"
+
+#define NE7_ST2_CM 0x40 /* control mark; found deleted data */
+#define NE7_ST2_DD 0x20 /* data error in data field, CRC fail */
+#define NE7_ST2_WC 0x10 /* wrong cylinder, ID field mismatches cmd */
+#define NE7_ST2_SH 0x08 /* scan equal hit */
+#define NE7_ST2_SN 0x04 /* scan not satisfied */
+#define NE7_ST2_BC 0x02 /* bad cylinder, cylinder marked 0xff */
+#define NE7_ST2_MD 0x01 /* missing address mark in data field */
+
+/* Status register ST3 */
+#define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002"
+
+#define NE7_ST3_FT 0x80 /* fault; PC: n/a */
+#define NE7_ST3_WP 0x40 /* write protected */
+#define NE7_ST3_RD 0x20 /* ready; PC: always true */
+#define NE7_ST3_T0 0x10 /* track 0 */
+#define NE7_ST3_TS 0x08 /* two-sided; PC: n/a */
+#define NE7_ST3_HD 0x04 /* upper head select */
+#define NE7_ST3_US 0x03 /* unit select */
+
+/* Commands */
+/*
+ * the top three bits -- where appropriate -- are set as follows:
+ *
+ * 0x80 - MT multi-track; allow both sides to be handled in single cmd
+ * 0x40 - MFM modified frequency modulation; use MFM encoding
+ * 0x20 - SK skip; skip sectors marked as "deleted"
+ */
+#define NE7CMD_READTRK 0x42 /* read whole track */
+#define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit
+ parameters byte */
+#define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */
+#define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */
+#define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */
+#define NE7CMD_RECAL 7 /* recalibrate drive - requires
+ unit select byte */
+#define NE7CMD_SENSEI 8 /* sense controller interrupt status */
+#define NE7CMD_WRITEDEL 0xc9 /* write deleted data */
+#define NE7CMD_READID 0x4a /* read ID field */
+#define NE7CMD_READDEL 0xec /* read deleted data */
+#define NE7CMD_FORMAT 0x4d /* format - requires five additional bytes */
+#define NE7CMD_SEEK 0x0f /* seek drive - requires unit select byte
+ and new cyl byte */
+#define NE7CMD_SCNEQU 0xf1 /* scan equal */
+#define NE7CMD_SCNLE 0xf9 /* scan less or equal */
+#define NE7CMD_SCNGE 0xfd /* scan greater or equal */
+
+/*
+ * Enhanced controller commands:
+ */
+#define NE7CMD_VERSION 0x10 /* version (ok for all controllers) */
+
+
+/*
+ * "specify" definitions
+ *
+ * acronyms (times are relative to a FDC clock of 8 MHz):
+ * srt - step rate; PC usually 3 ms
+ * hut - head unload time; PC usually maximum of 240 ms
+ * hlt - head load time; PC usually minimum of 2 ms
+ * nd - no DMA flag; PC usually not set (0)
+ */
+
+#define NE7_SPEC_1(srt, hut) (((16 - (srt)) << 4) | (((hut) / 16)))
+#define NE7_SPEC_2(hlt, nd) (((hlt) & 0xFE) | ((nd) & 1))
diff --git a/sys/pc98/pc98/ic/ns16550.h b/sys/pc98/pc98/ic/ns16550.h
new file mode 100644
index 0000000..49bfafd
--- /dev/null
+++ b/sys/pc98/pc98/ic/ns16550.h
@@ -0,0 +1,68 @@
+/*-
+ * 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: @(#)ns16550.h 7.1 (Berkeley) 5/9/91
+ * ns16550.h,v 1.2 1993/10/16 13:48:52 rgrimes Exp
+ */
+
+/*
+ * NS16550 UART registers
+ */
+/*
+ * modified for MC16550II
+ */
+
+#ifdef PC98
+#define com_data 0x000 /* data register (R/W) */
+#define com_dlbl 0x000 /* divisor latch low (W) */
+#define com_dlbh 0x100 /* divisor latch high (W) */
+#define com_ier 0x100 /* interrupt enable (W) */
+#define com_iir 0x200 /* interrupt identification (R) */
+#define com_fifo 0x200 /* FIFO control (W) */
+#define com_lctl 0x300 /* line control register (R/W) */
+#define com_cfcr 0x300 /* line control register (R/W) */
+#define com_mcr 0x400 /* modem control register (R/W) */
+#define com_lsr 0x500 /* line status register (R/W) */
+#define com_msr 0x600 /* modem status register (R/W) */
+#else
+#define com_data 0 /* data register (R/W) */
+#define com_dlbl 0 /* divisor latch low (W) */
+#define com_dlbh 1 /* divisor latch high (W) */
+#define com_ier 1 /* interrupt enable (W) */
+#define com_iir 2 /* interrupt identification (R) */
+#define com_fifo 2 /* FIFO control (W) */
+#define com_lctl 3 /* line control register (R/W) */
+#define com_cfcr 3 /* line control register (R/W) */
+#define com_mcr 4 /* modem control register (R/W) */
+#define com_lsr 5 /* line status register (R/W) */
+#define com_msr 6 /* modem status register (R/W) */
+#endif
diff --git a/sys/pc98/pc98/ic/wd33c93.h b/sys/pc98/pc98/ic/wd33c93.h
new file mode 100644
index 0000000..f1aa7f9
--- /dev/null
+++ b/sys/pc98/pc98/ic/wd33c93.h
@@ -0,0 +1,127 @@
+/*
+ * PC9801 SCSI I/F (PC-9801-55)
+ * modified for PC9801 by A.Kojima
+ * Kyoto University Microcomputer Club (KMC)
+ */
+
+/* I/O address */
+
+/* WD33C93 */
+#define SCSI_ADR_REG 0xcc0 /* write Address Register */
+#define SCSI_AUX_REG 0xcc0 /* read Aux. Status Register */
+#define SCSI_CTL_REG 0xcc2 /* read/write Control Registers */
+
+/* Port */
+#define SCSI_STAT_RD 0xcc4 /* read Status Read */
+#define SCSI_CMD_WRT 0xcc4 /* write Command Write */
+
+#if 0 /* H98 extended mode */
+/* WD33C93 */
+#define SCSI_ADR_REG 0xee0 /* write Address Register */
+#define SCSI_AUX_REG 0xee0 /* read Control Register */
+#define SCSI_CTL_REG 0xee2 /* read/write Registers */
+
+/* Port */
+#define SCSI_STAT_RD 0xee4 /* read Status Read */
+#define SCSI_CMD_WRT 0xee4 /* write Command Write */
+#endif
+
+/****************************************************************/
+
+/* WD33C93 Registers */
+#define REG_OWN_ID 0x00 /* Own ID */
+#define REG_CONTROL 0x01 /* Control */
+#define REG_TIMEOUT_PERIOD 0x02 /* Timeout Period */
+#define REG_TOTAL_SECTORS 0x03 /* Total Sectors */
+#define REG_TOTAL_HEADS 0x04 /* Total Heads */
+#define REG_TOTAL_CYL_H 0x05 /* Total Cylinders (MSB) */
+#define REG_TOTAL_CYL_L 0x06 /* Total Cylinders (LSB) */
+#define REG_LOG_SECTOR_HH 0x07 /* Logical Address (MSB) */
+#define REG_LOG_SECTOR_HL 0x08 /* Logical Address */
+#define REG_LOG_SECTOR_LH 0x09 /* Logical Address */
+#define REG_LOG_SECTOR_LL 0x0a /* Logical Address (LSB) */
+#define REG_SECTOR_NUMBER 0x0b /* Sector Number */
+#define REG_HEAD_NUMBER 0x0c /* Head Number */
+#define REG_CYL_NUMBER_H 0x0d /* Cylinder Number (MSB) */
+#define REG_CYL_NUMBER_L 0x0e /* Cylinder Number (LSB) */
+#define REG_TARGET_LUN 0x0f /* Target LUN */
+#define REG_CMD_PHASE 0x10 /* Command Phase */
+#define REG_SYNC_TFR 0x11 /* Synchronous Transfer */
+#define REG_TFR_COUNT_H 0x12 /* Transfer Count (MSB) */
+#define REG_TFR_COUNT_M 0x13 /* Transfer Count */
+#define REG_TFR_COUNT_L 0x14 /* Transfer Count (LSB) */
+#define REG_DST_ID 0x15 /* Destination ID */
+#define REG_SRC_ID 0x16 /* Source ID */
+#define REG_SCSI_STATUS 0x17 /* SCSI Status (Read Only) */
+#define REG_COMMAND 0x18 /* Command */
+#define REG_DATA 0x19 /* Data */
+
+/* PC98 only */
+#define REG_MEM_BANK 0x30 /* Memory Bank */
+#define REG_MEM_WIN 0x31 /* Memery Window */
+#define REG_RESERVED1 0x32 /* NEC Reserved 1 */
+#define REG_RESET_INT 0x33 /* Reset/Int */
+#define REG_RESERVED2 0x34 /* NEC Reserved 2 */
+
+/****************************************************************/
+
+/* WD33C93 Commands */
+#define CMD_RESET 0x00 /* Reset */
+#define CMD_ABORT 0x01 /* Abort */
+#define CMD_ASSERT_ATN 0x02 /* Assert ATN */
+#define CMD_NEGATE_ATN 0x03 /* Negate ATN */
+#define CMD_DISCONNECT 0x04 /* Disconnect */
+#define CMD_RESELECT 0x05 /* Reselect */
+#define CMD_SELECT_ATN 0x06 /* Select with ATN */
+#define CMD_SELECT_NO_ATN 0x07 /* Select without ATN */
+#define CMD_SELECT_ATN_TFR 0x08 /* Select with ATN and Transfer */
+#define CMD_SELECT_NO_ATN_TFR 0x09 /* Select without ATN and Transfer */
+#define CMD_RESELECT_RCV_DATA 0x0a /* Reselect and Recieve Data */
+#define CMD_RESELECT_SEND_DATA 0x0b /* Reselect and Send Data */
+#define CMD_WAIT_SELECT_RCV 0x0c /* Wait for Select and Recieve */
+#define CMD_RCV_CMD 0x10 /* Recieve Command */
+#define CMD_RCV_DATA 0x11 /* Recieve Data */
+#define CMD_RCV_MSG_OUT 0x12 /* Recieve Message Info Out*/
+#define CMD_RCV_UNSP_INFO_OUT 0x13 /* Recieve Unspecified Info Out */
+#define CMD_SEND_STATUS 0x14 /* Send Status */
+#define CMD_SEND_DATA 0x15 /* Send Data */
+#define CMD_SEND_MSG_IN 0x16 /* Send Message In */
+#define CMD_SEND_UNSP_INFO_IN 0x17 /* Send Unspecified Info In */
+#define CMD_TRANSLATE_ADDRESS 0x18 /* Translate Address */
+#define CMD_TFR_INFO 0x20 /* Transfer Info */
+#define CMD_TFR_PAD 0x21 /* Transfer Pad */
+#define CMD_SBT_SFX 0x80 /* single byte suffix */
+
+/* WD33C93 bus status register (lower nibble) */
+#define STAT_DATAOUT 0x08 /* Data out phase */
+#define STAT_DATAIN 0x09 /* Data in phase */
+#define STAT_CMDOUT 0x0a /* Command out phase */
+#define STAT_STATIN 0x0b /* Status in phase */
+#define STAT_MSGOUT 0x0e /* Message out phase */
+#define STAT_MSGIN 0x0f /* Message in phase */
+
+/* SCSI Status byte */
+#define SS_GOOD 0x00 /* Good status */
+#define SS_CHKCOND 0x02
+#define SS_MET 0x04
+#define SS_BUSY 0x08
+#define SS_INTERGOOD 0x10
+#define SS_INTERMET 0x14
+#define SS_CONFLICT 0x18
+
+/* SCSI message system */
+#define MSG_COMPLETE 0x00 /* Command complete message */
+#define MSG_EXTEND 0x01 /* Extend message */
+#define MSG_SAVEPTR 0x02 /* Save data pointer message */
+#define MSG_RESTORE 0x03 /* Restore data pointer message */
+#define MSG_DISCON 0x04 /* Disconnect message */
+#define MSG_INIERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT 0x07
+#define MSG_NOP 0x08
+#define MSG_PARERROR 0x09
+#define MSG_LCOMPLETE 0x0a
+#define MSG_LCOMPLETEF 0x0b
+#define MSG_DEVRESET 0x0c
+#define MSG_IDENTIFY 0x80 /* Identify message */
+
diff --git a/sys/pc98/pc98/icu.h b/sys/pc98/pc98/icu.h
new file mode 100644
index 0000000..47a5f2a
--- /dev/null
+++ b/sys/pc98/pc98/icu.h
@@ -0,0 +1,125 @@
+/*-
+ * 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: @(#)icu.h 5.6 (Berkeley) 5/9/91
+ * $Id: icu.h,v 1.7 1994/10/01 02:56:11 davidg Exp $
+ */
+
+/*
+ * AT/386 Interrupt Control constants
+ * W. Jolitz 8/89
+ *
+ * modified for PC98
+ * $Id: icu.h,v 1.2 1994/03/14 10:44:07 kakefuda Exp $
+ */
+
+#ifndef _PC98_PC98_ICU_H_
+#define _PC98_PC98_ICU_H_
+
+#ifndef LOCORE
+
+/*
+ * Interrupt "level" mechanism variables, masks, and macros
+ */
+extern unsigned imen; /* interrupt mask enable */
+
+#define INTREN(s) (imen &= ~(s), SET_ICUS())
+#define INTRDIS(s) (imen |= (s), SET_ICUS())
+#define INTRMASK(msk,s) (msk |= (s))
+#define INTRUNMASK(msk,s) (msk &= ~(s))
+#if 0
+#define SET_ICUS() (outb(IO_ICU1 + 1, imen), outb(IU_ICU2 + 1, imen >> 8))
+#else
+/*
+ * XXX - IO_ICU* are defined in pc98.h, not icu.h, and nothing much bothers to
+ * include pc98.h, while too many things include icu.h.
+ */
+#ifdef PC98
+#define SET_ICUS() (outb(0x02, imen), outb(0x0a, imen >> 8))
+#else
+#define SET_ICUS() (outb(0x21, imen), outb(0xa1, imen >> 8))
+#endif
+#endif
+
+#endif /* LOCORE */
+
+/*
+ * Interrupt enable bits - in normal order of priority (which we change)
+ */
+#ifdef PC98
+#define IRQ0 0x0001 /* highest priority - timer */
+#define IRQ1 0x0002
+#define IRQ2 0x0004
+#define IRQ3 0x0008
+#define IRQ4 0x0010
+#define IRQ5 0x0020
+#define IRQ6 0x0040
+#define IRQ7 0x0080 /* OK? */
+#define IRQ_SLAVE 0x0080
+#define IRQ8 0x0100
+#define IRQ9 0x0200
+#define IRQ10 0x0400
+#define IRQ11 0x0800
+#define IRQ12 0x1000
+#define IRQ13 0x2000
+#define IRQ14 0x4000
+#define IRQ15 0x8000 /* lowest */
+#else
+#define IRQ0 0x0001 /* highest priority - timer */
+#define IRQ1 0x0002
+#define IRQ_SLAVE 0x0004
+#define IRQ8 0x0100
+#define IRQ9 0x0200
+#define IRQ2 IRQ9
+#define IRQ10 0x0400
+#define IRQ11 0x0800
+#define IRQ12 0x1000
+#define IRQ13 0x2000
+#define IRQ14 0x4000
+#define IRQ15 0x8000
+#define IRQ3 0x0008 /* this is highest after rotation */
+#define IRQ4 0x0010
+#define IRQ5 0x0020
+#define IRQ6 0x0040
+#define IRQ7 0x0080 /* lowest - parallel printer */
+#endif
+
+/*
+ * Interrupt Control offset into Interrupt descriptor table (IDT)
+ */
+#define ICU_OFFSET 32 /* 0-31 are processor exceptions */
+#define ICU_LEN 16 /* 32-47 are PC98 interrupts */
+
+#endif /* !_PC98_PC98_ICU_H_ */
diff --git a/sys/pc98/pc98/icu.s b/sys/pc98/pc98/icu.s
new file mode 100644
index 0000000..567daf1
--- /dev/null
+++ b/sys/pc98/pc98/icu.s
@@ -0,0 +1,357 @@
+/*-
+ * Copyright (c) 1989, 1990 William F. Jolitz.
+ * 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.
+ *
+ * @(#)icu.s 7.2 (Berkeley) 5/21/91
+ *
+ * $Id: icu.s,v 1.25 1996/05/31 01:08:07 peter Exp $
+ */
+
+/*
+ * AT/386
+ * Vector interrupt control section
+ */
+
+/*
+ * XXX this file should be named ipl.s. All spls are now soft and the
+ * only thing related to the hardware icu is that the h/w interrupt
+ * numbers are used without translation in the masks.
+ */
+
+ .data
+ .globl _cpl
+_cpl: .long HWI_MASK | SWI_MASK /* current priority (all off) */
+ .globl _imen
+_imen: .long HWI_MASK /* interrupt mask enable (all h/w off) */
+ .globl _tty_imask
+_tty_imask: .long 0
+ .globl _bio_imask
+_bio_imask: .long 0
+ .globl _net_imask
+_net_imask: .long 0
+ .globl _ipending
+_ipending: .long 0
+ .globl _netisr
+_netisr: .long 0 /* set with bits for which queue to service */
+ .globl _netisrs
+_netisrs:
+ .long dummynetisr, dummynetisr, dummynetisr, dummynetisr
+ .long dummynetisr, dummynetisr, dummynetisr, dummynetisr
+ .long dummynetisr, dummynetisr, dummynetisr, dummynetisr
+ .long dummynetisr, dummynetisr, dummynetisr, dummynetisr
+ .long dummynetisr, dummynetisr, dummynetisr, dummynetisr
+ .long dummynetisr, dummynetisr, dummynetisr, dummynetisr
+ .long dummynetisr, dummynetisr, dummynetisr, dummynetisr
+ .long dummynetisr, dummynetisr, dummynetisr, dummynetisr
+vec:
+ .long vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7
+ .long vec8, vec9, vec10, vec11, vec12, vec13, vec14, vec15
+
+ .text
+
+/*
+ * Handle return from interrupts, traps and syscalls.
+ */
+ SUPERALIGN_TEXT
+_doreti:
+ FAKE_MCOUNT(_bintr) /* init "from" _bintr -> _doreti */
+ addl $4,%esp /* discard unit number */
+ popl %eax /* cpl to restore */
+doreti_next:
+ /*
+ * Check for pending HWIs and SWIs atomically with restoring cpl
+ * and exiting. The check has to be atomic with exiting to stop
+ * (ipending & ~cpl) changing from zero to nonzero while we're
+ * looking at it (this wouldn't be fatal but it would increase
+ * interrupt latency). Restoring cpl has to be atomic with exiting
+ * so that the stack cannot pile up (the nesting level of interrupt
+ * handlers is limited by the number of bits in cpl).
+ */
+ movl %eax,%ecx
+ notl %ecx
+ cli
+ andl _ipending,%ecx
+ jne doreti_unpend
+doreti_exit:
+ movl %eax,_cpl
+ decb _intr_nesting_level
+ MEXITCOUNT
+ .globl doreti_popl_es
+doreti_popl_es:
+ popl %es
+ .globl doreti_popl_ds
+doreti_popl_ds:
+ popl %ds
+ popal
+ addl $8,%esp
+ .globl doreti_iret
+doreti_iret:
+ iret
+
+ ALIGN_TEXT
+ .globl doreti_iret_fault
+doreti_iret_fault:
+ subl $8,%esp
+ pushal
+ pushl %ds
+ .globl doreti_popl_ds_fault
+doreti_popl_ds_fault:
+ pushl %es
+ .globl doreti_popl_es_fault
+doreti_popl_es_fault:
+ movl $0,4+4+32+4(%esp) /* XXX should be the error code */
+ movl $T_PROTFLT,4+4+32+0(%esp)
+ jmp alltraps_with_regs_pushed
+
+ ALIGN_TEXT
+doreti_unpend:
+ /*
+ * Enabling interrupts is safe because we haven't restored cpl yet.
+ * The locking from the "btrl" test is probably no longer necessary.
+ * We won't miss any new pending interrupts because we will check
+ * for them again.
+ */
+ sti
+ bsfl %ecx,%ecx /* slow, but not worth optimizing */
+ btrl %ecx,_ipending
+ jnc doreti_next /* some intr cleared memory copy */
+ movl ihandlers(,%ecx,4),%edx
+ testl %edx,%edx
+ je doreti_next /* "can't happen" */
+ cmpl $NHWI,%ecx
+ jae doreti_swi
+ cli
+ movl %eax,_cpl
+ MEXITCOUNT
+ jmp %edx
+
+ ALIGN_TEXT
+doreti_swi:
+ pushl %eax
+ /*
+ * The SWI_AST handler has to run at cpl = SWI_AST_MASK and the
+ * SWI_CLOCK handler at cpl = SWI_CLOCK_MASK, so we have to restore
+ * all the h/w bits in cpl now and have to worry about stack growth.
+ * The worst case is currently (30 Jan 1994) 2 SWI handlers nested
+ * in dying interrupt frames and about 12 HWIs nested in active
+ * interrupt frames. There are only 4 different SWIs and the HWI
+ * and SWI masks limit the nesting further.
+ */
+ orl imasks(,%ecx,4),%eax
+ movl %eax,_cpl
+ call %edx
+ popl %eax
+ jmp doreti_next
+
+ ALIGN_TEXT
+swi_ast:
+ addl $8,%esp /* discard raddr & cpl to get trap frame */
+ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp)
+ je swi_ast_phantom
+ movl $T_ASTFLT,(2+8+0)*4(%esp)
+ call _trap
+ subl %eax,%eax /* recover cpl */
+ jmp doreti_next
+
+ ALIGN_TEXT
+swi_ast_phantom:
+ /*
+ * These happen when there is an interrupt in a trap handler before
+ * ASTs can be masked or in an lcall handler before they can be
+ * masked or after they are unmasked. They could be avoided for
+ * trap entries by using interrupt gates, and for lcall exits by
+ * using by using cli, but they are unavoidable for lcall entries.
+ */
+ cli
+ orl $SWI_AST_PENDING,_ipending
+ subl %eax,%eax
+ jmp doreti_exit /* SWI_AST is highest so we must be done */
+
+/*
+ * Interrupt priority mechanism
+ * -- soft splXX masks with group mechanism (cpl)
+ * -- h/w masks for currently active or unused interrupts (imen)
+ * -- ipending = active interrupts currently masked by cpl
+ */
+
+ENTRY(splz)
+ /*
+ * The caller has restored cpl and checked that (ipending & ~cpl)
+ * is nonzero. We have to repeat the check since if there is an
+ * interrupt while we're looking, _doreti processing for the
+ * interrupt will handle all the unmasked pending interrupts
+ * because we restored early. We're repeating the calculation
+ * of (ipending & ~cpl) anyway so that the caller doesn't have
+ * to pass it, so this only costs one "jne". "bsfl %ecx,%ecx"
+ * is undefined when %ecx is 0 so we can't rely on the secondary
+ * btrl tests.
+ */
+ movl _cpl,%eax
+splz_next:
+ /*
+ * We don't need any locking here. (ipending & ~cpl) cannot grow
+ * while we're looking at it - any interrupt will shrink it to 0.
+ */
+ movl %eax,%ecx
+ notl %ecx
+ andl _ipending,%ecx
+ jne splz_unpend
+ ret
+
+ ALIGN_TEXT
+splz_unpend:
+ bsfl %ecx,%ecx
+ btrl %ecx,_ipending
+ jnc splz_next
+ movl ihandlers(,%ecx,4),%edx
+ testl %edx,%edx
+ je splz_next /* "can't happen" */
+ cmpl $NHWI,%ecx
+ jae splz_swi
+ /*
+ * We would prefer to call the intr handler directly here but that
+ * doesn't work for badly behaved handlers that want the interrupt
+ * frame. Also, there's a problem determining the unit number.
+ * We should change the interface so that the unit number is not
+ * determined at config time.
+ */
+ jmp *vec(,%ecx,4)
+
+ ALIGN_TEXT
+splz_swi:
+ cmpl $SWI_AST,%ecx
+ je splz_next /* "can't happen" */
+ pushl %eax
+ orl imasks(,%ecx,4),%eax
+ movl %eax,_cpl
+ call %edx
+ popl %eax
+ movl %eax,_cpl
+ jmp splz_next
+
+/*
+ * Fake clock interrupt(s) so that they appear to come from our caller instead
+ * of from here, so that system profiling works.
+ * XXX do this more generally (for all vectors; look up the C entry point).
+ * XXX frame bogusness stops us from just jumping to the C entry point.
+ */
+ ALIGN_TEXT
+vec0:
+ popl %eax /* return address */
+ pushfl
+#define KCSEL 8
+ pushl $KCSEL
+ pushl %eax
+ cli
+ MEXITCOUNT
+ jmp _Xintr0 /* XXX might need _Xfastintr0 */
+
+#ifndef PC98
+ ALIGN_TEXT
+vec8:
+ popl %eax
+ pushfl
+ pushl $KCSEL
+ pushl %eax
+ cli
+ MEXITCOUNT
+ jmp _Xintr8 /* XXX might need _Xfastintr8 */
+#endif
+
+#define BUILD_VEC(irq_num) \
+ ALIGN_TEXT ; \
+__CONCAT(vec,irq_num): ; \
+ int $ICU_OFFSET + (irq_num) ; \
+ ret
+
+ BUILD_VEC(1)
+ BUILD_VEC(2)
+ BUILD_VEC(3)
+ BUILD_VEC(4)
+ BUILD_VEC(5)
+ BUILD_VEC(6)
+ BUILD_VEC(7)
+#ifdef PC98
+ BUILD_VEC(8)
+#endif
+ BUILD_VEC(9)
+ BUILD_VEC(10)
+ BUILD_VEC(11)
+ BUILD_VEC(12)
+ BUILD_VEC(13)
+ BUILD_VEC(14)
+ BUILD_VEC(15)
+
+ ALIGN_TEXT
+swi_net:
+ MCOUNT
+ bsfl _netisr,%eax
+ je swi_net_done
+swi_net_more:
+ btrl %eax,_netisr
+ jnc swi_net_next
+ call *_netisrs(,%eax,4)
+swi_net_next:
+ bsfl _netisr,%eax
+ jne swi_net_more
+swi_net_done:
+ ret
+
+ ALIGN_TEXT
+dummynetisr:
+ MCOUNT
+ ret
+
+/*
+ * XXX there should be a registration function to put the handler for the
+ * attached driver directly in ihandlers. Then this function will go away.
+ */
+ ALIGN_TEXT
+swi_tty:
+ MCOUNT
+#include "cy.h"
+#if NCY > 0
+ call _cypoll
+#endif
+#include "rc.h"
+#if NRC > 0
+ call _rcpoll
+#endif
+#include "sio.h"
+#if NSIO > 0
+ jmp _siopoll
+#else
+ ret
+#endif
diff --git a/sys/pc98/pc98/if_ed.c b/sys/pc98/pc98/if_ed.c
new file mode 100644
index 0000000..40b9169
--- /dev/null
+++ b/sys/pc98/pc98/if_ed.c
@@ -0,0 +1,3442 @@
+/*
+ * Copyright (c) 1995, David Greenman
+ * 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 unmodified, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: if_ed.c,v 1.99 1996/05/27 22:32:23 gpalmer Exp $
+ */
+
+/*
+ * Device driver for National Semiconductor DS8390/WD83C690 based ethernet
+ * adapters. By David Greenman, 29-April-1993
+ *
+ * Currently supports the Western Digital/SMC 8003 and 8013 series,
+ * the SMC Elite Ultra (8216), the 3Com 3c503, the NE1000 and NE2000,
+ * and a variety of similar clones.
+ *
+ */
+
+/*
+ * FreeBSD(98) supports the LGY-98 series, EGY-98 series, LGH-98 series,
+ * IF_2766ET, AD-ET2-T, SIC-98 series, LD-BDN and LPC-T.
+ *
+ * Modified for FreeBSD(98) 2.2 by KATO T. of Nagoya University.
+ *
+ * LPC-T support routine was contributed by Chikun.
+ *
+ * SIC-98 spport routine was derived from the code by A. Kojima of
+ * Kyoto University Microcomputer Club (KMC).
+ */
+
+#include "ed.h"
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/devconf.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef IPX
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <machine/clock.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/if_edreg.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/if_edreg.h>
+#endif
+
+/*
+ * ed_softc: per line info and status
+ */
+struct ed_softc {
+ struct arpcom arpcom; /* ethernet common */
+
+ char *type_str; /* pointer to type string */
+ u_char vendor; /* interface vendor */
+ u_char type; /* interface type code */
+ u_char gone; /* HW missing, presumed having a good time */
+
+ u_short asic_addr; /* ASIC I/O bus address */
+ u_short nic_addr; /* NIC (DS8390) I/O bus address */
+
+/*
+ * The following 'proto' variable is part of a work-around for 8013EBT asics
+ * being write-only. It's sort of a prototype/shadow of the real thing.
+ */
+ u_char wd_laar_proto;
+ u_char cr_proto;
+ u_char isa16bit; /* width of access to card 0=8 or 1=16 */
+ int is790; /* set by the probe code if the card is 790
+ * based */
+
+ caddr_t mem_start; /* NIC memory start address */
+ caddr_t mem_end; /* NIC memory end address */
+ u_long mem_size; /* total NIC memory size */
+ caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */
+
+ u_char mem_shared; /* NIC memory is shared with host */
+ u_char xmit_busy; /* transmitter is busy */
+ u_char txb_cnt; /* number of transmit buffers */
+ u_char txb_inuse; /* number of TX buffers currently in-use */
+
+ u_char txb_new; /* pointer to where new buffer will be added */
+ u_char txb_next_tx; /* pointer to next buffer ready to xmit */
+ u_short txb_len[8]; /* buffered xmit buffer lengths */
+ u_char tx_page_start; /* first page of TX buffer area */
+ u_char rec_page_start; /* first page of RX ring-buffer */
+ u_char rec_page_stop; /* last page of RX ring-buffer */
+ u_char next_packet; /* pointer to next unread RX packet */
+ struct kern_devconf kdc; /* kernel configuration database info */
+#ifdef PC98
+ int unit;
+#endif
+};
+
+static struct ed_softc ed_softc[NED];
+
+static int ed_attach __P((struct ed_softc *, int, int));
+#ifdef PC98
+static int ed_attach_isa __P((struct pc98_device *));
+#else
+static int ed_attach_isa __P((struct isa_device *));
+#endif
+
+static void ed_init __P((struct ifnet *));
+static int ed_ioctl __P((struct ifnet *, int, caddr_t));
+#ifdef PC98
+static int ed_probe __P((struct pc98_device *));
+#else
+static int ed_probe __P((struct isa_device *));
+#endif
+static void ed_start __P((struct ifnet *));
+static void ed_reset __P((struct ifnet *));
+static void ed_watchdog __P((struct ifnet *));
+
+static void ed_stop __P((struct ed_softc *));
+static int ed_probe_generic8390 __P((struct ed_softc *));
+#ifdef PC98
+static int ed_probe_WD80x3 __P((struct pc98_device *));
+static int ed_probe_3Com __P((struct pc98_device *));
+static int ed_probe_Novell __P((struct pc98_device *));
+static int ed_probe_SIC98 __P((struct pc98_device *));
+#else
+static int ed_probe_WD80x3 __P((struct isa_device *));
+static int ed_probe_3Com __P((struct isa_device *));
+static int ed_probe_Novell __P((struct isa_device *));
+#endif
+static int ed_probe_Novell_generic __P((struct ed_softc *, int, int, int));
+
+#include "pci.h"
+#if NPCI > 0
+void *ed_attach_NE2000_pci __P((int, int));
+#endif
+
+#include "crd.h"
+#if NCRD > 0
+#ifdef PC98
+static int ed_probe_pccard __P((struct pc98_device *, u_char *));
+#else
+static int ed_probe_pccard __P((struct isa_device *, u_char *));
+#endif
+#endif
+
+static void ds_getmcaf __P((struct ed_softc *, u_long *));
+
+static void ed_get_packet(struct ed_softc *, char *, /* u_short */ int, int);
+
+static void ed_rint __P((struct ed_softc *));
+static void ed_xmit __P((struct ed_softc *));
+static char * ed_ring_copy __P((struct ed_softc *, char *, char *,
+ /* u_short */ int));
+
+static void ed_pio_readmem __P((struct ed_softc *, int, unsigned char *,
+ /* u_short */ int));
+static void ed_pio_writemem __P((struct ed_softc *, char *,
+ /* u_short */ int, /* u_short */ int));
+static u_short ed_pio_write_mbufs __P((struct ed_softc *, struct mbuf *,
+ int));
+
+static void ed_setrcr(struct ed_softc *);
+static u_long ds_crc(u_char *ep);
+
+#if NCRD > 0
+#include <sys/select.h>
+#include <pccard/card.h>
+#include <pccard/driver.h>
+#include <pccard/slot.h>
+
+/*
+ * PC-Card (PCMCIA) specific code.
+ */
+static int card_intr(struct pccard_dev *); /* Interrupt handler */
+static void edunload(struct pccard_dev *); /* Disable driver */
+static void edsuspend(struct pccard_dev *); /* Suspend driver */
+static int edinit(struct pccard_dev *, int); /* init device */
+
+void edintr_sc __P((struct ed_softc *));
+
+static struct pccard_drv ed_info = {
+ "ed",
+ card_intr,
+ edunload,
+ edsuspend,
+ edinit,
+ 0, /* Attributes - presently unused */
+ &net_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
+ * edinit with first=0. This is called when the user suspends
+ * the system, or the APM code suspends the system.
+ */
+static void
+edsuspend(struct pccard_dev *dp)
+{
+ printf("ed%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.
+ */
+static int
+edinit(struct pccard_dev *dp, int first)
+{
+ struct ed_softc *sc = &ed_softc[dp->isahd.id_unit];
+
+ /* validate unit number. */
+ if (first) {
+ if (dp->isahd.id_unit >= NED)
+ return(ENODEV);
+ /*
+ * Probe the device. If a value is returned, the
+ * device was found at the location.
+ */
+ sc->gone = 0;
+ if (ed_probe_pccard(&dp->isahd,dp->misc)==0)
+ return(ENXIO);
+ if (ed_attach_isa(&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);
+}
+
+/*
+ * edunload - 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
+edunload(struct pccard_dev *dp)
+{
+ struct ed_softc *sc = &ed_softc[dp->isahd.id_unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ if (sc->kdc.kdc_state == DC_UNCONFIGURED) {
+ printf("ed%d: already unloaded\n", dp->isahd.id_unit);
+ return;
+ }
+ sc->kdc.kdc_state = DC_UNCONFIGURED;
+ ifp->if_flags &= ~IFF_RUNNING;
+ if_down(ifp);
+ sc->gone = 1;
+ printf("ed%d: unload\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)
+{
+ edintr_sc(&ed_softc[dp->isahd.id_unit]);
+ return(1);
+}
+#endif /* NCRD > 0 */
+
+#ifdef PC98
+/* LPC-T support */
+#define LPCT_1d0_ON() \
+{ \
+ outb(0x2a8e, 0x84); \
+ outw(0x4a8e, 0x1d0); \
+ outw(0x5a8e, 0x0310); \
+}
+
+#define LPCT_1d0_OFF() \
+{ \
+ outb(0x2a8e, 0xa4); \
+ outw(0x4a8e, 0xd0); \
+ outw(0x5a8e, 0x0300); \
+}
+
+/* register offsets */
+static unsigned int *edp[NED];
+static unsigned int pc98_io_skip[NED];
+static int ed_novell_nic_offset[NED];
+static int ed_novell_asic_offset[NED];
+static int ed_novell_data[NED];
+static int ed_novell_reset[NED];
+
+/* NE2000, LGY-98, ICM, LPC-T */
+static unsigned int edp_generic[16] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+/* EGY-98 */
+static unsigned int edp_egy98[16] = {
+ 0, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+ 0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e
+};
+
+/* LD-BDN */
+static unsigned int edp_bdn98[16] = {
+ 0x00000, 0x01000, 0x02000, 0x03000, 0x04000, 0x05000, 0x06000, 0x07000,
+ 0x08000, 0x0a000, 0x0b000, 0x0c000, 0x0d000, 0x0d000, 0x0e000, 0x0f000
+};
+
+/* SIC-98 */
+static unsigned int edp_sic98[16] = {
+ 0x0000, 0x0200, 0x0400, 0x0600, 0x0800, 0x0a00, 0x0c00, 0x0e00,
+ 0x1000, 0x1200, 0x1400, 0x1600, 0x1800, 0x1a00, 0x1c00, 0x1e00
+};
+
+
+static void pc98_set_register(int unit, int type)
+{
+ switch (type) {
+ case ED_TYPE98_GENERIC:
+ edp[unit] = edp_generic;
+ pc98_io_skip[unit] = 1;
+ ED_NOVELL_NIC_OFFSET = 0x0000;
+ ED_NOVELL_ASIC_OFFSET = 0x0010;
+ ED_NOVELL_DATA = 0x0000;
+ ED_NOVELL_RESET = 0x000f;
+ break;
+
+ case ED_TYPE98_LGY:
+ edp[unit] = edp_generic;
+ pc98_io_skip[unit] = 1;
+ ED_NOVELL_NIC_OFFSET = 0x0000;
+ ED_NOVELL_ASIC_OFFSET = 0x0200;
+ ED_NOVELL_DATA = 0x0000;
+ ED_NOVELL_RESET = 0x0100;
+ break;
+
+ case ED_TYPE98_EGY:
+ edp[unit] = edp_egy98;
+ pc98_io_skip[unit] = 2;
+ ED_NOVELL_NIC_OFFSET = 0;
+ ED_NOVELL_ASIC_OFFSET = 0x0200;
+ ED_NOVELL_DATA = 0x0000;
+ ED_NOVELL_RESET = 0x0100;
+ break;
+
+ case ED_TYPE98_ICM:
+ edp[unit] = edp_generic;
+ pc98_io_skip[unit] = 1;
+ ED_NOVELL_NIC_OFFSET = 0;
+ ED_NOVELL_ASIC_OFFSET = 0x0100;
+ ED_NOVELL_DATA = 0x0000;
+ ED_NOVELL_RESET = 0x000f;
+ break;
+
+ case ED_TYPE98_BDN:
+ edp[unit] = edp_bdn98;
+ pc98_io_skip[unit] = 0x1000;
+ ED_NOVELL_NIC_OFFSET = 0x0000;
+ ED_NOVELL_ASIC_OFFSET = 0x0100;
+ ED_NOVELL_DATA = 0;
+ ED_NOVELL_RESET = 0xc100;
+ break;
+
+ case ED_TYPE98_SIC:
+ edp[unit] = edp_sic98;
+ pc98_io_skip[unit] = 0x200;
+ ED_NOVELL_NIC_OFFSET = 0x0000;
+ ED_NOVELL_ASIC_OFFSET = 0x2000;
+ ED_NOVELL_DATA = 0x00; /* dummy */
+ ED_NOVELL_RESET = 0x00;
+ break;
+
+ case ED_TYPE98_LPC:
+ edp[unit] = edp_generic;
+ pc98_io_skip[unit] = 0x1;
+ ED_NOVELL_NIC_OFFSET = 0x0000;
+ ED_NOVELL_ASIC_OFFSET = 0x0100;
+ ED_NOVELL_DATA = 0x0000;
+ ED_NOVELL_RESET = 0x0200;
+ }
+}
+
+struct pc98_driver eddriver = {
+#else
+struct isa_driver eddriver = {
+#endif
+ ed_probe,
+ ed_attach_isa,
+ "ed",
+ 1 /* We are ultra sensitive */
+};
+
+/*
+ * Interrupt conversion table for WD/SMC ASIC/83C584
+ * (IRQ* are defined in icu.h)
+ */
+static unsigned short ed_intr_mask[] = {
+ IRQ9,
+ IRQ3,
+ IRQ5,
+ IRQ7,
+ IRQ10,
+ IRQ11,
+ IRQ15,
+ IRQ4
+};
+
+/*
+ * Interrupt conversion table for 83C790
+ */
+static unsigned short ed_790_intr_mask[] = {
+ 0,
+ IRQ9,
+ IRQ3,
+ IRQ5,
+ IRQ7,
+ IRQ10,
+ IRQ11,
+ IRQ15
+};
+
+#define ETHER_MIN_LEN 60
+#define ETHER_MAX_LEN 1514
+#define ETHER_ADDR_LEN 6
+#define ETHER_HDR_SIZE 14
+
+static struct kern_devconf kdc_ed_template = {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ "ed", 0, { MDDT_PC98, 0, "net" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "ed", 0, { MDDT_ISA, 0, "net" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "", /* description */
+ DC_CLS_NETIF /* class */
+};
+
+static inline void
+#ifdef PC98
+ed_registerdev(struct pc98_device *id, const char *descr)
+#else
+ed_registerdev(struct isa_device *id, const char *descr)
+#endif
+{
+ struct kern_devconf *kdc = &ed_softc[id->id_unit].kdc;
+ *kdc = kdc_ed_template;
+ kdc->kdc_unit = id->id_unit;
+ kdc->kdc_parentdata = id;
+ kdc->kdc_description = descr;
+ dev_attach(kdc);
+}
+
+/*
+ * Determine if the device is present
+ *
+ * on entry:
+ * a pointer to an isa_device struct
+ * on exit:
+ * NULL if device not found
+ * or # of i/o addresses used (if found)
+ */
+static int
+ed_probe(isa_dev)
+#ifdef PC98
+ struct pc98_device *isa_dev;
+#else
+ struct isa_device *isa_dev;
+#endif
+{
+ int nports;
+
+#if NCRD > 0
+ /*
+ * If PC-Card probe required, then register driver with
+ * slot manager.
+ */
+ pccard_add_driver(&ed_info);
+#endif
+
+#ifndef DEV_LKM
+ ed_registerdev(isa_dev, "Ethernet adapter");
+#endif /* not DEV_LKM */
+
+#ifdef PC98
+ ed_softc[isa_dev->id_unit].unit = isa_dev->id_unit;
+
+ ed_softc[isa_dev->id_unit].type = ED_TYPE98_LPC;
+ pc98_set_register(isa_dev->id_unit, ED_TYPE98_LPC);
+ nports = ed_probe_Novell(isa_dev);
+ if (nports)
+ return (nports);
+
+ ed_softc[isa_dev->id_unit].type = ED_TYPE98_GENERIC;
+ pc98_set_register(isa_dev->id_unit, ED_TYPE98_GENERIC);
+#endif
+
+ nports = ed_probe_WD80x3(isa_dev);
+ if (nports)
+ return (nports);
+
+ nports = ed_probe_3Com(isa_dev);
+ if (nports)
+ return (nports);
+
+ nports = ed_probe_Novell(isa_dev);
+ if (nports)
+ return (nports);
+
+ ed_softc[isa_dev->id_unit].type = ED_TYPE98_SIC;
+ pc98_set_register(isa_dev->id_unit, ED_TYPE98_SIC);
+ nports = ed_probe_SIC98(isa_dev);
+ if (nports)
+ return (nports);
+
+ ed_softc[isa_dev->id_unit].type = ED_TYPE98_BDN;
+ pc98_set_register(isa_dev->id_unit, ED_TYPE98_BDN);
+ nports = ed_probe_Novell(isa_dev);
+ if (nports)
+ return (nports);
+
+ ed_softc[isa_dev->id_unit].type = ED_TYPE98_LGY;
+ pc98_set_register(isa_dev->id_unit, ED_TYPE98_LGY);
+ nports = ed_probe_Novell(isa_dev);
+ if (nports)
+ return (nports);
+
+ ed_softc[isa_dev->id_unit].type = ED_TYPE98_ICM;
+ pc98_set_register(isa_dev->id_unit, ED_TYPE98_ICM);
+ nports = ed_probe_Novell(isa_dev);
+ if (nports)
+ return (nports);
+
+ ed_softc[isa_dev->id_unit].type = ED_TYPE98_EGY;
+ pc98_set_register(isa_dev->id_unit, ED_TYPE98_EGY);
+ nports = ed_probe_Novell(isa_dev);
+ if (nports)
+ return (nports);
+
+ return (0);
+}
+
+/*
+ * Generic probe routine for testing for the existance of a DS8390.
+ * Must be called after the NIC has just been reset. This routine
+ * works by looking at certain register values that are guaranteed
+ * to be initialized a certain way after power-up or reset. Seems
+ * not to currently work on the 83C690.
+ *
+ * Specifically:
+ *
+ * Register reset bits set bits
+ * Command Register (CR) TXP, STA RD2, STP
+ * Interrupt Status (ISR) RST
+ * Interrupt Mask (IMR) All bits
+ * Data Control (DCR) LAS
+ * Transmit Config. (TCR) LB1, LB0
+ *
+ * We only look at the CR and ISR registers, however, because looking at
+ * the others would require changing register pages (which would be
+ * intrusive if this isn't an 8390).
+ *
+ * Return 1 if 8390 was found, 0 if not.
+ */
+
+static int
+ed_probe_generic8390(sc)
+ struct ed_softc *sc;
+{
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC) {
+ if ((inb(sc->nic_addr + ED_P0_CR) &
+ (ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) !=
+ (ED_CR_RD2 | ED_CR_STP | ED_CR_STA))
+ return (0);
+ } else {
+#endif
+ if ((inb(sc->nic_addr + ED_P0_CR) &
+ (ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) !=
+ (ED_CR_RD2 | ED_CR_STP))
+ return (0);
+#ifdef PC98
+ }
+#endif
+ if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Probe and vendor-specific initialization routine for SMC/WD80x3 boards
+ */
+static int
+ed_probe_WD80x3(isa_dev)
+#ifdef PC98
+ struct pc98_device *isa_dev;
+#else
+ struct isa_device *isa_dev;
+#endif
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ int i;
+ u_int memsize;
+ u_char iptr, isa16bit, sum;
+
+ sc->asic_addr = isa_dev->id_iobase;
+ sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET;
+ sc->is790 = 0;
+
+#ifdef TOSH_ETHER
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_POW);
+ DELAY(10000);
+#endif
+
+ /*
+ * Attempt to do a checksum over the station address PROM. If it
+ * fails, it's probably not a SMC/WD board. There is a problem with
+ * this, though: some clone WD boards don't pass the checksum test.
+ * Danpex boards for one.
+ */
+ for (sum = 0, i = 0; i < 8; ++i)
+ sum += inb(sc->asic_addr + ED_WD_PROM + i);
+
+ if (sum != ED_WD_ROM_CHECKSUM_TOTAL) {
+
+ /*
+ * Checksum is invalid. This often happens with cheap WD8003E
+ * clones. In this case, the checksum byte (the eighth byte)
+ * seems to always be zero.
+ */
+ if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E ||
+ inb(sc->asic_addr + ED_WD_PROM + 7) != 0)
+ return (0);
+ }
+ /* reset card to force it into a known state. */
+#ifdef TOSH_ETHER
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW);
+#else
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST);
+#endif
+ DELAY(100);
+ outb(sc->asic_addr + ED_WD_MSR, inb(sc->asic_addr + ED_WD_MSR) & ~ED_WD_MSR_RST);
+ /* wait in the case this card is reading it's EEROM */
+ DELAY(5000);
+
+ sc->vendor = ED_VENDOR_WD_SMC;
+ sc->type = inb(sc->asic_addr + ED_WD_CARD_ID);
+
+ /*
+ * Set initial values for width/size.
+ */
+ memsize = 8192;
+ isa16bit = 0;
+ switch (sc->type) {
+ case ED_TYPE_WD8003S:
+ sc->type_str = "WD8003S";
+ sc->kdc.kdc_description = "Ethernet adapter: WD 8003S";
+ break;
+ case ED_TYPE_WD8003E:
+ sc->type_str = "WD8003E";
+ sc->kdc.kdc_description = "Ethernet adapter: WD 8003E";
+ break;
+ case ED_TYPE_WD8003EB:
+ sc->type_str = "WD8003EB";
+ sc->kdc.kdc_description = "Ethernet adapter: WD 8003EB";
+ break;
+ case ED_TYPE_WD8003W:
+ sc->type_str = "WD8003W";
+ sc->kdc.kdc_description = "Ethernet adapter: WD 8003W";
+ break;
+ case ED_TYPE_WD8013EBT:
+ sc->type_str = "WD8013EBT";
+ sc->kdc.kdc_description = "Ethernet adapter: WD 8013EBT";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013W:
+ sc->type_str = "WD8013W";
+ sc->kdc.kdc_description = "Ethernet adapter: WD 8013W";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EP: /* also WD8003EP */
+ if (inb(sc->asic_addr + ED_WD_ICR)
+ & ED_WD_ICR_16BIT) {
+ isa16bit = 1;
+ memsize = 16384;
+ sc->type_str = "WD8013EP";
+ sc->kdc.kdc_description =
+ "Ethernet adapter: WD 8013EP";
+ } else {
+ sc->type_str = "WD8003EP";
+ sc->kdc.kdc_description =
+ "Ethernet adapter: WD 8003EP";
+ }
+ break;
+ case ED_TYPE_WD8013WC:
+ sc->type_str = "WD8013WC";
+ sc->kdc.kdc_description = "Ethernet adapter: WD 8013WC";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EBP:
+ sc->type_str = "WD8013EBP";
+ sc->kdc.kdc_description = "Ethernet adapter: WD 8013EBP";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_WD8013EPC:
+ sc->type_str = "WD8013EPC";
+ sc->kdc.kdc_description = "Ethernet adapter: WD 8013EPC";
+ memsize = 16384;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_SMC8216C: /* 8216 has 16K shared mem -- 8416 has 8K */
+ case ED_TYPE_SMC8216T:
+ if (sc->type == ED_TYPE_SMC8216C) {
+ sc->type_str = "SMC8216/SMC8216C";
+ sc->kdc.kdc_description =
+ "Ethernet adapter: SMC 8216 or 8216C";
+ } else {
+ sc->type_str = "SMC8216T";
+ sc->kdc.kdc_description =
+ "Ethernet adapter: SMC 8216T";
+ }
+
+ outb(sc->asic_addr + ED_WD790_HWR,
+ inb(sc->asic_addr + ED_WD790_HWR) | ED_WD790_HWR_SWH);
+ switch (inb(sc->asic_addr + ED_WD790_RAR) & ED_WD790_RAR_SZ64) {
+ case ED_WD790_RAR_SZ64:
+ memsize = 65536;
+ break;
+ case ED_WD790_RAR_SZ32:
+ memsize = 32768;
+ break;
+ case ED_WD790_RAR_SZ16:
+ memsize = 16384;
+ break;
+ case ED_WD790_RAR_SZ8:
+ /* 8216 has 16K shared mem -- 8416 has 8K */
+ if (sc->type == ED_TYPE_SMC8216C) {
+ sc->type_str = "SMC8416C/SMC8416BT";
+ sc->kdc.kdc_description =
+ "Ethernet adapter: SMC 8416C or 8416BT";
+ } else {
+ sc->type_str = "SMC8416T";
+ sc->kdc.kdc_description =
+ "Ethernet adapter: SMC 8416T";
+ }
+ memsize = 8192;
+ break;
+ }
+ outb(sc->asic_addr + ED_WD790_HWR,
+ inb(sc->asic_addr + ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
+
+ isa16bit = 1;
+ sc->is790 = 1;
+ break;
+#ifdef TOSH_ETHER
+ case ED_TYPE_TOSHIBA1:
+ sc->type_str = "Toshiba1";
+ sc->kdc.kdc_description = "Ethernet adapter: Toshiba1";
+ memsize = 32768;
+ isa16bit = 1;
+ break;
+ case ED_TYPE_TOSHIBA4:
+ sc->type_str = "Toshiba4";
+ sc->kdc.kdc_description = "Ethernet adapter: Toshiba4";
+ memsize = 32768;
+ isa16bit = 1;
+ break;
+#endif
+ default:
+ sc->type_str = "";
+ break;
+ }
+
+ /*
+ * Make some adjustments to initial values depending on what is found
+ * in the ICR.
+ */
+ if (isa16bit && (sc->type != ED_TYPE_WD8013EBT)
+#ifdef TOSH_ETHER
+ && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4)
+#endif
+ && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) {
+ isa16bit = 0;
+ memsize = 8192;
+ }
+
+#if ED_DEBUG
+ printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n",
+ sc->type, sc->type_str, isa16bit, memsize, isa_dev->id_msize);
+ for (i = 0; i < 8; i++)
+ printf("%x -> %x\n", i, inb(sc->asic_addr + i));
+#endif
+
+ /*
+ * Allow the user to override the autoconfiguration
+ */
+ if (isa_dev->id_msize)
+ memsize = isa_dev->id_msize;
+
+ /*
+ * (note that if the user specifies both of the following flags that
+ * '8bit' mode intentionally has precedence)
+ */
+ if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE)
+ isa16bit = 1;
+ if (isa_dev->id_flags & ED_FLAGS_FORCE_8BIT_MODE)
+ isa16bit = 0;
+
+ /*
+ * If possible, get the assigned interrupt number from the card and
+ * use it.
+ */
+ if ((sc->type & ED_WD_SOFTCONFIG) && (!sc->is790)) {
+
+ /*
+ * Assemble together the encoded interrupt number.
+ */
+ iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) |
+ ((inb(isa_dev->id_iobase + ED_WD_IRR) &
+ (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5);
+
+ /*
+ * If no interrupt specified (or "?"), use what the board tells us.
+ */
+ if (isa_dev->id_irq <= 0)
+ isa_dev->id_irq = ed_intr_mask[iptr];
+
+ /*
+ * Enable the interrupt.
+ */
+ outb(isa_dev->id_iobase + ED_WD_IRR,
+ inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN);
+ }
+ if (sc->is790) {
+ outb(isa_dev->id_iobase + ED_WD790_HWR,
+ inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH);
+ iptr = (((inb(isa_dev->id_iobase + ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) |
+ (inb(isa_dev->id_iobase + ED_WD790_GCR) &
+ (ED_WD790_GCR_IR1 | ED_WD790_GCR_IR0)) >> 2);
+ outb(isa_dev->id_iobase + ED_WD790_HWR,
+ inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH);
+
+ /*
+ * If no interrupt specified (or "?"), use what the board tells us.
+ */
+ if (isa_dev->id_irq <= 0)
+ isa_dev->id_irq = ed_790_intr_mask[iptr];
+
+ /*
+ * Enable interrupts.
+ */
+ outb(isa_dev->id_iobase + ED_WD790_ICR,
+ inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL);
+ }
+ if (isa_dev->id_irq <= 0) {
+ printf("ed%d: %s cards don't support auto-detected/assigned interrupts.\n",
+ isa_dev->id_unit, sc->type_str);
+ return (0);
+ }
+ sc->isa16bit = isa16bit;
+ sc->mem_shared = 1;
+ isa_dev->id_msize = memsize;
+ sc->mem_start = (caddr_t) isa_dev->id_maddr;
+
+ /*
+ * allocate one xmit buffer if < 16k, two buffers otherwise
+ */
+ if ((memsize < 16384) ||
+ (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) {
+ sc->txb_cnt = 1;
+ } else {
+ sc->txb_cnt = 2;
+ }
+ sc->tx_page_start = ED_WD_PAGE_OFFSET;
+ sc->rec_page_start = ED_WD_PAGE_OFFSET + ED_TXBUF_SIZE * sc->txb_cnt;
+ sc->rec_page_stop = ED_WD_PAGE_OFFSET + memsize / ED_PAGE_SIZE;
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * sc->rec_page_start);
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+
+ /*
+ * Get station address from on-board ROM
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i);
+
+ /*
+ * Set upper address bits and 8/16 bit access to shared memory
+ */
+ if (isa16bit) {
+ if (sc->is790) {
+ sc->wd_laar_proto = inb(sc->asic_addr + ED_WD_LAAR);
+ outb(sc->asic_addr + ED_WD_LAAR, ED_WD_LAAR_M16EN);
+ } else {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto =
+ ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN |
+ ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI)));
+ }
+ } else {
+ if (((sc->type & ED_WD_SOFTCONFIG) ||
+#ifdef TOSH_ETHER
+ (sc->type == ED_TYPE_TOSHIBA1) || (sc->type == ED_TYPE_TOSHIBA4) ||
+#endif
+ (sc->type == ED_TYPE_WD8013EBT)) && (!sc->is790)) {
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto =
+ ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI)));
+ }
+ }
+
+ /*
+ * Set address and enable interface shared memory.
+ */
+ if (!sc->is790) {
+#ifdef TOSH_ETHER
+ outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4);
+ outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f));
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW);
+
+#else
+ outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) &
+ ED_WD_MSR_ADDR) | ED_WD_MSR_MENB);
+#endif
+ sc->cr_proto = ED_CR_RD2;
+ } else {
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+ outb(sc->asic_addr + ED_WD790_HWR, (inb(sc->asic_addr + ED_WD790_HWR) | ED_WD790_HWR_SWH));
+ outb(sc->asic_addr + ED_WD790_RAR, ((kvtop(sc->mem_start) >> 13) & 0x0f) |
+ ((kvtop(sc->mem_start) >> 11) & 0x40) |
+ (inb(sc->asic_addr + ED_WD790_RAR) & 0xb0));
+ outb(sc->asic_addr + ED_WD790_HWR, (inb(sc->asic_addr + ED_WD790_HWR) & ~ED_WD790_HWR_SWH));
+ sc->cr_proto = 0;
+ }
+
+#if 0
+ printf("starting memory performance test at 0x%x, size %d...\n",
+ sc->mem_start, memsize*16384);
+ for (i = 0; i < 16384; i++)
+ bzero(sc->mem_start, memsize);
+ printf("***DONE***\n");
+#endif
+
+ /*
+ * Now zero memory and verify that it is clear
+ */
+ bzero(sc->mem_start, memsize);
+
+ for (i = 0; i < memsize; ++i) {
+ if (sc->mem_start[i]) {
+ printf("ed%d: failed to clear shared memory at %lx - check configuration\n",
+ isa_dev->id_unit, kvtop(sc->mem_start + i));
+
+ /*
+ * Disable 16 bit access to shared memory
+ */
+ if (isa16bit) {
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ }
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ }
+ return (0);
+ }
+ }
+
+ /*
+ * Disable 16bit access to shared memory - we leave it
+ * disabled so that 1) machines reboot properly when the board
+ * is set 16 bit mode and there are conflicting 8bit
+ * devices/ROMS in the same 128k address space as this boards
+ * shared memory. and 2) so that other 8 bit devices with
+ * shared memory can be used in this 128k region, too.
+ */
+ if (isa16bit) {
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ }
+ outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ }
+ return (ED_WD_IO_PORTS);
+}
+
+/*
+ * Probe and vendor-specific initialization routine for 3Com 3c503 boards
+ */
+static int
+ed_probe_3Com(isa_dev)
+#ifdef PC98
+ struct pc98_device *isa_dev;
+#else
+ struct isa_device *isa_dev;
+#endif
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ int i;
+ u_int memsize;
+ u_char isa16bit;
+#ifdef PC98
+ int unit = isa_dev->id_unit;
+#endif
+
+ sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET;
+ sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET;
+
+ /*
+ * Verify that the kernel configured I/O address matches the board
+ * configured address
+ */
+ switch (inb(sc->asic_addr + ED_3COM_BCFR)) {
+ case ED_3COM_BCFR_300:
+ if (isa_dev->id_iobase != 0x300)
+ return (0);
+ break;
+ case ED_3COM_BCFR_310:
+ if (isa_dev->id_iobase != 0x310)
+ return (0);
+ break;
+ case ED_3COM_BCFR_330:
+ if (isa_dev->id_iobase != 0x330)
+ return (0);
+ break;
+ case ED_3COM_BCFR_350:
+ if (isa_dev->id_iobase != 0x350)
+ return (0);
+ break;
+ case ED_3COM_BCFR_250:
+ if (isa_dev->id_iobase != 0x250)
+ return (0);
+ break;
+ case ED_3COM_BCFR_280:
+ if (isa_dev->id_iobase != 0x280)
+ return (0);
+ break;
+ case ED_3COM_BCFR_2A0:
+ if (isa_dev->id_iobase != 0x2a0)
+ return (0);
+ break;
+ case ED_3COM_BCFR_2E0:
+ if (isa_dev->id_iobase != 0x2e0)
+ return (0);
+ break;
+ default:
+ return (0);
+ }
+
+ /*
+ * Verify that the kernel shared memory address matches the board
+ * configured address.
+ */
+ switch (inb(sc->asic_addr + ED_3COM_PCFR)) {
+ case ED_3COM_PCFR_DC000:
+ if (kvtop(isa_dev->id_maddr) != 0xdc000)
+ return (0);
+ break;
+ case ED_3COM_PCFR_D8000:
+ if (kvtop(isa_dev->id_maddr) != 0xd8000)
+ return (0);
+ break;
+ case ED_3COM_PCFR_CC000:
+ if (kvtop(isa_dev->id_maddr) != 0xcc000)
+ return (0);
+ break;
+ case ED_3COM_PCFR_C8000:
+ if (kvtop(isa_dev->id_maddr) != 0xc8000)
+ return (0);
+ break;
+ default:
+ return (0);
+ }
+
+
+ /*
+ * Reset NIC and ASIC. Enable on-board transceiver throughout reset
+ * sequence because it'll lock up if the cable isn't connected if we
+ * don't.
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL);
+
+ /*
+ * Wait for a while, then un-reset it
+ */
+ DELAY(50);
+
+ /*
+ * The 3Com ASIC defaults to rather strange settings for the CR after
+ * a reset - it's important to set it again after the following outb
+ * (this is done when we map the PROM below).
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+
+ /*
+ * Wait a bit for the NIC to recover from the reset
+ */
+ DELAY(5000);
+
+ sc->vendor = ED_VENDOR_3COM;
+ sc->type_str = "3c503";
+ sc->kdc.kdc_description = "Ethernet adapter: 3c503";
+ sc->mem_shared = 1;
+ sc->cr_proto = ED_CR_RD2;
+
+ /*
+ * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k window
+ * to it.
+ */
+ memsize = 8192;
+
+ /*
+ * Get station address from on-board ROM
+ */
+
+ /*
+ * First, map ethernet address PROM over the top of where the NIC
+ * registers normally appear.
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL);
+
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = inb(sc->nic_addr + i);
+
+ /*
+ * Unmap PROM - select NIC registers. The proper setting of the
+ * tranceiver is set in ed_init so that the attach code is given a
+ * chance to set the default based on a compile-time config option
+ */
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+
+ /*
+ * Determine if this is an 8bit or 16bit board
+ */
+
+ /*
+ * select page 0 registers
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
+
+ /*
+ * Attempt to clear WTS bit. If it doesn't clear, then this is a 16bit
+ * board.
+ */
+ outb(sc->nic_addr + ED_P0_DCR, 0);
+
+ /*
+ * select page 2 registers
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2 | ED_CR_RD2 | ED_CR_STP);
+
+ /*
+ * The 3c503 forces the WTS bit to a one if this is a 16bit board
+ */
+ if (inb(sc->nic_addr + ED_P2_DCR) & ED_DCR_WTS)
+ isa16bit = 1;
+ else
+ isa16bit = 0;
+
+ /*
+ * select page 0 registers
+ */
+ outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2 | ED_CR_STP);
+
+ sc->mem_start = (caddr_t) isa_dev->id_maddr;
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+
+ /*
+ * We have an entire 8k window to put the transmit buffers on the
+ * 16bit boards. But since the 16bit 3c503's shared memory is only
+ * fast enough to overlap the loading of one full-size packet, trying
+ * to load more than 2 buffers can actually leave the transmitter idle
+ * during the load. So 2 seems the best value. (Although a mix of
+ * variable-sized packets might change this assumption. Nonetheless,
+ * we optimize for linear transfers of same-size packets.)
+ */
+ if (isa16bit) {
+ if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)
+ sc->txb_cnt = 1;
+ else
+ sc->txb_cnt = 2;
+
+ sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT;
+ sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT;
+ sc->rec_page_stop = memsize / ED_PAGE_SIZE +
+ ED_3COM_RX_PAGE_OFFSET_16BIT;
+ sc->mem_ring = sc->mem_start;
+ } else {
+ sc->txb_cnt = 1;
+ sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT;
+ sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT;
+ sc->rec_page_stop = memsize / ED_PAGE_SIZE +
+ ED_3COM_TX_PAGE_OFFSET_8BIT;
+ sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE);
+ }
+
+ sc->isa16bit = isa16bit;
+
+ /*
+ * Initialize GA page start/stop registers. Probably only needed if
+ * doing DMA, but what the hell.
+ */
+ outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start);
+ outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop);
+
+ /*
+ * Set IRQ. 3c503 only allows a choice of irq 2-5.
+ */
+ switch (isa_dev->id_irq) {
+ case IRQ2:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ2);
+ break;
+ case IRQ3:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ3);
+ break;
+ case IRQ4:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ4);
+ break;
+ case IRQ5:
+ outb(sc->asic_addr + ED_3COM_IDCFR, ED_3COM_IDCFR_IRQ5);
+ break;
+ default:
+ printf("ed%d: Invalid irq configuration (%d) must be 3-5,9 for 3c503\n",
+ isa_dev->id_unit, ffs(isa_dev->id_irq) - 1);
+ return (0);
+ }
+
+ /*
+ * Initialize GA configuration register. Set bank and enable shared
+ * mem.
+ */
+ outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL |
+ ED_3COM_GACFR_MBS0);
+
+ /*
+ * Initialize "Vector Pointer" registers. These gawd-awful things are
+ * compared to 20 bits of the address on ISA, and if they match, the
+ * shared memory is disabled. We set them to 0xffff0...allegedly the
+ * reset vector.
+ */
+ outb(sc->asic_addr + ED_3COM_VPTR2, 0xff);
+ outb(sc->asic_addr + ED_3COM_VPTR1, 0xff);
+ outb(sc->asic_addr + ED_3COM_VPTR0, 0x00);
+
+ /*
+ * Zero memory and verify that it is clear
+ */
+ bzero(sc->mem_start, memsize);
+
+ for (i = 0; i < memsize; ++i)
+ if (sc->mem_start[i]) {
+ printf("ed%d: failed to clear shared memory at %lx - check configuration\n",
+ isa_dev->id_unit, kvtop(sc->mem_start + i));
+ return (0);
+ }
+ isa_dev->id_msize = memsize;
+ return (ED_3COM_IO_PORTS);
+}
+
+/*
+ * Probe and vendor-specific initialization routine for NE1000/2000 boards
+ */
+static int
+ed_probe_Novell_generic(sc, port, unit, flags)
+ struct ed_softc *sc;
+ int port;
+ int unit;
+ int flags;
+{
+ u_int memsize, n;
+#ifdef PC98
+ u_char romdata[16], tmp, st1d01;
+#else
+ u_char romdata[16], tmp;
+#endif
+ static char test_pattern[32] = "THIS is A memory TEST pattern";
+ char test_buffer[32];
+
+ sc->asic_addr = port + ED_NOVELL_ASIC_OFFSET;
+ sc->nic_addr = port + ED_NOVELL_NIC_OFFSET;
+
+ /* XXX - do Novell-specific probe here */
+
+ /* Reset the board */
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_ON();
+#endif
+#ifdef GWETHER
+ outb(sc->asic_addr + ED_NOVELL_RESET, 0);
+ DELAY(200);
+#endif /* GWETHER */
+#ifdef PC98
+ switch (sc->type) {
+ case ED_TYPE98_BDN:
+ st1d01 = inb(sc->nic_addr + ED_NOVELL_RESET);
+ outb(sc->asic_addr + 0xc000, st1d01 & 0xf0 | 0x08);
+ outb(sc->nic_addr + 0x4000, st1d01);
+ tmp = inb(sc->asic_addr + 0x8000);
+ outb(sc->asic_addr + 0x8000, st1d01);
+ outb(sc->asic_addr + 0x8000, st1d01 & 0x7f);
+ break;
+ default:
+ tmp = inb(sc->asic_addr + ED_NOVELL_RESET);
+ }
+#else
+ tmp = inb(sc->asic_addr + ED_NOVELL_RESET);
+#endif
+ /*
+ * I don't know if this is necessary; probably cruft leftover from
+ * Clarkson packet driver code. Doesn't do a thing on the boards I've
+ * tested. -DG [note that a outb(0x84, 0) seems to work here, and is
+ * non-invasive...but some boards don't seem to reset and I don't have
+ * complete documentation on what the 'right' thing to do is...so we
+ * do the invasive thing for now. Yuck.]
+ */
+#ifdef PC98
+ if (sc->type != ED_TYPE98_BDN)
+#endif
+ outb(sc->asic_addr + ED_NOVELL_RESET, tmp);
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_OFF();
+#endif
+
+ DELAY(5000);
+
+ /*
+ * This is needed because some NE clones apparently don't reset the
+ * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX
+ * - this makes the probe invasive! ...Done against my better
+ * judgement. -DLG
+ */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
+
+ DELAY(5000);
+
+ /* Make sure that we really have an 8390 based board */
+ if (!ed_probe_generic8390(sc))
+ return (0);
+
+ sc->vendor = ED_VENDOR_NOVELL;
+ sc->mem_shared = 0;
+ sc->cr_proto = ED_CR_RD2;
+
+ /*
+ * Test the ability to read and write to the NIC memory. This has the
+ * side affect of determining if this is an NE1000 or an NE2000.
+ */
+
+ /*
+ * This prevents packets from being stored in the NIC memory when the
+ * readmem routine turns on the start bit in the CR.
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON);
+
+ /* Temporarily initialize DCR for byte operations */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
+
+ outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE);
+ outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE);
+
+ sc->isa16bit = 0;
+
+ /*
+ * Write a test pattern in byte mode. If this fails, then there
+ * probably isn't any memory at 8k - which likely means that the board
+ * is an NE2000.
+ */
+ ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern));
+ ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern));
+
+ if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) {
+ /* not an NE1000 - try NE2000 */
+
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS | ED_DCR_FT1 | ED_DCR_LS);
+ outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE);
+ outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE);
+
+ sc->isa16bit = 1;
+
+ /*
+ * Write a test pattern in word mode. If this also fails, then
+ * we don't know what this board is.
+ */
+ ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern));
+ ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern));
+
+ if (bcmp(test_pattern, test_buffer, sizeof(test_pattern)))
+ return (0); /* not an NE2000 either */
+
+#ifndef PC98
+ sc->type = ED_TYPE_NE2000;
+ sc->type_str = "NE2000";
+ sc->kdc.kdc_description = "Ethernet adapter: NE2000";
+ } else {
+ sc->type = ED_TYPE_NE1000;
+ sc->type_str = "NE1000";
+ sc->kdc.kdc_description = "Ethernet adapter: NE1000";
+#else
+ }
+ switch (sc->type) {
+ case ED_TYPE98_GENERIC:
+ sc->type_str = "NE2000";
+ sc->kdc.kdc_description = "Ethernet adapter: NE2000";
+ break;
+ case ED_TYPE98_LGY:
+ sc->type_str = "LGY-98";
+ sc->kdc.kdc_description = "Ethernet adapter: LGY-98";
+ break;
+ case ED_TYPE98_EGY:
+ sc->type_str = "EGY-98";
+ sc->kdc.kdc_description = "Ethernet adapter: EGY-98";
+ break;
+ case ED_TYPE98_ICM:
+ sc->type_str = "ICM";
+ sc->kdc.kdc_description = "Ethernet adapter: ICM";
+ break;
+ case ED_TYPE98_BDN:
+ sc->type_str = "LD-BDN";
+ sc->kdc.kdc_description = "Ethernet adapter: LD-BDN";
+ break;
+ default:
+ sc->type_str = "Unknown";
+ sc->kdc.kdc_description = "Ethernet adapter: Unkonwn";
+ break;
+#endif
+ }
+
+ /* 8k of memory plus an additional 8k if 16bit */
+ memsize = 8192 + sc->isa16bit * 8192;
+
+#if 0 /* probably not useful - NE boards only come two ways */
+ /* allow kernel config file overrides */
+ if (isa_dev->id_msize)
+ memsize = isa_dev->id_msize;
+#endif
+
+ sc->mem_size = memsize;
+
+ /* NIC memory doesn't start at zero on an NE board */
+ /* The start address is tied to the bus width */
+ sc->mem_start = (char *) 8192 + sc->isa16bit * 8192;
+ sc->mem_end = sc->mem_start + memsize;
+ sc->tx_page_start = memsize / ED_PAGE_SIZE;
+
+#ifdef GWETHER
+ {
+ int x, i, mstart = 0, msize = 0;
+ char pbuf0[ED_PAGE_SIZE], pbuf[ED_PAGE_SIZE], tbuf[ED_PAGE_SIZE];
+
+ for (i = 0; i < ED_PAGE_SIZE; i++)
+ pbuf0[i] = 0;
+
+ /* Clear all the memory. */
+ for (x = 1; x < 256; x++)
+ ed_pio_writemem(sc, pbuf0, x * 256, ED_PAGE_SIZE);
+
+ /* Search for the start of RAM. */
+ for (x = 1; x < 256; x++) {
+ ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE);
+ if (bcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) {
+ for (i = 0; i < ED_PAGE_SIZE; i++)
+ pbuf[i] = 255 - x;
+ ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE);
+ ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE);
+ if (bcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) {
+ mstart = x * ED_PAGE_SIZE;
+ msize = ED_PAGE_SIZE;
+ break;
+ }
+ }
+ }
+
+ if (mstart == 0) {
+ printf("ed%d: Cannot find start of RAM.\n", unit);
+ return 0;
+ }
+ /* Search for the start of RAM. */
+ for (x = (mstart / ED_PAGE_SIZE) + 1; x < 256; x++) {
+ ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE);
+ if (bcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) {
+ for (i = 0; i < ED_PAGE_SIZE; i++)
+ pbuf[i] = 255 - x;
+ ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE);
+ ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE);
+ if (bcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0)
+ msize += ED_PAGE_SIZE;
+ else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (msize == 0) {
+ printf("ed%d: Cannot find any RAM, start : %d, x = %d.\n", unit, mstart, x);
+ return 0;
+ }
+ printf("ed%d: RAM start at %d, size : %d.\n", unit, mstart, msize);
+
+ sc->mem_size = msize;
+ sc->mem_start = (char *) mstart;
+ sc->mem_end = (char *) (msize + mstart);
+ sc->tx_page_start = mstart / ED_PAGE_SIZE;
+ }
+#endif /* GWETHER */
+
+ /*
+ * Use one xmit buffer if < 16k, two buffers otherwise (if not told
+ * otherwise).
+ */
+ if ((memsize < 16384) || (flags & ED_FLAGS_NO_MULTI_BUFFERING))
+ sc->txb_cnt = 1;
+ else
+ sc->txb_cnt = 2;
+
+ sc->rec_page_start = sc->tx_page_start + sc->txb_cnt * ED_TXBUF_SIZE;
+ sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE;
+
+ sc->mem_ring = sc->mem_start + sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE;
+
+ ed_pio_readmem(sc, 0, romdata, 16);
+ for (n = 0; n < ETHER_ADDR_LEN; n++)
+ sc->arpcom.ac_enaddr[n] = romdata[n * (sc->isa16bit + 1)];
+
+#ifdef GWETHER
+ if (sc->arpcom.ac_enaddr[2] == 0x86) {
+ sc->type_str = "Gateway AT";
+ sc->kdc.kdc_description = "Ethernet adapter: Gateway AT";
+ }
+#endif /* GWETHER */
+
+ /* clear any pending interrupts that might have occurred above */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+
+ return (ED_NOVELL_IO_PORTS);
+}
+
+static int
+ed_probe_Novell(isa_dev)
+#ifdef PC98
+ struct pc98_device *isa_dev;
+#else
+ struct isa_device *isa_dev;
+#endif
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+
+#ifndef PC98
+ isa_dev->id_maddr = 0;
+#endif
+ return ed_probe_Novell_generic(sc, isa_dev->id_iobase,
+ isa_dev->id_unit, isa_dev->id_flags);
+}
+
+#if NCRD > 0
+
+/*
+ * Probe and vendor-specific initialization routine for PCCARDs
+ */
+static int
+ed_probe_pccard(isa_dev, ether)
+#ifdef PC98
+ struct pc98_device *isa_dev;
+#else
+ struct isa_device *isa_dev;
+#endif
+ u_char *ether;
+{
+ struct ed_softc *sc = &ed_softc[isa_dev->id_unit];
+ int i;
+ u_int memsize;
+ u_char isa16bit;
+#ifdef PC98
+ int unit = isa_dev->id_unit;
+#endif
+
+ sc->nic_addr = isa_dev->id_iobase;
+ sc->gone = 0;
+ sc->is790 = 0;
+ sc->cr_proto = ED_CR_RD2;
+ sc->vendor = ED_VENDOR_PCCARD;
+ sc->type = 0;
+ sc->type_str = "PCCARD";
+ sc->kdc.kdc_description = "PCCARD Ethernet";
+ sc->mem_size = isa_dev->id_msize = memsize = 16384;
+ sc->isa16bit = isa16bit = 1;
+
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ sc->arpcom.ac_enaddr[i] = ether[i];
+
+#if ED_DEBUG
+ printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n",
+ sc->type, sc->type_str, isa16bit, memsize, isa_dev->id_msize);
+#endif
+
+ i = inb(sc->nic_addr + ED_PC_RESET);
+ DELAY(100000);
+ outb(sc->nic_addr + ED_PC_RESET,i);
+ DELAY(100000);
+ i = inb(sc->nic_addr + ED_PC_MISC);
+ if (!i) {
+ int j;
+ printf("ed_probe_pccard: possible failure\n");
+ for (j=0;j<20 && !i;j++) {
+ printf(".");
+ DELAY(100000);
+ i = inb(sc->nic_addr + ED_PC_MISC);
+ }
+ if (!i) {
+ printf("dead :-(\n");
+ return 0;
+ }
+ printf("\n");
+ }
+ /*
+ * Set initial values for width/size.
+ */
+
+ /* Make sure that we really have an 8390 based board */
+ if (!ed_probe_generic8390(sc)) {
+ printf("ed_probe_generic8390 failed\n");
+ return (0);
+ }
+ sc->txb_cnt = 2;
+ sc->tx_page_start = ED_PC_PAGE_OFFSET;
+ sc->rec_page_start = sc->tx_page_start + ED_TXBUF_SIZE * sc->txb_cnt;
+ sc->rec_page_stop = sc->tx_page_start + memsize / ED_PAGE_SIZE;
+
+ sc->mem_shared = 1;
+ sc->mem_start = (caddr_t) isa_dev->id_maddr;
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+
+ sc->mem_ring = sc->mem_start +
+ sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE;
+
+ /*
+ * Now zero memory and verify that it is clear
+ */
+ bzero(sc->mem_start, memsize);
+
+ for (i = 0; i < memsize; ++i) {
+ if (sc->mem_start[i]) {
+ printf("ed%d: failed to clear shared memory at %lx - check configuration\n",
+ isa_dev->id_unit, kvtop(sc->mem_start + i));
+
+ return (0);
+ }
+ sc->mem_start[i] = (i - 5) & 0xff;
+ }
+ for (i = 0; i < memsize; ++i) {
+ if ((sc->mem_start[i] & 0xff) != ((i - 5) & 0xff)) {
+ printf("ed%d: shared memory failed at %lx (%x != %x) - check configuration\n",
+ isa_dev->id_unit, kvtop(sc->mem_start + i),
+ sc->mem_start[i], (i-5) & 0xff);
+ return (0);
+
+ }
+ }
+
+ i = inb(sc->nic_addr + ED_PC_MISC);
+ if (!i) {
+ printf("ed_probe_pccard: possible failure(2)\n");
+ }
+
+ /* clear any pending interupts that we may have caused */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+
+ return (ED_PC_IO_PORTS);
+}
+
+#endif /* NCRD > 0 */
+
+
+#ifdef PC98
+static int ed_probe_SIC98(struct pc98_device* pc98_dev)
+{
+ int i;
+ struct ed_softc *sc = &ed_softc[pc98_dev->id_unit];
+ u_char sum;
+ u_int memsize;
+ int unit = pc98_dev->id_unit;
+
+ if ((pc98_dev->id_maddr == 0) || (pc98_dev->id_msize == 0))
+ return 0;
+
+ /* Setup card RAM and I/O address
+ * Kernel Veirtual to segment C0000-DFFFF????
+ */
+ sc->asic_addr = pc98_dev->id_iobase + ED_NOVELL_ASIC_OFFSET;
+ sc->nic_addr = pc98_dev->id_iobase + ED_NOVELL_NIC_OFFSET;
+ sc->mem_start = (caddr_t) pc98_dev->id_maddr;
+ memsize = pc98_dev->id_msize;
+
+ /* reset card to force it into a known state. */
+ outb(sc->asic_addr, 0x00);
+ DELAY(100);
+ outb(sc->asic_addr, 0x94);
+ DELAY(100);
+ outb(sc->asic_addr, 0x94);
+ DELAY(100);
+
+ /* Here we check the card ROM, if the checksum passes, and the
+ * type code and ethernet address check out, then we know we have
+ * a SIC card.
+ */
+ for (sum = 0, i = 0; i < 7; ++i)
+ sum ^= sc->mem_start[i*2];
+ if (sum != 0)
+ return 0;
+
+ sc->isa16bit = 1;
+ sc->mem_shared = 1;
+ sc->vendor = ED_VENDOR_MISC;
+ sc->type_str = "SIC98";
+ sc->kdc.kdc_description = "Ethernet adpater: SIC-98";
+ sc->cr_proto = 0;
+ sc->txb_cnt = 1;
+
+ /*
+ * Save board ROM station address
+ */
+ for (i = 0; i < 6; ++i)
+ sc->arpcom.ac_enaddr[i] = sc->mem_start[i*2];
+
+ /*
+ * SIC ram page 0x0000-0x3fff (or 0x7fff)
+ */
+ outb(sc->asic_addr, 0x90);
+ DELAY(100);
+
+ sc->mem_size = memsize;
+ sc->mem_end = sc->mem_start + memsize;
+ sc->tx_page_start = 0;
+ sc->rec_page_start = ED_TXBUF_SIZE;
+ sc->rec_page_stop = (memsize / ED_PAGE_SIZE);
+ sc->mem_ring = sc->mem_start + (ED_TXBUF_SIZE * ED_PAGE_SIZE);
+
+ /*
+ * clear interface memory, then sum to make sure its valid
+ */
+ bzero(sc->mem_start, memsize);
+
+ for (i = 0; i < memsize; ++i)
+ if (sc->mem_start[i]) {
+ printf("ed%d: failed to clear shared memory at %lx - check configuration\n",
+ pc98_dev->id_unit, kvtop(sc->mem_start + i));
+ return (0);
+ }
+
+ /*
+ * select page 0 regsister
+ */
+ outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2 | ED_CR_PAGE_0 | ED_CR_STP);
+
+ return (1);
+}
+#endif
+
+/*
+ * Install interface into kernel networking data structures
+ */
+static int
+ed_attach(sc, unit, flags)
+ struct ed_softc *sc;
+ int unit;
+ int flags;
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ /*
+ * Set interface to stopped condition (reset)
+ */
+ ed_stop(sc);
+
+ if (!ifp->if_name) {
+ /*
+ * Initialize ifnet structure
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unit;
+ ifp->if_name = "ed";
+ ifp->if_output = ether_output;
+ ifp->if_start = ed_start;
+ ifp->if_ioctl = ed_ioctl;
+ ifp->if_watchdog = ed_watchdog;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ /*
+ * Set default state for ALTPHYS flag (used to disable the
+ * tranceiver for AUI operation), based on compile-time
+ * config option.
+ */
+ if (flags & ED_FLAGS_DISABLE_TRANCEIVER)
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX |
+ IFF_MULTICAST | IFF_ALTPHYS);
+ else
+ ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX |
+ IFF_MULTICAST);
+
+ /*
+ * Attach the interface
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+ }
+ /* device attach does transition from UNCONFIGURED to IDLE state */
+ sc->kdc.kdc_state = DC_IDLE;
+
+ /*
+ * Print additional info when attached
+ */
+ printf("%s%d: address %6D, ", ifp->if_name, ifp->if_unit,
+ sc->arpcom.ac_enaddr, ":");
+
+ if (sc->type_str && (*sc->type_str != 0))
+ printf("type %s ", sc->type_str);
+ else
+ printf("type unknown (0x%x) ", sc->type);
+
+ printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)");
+
+ printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) &&
+ (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : "");
+
+ /*
+ * If BPF is in the kernel, call the attach for it
+ */
+#if NBPFILTER > 0
+ bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+static int
+ed_attach_isa(isa_dev)
+#ifdef PC98
+ struct pc98_device *isa_dev;
+#else
+ struct isa_device *isa_dev;
+#endif
+{
+ int unit = isa_dev->id_unit;
+ struct ed_softc *sc = &ed_softc[unit];
+ int flags = isa_dev->id_flags;
+
+ return ed_attach(sc, unit, flags);
+}
+
+#if NPCI > 0
+void *
+ed_attach_NE2000_pci(unit, port)
+ int unit;
+ int port;
+{
+ struct ed_softc *sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT);
+ int isa_flags = 0;
+
+ if (!sc)
+ return sc;
+
+ if (ed_probe_Novell_generic(sc, port, unit, isa_flags) == 0
+ || ed_attach(sc, unit, isa_flags) == 0) {
+ free(sc, M_DEVBUF);
+ return NULL;
+ }
+ return sc;
+}
+#endif
+
+/*
+ * Reset interface.
+ */
+static void
+ed_reset(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = ifp->if_softc;
+ int s;
+
+ if (sc->gone)
+ return;
+ s = splimp();
+
+ /*
+ * Stop interface and re-initialize.
+ */
+ ed_stop(sc);
+ ed_init(ifp);
+
+ (void) splx(s);
+}
+
+/*
+ * Take interface offline.
+ */
+static void
+ed_stop(sc)
+ struct ed_softc *sc;
+{
+ int n = 5000;
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+ if (sc->gone)
+ return;
+ /*
+ * Stop everything on the interface, and select page 0 registers.
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ /*
+ * Wait for interface to enter stopped state, but limit # of checks to
+ * 'n' (about 5ms). It shouldn't even take 5us on modern DS8390's, but
+ * just in case it's an old one.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n);
+}
+
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ * generate an interrupt after a transmit has been started on it.
+ */
+static void
+ed_watchdog(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = ifp->if_softc;
+
+ if (sc->gone)
+ return;
+ log(LOG_ERR, "ed%d: device timeout\n", ifp->if_unit);
+ ifp->if_oerrors++;
+
+ ed_reset(ifp);
+}
+
+/*
+ * Initialize device.
+ */
+static void
+ed_init(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = ifp->if_softc;
+ int i, s;
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+ if (sc->gone)
+ return;
+
+ /* address not known */
+ if (ifp->if_addrlist == (struct ifaddr *) 0)
+ return;
+
+ /*
+ * Initialize the NIC in the exact order outlined in the NS manual.
+ * This init procedure is "mandatory"...don't change what or when
+ * things happen.
+ */
+ s = splimp();
+
+ /* reset transmitter flags */
+ sc->xmit_busy = 0;
+ ifp->if_timer = 0;
+
+ sc->txb_inuse = 0;
+ sc->txb_new = 0;
+ sc->txb_next_tx = 0;
+
+ /* This variable is used below - don't move this assignment */
+ sc->next_packet = sc->rec_page_start + 1;
+
+ /*
+ * Set interface for page 0, Remote DMA complete, Stopped
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ if (sc->isa16bit) {
+
+ /*
+ * Set FIFO threshold to 8, No auto-init Remote DMA, byte
+ * order=80x86, word-wide DMA xfers,
+ */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_WTS | ED_DCR_LS);
+ } else {
+
+ /*
+ * Same as above, but byte-wide DMA xfers
+ */
+ outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
+ }
+
+ /*
+ * Clear Remote Byte Count Registers
+ */
+ outb(sc->nic_addr + ED_P0_RBCR0, 0);
+ outb(sc->nic_addr + ED_P0_RBCR1, 0);
+
+ /*
+ * For the moment, don't store incoming packets in memory.
+ */
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON);
+
+ /*
+ * Place NIC in internal loopback mode
+ */
+ outb(sc->nic_addr + ED_P0_TCR, ED_TCR_LB0);
+
+ /*
+ * Initialize transmit/receive (ring-buffer) Page Start
+ */
+ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start);
+ outb(sc->nic_addr + ED_P0_PSTART, sc->rec_page_start);
+ /* Set lower bits of byte addressable framing to 0 */
+ if (sc->is790)
+ outb(sc->nic_addr + 0x09, 0);
+
+ /*
+ * Initialize Receiver (ring-buffer) Page Stop and Boundry
+ */
+ outb(sc->nic_addr + ED_P0_PSTOP, sc->rec_page_stop);
+ outb(sc->nic_addr + ED_P0_BNRY, sc->rec_page_start);
+
+ /*
+ * Clear all interrupts. A '1' in each bit position clears the
+ * corresponding flag.
+ */
+ outb(sc->nic_addr + ED_P0_ISR, 0xff);
+
+ /*
+ * Enable the following interrupts: receive/transmit complete,
+ * receive/transmit error, and Receiver OverWrite.
+ *
+ * Counter overflow and Remote DMA complete are *not* enabled.
+ */
+ outb(sc->nic_addr + ED_P0_IMR,
+ ED_IMR_PRXE | ED_IMR_PTXE | ED_IMR_RXEE | ED_IMR_TXEE | ED_IMR_OVWE);
+
+ /*
+ * Program Command Register for page 1
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STP);
+
+ /*
+ * Copy out our station address
+ */
+#ifdef PC98
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ outb(sc->nic_addr + ED_P1_PAR0 + i * pc98_io_skip[unit],
+ sc->arpcom.ac_enaddr[i]);
+#else
+ for (i = 0; i < ETHER_ADDR_LEN; ++i)
+ outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]);
+#endif
+
+ /*
+ * Set Current Page pointer to next_packet (initialized above)
+ */
+ outb(sc->nic_addr + ED_P1_CURR, sc->next_packet);
+
+ /*
+ * Program Receiver Configuration Register and multicast filter. CR is
+ * set to page 0 on return.
+ */
+ ed_setrcr(sc);
+
+ /*
+ * Take interface out of loopback
+ */
+ outb(sc->nic_addr + ED_P0_TCR, 0);
+
+ /*
+ * If this is a 3Com board, the tranceiver must be software enabled
+ * (there is no settable hardware default).
+ */
+ if (sc->vendor == ED_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_ALTPHYS) {
+ outb(sc->asic_addr + ED_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+ }
+ }
+
+ /*
+ * Set 'running' flag, and clear output active flag.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * ...and attempt to start output
+ */
+ ed_start(ifp);
+
+ (void) splx(s);
+}
+
+/*
+ * This routine actually starts the transmission on the interface
+ */
+static inline void
+ed_xmit(sc)
+ struct ed_softc *sc;
+{
+ struct ifnet *ifp = (struct ifnet *)sc;
+ unsigned short len;
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+ if (sc->gone)
+ return;
+ len = sc->txb_len[sc->txb_next_tx];
+
+ /*
+ * Set NIC for page 0 register access
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+
+ /*
+ * Set TX buffer start page
+ */
+ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start +
+ sc->txb_next_tx * ED_TXBUF_SIZE);
+
+ /*
+ * Set TX length
+ */
+ outb(sc->nic_addr + ED_P0_TBCR0, len);
+ outb(sc->nic_addr + ED_P0_TBCR1, len >> 8);
+
+ /*
+ * Set page 0, Remote DMA complete, Transmit Packet, and *Start*
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_TXP | ED_CR_STA);
+ sc->xmit_busy = 1;
+
+ /*
+ * Point to next transmit buffer slot and wrap if necessary.
+ */
+ sc->txb_next_tx++;
+ if (sc->txb_next_tx == sc->txb_cnt)
+ sc->txb_next_tx = 0;
+
+ /*
+ * Set a timer just in case we never hear from the board again
+ */
+ ifp->if_timer = 2;
+}
+
+/*
+ * Start output on interface.
+ * We make two assumptions here:
+ * 1) that the current priority is set to splimp _before_ this code
+ * is called *and* is returned to the appropriate priority after
+ * return
+ * 2) that the IFF_OACTIVE flag is checked before this code is called
+ * (i.e. that the output part of the interface is idle)
+ */
+static void
+ed_start(ifp)
+ struct ifnet *ifp;
+{
+ struct ed_softc *sc = ifp->if_softc;
+ struct mbuf *m0, *m;
+ caddr_t buffer;
+ int len;
+
+ if (sc->gone) {
+ printf("ed_start(%p) GONE\n",ifp);
+ return;
+ }
+outloop:
+
+ /*
+ * First, see if there are buffered packets and an idle transmitter -
+ * should never happen at this point.
+ */
+ if (sc->txb_inuse && (sc->xmit_busy == 0)) {
+ printf("ed: packets buffered, but transmitter idle\n");
+ ed_xmit(sc);
+ }
+
+ /*
+ * See if there is room to put another packet in the buffer.
+ */
+ if (sc->txb_inuse == sc->txb_cnt) {
+
+ /*
+ * No room. Indicate this to the outside world and exit.
+ */
+ ifp->if_flags |= IFF_OACTIVE;
+ return;
+ }
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m == 0) {
+
+ /*
+ * We are using the !OACTIVE flag to indicate to the outside
+ * world that we can accept an additional packet rather than
+ * that the transmitter is _actually_ active. Indeed, the
+ * transmitter may be active, but if we haven't filled all the
+ * buffers with data then we still want to accept more.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ return;
+ }
+
+ /*
+ * Copy the mbuf chain into the transmit buffer
+ */
+
+ m0 = m;
+
+ /* txb_new points to next open buffer slot */
+ buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE);
+
+ if (sc->mem_shared) {
+
+ /*
+ * Special case setup for 16 bit boards...
+ */
+ if (sc->isa16bit) {
+ switch (sc->vendor) {
+
+ /*
+ * For 16bit 3Com boards (which have 16k of
+ * memory), we have the xmit buffers in a
+ * different page of memory ('page 0') - so
+ * change pages.
+ */
+ case ED_VENDOR_3COM:
+ outb(sc->asic_addr + ED_3COM_GACFR,
+ ED_3COM_GACFR_RSEL);
+ break;
+
+ /*
+ * Enable 16bit access to shared memory on
+ * WD/SMC boards.
+ */
+ case ED_VENDOR_WD_SMC:{
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto | ED_WD_LAAR_M16EN));
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB);
+ }
+ break;
+ }
+ }
+ }
+ for (len = 0; m != 0; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+ /*
+ * Restore previous shared memory access
+ */
+ if (sc->isa16bit) {
+ switch (sc->vendor) {
+ case ED_VENDOR_3COM:
+ outb(sc->asic_addr + ED_3COM_GACFR,
+ ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0);
+ break;
+ case ED_VENDOR_WD_SMC:{
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ }
+ outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto);
+ break;
+ }
+ }
+ }
+ } else {
+ len = ed_pio_write_mbufs(sc, m, (int)buffer);
+ if (len == 0)
+ goto outloop;
+ }
+
+ sc->txb_len[sc->txb_new] = max(len, ETHER_MIN_LEN);
+
+ sc->txb_inuse++;
+
+ /*
+ * Point to next buffer slot and wrap if necessary.
+ */
+ sc->txb_new++;
+ if (sc->txb_new == sc->txb_cnt)
+ sc->txb_new = 0;
+
+ if (sc->xmit_busy == 0)
+ ed_xmit(sc);
+
+ /*
+ * Tap off here if there is a bpf listener.
+ */
+#if NBPFILTER > 0
+ if (ifp->if_bpf) {
+ bpf_mtap(ifp, m0);
+ }
+#endif
+
+ m_freem(m0);
+
+ /*
+ * Loop back to the top to possibly buffer more packets
+ */
+ goto outloop;
+}
+
+/*
+ * Ethernet interface receiver interrupt.
+ */
+static inline void
+ed_rint(sc)
+ struct ed_softc *sc;
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ u_char boundry;
+ u_short len;
+ struct ed_ring packet_hdr;
+ char *packet_ptr;
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+ if (sc->gone)
+ return;
+
+ /*
+ * Set NIC to page 1 registers to get 'current' pointer
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STA);
+
+ /*
+ * 'sc->next_packet' is the logical beginning of the ring-buffer -
+ * i.e. it points to where new data has been buffered. The 'CURR'
+ * (current) register points to the logical end of the ring-buffer -
+ * i.e. it points to where additional new data will be added. We loop
+ * here until the logical beginning equals the logical end (or in
+ * other words, until the ring-buffer is empty).
+ */
+ while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) {
+
+ /* get pointer to this buffer's header structure */
+ packet_ptr = sc->mem_ring +
+ (sc->next_packet - sc->rec_page_start) * ED_PAGE_SIZE;
+
+ /*
+ * The byte count includes a 4 byte header that was added by
+ * the NIC.
+ */
+ if (sc->mem_shared)
+ packet_hdr = *(struct ed_ring *) packet_ptr;
+ else
+ ed_pio_readmem(sc, (int)packet_ptr, (char *) &packet_hdr,
+ sizeof(packet_hdr));
+ len = packet_hdr.count;
+ if (len > (ETHER_MAX_LEN + sizeof(struct ed_ring)) ||
+ len < (ETHER_HDR_SIZE + sizeof(struct ed_ring))) {
+ /*
+ * Length is a wild value. There's a good chance that
+ * this was caused by the NIC being old and buggy.
+ * The bug is that the length low byte is duplicated in
+ * the high byte. Try to recalculate the length based on
+ * the pointer to the next packet.
+ */
+ /*
+ * NOTE: sc->next_packet is pointing at the current packet.
+ */
+ len &= ED_PAGE_SIZE - 1; /* preserve offset into page */
+ if (packet_hdr.next_packet >= sc->next_packet) {
+ len += (packet_hdr.next_packet - sc->next_packet) * ED_PAGE_SIZE;
+ } else {
+ len += ((packet_hdr.next_packet - sc->rec_page_start) +
+ (sc->rec_page_stop - sc->next_packet)) * ED_PAGE_SIZE;
+ }
+ }
+ /*
+ * Be fairly liberal about what we allow as a "reasonable" length
+ * so that a [crufty] packet will make it to BPF (and can thus
+ * be analyzed). Note that all that is really important is that
+ * we have a length that will fit into one mbuf cluster or less;
+ * the upper layer protocols can then figure out the length from
+ * their own length field(s).
+ */
+ if ((len > sizeof(struct ed_ring)) &&
+ (len <= MCLBYTES) &&
+ (packet_hdr.next_packet >= sc->rec_page_start) &&
+ (packet_hdr.next_packet < sc->rec_page_stop)) {
+ /*
+ * Go get packet.
+ */
+ ed_get_packet(sc, packet_ptr + sizeof(struct ed_ring),
+ len - sizeof(struct ed_ring), packet_hdr.rsr & ED_RSR_PHY);
+ ifp->if_ipackets++;
+ } else {
+ /*
+ * Really BAD. The ring pointers are corrupted.
+ */
+ log(LOG_ERR,
+ "ed%d: NIC memory corrupt - invalid packet length %d\n",
+ ifp->if_unit, len);
+ ifp->if_ierrors++;
+ ed_reset(ifp);
+ return;
+ }
+
+ /*
+ * Update next packet pointer
+ */
+ sc->next_packet = packet_hdr.next_packet;
+
+ /*
+ * Update NIC boundry pointer - being careful to keep it one
+ * buffer behind. (as recommended by NS databook)
+ */
+ boundry = sc->next_packet - 1;
+ if (boundry < sc->rec_page_start)
+ boundry = sc->rec_page_stop - 1;
+
+ /*
+ * Set NIC to page 0 registers to update boundry register
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+
+ outb(sc->nic_addr + ED_P0_BNRY, boundry);
+
+ /*
+ * Set NIC to page 1 registers before looping to top (prepare
+ * to get 'CURR' current pointer)
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STA);
+ }
+}
+
+/*
+ * Ethernet interface interrupt processor
+ */
+void
+edintr_sc(sc)
+ struct ed_softc *sc;
+{
+ struct ifnet *ifp = (struct ifnet *)sc;
+ u_char isr;
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+ if (sc->gone)
+ return;
+ /*
+ * Set NIC to page 0 registers
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+
+ /*
+ * loop until there are no more new interrupts
+ */
+ while ((isr = inb(sc->nic_addr + ED_P0_ISR)) != 0) {
+
+ /*
+ * reset all the bits that we are 'acknowledging' by writing a
+ * '1' to each bit position that was set (writing a '1'
+ * *clears* the bit)
+ */
+ outb(sc->nic_addr + ED_P0_ISR, isr);
+
+ /*
+ * Handle transmitter interrupts. Handle these first because
+ * the receiver will reset the board under some conditions.
+ */
+ if (isr & (ED_ISR_PTX | ED_ISR_TXE)) {
+ u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f;
+
+ /*
+ * Check for transmit error. If a TX completed with an
+ * error, we end up throwing the packet away. Really
+ * the only error that is possible is excessive
+ * collisions, and in this case it is best to allow
+ * the automatic mechanisms of TCP to backoff the
+ * flow. Of course, with UDP we're screwed, but this
+ * is expected when a network is heavily loaded.
+ */
+ (void) inb(sc->nic_addr + ED_P0_TSR);
+ if (isr & ED_ISR_TXE) {
+
+ /*
+ * Excessive collisions (16)
+ */
+ if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT)
+ && (collisions == 0)) {
+
+ /*
+ * When collisions total 16, the
+ * P0_NCR will indicate 0, and the
+ * TSR_ABT is set.
+ */
+ collisions = 16;
+ }
+
+ /*
+ * update output errors counter
+ */
+ ifp->if_oerrors++;
+ } else {
+
+ /*
+ * Update total number of successfully
+ * transmitted packets.
+ */
+ ifp->if_opackets++;
+ }
+
+ /*
+ * reset tx busy and output active flags
+ */
+ sc->xmit_busy = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * clear watchdog timer
+ */
+ ifp->if_timer = 0;
+
+ /*
+ * Add in total number of collisions on last
+ * transmission.
+ */
+ ifp->if_collisions += collisions;
+
+ /*
+ * Decrement buffer in-use count if not zero (can only
+ * be zero if a transmitter interrupt occured while
+ * not actually transmitting). If data is ready to
+ * transmit, start it transmitting, otherwise defer
+ * until after handling receiver
+ */
+ if (sc->txb_inuse && --sc->txb_inuse)
+ ed_xmit(sc);
+ }
+
+ /*
+ * Handle receiver interrupts
+ */
+ if (isr & (ED_ISR_PRX | ED_ISR_RXE | ED_ISR_OVW)) {
+
+ /*
+ * Overwrite warning. In order to make sure that a
+ * lockup of the local DMA hasn't occurred, we reset
+ * and re-init the NIC. The NSC manual suggests only a
+ * partial reset/re-init is necessary - but some chips
+ * seem to want more. The DMA lockup has been seen
+ * only with early rev chips - Methinks this bug was
+ * fixed in later revs. -DG
+ */
+ if (isr & ED_ISR_OVW) {
+ ifp->if_ierrors++;
+#ifdef DIAGNOSTIC
+ log(LOG_WARNING,
+ "ed%d: warning - receiver ring buffer overrun\n",
+ ifp->if_unit);
+#endif
+
+ /*
+ * Stop/reset/re-init NIC
+ */
+ ed_reset(ifp);
+ } else {
+
+ /*
+ * Receiver Error. One or more of: CRC error,
+ * frame alignment error FIFO overrun, or
+ * missed packet.
+ */
+ if (isr & ED_ISR_RXE) {
+ ifp->if_ierrors++;
+#ifdef ED_DEBUG
+ printf("ed%d: receive error %x\n", ifp->if_unit,
+ inb(sc->nic_addr + ED_P0_RSR));
+#endif
+ }
+
+ /*
+ * Go get the packet(s) XXX - Doing this on an
+ * error is dubious because there shouldn't be
+ * any data to get (we've configured the
+ * interface to not accept packets with
+ * errors).
+ */
+
+ /*
+ * Enable 16bit access to shared memory first
+ * on WD/SMC boards.
+ */
+ if (sc->isa16bit &&
+ (sc->vendor == ED_VENDOR_WD_SMC)) {
+
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto |=
+ ED_WD_LAAR_M16EN));
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR,
+ ED_WD_MSR_MENB);
+ }
+ }
+ ed_rint(sc);
+
+ /* disable 16bit access */
+ if (sc->isa16bit &&
+ (sc->vendor == ED_VENDOR_WD_SMC)) {
+
+ if (sc->is790) {
+ outb(sc->asic_addr + ED_WD_MSR, 0x00);
+ }
+ outb(sc->asic_addr + ED_WD_LAAR,
+ (sc->wd_laar_proto &=
+ ~ED_WD_LAAR_M16EN));
+ }
+ }
+ }
+
+ /*
+ * If it looks like the transmitter can take more data,
+ * attempt to start output on the interface. This is done
+ * after handling the receiver to give the receiver priority.
+ */
+ if ((ifp->if_flags & IFF_OACTIVE) == 0)
+ ed_start(ifp);
+
+ /*
+ * return NIC CR to standard state: page 0, remote DMA
+ * complete, start (toggling the TXP bit off, even if was just
+ * set in the transmit routine, is *okay* - it is 'edge'
+ * triggered from low to high)
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+
+ /*
+ * If the Network Talley Counters overflow, read them to reset
+ * them. It appears that old 8390's won't clear the ISR flag
+ * otherwise - resulting in an infinite loop.
+ */
+ if (isr & ED_ISR_CNT) {
+ (void) inb(sc->nic_addr + ED_P0_CNTR0);
+ (void) inb(sc->nic_addr + ED_P0_CNTR1);
+ (void) inb(sc->nic_addr + ED_P0_CNTR2);
+ }
+ }
+}
+
+void
+edintr(unit)
+ int unit;
+{
+ edintr_sc (&ed_softc[unit]);
+}
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ * pretty ugly.
+ */
+static int
+ed_ioctl(ifp, command, data)
+ register struct ifnet *ifp;
+ int command;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ed_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
+
+ if (sc->gone) {
+ ifp->if_flags &= ~IFF_RUNNING;
+ return ENXIO;
+ }
+ s = splimp();
+
+ switch (command) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ /* netifs are BUSY when UP */
+ sc->kdc.kdc_state = DC_BUSY;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ ed_init(ifp); /* before arpwhohas */
+ arp_ifinit((struct arpcom *)ifp, ifa);
+ break;
+#endif
+#ifdef IPX
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_IPX:
+ {
+ register struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
+
+ if (ipx_nullhost(*ina))
+ ina->x_host =
+ *(union ipx_host *) (sc->arpcom.ac_enaddr);
+ else {
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t) sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+
+ /*
+ * Set new address
+ */
+ ed_init(ifp);
+ break;
+ }
+#endif
+#ifdef NS
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *) (sc->arpcom.ac_enaddr);
+ else {
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t) sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+
+ /*
+ * Set new address
+ */
+ ed_init(ifp);
+ break;
+ }
+#endif
+ default:
+ ed_init(ifp);
+ break;
+ }
+ break;
+
+ case SIOCGIFADDR:
+ {
+ struct sockaddr *sa;
+
+ sa = (struct sockaddr *) & ifr->ifr_data;
+ bcopy((caddr_t) sc->arpcom.ac_enaddr,
+ (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+
+ /*
+ * If the interface is marked up and stopped, then start it.
+ * If it is marked down and running, then stop it.
+ */
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_flags & IFF_RUNNING) == 0)
+ ed_init(ifp);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING) {
+ ed_stop(sc);
+ ifp->if_flags &= ~IFF_RUNNING;
+ }
+ }
+ /* UP controls BUSY/IDLE */
+ sc->kdc.kdc_state = ((ifp->if_flags & IFF_UP)
+ ? DC_BUSY
+ : DC_IDLE);
+
+#if NBPFILTER > 0
+
+ /*
+ * Promiscuous flag may have changed, so reprogram the RCR.
+ */
+ ed_setrcr(sc);
+#endif
+
+ /*
+ * An unfortunate hack to provide the (required) software
+ * control of the tranceiver for 3Com boards. The ALTPHYS flag
+ * disables the tranceiver if set.
+ */
+ if (sc->vendor == ED_VENDOR_3COM) {
+ if (ifp->if_flags & IFF_ALTPHYS) {
+ outb(sc->asic_addr + ED_3COM_CR, 0);
+ } else {
+ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL);
+ }
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ /*
+ * Update out multicast list.
+ */
+ error = (command == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->arpcom) :
+ ether_delmulti(ifr, &sc->arpcom);
+
+ if (error == ENETRESET) {
+
+ /*
+ * Multicast list has changed; set the hardware filter
+ * accordingly.
+ */
+ ed_setrcr(sc);
+ error = 0;
+ }
+ break;
+
+ case SIOCSIFMTU:
+ /*
+ * Set the interface MTU.
+ */
+ if (ifr->ifr_mtu > ETHERMTU) {
+ error = EINVAL;
+ } else {
+ ifp->if_mtu = ifr->ifr_mtu;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return (error);
+}
+
+/*
+ * Given a source and destination address, copy 'amount' of a packet from
+ * the ring buffer into a linear destination buffer. Takes into account
+ * ring-wrap.
+ */
+static inline char *
+ed_ring_copy(sc, src, dst, amount)
+ struct ed_softc *sc;
+ char *src;
+ char *dst;
+ u_short amount;
+{
+ u_short tmp_amount;
+
+ /* does copy wrap to lower addr in ring buffer? */
+ if (src + amount > sc->mem_end) {
+ tmp_amount = sc->mem_end - src;
+
+ /* copy amount up to end of NIC memory */
+ if (sc->mem_shared)
+ bcopy(src, dst, tmp_amount);
+ else
+ ed_pio_readmem(sc, (int)src, dst, tmp_amount);
+
+ amount -= tmp_amount;
+ src = sc->mem_ring;
+ dst += tmp_amount;
+ }
+ if (sc->mem_shared)
+ bcopy(src, dst, amount);
+ else
+ ed_pio_readmem(sc, (int)src, dst, amount);
+
+ return (src + amount);
+}
+
+/*
+ * Retreive packet from shared memory and send to the next level up via
+ * ether_input(). If there is a BPF listener, give a copy to BPF, too.
+ */
+static void
+ed_get_packet(sc, buf, len, multicast)
+ struct ed_softc *sc;
+ char *buf;
+ u_short len;
+ int multicast;
+{
+ struct ether_header *eh;
+ struct mbuf *m;
+
+ /* Allocate a header mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return;
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ m->m_pkthdr.len = m->m_len = len;
+
+ /*
+ * We always put the received packet in a single buffer -
+ * either with just an mbuf header or in a cluster attached
+ * to the header. The +2 is to compensate for the alignment
+ * fixup below.
+ */
+ if ((len + 2) > MHLEN) {
+ /* Attach an mbuf cluster */
+ MCLGET(m, M_DONTWAIT);
+
+ /* Insist on getting a cluster */
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ return;
+ }
+ }
+
+ /*
+ * The +2 is to longword align the start of the real packet.
+ * This is important for NFS.
+ */
+ m->m_data += 2;
+ eh = mtod(m, struct ether_header *);
+
+ /*
+ * Get packet, including link layer address, from interface.
+ */
+ ed_ring_copy(sc, buf, (char *)eh, len);
+
+#if NBPFILTER > 0
+
+ /*
+ * Check if there's a BPF listener on this interface. If so, hand off
+ * the raw packet to bpf.
+ */
+ if (sc->arpcom.ac_if.if_bpf) {
+ bpf_mtap(&sc->arpcom.ac_if, m);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours.
+ */
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 && multicast == 0) {
+ m_freem(m);
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Remove link layer address.
+ */
+ m->m_pkthdr.len = m->m_len = len - sizeof(struct ether_header);
+ m->m_data += sizeof(struct ether_header);
+
+ ether_input(&sc->arpcom.ac_if, eh, m);
+ return;
+}
+
+/*
+ * Supporting routines
+ */
+
+/*
+ * Given a NIC memory source address and a host memory destination
+ * address, copy 'amount' from NIC to host using Programmed I/O.
+ * The 'amount' is rounded up to a word - okay as long as mbufs
+ * are word sized.
+ * This routine is currently Novell-specific.
+ */
+static void
+ed_pio_readmem(sc, src, dst, amount)
+ struct ed_softc *sc;
+ int src;
+ unsigned char *dst;
+ unsigned short amount;
+{
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA);
+
+ /* round up to a word */
+ if (amount & 1)
+ ++amount;
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, amount);
+ outb(sc->nic_addr + ED_P0_RBCR1, amount >> 8);
+
+ /* set up source address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, src);
+ outb(sc->nic_addr + ED_P0_RSAR1, src >> 8);
+
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA);
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_ON();
+#endif
+
+ if (sc->isa16bit) {
+ insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount / 2);
+ } else
+ insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount);
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_OFF();
+#endif
+}
+
+/*
+ * Stripped down routine for writing a linear buffer to NIC memory.
+ * Only used in the probe routine to test the memory. 'len' must
+ * be even.
+ */
+static void
+ed_pio_writemem(sc, src, dst, len)
+ struct ed_softc *sc;
+ char *src;
+ unsigned short dst;
+ unsigned short len;
+{
+ int maxwait = 200; /* about 240us */
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA);
+
+ /* reset remote DMA complete flag */
+ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC);
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, len);
+ outb(sc->nic_addr + ED_P0_RBCR1, len >> 8);
+
+ /* set up destination address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, dst);
+ outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8);
+
+ /* set remote DMA write */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_ON();
+#endif
+ if (sc->isa16bit)
+ outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2);
+ else
+ outsb(sc->asic_addr + ED_NOVELL_DATA, src, len);
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_OFF();
+#endif
+
+ /*
+ * Wait for remote DMA complete. This is necessary because on the
+ * transmit side, data is handled internally by the NIC in bursts and
+ * we can't start another remote DMA until this one completes. Not
+ * waiting causes really bad things to happen - like the NIC
+ * irrecoverably jamming the ISA bus.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait);
+}
+
+/*
+ * Write an mbuf chain to the destination NIC memory address using
+ * programmed I/O.
+ */
+static u_short
+ed_pio_write_mbufs(sc, m, dst)
+ struct ed_softc *sc;
+ struct mbuf *m;
+ int dst;
+{
+ struct ifnet *ifp = (struct ifnet *)sc;
+ unsigned short total_len, dma_len;
+ struct mbuf *mp;
+ int maxwait = 200; /* about 240us */
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+ /* First, count up the total number of bytes to copy */
+ for (total_len = 0, mp = m; mp; mp = mp->m_next)
+ total_len += mp->m_len;
+
+ dma_len = total_len;
+ if (sc->isa16bit && (dma_len & 1))
+ dma_len++;
+
+ /* select page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA);
+
+ /* reset remote DMA complete flag */
+ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC);
+
+ /* set up DMA byte count */
+ outb(sc->nic_addr + ED_P0_RBCR0, dma_len);
+ outb(sc->nic_addr + ED_P0_RBCR1, dma_len >> 8);
+
+ /* set up destination address in NIC mem */
+ outb(sc->nic_addr + ED_P0_RSAR0, dst);
+ outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8);
+
+ /* set remote DMA write */
+ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
+
+ /*
+ * Transfer the mbuf chain to the NIC memory.
+ * 16-bit cards require that data be transferred as words, and only words.
+ * So that case requires some extra code to patch over odd-length mbufs.
+ */
+
+ if (!sc->isa16bit) {
+ /* NE1000s are easy */
+ while (m) {
+ if (m->m_len) {
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_ON();
+#endif
+ outsb(sc->asic_addr + ED_NOVELL_DATA,
+ m->m_data, m->m_len);
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_OFF();
+#endif
+ }
+ m = m->m_next;
+ }
+ } else {
+ /* NE2000s are a pain */
+ unsigned char *data;
+ int len, wantbyte;
+ unsigned char savebyte[2];
+
+ wantbyte = 0;
+
+ while (m) {
+ len = m->m_len;
+ if (len) {
+ data = mtod(m, caddr_t);
+ /* finish the last word */
+ if (wantbyte) {
+ savebyte[1] = *data;
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_ON();
+#endif
+ outw(sc->asic_addr + ED_NOVELL_DATA, *(u_short *)savebyte);
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_OFF();
+#endif
+ data++;
+ len--;
+ wantbyte = 0;
+ }
+ /* output contiguous words */
+ if (len > 1) {
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_ON();
+#endif
+ outsw(sc->asic_addr + ED_NOVELL_DATA,
+ data, len >> 1);
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_OFF();
+#endif
+ data += len & ~1;
+ len &= 1;
+ }
+ /* save last byte, if necessary */
+ if (len == 1) {
+ savebyte[0] = *data;
+ wantbyte = 1;
+ }
+ }
+ m = m->m_next;
+ }
+ /* spit last byte */
+ if (wantbyte) {
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_ON();
+#endif
+ outw(sc->asic_addr + ED_NOVELL_DATA, *(u_short *)savebyte);
+#ifdef PC98
+ if (sc->type == ED_TYPE98_LPC)
+ LPCT_1d0_OFF();
+#endif
+ }
+ }
+
+ /*
+ * Wait for remote DMA complete. This is necessary because on the
+ * transmit side, data is handled internally by the NIC in bursts and
+ * we can't start another remote DMA until this one completes. Not
+ * waiting causes really bad things to happen - like the NIC
+ * irrecoverably jamming the ISA bus.
+ */
+ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait);
+
+ if (!maxwait) {
+ log(LOG_WARNING, "ed%d: remote transmit DMA failed to complete\n",
+ ifp->if_unit);
+ ed_reset(ifp);
+ return(0);
+ }
+ return (total_len);
+}
+
+static void
+ed_setrcr(sc)
+ struct ed_softc *sc;
+{
+ struct ifnet *ifp = (struct ifnet *)sc;
+ int i;
+#ifdef PC98
+ int unit = sc->unit;
+#endif
+
+ /* set page 1 registers */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_PAGE_1 | ED_CR_STP);
+
+ if (ifp->if_flags & IFF_PROMISC) {
+
+ /*
+ * Reconfigure the multicast filter.
+ */
+#ifdef PC98
+ for (i = 0; i < 8; i++)
+ outb(sc->nic_addr + ED_P1_MAR0 + i * pc98_io_skip[unit], 0xff);
+#else
+ for (i = 0; i < 8; i++)
+ outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff);
+#endif
+ /*
+ * And turn on promiscuous mode. Also enable reception of
+ * runts and packets with CRC & alignment errors.
+ */
+ /* Set page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_PRO | ED_RCR_AM |
+ ED_RCR_AB | ED_RCR_AR | ED_RCR_SEP);
+ } else {
+ /* set up multicast addresses and filter modes */
+ if (ifp->if_flags & IFF_MULTICAST) {
+ u_long mcaf[2];
+
+ if (ifp->if_flags & IFF_ALLMULTI) {
+ mcaf[0] = 0xffffffff;
+ mcaf[1] = 0xffffffff;
+ } else
+ ds_getmcaf(sc, mcaf);
+
+ /*
+ * Set multicast filter on chip.
+ */
+#ifdef PC98
+ for (i = 0; i < 8; i++)
+ outb(sc->nic_addr + ED_P1_MAR0 + i * pc98_io_skip[unit],
+ ((u_char *) mcaf)[i]);
+#else
+ for (i = 0; i < 8; i++)
+ outb(sc->nic_addr + ED_P1_MAR0 + i, ((u_char *) mcaf)[i]);
+#endif
+ /* Set page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AM | ED_RCR_AB);
+ } else {
+
+ /*
+ * Initialize multicast address hashing registers to
+ * not accept multicasts.
+ */
+#ifndef PC98
+ for (i = 0; i < 8; ++i)
+ outb(sc->nic_addr + ED_P1_MAR0 + i, 0x00);
+#else
+ for (i = 0; i < 8; ++i)
+ outb(sc->nic_addr + ED_P1_MAR0 + i * pc98_io_skip[unit], 0x00);
+#endif
+
+ /* Set page 0 registers */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STP);
+
+ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB);
+ }
+ }
+
+ /*
+ * Start interface.
+ */
+ outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA);
+}
+
+/*
+ * Compute crc for ethernet address
+ */
+static u_long
+ds_crc(ep)
+ u_char *ep;
+{
+#define POLYNOMIAL 0x04c11db6
+ register u_long crc = 0xffffffffL;
+ register int carry, i, j;
+ register u_char b;
+
+ for (i = 6; --i >= 0;) {
+ b = *ep++;
+ for (j = 8; --j >= 0;) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry)
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ return crc;
+#undef POLYNOMIAL
+}
+
+/*
+ * Compute the multicast address filter from the
+ * list of multicast addresses we need to listen to.
+ */
+static void
+ds_getmcaf(sc, mcaf)
+ struct ed_softc *sc;
+ u_long *mcaf;
+{
+ register u_int index;
+ register u_char *af = (u_char *) mcaf;
+ register struct ether_multi *enm;
+ register struct ether_multistep step;
+
+ mcaf[0] = 0;
+ mcaf[1] = 0;
+
+ ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
+ while (enm != NULL) {
+ if (bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) {
+ mcaf[0] = 0xffffffff;
+ mcaf[1] = 0xffffffff;
+ return;
+ }
+ index = ds_crc(enm->enm_addrlo) >> 26;
+ af[index >> 3] |= 1 << (index & 7);
+
+ ETHER_NEXT_MULTI(step, enm);
+ }
+}
diff --git a/sys/pc98/pc98/if_edreg.h b/sys/pc98/pc98/if_edreg.h
new file mode 100644
index 0000000..fae62f8
--- /dev/null
+++ b/sys/pc98/pc98/if_edreg.h
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ * $Id: if_edreg.h,v 1.20 1996/01/30 22:55:38 mpp Exp $
+ */
+/*
+ * National Semiconductor DS8390 NIC register definitions
+ *
+ *
+ * Modification history
+ *
+ * Revision 2.2 1993/11/29 16:33:39 davidg
+ * From Thomas Sandford <t.d.g.sandford@comp.brad.ac.uk>
+ * Add support for the 8013W board type
+ *
+ * Revision 2.1 1993/11/22 10:52:33 davidg
+ * patch to add support for SMC8216 (Elite-Ultra) boards
+ * from Glen H. Lowe
+ *
+ * Revision 2.0 93/09/29 00:37:15 davidg
+ * changed double buffering flag to multi buffering
+ * made changes/additions for 3c503 multi-buffering
+ * ...companion to Rev. 2.0 of 'ed' driver.
+ *
+ * Revision 1.1 93/06/23 03:01:07 davidg
+ * Initial revision
+ *
+ */
+
+/*
+ * Page 0 register offsets
+ */
+#define ED_P0_CR 0x00 /* Command Register */
+
+#define ED_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */
+#define ED_P0_PSTART 0x01 /* Page Start register (write) */
+
+#define ED_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */
+#define ED_P0_PSTOP 0x02 /* Page Stop register (write) */
+
+#define ED_P0_BNRY 0x03 /* Boundary Pointer */
+
+#define ED_P0_TSR 0x04 /* Transmit Status Register (read) */
+#define ED_P0_TPSR 0x04 /* Transmit Page Start (write) */
+
+#define ED_P0_NCR 0x05 /* Number of Collisions Reg (read) */
+#define ED_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */
+
+#define ED_P0_FIFO 0x06 /* FIFO register (read) */
+#define ED_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */
+
+#define ED_P0_ISR 0x07 /* Interrupt Status Register */
+
+#define ED_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */
+#define ED_P0_RSAR0 0x08 /* Remote Start Address low (write) */
+
+#define ED_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */
+#define ED_P0_RSAR1 0x09 /* Remote Start Address high (write) */
+
+#define ED_P0_RBCR0 0x0a /* Remote Byte Count low (write) */
+
+#define ED_P0_RBCR1 0x0b /* Remote Byte Count high (write) */
+
+#define ED_P0_RSR 0x0c /* Receive Status (read) */
+#define ED_P0_RCR 0x0c /* Receive Configuration Reg (write) */
+
+#define ED_P0_CNTR0 0x0d /* frame alignment error counter (read) */
+#define ED_P0_TCR 0x0d /* Transmit Configuration Reg (write) */
+
+#define ED_P0_CNTR1 0x0e /* CRC error counter (read) */
+#define ED_P0_DCR 0x0e /* Data Configuration Reg (write) */
+
+#define ED_P0_CNTR2 0x0f /* missed packet counter (read) */
+#define ED_P0_IMR 0x0f /* Interrupt Mask Register (write) */
+
+/*
+ * Page 1 register offsets
+ */
+#define ED_P1_CR 0x00 /* Command Register */
+#define ED_P1_PAR0 0x01 /* Physical Address Register 0 */
+#define ED_P1_PAR1 0x02 /* Physical Address Register 1 */
+#define ED_P1_PAR2 0x03 /* Physical Address Register 2 */
+#define ED_P1_PAR3 0x04 /* Physical Address Register 3 */
+#define ED_P1_PAR4 0x05 /* Physical Address Register 4 */
+#define ED_P1_PAR5 0x06 /* Physical Address Register 5 */
+#define ED_P1_CURR 0x07 /* Current RX ring-buffer page */
+#define ED_P1_MAR0 0x08 /* Multicast Address Register 0 */
+#define ED_P1_MAR1 0x09 /* Multicast Address Register 1 */
+#define ED_P1_MAR2 0x0a /* Multicast Address Register 2 */
+#define ED_P1_MAR3 0x0b /* Multicast Address Register 3 */
+#define ED_P1_MAR4 0x0c /* Multicast Address Register 4 */
+#define ED_P1_MAR5 0x0d /* Multicast Address Register 5 */
+#define ED_P1_MAR6 0x0e /* Multicast Address Register 6 */
+#define ED_P1_MAR7 0x0f /* Multicast Address Register 7 */
+
+/*
+ * Page 2 register offsets
+ */
+#define ED_P2_CR 0x00 /* Command Register */
+#define ED_P2_PSTART 0x01 /* Page Start (read) */
+#define ED_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */
+#define ED_P2_PSTOP 0x02 /* Page Stop (read) */
+#define ED_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */
+#define ED_P2_RNPP 0x03 /* Remote Next Packet Pointer */
+#define ED_P2_TPSR 0x04 /* Transmit Page Start (read) */
+#define ED_P2_LNPP 0x05 /* Local Next Packet Pointer */
+#define ED_P2_ACU 0x06 /* Address Counter Upper */
+#define ED_P2_ACL 0x07 /* Address Counter Lower */
+#define ED_P2_RCR 0x0c /* Receive Configuration Register (read) */
+#define ED_P2_TCR 0x0d /* Transmit Configuration Register (read) */
+#define ED_P2_DCR 0x0e /* Data Configuration Register (read) */
+#define ED_P2_IMR 0x0f /* Interrupt Mask Register (read) */
+
+/*
+ * Command Register (CR) definitions
+ */
+
+/*
+ * STP: SToP. Software reset command. Takes the controller offline. No
+ * packets will be received or transmitted. Any reception or
+ * transmission in progress will continue to completion before
+ * entering reset state. To exit this state, the STP bit must
+ * reset and the STA bit must be set. The software reset has
+ * executed only when indicated by the RST bit in the ISR being
+ * set.
+ */
+#define ED_CR_STP 0x01
+
+/*
+ * STA: STArt. This bit is used to activate the NIC after either power-up,
+ * or when the NIC has been put in reset mode by software command
+ * or error.
+ */
+#define ED_CR_STA 0x02
+
+/*
+ * TXP: Transmit Packet. This bit must be set to indicate transmission of
+ * a packet. TXP is internally reset either after the transmission is
+ * completed or aborted. This bit should be set only after the Transmit
+ * Byte Count and Transmit Page Start register have been programmed.
+ */
+#define ED_CR_TXP 0x04
+
+/*
+ * RD0, RD1, RD2: Remote DMA Command. These three bits control the operation
+ * of the remote DMA channel. RD2 can be set to abort any remote DMA
+ * command in progress. The Remote Byte Count registers should be cleared
+ * when a remote DMA has been aborted. The Remote Start Addresses are not
+ * restored to the starting address if the remote DMA is aborted.
+ *
+ * RD2 RD1 RD0 function
+ * 0 0 0 not allowed
+ * 0 0 1 remote read
+ * 0 1 0 remote write
+ * 0 1 1 send packet
+ * 1 X X abort
+ */
+#define ED_CR_RD0 0x08
+#define ED_CR_RD1 0x10
+#define ED_CR_RD2 0x20
+
+/*
+ * PS0, PS1: Page Select. The two bits select which register set or 'page' to
+ * access.
+ *
+ * PS1 PS0 page
+ * 0 0 0
+ * 0 1 1
+ * 1 0 2
+ * 1 1 reserved
+ */
+#define ED_CR_PS0 0x40
+#define ED_CR_PS1 0x80
+/* bit encoded aliases */
+#define ED_CR_PAGE_0 0x00 /* (for consistency) */
+#define ED_CR_PAGE_1 0x40
+#define ED_CR_PAGE_2 0x80
+
+/*
+ * Interrupt Status Register (ISR) definitions
+ */
+
+/*
+ * PRX: Packet Received. Indicates packet received with no errors.
+ */
+#define ED_ISR_PRX 0x01
+
+/*
+ * PTX: Packet Transmitted. Indicates packet transmitted with no errors.
+ */
+#define ED_ISR_PTX 0x02
+
+/*
+ * RXE: Receive Error. Indicates that a packet was received with one or more
+ * the following errors: CRC error, frame alignment error, FIFO overrun,
+ * missed packet.
+ */
+#define ED_ISR_RXE 0x04
+
+/*
+ * TXE: Transmission Error. Indicates that an attempt to transmit a packet
+ * resulted in one or more of the following errors: excessive
+ * collisions, FIFO underrun.
+ */
+#define ED_ISR_TXE 0x08
+
+/*
+ * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network
+ * would exceed (has exceeded?) the boundary pointer, resulting in data
+ * that was previously received and not yet read from the buffer to be
+ * overwritten.
+ */
+#define ED_ISR_OVW 0x10
+
+/*
+ * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley
+ * Counters has been set.
+ */
+#define ED_ISR_CNT 0x20
+
+/*
+ * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed.
+ */
+#define ED_ISR_RDC 0x40
+
+/*
+ * RST: Reset status. Set when the NIC enters the reset state and cleared when a
+ * Start Command is issued to the CR. This bit is also set when a receive
+ * ring-buffer overrun (OverWrite) occurs and is cleared when one or more
+ * packets have been removed from the ring. This is a read-only bit.
+ */
+#define ED_ISR_RST 0x80
+
+/*
+ * Interrupt Mask Register (IMR) definitions
+ */
+
+/*
+ * PRXE: Packet Received interrupt Enable. If set, a received packet will cause
+ * an interrupt.
+ */
+#define ED_IMR_PRXE 0x01
+
+/*
+ * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when
+ * a packet transmission completes.
+ */
+#define ED_IMR_PTXE 0x02
+
+/*
+ * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a
+ * packet is received with an error.
+ */
+#define ED_IMR_RXEE 0x04
+
+/*
+ * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever
+ * a transmission results in an error.
+ */
+#define ED_IMR_TXEE 0x08
+
+/*
+ * OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever
+ * the receive ring-buffer is overrun. i.e. when the boundary pointer is exceeded.
+ */
+#define ED_IMR_OVWE 0x10
+
+/*
+ * CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever
+ * the MSB of one or more of the Network Statistics counters has been set.
+ */
+#define ED_IMR_CNTE 0x20
+
+/*
+ * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated
+ * when a remote DMA transfer has completed.
+ */
+#define ED_IMR_RDCE 0x40
+
+/*
+ * bit 7 is unused/reserved
+ */
+
+/*
+ * Data Configuration Register (DCR) definitions
+ */
+
+/*
+ * WTS: Word Transfer Select. WTS establishes byte or word transfers for
+ * both remote and local DMA transfers
+ */
+#define ED_DCR_WTS 0x01
+
+/*
+ * BOS: Byte Order Select. BOS sets the byte order for the host.
+ * Should be 0 for 80x86, and 1 for 68000 series processors
+ */
+#define ED_DCR_BOS 0x02
+
+/*
+ * LAS: Long Address Select. When LAS is 1, the contents of the remote
+ * DMA registers RSAR0 and RSAR1 are used to provide A16-A31
+ */
+#define ED_DCR_LAS 0x04
+
+/*
+ * LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2
+ * of the TCR must also be programmed for loopback operation.
+ * When 1, normal operation is selected.
+ */
+#define ED_DCR_LS 0x08
+
+/*
+ * AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer
+ * under program control. When 1, remote DMA is automatically initiated
+ * and the boundary pointer is automatically updated
+ */
+#define ED_DCR_AR 0x10
+
+/*
+ * FT0, FT1: Fifo Threshold select.
+ * FT1 FT0 Word-width Byte-width
+ * 0 0 1 word 2 bytes
+ * 0 1 2 words 4 bytes
+ * 1 0 4 words 8 bytes
+ * 1 1 8 words 12 bytes
+ *
+ * During transmission, the FIFO threshold indicates the number of bytes
+ * or words that the FIFO has filled from the local DMA before BREQ is
+ * asserted. The transmission threshold is 16 bytes minus the receiver
+ * threshold.
+ */
+#define ED_DCR_FT0 0x20
+#define ED_DCR_FT1 0x40
+
+/*
+ * bit 7 (0x80) is unused/reserved
+ */
+
+/*
+ * Transmit Configuration Register (TCR) definitions
+ */
+
+/*
+ * CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC
+ * is not appended by the transmitter.
+ */
+#define ED_TCR_CRC 0x01
+
+/*
+ * LB0, LB1: Loopback control. These two bits set the type of loopback that is
+ * to be performed.
+ *
+ * LB1 LB0 mode
+ * 0 0 0 - normal operation (DCR_LS = 0)
+ * 0 1 1 - internal loopback (DCR_LS = 0)
+ * 1 0 2 - external loopback (DCR_LS = 1)
+ * 1 1 3 - external loopback (DCR_LS = 0)
+ */
+#define ED_TCR_LB0 0x02
+#define ED_TCR_LB1 0x04
+
+/*
+ * ATD: Auto Transmit Disable. Clear for normal operation. When set, allows
+ * another station to disable the NIC's transmitter by transmitting to
+ * a multicast address hashing to bit 62. Reception of a multicast address
+ * hashing to bit 63 enables the transmitter.
+ */
+#define ED_TCR_ATD 0x08
+
+/*
+ * OFST: Collision Offset enable. This bit when set modifies the backoff
+ * algorithm to allow prioritization of nodes.
+ */
+#define ED_TCR_OFST 0x10
+
+/*
+ * bits 5, 6, and 7 are unused/reserved
+ */
+
+/*
+ * Transmit Status Register (TSR) definitions
+ */
+
+/*
+ * PTX: Packet Transmitted. Indicates successful transmission of packet.
+ */
+#define ED_TSR_PTX 0x01
+
+/*
+ * bit 1 (0x02) is unused/reserved
+ */
+
+/*
+ * COL: Transmit Collided. Indicates that the transmission collided at least
+ * once with another station on the network.
+ */
+#define ED_TSR_COL 0x04
+
+/*
+ * ABT: Transmit aborted. Indicates that the transmission was aborted due to
+ * excessive collisions.
+ */
+#define ED_TSR_ABT 0x08
+
+/*
+ * CRS: Carrier Sense Lost. Indicates that carrier was lost during the
+ * transmission of the packet. (Transmission is not aborted because
+ * of a loss of carrier)
+ */
+#define ED_TSR_CRS 0x10
+
+/*
+ * FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/
+ * transmission memory before the FIFO emptied. Transmission of the
+ * packet was aborted.
+ */
+#define ED_TSR_FU 0x20
+
+/*
+ * CDH: CD Heartbeat. Indicates that the collision detection circuitry
+ * isn't working correctly during a collision heartbeat test.
+ */
+#define ED_TSR_CDH 0x40
+
+/*
+ * OWC: Out of Window Collision: Indicates that a collision occurred after
+ * a slot time (51.2us). The transmission is rescheduled just as in
+ * normal collisions.
+ */
+#define ED_TSR_OWC 0x80
+
+/*
+ * Receiver Configuration Register (RCR) definitions
+ */
+
+/*
+ * SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1,
+ * packets with CRC and frame errors are not discarded.
+ */
+#define ED_RCR_SEP 0x01
+
+/*
+ * AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded.
+ * If set to 1, packets with less than 64 byte are not discarded.
+ */
+#define ED_RCR_AR 0x02
+
+/*
+ * AB: Accept Broadcast. If set, packets sent to the broadcast address will be
+ * accepted.
+ */
+#define ED_RCR_AB 0x04
+
+/*
+ * AM: Accept Multicast. If set, packets sent to a multicast address are checked
+ * for a match in the hashing array. If clear, multicast packets are ignored.
+ */
+#define ED_RCR_AM 0x08
+
+/*
+ * PRO: Promiscuous Physical. If set, all packets with a physical addresses are
+ * accepted. If clear, a physical destination address must match this
+ * station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM
+ * must also be set. In addition, the multicast hashing array must be set
+ * to all 1's so that all multicast addresses are accepted.
+ */
+#define ED_RCR_PRO 0x10
+
+/*
+ * MON: Monitor Mode. If set, packets will be checked for good CRC and framing,
+ * but are not stored in the ring-buffer. If clear, packets are stored (normal
+ * operation).
+ */
+#define ED_RCR_MON 0x20
+
+/*
+ * bits 6 and 7 are unused/reserved.
+ */
+
+/*
+ * Receiver Status Register (RSR) definitions
+ */
+
+/*
+ * PRX: Packet Received without error.
+ */
+#define ED_RSR_PRX 0x01
+
+/*
+ * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame
+ * alignment errors.
+ */
+#define ED_RSR_CRC 0x02
+
+/*
+ * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on
+ * a byte boundary and the CRC did not match at the last byte boundary.
+ */
+#define ED_RSR_FAE 0x04
+
+/*
+ * FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA)
+ * causing it to overrun. Reception of the packet is aborted.
+ */
+#define ED_RSR_FO 0x08
+
+/*
+ * MPA: Missed Packet. Indicates that the received packet couldn't be stored in
+ * the ring-buffer because of insufficient buffer space (exceeding the
+ * boundary pointer), or because the transfer to the ring-buffer was inhibited
+ * by RCR_MON - monitor mode.
+ */
+#define ED_RSR_MPA 0x10
+
+/*
+ * PHY: Physical address. If 0, the packet received was sent to a physical address.
+ * If 1, the packet was accepted because of a multicast/broadcast address
+ * match.
+ */
+#define ED_RSR_PHY 0x20
+
+/*
+ * DIS: Receiver Disabled. Set to indicate that the receiver has entered monitor
+ * mode. Cleared when the receiver exits monitor mode.
+ */
+#define ED_RSR_DIS 0x40
+
+/*
+ * DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs
+ * are active, and the transceiver has set the CD line as a result of the
+ * jabber.
+ */
+#define ED_RSR_DFR 0x80
+
+/*
+ * receive ring descriptor
+ *
+ * The National Semiconductor DS8390 Network interface controller uses
+ * the following receive ring headers. The way this works is that the
+ * memory on the interface card is chopped up into 256 bytes blocks.
+ * A contiguous portion of those blocks are marked for receive packets
+ * by setting start and end block #'s in the NIC. For each packet that
+ * is put into the receive ring, one of these headers (4 bytes each) is
+ * tacked onto the front. The first byte is a copy of the receiver status
+ * register at the time the packet was received.
+ */
+struct ed_ring {
+ u_char rsr; /* receiver status */
+ u_char next_packet; /* pointer to next packet */
+ u_short count; /* bytes in packet (length + 4) */
+};
+
+/*
+ * Common constants
+ */
+#define ED_PAGE_SIZE 256 /* Size of RAM pages in bytes */
+#define ED_TXBUF_SIZE 6 /* Size of TX buffer in pages */
+
+/*
+ * Vendor types
+ */
+#define ED_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */
+#define ED_VENDOR_3COM 0x01 /* 3Com */
+#define ED_VENDOR_NOVELL 0x02 /* Novell */
+#define ED_VENDOR_PCCARD 0x03 /* PCMCIA/PCCARD */
+#ifdef PC98
+#define ED_VENDOR_MISC 0xf0 /* others */
+#endif
+
+/*
+ * Compile-time config flags
+ */
+/*
+ * this sets the default for enabling/disabling the transceiver
+ */
+#define ED_FLAGS_DISABLE_TRANCEIVER 0x0001
+
+/*
+ * This forces the board to be used in 8/16bit mode even if it
+ * autoconfigs differently
+ */
+#define ED_FLAGS_FORCE_8BIT_MODE 0x0002
+#define ED_FLAGS_FORCE_16BIT_MODE 0x0004
+
+/*
+ * This disables the use of double transmit buffers.
+ */
+#define ED_FLAGS_NO_MULTI_BUFFERING 0x0008
+
+/*
+ * This forces all operations with the NIC memory to use Programmed
+ * I/O (i.e. not via shared memory)
+ */
+#define ED_FLAGS_FORCE_PIO 0x0010
+
+/*
+ * Definitions for Western digital/SMC WD80x3 series ASIC
+ */
+/*
+ * Memory Select Register (MSR)
+ */
+#define ED_WD_MSR 0
+
+/* next three definitions for Toshiba */
+#define ED_WD_MSR_POW 0x02 /* 0 = power save, 1 = normal (R/W) */
+#define ED_WD_MSR_BSY 0x04 /* gate array busy (R) */
+#define ED_WD_MSR_LEN 0x20 /* data bus width, 0 = 16 bits,
+ 1 = 8 bits (R/W) */
+#define ED_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */
+#define ED_WD_MSR_MENB 0x40 /* Memory enable */
+#define ED_WD_MSR_RST 0x80 /* Reset board */
+
+/*
+ * Interface Configuration Register (ICR)
+ */
+#define ED_WD_ICR 1
+
+#define ED_WD_ICR_16BIT 0x01 /* 16-bit interface */
+#define ED_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */
+#define ED_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */
+#define ED_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */
+#define ED_WD_ICR_RLA 0x10 /* recall LAN address */
+#define ED_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */
+#define ED_WD_ICR_RIO 0x40 /* recall i/o address */
+#define ED_WD_ICR_STO 0x80 /* store to non-volatile memory */
+#ifdef TOSH_ETHER
+#define ED_WD_ICR_MEM 0xe0 /* shared mem address A15-A13 (R/W) */
+#define ED_WD_ICR_MSZ1 0x0f /* memory size, 0x08 = 64K, 0x04 = 32K,
+ 0x02 = 16K, 0x01 = 8K */
+ /* 64K can only be used if mem address
+ above 1Mb */
+ /* IAR holds address A23-A16 (R/W) */
+#endif
+
+/*
+ * IO Address Register (IAR)
+ */
+#define ED_WD_IAR 2
+
+/*
+ * EEROM Address Register
+ */
+#define ED_WD_EAR 3
+
+/*
+ * Interrupt Request Register (IRR)
+ */
+#define ED_WD_IRR 4
+
+#define ED_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */
+#define ED_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */
+#define ED_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */
+#define ED_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */
+#define ED_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */
+
+/*
+ * The three bits of the encoded IRQ are decoded as follows:
+ *
+ * IR2 IR1 IR0 IRQ
+ * 0 0 0 2/9
+ * 0 0 1 3
+ * 0 1 0 5
+ * 0 1 1 7
+ * 1 0 0 10
+ * 1 0 1 11
+ * 1 1 0 15
+ * 1 1 1 4
+ */
+#define ED_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */
+#define ED_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */
+#define ED_WD_IRR_IEN 0x80 /* Interrupt enable */
+
+/*
+ * LA Address Register (LAAR)
+ */
+#define ED_WD_LAAR 5
+
+#define ED_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */
+#define ED_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */
+#define ED_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */
+#define ED_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */
+
+/* i/o base offset to station address/card-ID PROM */
+#define ED_WD_PROM 8
+
+/*
+ * 83C790 specific registers
+ */
+/*
+ * Hardware Support Register (HWR) ('790)
+ */
+#define ED_WD790_HWR 4
+
+#define WD_WD790_HWR_NUKE 0x10 /* hardware reset */
+#define ED_WD790_HWR_LPRM 0x40 /* LAN PROM select */
+#define ED_WD790_HWR_SWH 0x80 /* switch register set */
+
+/*
+ * ICR790 Interrupt Control Register for the 83C790
+ */
+#define ED_WD790_ICR 6
+
+#define ED_WD790_ICR_EIL 0x01 /* enable interrupts */
+
+/*
+ * REV/IOPA Revision / I/O Pipe register for the 83C79X
+ */
+#define ED_WD790_REV 7
+
+#define ED_WD790 0x20
+#define ED_WD795 0x40
+
+/*
+ * 79X RAM Address Register (RAR)
+ * Enabled with SWH bit=1 in HWR register
+ */
+#define ED_WD790_RAR 0x0b
+
+#define ED_WD790_RAR_SZ8 0x00 /* 8k memory buffer */
+#define ED_WD790_RAR_SZ16 0x10 /* 16k memory buffer */
+#define ED_WD790_RAR_SZ32 0x20 /* 32k memory buffer */
+#define ED_WD790_RAR_SZ64 0x30 /* 64k memory buffer */
+
+/*
+ * General Control Register (GCR)
+ * Enabled with SWH bit=1 in HWR register
+ */
+#define ED_WD790_GCR 0x0d
+
+#define ED_WD790_GCR_IR0 0x04 /* bit 0 of encoded IRQ */
+#define ED_WD790_GCR_IR1 0x08 /* bit 1 of encoded IRQ */
+#define ED_WD790_GCR_ZWSEN 0x20 /* zero wait state enable */
+#define ED_WD790_GCR_IR2 0x40 /* bit 2 of encoded IRQ */
+#define ED_WD790_GCR_LIT 0x01 /* Link Integrity Test Enable */
+/*
+ * The three bits of the encoded IRQ are decoded as follows:
+ *
+ * IR2 IR1 IR0 IRQ
+ * 0 0 0 none
+ * 0 0 1 9
+ * 0 1 0 3
+ * 0 1 1 5
+ * 1 0 0 7
+ * 1 0 1 10
+ * 1 1 0 11
+ * 1 1 1 15
+ */
+
+/* i/o base offset to CARD ID */
+#define ED_WD_CARD_ID ED_WD_PROM+6
+
+/* Board type codes in card ID */
+#define ED_TYPE_WD8003S 0x02
+#define ED_TYPE_WD8003E 0x03
+#define ED_TYPE_WD8013EBT 0x05
+#define ED_TYPE_TOSHIBA1 0x11 /* named PCETA1 */
+#define ED_TYPE_TOSHIBA2 0x12 /* named PCETA2 */
+#define ED_TYPE_TOSHIBA3 0x13 /* named PCETB */
+#define ED_TYPE_TOSHIBA4 0x14 /* named PCETC */
+#define ED_TYPE_WD8003W 0x24
+#define ED_TYPE_WD8003EB 0x25
+#define ED_TYPE_WD8013W 0x26
+#define ED_TYPE_WD8013EP 0x27
+#define ED_TYPE_WD8013WC 0x28
+#define ED_TYPE_WD8013EPC 0x29
+#define ED_TYPE_SMC8216T 0x2a
+#define ED_TYPE_SMC8216C 0x2b
+#define ED_TYPE_WD8013EBP 0x2c
+
+/* Bit definitions in card ID */
+#define ED_WD_REV_MASK 0x1f /* Revision mask */
+#define ED_WD_SOFTCONFIG 0x20 /* Soft config */
+#define ED_WD_LARGERAM 0x40 /* Large RAM */
+#define ED_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */
+
+/*
+ * Checksum total. All 8 bytes in station address PROM will add up to this
+ */
+#ifdef TOSH_ETHER
+#define ED_WD_ROM_CHECKSUM_TOTAL 0xA5
+#else
+#define ED_WD_ROM_CHECKSUM_TOTAL 0xFF
+#endif
+
+#define ED_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */
+#define ED_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */
+#define ED_WD_IO_PORTS 32 /* # of i/o addresses used */
+
+#define ED_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */
+
+/*
+ * Definitions for 3Com 3c503
+ */
+#define ED_3COM_NIC_OFFSET 0
+#define ED_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */
+
+/*
+ * XXX - The I/O address range is fragmented in the 3c503; this is the
+ * number of regs at iobase.
+ */
+#define ED_3COM_IO_PORTS 16 /* # of i/o addresses used */
+
+/* tx memory starts in second bank on 8bit cards */
+#define ED_3COM_TX_PAGE_OFFSET_8BIT 0x20
+
+/* tx memory starts in first bank on 16bit cards */
+#define ED_3COM_TX_PAGE_OFFSET_16BIT 0x0
+
+/* ...and rx memory starts in second bank */
+#define ED_3COM_RX_PAGE_OFFSET_16BIT 0x20
+
+
+/*
+ * Page Start Register. Must match PSTART in NIC
+ */
+#define ED_3COM_PSTR 0
+
+/*
+ * Page Stop Register. Must match PSTOP in NIC
+ */
+#define ED_3COM_PSPR 1
+
+/*
+ * Drq Timer Register. Determines number of bytes to be transfered during
+ * a DMA burst.
+ */
+#define ED_3COM_DQTR 2
+
+/*
+ * Base Configuration Register. Read-only register which contains the
+ * board-configured I/O base address of the adapter. Bit encoded.
+ */
+#define ED_3COM_BCFR 3
+
+#define ED_3COM_BCFR_2E0 0x01
+#define ED_3COM_BCFR_2A0 0x02
+#define ED_3COM_BCFR_280 0x04
+#define ED_3COM_BCFR_250 0x08
+#define ED_3COM_BCFR_350 0x10
+#define ED_3COM_BCFR_330 0x20
+#define ED_3COM_BCFR_310 0x40
+#define ED_3COM_BCFR_300 0x80
+
+/*
+ * EPROM Configuration Register. Read-only register which contains the
+ * board-configured memory base address. Bit encoded.
+ */
+#define ED_3COM_PCFR 4
+
+#define ED_3COM_PCFR_C8000 0x10
+#define ED_3COM_PCFR_CC000 0x20
+#define ED_3COM_PCFR_D8000 0x40
+#define ED_3COM_PCFR_DC000 0x80
+
+/*
+ * GA Configuration Register. Gate-Array Configuration Register.
+ */
+#define ED_3COM_GACFR 5
+
+/*
+ * mbs2 mbs1 mbs0 start address
+ * 0 0 0 0x0000
+ * 0 0 1 0x2000
+ * 0 1 0 0x4000
+ * 0 1 1 0x6000
+ *
+ * Note that with adapters with only 8K, the setting for 0x2000 must
+ * always be used.
+ */
+#define ED_3COM_GACFR_MBS0 0x01
+#define ED_3COM_GACFR_MBS1 0x02
+#define ED_3COM_GACFR_MBS2 0x04
+
+#define ED_3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define ED_3COM_GACFR_TEST 0x10 /* for GA testing */
+#define ED_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define ED_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define ED_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+
+/*
+ * Control Register. Miscellaneous control functions.
+ */
+#define ED_3COM_CR 6
+
+#define ED_3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define ED_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define ED_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define ED_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define ED_3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define ED_3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define ED_3COM_CR_DDIR 0x40 /* DMA direction select */
+#define ED_3COM_CR_START 0x80 /* Start DMA controller */
+
+/*
+ * Status Register. Miscellaneous status information.
+ */
+#define ED_3COM_STREG 7
+
+#define ED_3COM_STREG_REV 0x07 /* GA revision */
+#define ED_3COM_STREG_DIP 0x08 /* DMA in progress */
+#define ED_3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define ED_3COM_STREG_OFLW 0x20 /* Overflow */
+#define ED_3COM_STREG_UFLW 0x40 /* Underflow */
+#define ED_3COM_STREG_DPRDY 0x80 /* Data port ready */
+
+/*
+ * Interrupt/DMA Configuration Register
+ */
+#define ED_3COM_IDCFR 8
+
+#define ED_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define ED_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define ED_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define ED_3COM_IDCFR_UNUSED 0x08 /* not used */
+#define ED_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define ED_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define ED_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define ED_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+
+/*
+ * DMA Address Register MSB
+ */
+#define ED_3COM_DAMSB 9
+
+/*
+ * DMA Address Register LSB
+ */
+#define ED_3COM_DALSB 0x0a
+
+/*
+ * Vector Pointer Register 2
+ */
+#define ED_3COM_VPTR2 0x0b
+
+/*
+ * Vector Pointer Register 1
+ */
+#define ED_3COM_VPTR1 0x0c
+
+/*
+ * Vector Pointer Register 0
+ */
+#define ED_3COM_VPTR0 0x0d
+
+/*
+ * Register File Access MSB
+ */
+#define ED_3COM_RFMSB 0x0e
+
+/*
+ * Register File Access LSB
+ */
+#define ED_3COM_RFLSB 0x0f
+
+/*
+ * Definitions for Novell NE1000/2000 boards
+ */
+
+/*
+ * Board type codes
+ */
+#define ED_TYPE_NE1000 0x01
+#define ED_TYPE_NE2000 0x02
+
+/*
+ * Register offsets/total
+ */
+#ifdef PC98 /* PC98 (LGY-98) */
+#define ED_NOVELL_NIC_OFFSET ed_novell_nic_offset[unit]
+#define ED_NOVELL_ASIC_OFFSET ed_novell_asic_offset[unit]
+#else
+#define ED_NOVELL_NIC_OFFSET 0x00
+#define ED_NOVELL_ASIC_OFFSET 0x10
+#endif
+#define ED_NOVELL_IO_PORTS 32
+
+/*
+ * Remote DMA data register; for reading or writing to the NIC mem
+ * via programmed I/O (offset from ASIC base)
+ */
+#ifdef PC98 /* PC98 (LGY-98) */
+
+/*
+ * Definitions for PCCARD
+ */
+#define ED_PC_PAGE_OFFSET 0x40 /* page offset for NIC access to mem */
+#define ED_PC_IO_PORTS 32
+#define ED_PC_RESET 0x1f
+#define ED_PC_MISC 0x18
+
+/*
+ * if_ze.h constants
+ */
+
+#define ZE_PAGE_OFFSET 0x40 /* mem buffer starts at 0x4000 */
+
+#define ZE_DATA_IO 0x10
+#define ZE_MISC 0x18
+#define ZE_RESET 0x1F
+
+#define ED_NOVELL_DATA ed_novell_data[unit]
+#else
+#define ED_NOVELL_DATA 0x00
+#endif
+
+/*
+ * Reset register; reading from this register causes a board reset
+ */
+#ifdef PC98 /* PC98 (LGY-98) */
+#define ED_NOVELL_RESET ed_novell_reset[unit]
+#else
+#define ED_NOVELL_RESET 0x0f
+#endif
+
+#ifdef PC98
+#define ED_TYPE98_GENERIC 0x10
+#define ED_TYPE98_LGY 0x11
+#define ED_TYPE98_EGY 0x12
+#define ED_TYPE98_ICM 0x13
+#define ED_TYPE98_BDN 0x14
+#define ED_TYPE98_SIC 0x15
+#define ED_TYPE98_LPC 0x16
+
+
+#undef ED_P0_CR
+#define ED_P0_CR edp[unit][0x00]
+
+#undef ED_P0_CLDA0
+#define ED_P0_CLDA0 edp[unit][0x01]
+#undef ED_P0_PSTART
+#define ED_P0_PSTART edp[unit][0x01]
+
+#undef ED_P0_CLDA1
+#define ED_P0_CLDA1 edp[unit][0x02]
+#undef ED_P0_PSTOP
+#define ED_P0_PSTOP edp[unit][0x02]
+
+#undef ED_P0_BNRY
+#define ED_P0_BNRY edp[unit][0x03]
+
+#undef ED_P0_TSR
+#define ED_P0_TSR edp[unit][0x04]
+#undef ED_P0_TPSR
+#define ED_P0_TPSR edp[unit][0x04]
+
+#undef ED_P0_NCR
+#define ED_P0_NCR edp[unit][0x05]
+#undef ED_P0_TBCR0
+#define ED_P0_TBCR0 edp[unit][0x05]
+
+#undef ED_P0_FIFO
+#define ED_P0_FIFO edp[unit][0x06]
+#undef ED_P0_TBCR1
+#define ED_P0_TBCR1 edp[unit][0x06]
+
+#undef ED_P0_ISR
+#define ED_P0_ISR edp[unit][0x07]
+
+#undef ED_P0_CRDA0
+#define ED_P0_CRDA0 edp[unit][0x08]
+#undef ED_P0_RSAR0
+#define ED_P0_RSAR0 edp[unit][0x08]
+
+#undef ED_P0_CRDA1
+#define ED_P0_CRDA1 edp[unit][0x09]
+#undef ED_P0_RSAR1
+#define ED_P0_RSAR1 edp[unit][0x09]
+
+#undef ED_P0_RBCR0
+#define ED_P0_RBCR0 edp[unit][0x0a]
+
+#undef ED_P0_RBCR1
+#define ED_P0_RBCR1 edp[unit][0x0b]
+
+#undef ED_P0_RSR
+#define ED_P0_RSR edp[unit][0x0c]
+#undef ED_P0_RCR
+#define ED_P0_RCR edp[unit][0x0c]
+
+#undef ED_P0_CNTR0
+#define ED_P0_CNTR0 edp[unit][0x0d]
+#undef ED_P0_TCR
+#define ED_P0_TCR edp[unit][0x0d]
+
+#undef ED_P0_CNTR1
+#define ED_P0_CNTR1 edp[unit][0x0e]
+#undef ED_P0_DCR
+#define ED_P0_DCR edp[unit][0x0e]
+
+#undef ED_P0_CNTR2
+#define ED_P0_CNTR2 edp[unit][0x0f]
+#undef ED_P0_IMR
+#define ED_P0_IMR edp[unit][0x0f]
+
+#undef ED_P1_CR
+#define ED_P1_CR edp[unit][0x00]
+#undef ED_P1_PAR0
+#define ED_P1_PAR0 edp[unit][0x01]
+#undef ED_P1_PAR1
+#define ED_P1_PAR1 edp[unit][0x02]
+#undef ED_P1_PAR2
+#define ED_P1_PAR2 edp[unit][0x03]
+#undef ED_P1_PAR3
+#define ED_P1_PAR3 edp[unit][0x04]
+#undef ED_P1_PAR4
+#define ED_P1_PAR4 edp[unit][0x05]
+#undef ED_P1_PAR5
+#define ED_P1_PAR5 edp[unit][0x06]
+#undef ED_P1_CURR
+#define ED_P1_CURR edp[unit][0x07]
+#undef ED_P1_MAR0
+#define ED_P1_MAR0 edp[unit][0x08]
+#undef ED_P1_MAR1
+#define ED_P1_MAR1 edp[unit][0x09]
+#undef ED_P1_MAR2
+#define ED_P1_MAR2 edp[unit][0x0a]
+#undef ED_P1_MAR3
+#define ED_P1_MAR3 edp[unit][0x0b]
+#undef ED_P1_MAR4
+#define ED_P1_MAR4 edp[unit][0x0c]
+#undef ED_P1_MAR5
+#define ED_P1_MAR5 edp[unit][0x0d]
+#undef ED_P1_MAR6
+#define ED_P1_MAR6 edp[unit][0x0e]
+#undef ED_P1_MAR7
+#define ED_P1_MAR7 edp[unit][0x0f]
+
+#undef ED_P2_CR
+#define ED_P2_CR edp[unit][0x00]
+#undef ED_P2_PSTART
+#define ED_P2_PSTART edp[unit][0x01]
+#undef ED_P2_CLDA0
+#define ED_P2_CLDA0 edp[unit][0x01]
+#undef ED_P2_PSTOP
+#define ED_P2_PSTOP edp[unit][0x02]
+#undef ED_P2_CLDA1
+#define ED_P2_CLDA1 edp[unit][0x02]
+#undef ED_P2_RNPP
+#define ED_P2_RNPP edp[unit][0x03]
+#undef ED_P2_TPSR
+#define ED_P2_TPSR edp[unit][0x04]
+#undef ED_P2_LNPP
+#define ED_P2_LNPP edp[unit][0x05]
+#undef ED_P2_ACU
+#define ED_P2_ACU edp[unit][0x06]
+#undef ED_P2_ACL
+#define ED_P2_ACL edp[unit][0x07]
+#undef ED_P2_RCR
+#define ED_P2_RCR edp[unit][0x0c]
+#undef ED_P2_TCR
+#define ED_P2_TCR edp[unit][0x0d]
+#undef ED_P2_DCR
+#define ED_P2_DCR edp[unit][0x0e]
+#undef ED_P2_IMR
+#define ED_P2_IMR edp[unit][0x0f]
+#endif /* PC98 */
diff --git a/sys/pc98/pc98/if_ep.c b/sys/pc98/pc98/if_ep.c
new file mode 100644
index 0000000..c8dd62f
--- /dev/null
+++ b/sys/pc98/pc98/if_ep.c
@@ -0,0 +1,1502 @@
+/*
+ * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
+ * 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 Herb Peyerl.
+ * 4. The name of Herb Peyerl 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.
+ *
+ * if_ep.c,v 1.19 1995/01/24 20:53:45 davidg Exp
+ */
+
+/*
+ * Modified from the FreeBSD 1.1.5.1 version by:
+ * Andres Vega Garcia
+ * INRIA - Sophia Antipolis, France
+ * avega@sophia.inria.fr
+ */
+
+/*
+ * $Id: if_ep.c,v 1.44 1996/05/24 15:22:36 gibbs Exp $
+ *
+ * Promiscuous mode added and interrupt logic slightly changed
+ * to reduce the number of adapter failures. Transceiver select
+ * logic changed to use value from EEPROM. Autoconfiguration
+ * features added.
+ * Done by:
+ * Serge Babkin
+ * Chelindbank (Chelyabinsk, Russia)
+ * babkin@hq.icb.chel.su
+ */
+
+#include "ep.h"
+#if NEP > 0
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#if defined(__FreeBSD__)
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/devconf.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#if defined(__NetBSD__)
+#include <sys/select.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef IPX
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#if defined(__FreeBSD__)
+#include <machine/clock.h>
+#endif
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/if_epreg.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/if_epreg.h>
+#endif
+#include <i386/isa/elink.h>
+
+/* Exported variables */
+u_long ep_unit;
+int ep_boards;
+struct ep_board ep_board[EP_MAX_BOARDS + 1];
+
+static int eeprom_rdy __P((struct ep_softc *sc));
+
+#ifdef PC98
+static int ep_isa_probe __P((struct pc98_device *));
+static struct ep_board * ep_look_for_board_at __P((struct pc98_device *is));
+static int ep_isa_attach __P((struct pc98_device *));
+static void ep_isa_registerdev __P((struct ep_softc *sc,
+ struct pc98_device *id));
+#else
+static int ep_isa_probe __P((struct isa_device *));
+static struct ep_board * ep_look_for_board_at __P((struct isa_device *is));
+static int ep_isa_attach __P((struct isa_device *));
+static void ep_isa_registerdev __P((struct ep_softc *sc,
+ struct isa_device *id));
+#endif
+static int epioctl __P((struct ifnet * ifp, int, caddr_t));
+static void epmbuffill __P((caddr_t, int));
+static void epmbufempty __P((struct ep_softc *));
+
+static void epinit __P((struct ep_softc *));
+static void epread __P((struct ep_softc *));
+void epreset __P((int));
+static void epstart __P((struct ifnet *));
+static void epstop __P((struct ep_softc *));
+static void epwatchdog __P((struct ifnet *));
+
+static int send_ID_sequence __P((int));
+static int get_eeprom_data __P((int, int));
+
+static struct ep_softc* ep_softc[NEP];
+static int ep_current_tag = EP_LAST_TAG + 1;
+static char *ep_conn_type[] = {"UTP", "AUI", "???", "BNC"};
+
+#define ep_ftst(f) (sc->stat&(f))
+#define ep_fset(f) (sc->stat|=(f))
+#define ep_frst(f) (sc->stat&=~(f))
+
+#ifdef PC98
+struct pc98_driver epdriver = {
+#else
+struct isa_driver epdriver = {
+#endif
+ ep_isa_probe,
+ ep_isa_attach,
+ "ep",
+ 0
+};
+
+static struct kern_devconf kdc_isa_ep = {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ "ep", 0, { MDDT_PC98, 0, "net" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "ep", 0, { MDDT_ISA, 0, "net" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "3Com 3C509 Ethernet adapter",
+ DC_CLS_NETIF /* class */
+};
+
+static void
+ep_isa_registerdev(sc, id)
+ struct ep_softc *sc;
+#ifdef PC98
+ struct pc98_device *id;
+#else
+ struct isa_device *id;
+#endif
+{
+ sc->kdc = (struct kern_devconf *)malloc(sizeof(struct kern_devconf),
+ M_DEVBUF, M_NOWAIT);
+ if (!sc->kdc) {
+ printf("WARNING: ep_isa_registerdev unable to malloc! "
+ "Device kdc will not be registerd\n");
+ return;
+ }
+ bcopy(&kdc_isa_ep, sc->kdc, sizeof(kdc_isa_ep));
+ sc->kdc->kdc_unit = sc->unit;
+ sc->kdc->kdc_parentdata = id;
+ dev_attach(sc->kdc);
+}
+
+static int
+eeprom_rdy(sc)
+ struct ep_softc *sc;
+{
+ int i;
+
+ for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++);
+ if (i >= MAX_EEPROMBUSY) {
+ printf("ep%d: eeprom failed to come ready.\n", sc->unit);
+ return (0);
+ }
+ return (1);
+}
+
+static struct ep_board *
+ep_look_for_board_at(is)
+#ifdef PC98
+ struct pc98_device *is;
+#else
+ struct isa_device *is;
+#endif
+{
+ int data, i, j, io_base, id_port = ELINK_ID_PORT;
+ int count = 0;
+
+ if (ep_current_tag == (EP_LAST_TAG + 1)) {
+ /* Come here just one time */
+
+ ep_current_tag--;
+
+ /* Look for the ISA boards. Init and leave them actived */
+ outb(id_port, 0);
+ outb(id_port, 0);
+
+ elink_idseq(0xCF);
+
+ elink_reset();
+ DELAY(10000);
+ for (i = 0; i < EP_MAX_BOARDS; i++) {
+ outb(id_port, 0);
+ outb(id_port, 0);
+ elink_idseq(0xCF);
+
+ data = get_eeprom_data(id_port, EEPROM_MFG_ID);
+ if (data != MFG_ID)
+ break;
+
+ /* resolve contention using the Ethernet address */
+
+ for (j = 0; j < 3; j++)
+ get_eeprom_data(id_port, j);
+
+ /* and save this address for later use */
+
+ for (j = 0; j < 3; j++)
+ ep_board[ep_boards].eth_addr[j] = get_eeprom_data(id_port, j);
+
+ ep_board[ep_boards].res_cfg =
+ get_eeprom_data(id_port, EEPROM_RESOURCE_CFG);
+
+ ep_board[ep_boards].prod_id =
+ get_eeprom_data(id_port, EEPROM_PROD_ID);
+
+ ep_board[ep_boards].epb_used = 0;
+ ep_board[ep_boards].epb_addr =
+ (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200;
+
+ if(ep_board[ep_boards].epb_addr > 0x3E0)
+ /* Board in EISA configuration mode */
+ continue;
+
+ outb(id_port, ep_current_tag); /* tags board */
+ outb(id_port, ACTIVATE_ADAPTER_TO_CONFIG);
+ ep_boards++;
+ count++;
+ ep_current_tag--;
+ }
+
+ ep_board[ep_boards].epb_addr = 0;
+ if (count) {
+ printf("%d 3C5x9 board(s) on ISA found at", count);
+ for (j = 0; ep_board[j].epb_addr; j++)
+ if (ep_board[j].epb_addr <= 0x3E0)
+ printf(" 0x%x", ep_board[j].epb_addr);
+ printf("\n");
+ }
+ }
+
+ /* we have two cases:
+ *
+ * 1. Device was configured with 'port ?'
+ * In this case we search for the first unused card in list
+ *
+ * 2. Device was configured with 'port xxx'
+ * In this case we search for the unused card with that address
+ *
+ */
+
+ if(IS_BASE==-1) { /* port? */
+ for (i = 0; ep_board[i].epb_addr && ep_board[i].epb_used; i++);
+ if(ep_board[i].epb_addr==0)
+ return 0;
+
+ IS_BASE=ep_board[i].epb_addr;
+ ep_board[i].epb_used=1;
+
+ return &ep_board[i];
+ } else {
+ for (i=0; ep_board[i].epb_addr && ep_board[i].epb_addr != IS_BASE; i++);
+
+ if( ep_board[i].epb_used || ep_board[i].epb_addr != IS_BASE)
+ return 0;
+
+ if (inw(IS_BASE + EP_W0_EEPROM_COMMAND) & EEPROM_TST_MODE)
+ printf("ep%d: 3c5x9 at 0x%x in PnP mode. Disable PnP mode!\n",
+ is->id_unit, IS_BASE);
+ ep_board[i].epb_used=1;
+
+ return &ep_board[i];
+ }
+}
+
+/*
+ * get_e: gets a 16 bits word from the EEPROM. we must have set the window
+ * before
+ */
+u_int16_t
+get_e(sc, offset)
+ struct ep_softc *sc;
+ int offset;
+{
+ if (!eeprom_rdy(sc))
+ return (0xffff);
+ outw(BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset);
+ if (!eeprom_rdy(sc))
+ return (0xffff);
+ return (inw(BASE + EP_W0_EEPROM_DATA));
+}
+
+struct ep_softc *
+ep_alloc(unit, epb)
+ int unit;
+ struct ep_board *epb;
+{
+ struct ep_softc *sc;
+
+ if (unit >= NEP) {
+ printf("ep: unit number (%d) too high\n", unit);
+ return NULL;
+ }
+
+ /*
+ * Allocate a storage area for us
+ */
+ if (ep_softc[unit]) {
+ printf("ep%d: unit number already allocated to another "
+ "adaptor\n", unit);
+ return NULL;
+ }
+
+ sc = malloc(sizeof(struct ep_softc), M_DEVBUF, M_NOWAIT);
+ if(!sc) {
+ printf("ep%d: cannot malloc!\n", unit);
+ return NULL;
+ }
+ bzero(sc, sizeof(struct ep_softc));
+ ep_softc[unit] = sc;
+ sc->unit = unit;
+ sc->ep_io_addr = epb->epb_addr;
+ sc->epb = epb;
+
+ return(sc);
+}
+
+void
+ep_free(sc)
+ struct ep_softc *sc;
+{
+ ep_softc[sc->unit] = NULL;
+ free(sc, M_DEVBUF);
+ return;
+}
+
+int
+ep_isa_probe(is)
+#ifdef PC98
+ struct pc98_device *is;
+#else
+ struct isa_device *is;
+#endif
+{
+ struct ep_softc *sc;
+ struct ep_board *epb;
+ u_short k;
+ int i;
+
+ if(( epb=ep_look_for_board_at(is) )==0)
+ return (0);
+
+ /*
+ * Allocate a storage area for us
+ */
+ sc = ep_alloc(ep_unit, epb);
+ if( !sc )
+ return (0);
+
+ is->id_unit = ep_unit++;
+
+ ep_isa_registerdev(sc, is);
+
+ /*
+ * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be
+ * 0x9[0-f]50
+ */
+ GO_WINDOW(0);
+ k = sc->epb->prod_id;
+ if ((k & 0xf0ff) != (PROD_ID & 0xf0ff)) {
+ printf("ep_isa_probe: ignoring model %04x\n", k);
+ ep_free(sc);
+ return (0);
+ }
+
+ k = sc->epb->res_cfg;
+
+ k >>= 12;
+
+ /* Now we have two cases again:
+ *
+ * 1. Device was configured with 'irq?'
+ * In this case we use irq read from the board
+ *
+ * 2. Device was configured with 'irq xxx'
+ * In this case we set up the board to use specified interrupt
+ *
+ */
+
+ if(is->id_irq==0) { /* irq? */
+ is->id_irq= 1 << ( (k==2) ? 9 : k );
+ }
+
+ sc->stat = 0; /* 16 bit access */
+
+ /* By now, the adapter is already activated */
+
+ return (EP_IOSIZE); /* 16 bytes of I/O space used. */
+}
+
+static int
+ep_isa_attach(is)
+#ifdef PC98
+ struct pc98_device *is;
+#else
+ struct isa_device *is;
+#endif
+{
+ struct ep_softc *sc = ep_softc[is->id_unit];
+ u_short config;
+ int irq;
+
+ sc->ep_connectors = 0;
+ config = inw(IS_BASE + EP_W0_CONFIG_CTRL);
+ if (config & IS_AUI) {
+ sc->ep_connectors |= AUI;
+ }
+ if (config & IS_BNC) {
+ sc->ep_connectors |= BNC;
+ }
+ if (config & IS_UTP) {
+ sc->ep_connectors |= UTP;
+ }
+ if (!(sc->ep_connectors & 7))
+ printf("no connectors!");
+ sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS;
+ /*
+ * Write IRQ value to board
+ */
+
+ irq = ffs(is->id_irq) - 1;
+ if(irq == -1) {
+ printf(" invalid irq... cannot attach\n");
+ return 0;
+ }
+
+ GO_WINDOW(0);
+ if(irq == 9)
+ irq = 2;
+ outw(BASE + EP_W0_RESOURCE_CFG, SET_IRQ(irq));
+
+ ep_attach(sc);
+ return 1;
+}
+
+int
+ep_attach(sc)
+ struct ep_softc *sc;
+{
+ struct ifaddr *ifa;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct sockaddr_dl *sdl;
+ u_short *p;
+ int i;
+
+ printf("ep%d: ", sc->unit);
+ /*
+ * Current media type
+ */
+ if(sc->ep_connectors & AUI) {
+ printf("aui");
+ if(sc->ep_connectors & ~AUI)
+ printf("/");
+ }
+ if(sc->ep_connectors & UTP) {
+ printf("utp");
+ if(sc->ep_connectors & BNC)
+ printf("/");
+ }
+ if(sc->ep_connectors & BNC) {
+ printf("bnc");
+ }
+
+ printf("[*%s*]", ep_conn_type[sc->ep_connector]);
+
+ /*
+ * Setup the station address
+ */
+ p = (u_short *) & sc->arpcom.ac_enaddr;
+ GO_WINDOW(2);
+ for (i = 0; i < 3; i++) {
+ p[i] = htons(sc->epb->eth_addr[i]);
+ outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(p[i]));
+ }
+ printf(" address %6D\n", sc->arpcom.ac_enaddr, ":");
+
+ ifp->if_softc = sc;
+ ifp->if_unit = sc->unit;
+ ifp->if_name = "ep";
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_output = ether_output;
+ ifp->if_start = epstart;
+ ifp->if_ioctl = epioctl;
+ ifp->if_watchdog = epwatchdog;
+
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+ /* device attach does transition from UNCONFIGURED to IDLE state */
+ sc->kdc->kdc_state=DC_IDLE;
+
+ /*
+ * Fill the hardware address into ifa_addr if we find an AF_LINK entry.
+ * We need to do this so bpf's can get the hardware addr of this card.
+ * netstat likes this too!
+ */
+ ifa = ifp->if_addrlist;
+ while ((ifa != 0) && (ifa->ifa_addr != 0) &&
+ (ifa->ifa_addr->sa_family != AF_LINK))
+ ifa = ifa->ifa_next;
+
+ if ((ifa != 0) && (ifa->ifa_addr != 0)) {
+ sdl = (struct sockaddr_dl *) ifa->ifa_addr;
+ sdl->sdl_type = IFT_ETHER;
+ sdl->sdl_alen = ETHER_ADDR_LEN;
+ sdl->sdl_slen = 0;
+ bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN);
+ }
+ /* we give some initial parameters */
+ sc->rx_avg_pkt = 128;
+
+ /*
+ * NOTE: In all this I multiply everything by 64.
+ * W_s = the speed the CPU is able to write to the TX FIFO.
+ * T_s = the speed the board sends the info to the Ether.
+ * W_s/T_s = 16 (represents 16/64) => W_s = 25 % of T_s.
+ * This will give us for a packet of 1500 bytes
+ * tx_start_thresh=1125 and for a pkt of 64 bytes tx_start_threshold=48.
+ * We prefer to start thinking the CPU is much slower than the Ethernet
+ * transmission.
+ */
+ sc->tx_rate = TX_INIT_RATE;
+ sc->tx_counter = 0;
+ sc->rx_latency = RX_INIT_LATENCY;
+ sc->rx_early_thresh = RX_INIT_EARLY_THRESH;
+#ifdef EP_LOCAL_STATS
+ sc->rx_no_first = sc->rx_no_mbuf =
+ sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl =
+ sc->tx_underrun = 0;
+#endif
+ ep_fset(F_RX_FIRST);
+ sc->top = sc->mcur = 0;
+
+#if NBPFILTER > 0
+ bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 0;
+}
+
+
+/*
+ * The order in here seems important. Otherwise we may not receive
+ * interrupts. ?!
+ */
+static void
+epinit(sc)
+ struct ep_softc *sc;
+{
+ register struct ifnet *ifp = &sc->arpcom.ac_if;
+ int s, i, j;
+
+ /*
+ if (ifp->if_addrlist == (struct ifaddr *) 0)
+ return;
+ */
+
+ s = splimp();
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+
+ GO_WINDOW(0);
+ outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
+ GO_WINDOW(4);
+ outw(BASE + EP_W4_MEDIA_TYPE, DISABLE_UTP);
+ GO_WINDOW(0);
+
+ /* Disable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, 0);
+
+ /* Enable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
+
+ GO_WINDOW(2);
+
+ /* Reload the ether_addr. */
+ for (i = 0; i < 6; i++)
+ outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]);
+
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+
+ /* Window 1 is operating window */
+ GO_WINDOW(1);
+ for (i = 0; i < 31; i++)
+ inb(BASE + EP_W1_TX_STATUS);
+
+ /* get rid of stray intr's */
+ outw(BASE + EP_COMMAND, ACK_INTR | 0xff);
+
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS);
+
+ outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS);
+
+ if(ifp->if_flags & IFF_PROMISC)
+ outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
+ FIL_GROUP | FIL_BRDCST | FIL_ALL);
+ else
+ outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
+ FIL_GROUP | FIL_BRDCST);
+
+ /*
+ * S.B.
+ *
+ * Now behavior was slightly changed:
+ *
+ * if any of flags link[0-2] is used and its connector is
+ * physically present the following connectors are used:
+ *
+ * link0 - AUI * highest precedence
+ * link1 - BNC
+ * link2 - UTP * lowest precedence
+ *
+ * If none of them is specified then
+ * connector specified in the EEPROM is used
+ * (if present on card or AUI if not).
+ *
+ */
+
+ if(ifp->if_flags & IFF_LINK0 && sc->ep_connectors & AUI) {
+ /* nothing */
+ } else if(ifp->if_flags & IFF_LINK1 && sc->ep_connectors & BNC) {
+ outw(BASE + EP_COMMAND, START_TRANSCEIVER);
+ DELAY(1000);
+ } else if(ifp->if_flags & IFF_LINK2 && sc->ep_connectors & UTP) {
+ GO_WINDOW(4);
+ outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
+ GO_WINDOW(1);
+ } else {
+ GO_WINDOW(1);
+ switch(sc->ep_connector) {
+ case ACF_CONNECTOR_UTP:
+ if(sc->ep_connectors & UTP) {
+ GO_WINDOW(4);
+ outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
+ GO_WINDOW(1);
+ }
+ break;
+ case ACF_CONNECTOR_BNC:
+ if(sc->ep_connectors & BNC) {
+ outw(BASE + EP_COMMAND, START_TRANSCEIVER);
+ DELAY(1000);
+ }
+ break;
+ case ACF_CONNECTOR_AUI:
+ /* nothing to do */
+ break;
+ default:
+ printf("ep%d: strange connector type in EEPROM: assuming AUI\n",
+ sc->unit);
+ break;
+ }
+ }
+
+ outw(BASE + EP_COMMAND, RX_ENABLE);
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE; /* just in case */
+
+ sc->tx_rate = TX_INIT_RATE;
+ sc->tx_counter = 0;
+ sc->rx_latency = RX_INIT_LATENCY;
+ sc->rx_early_thresh = RX_INIT_EARLY_THRESH;
+#ifdef EP_LOCAL_STATS
+ sc->rx_no_first = sc->rx_no_mbuf =
+ sc->rx_bpf_disc = sc->rx_overrunf = sc->rx_overrunl =
+ sc->tx_underrun = 0;
+#endif
+ ep_fset(F_RX_FIRST);
+ ep_frst(F_RX_TRAILER);
+ if (sc->top) {
+ m_freem(sc->top);
+ sc->top = sc->mcur = 0;
+ }
+ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | sc->rx_early_thresh);
+
+ /*
+ * These clever computations look very interesting
+ * but the fixed threshold gives near no output errors
+ * and if it as low as 16 bytes it gives the max. throughput.
+ * We think that processor is anyway quicker than Ethernet
+ * (and this should be true for any 386 and higher)
+ */
+
+ outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16);
+
+ /*
+ * Store up a bunch of mbuf's for use later. (MAX_MBS). First we free up
+ * any that we had in case we're being called from intr or somewhere
+ * else.
+ */
+ sc->last_mb = 0;
+ sc->next_mb = 0;
+ epmbuffill((caddr_t) sc, 0);
+
+ epstart(ifp);
+
+ splx(s);
+}
+
+static const char padmap[] = {0, 3, 2, 1};
+
+static void
+epstart(ifp)
+ struct ifnet *ifp;
+{
+ register struct ep_softc *sc = ifp->if_softc;
+ register u_int len;
+ register struct mbuf *m;
+ struct mbuf *top;
+ int s, pad;
+
+ s = splimp();
+ if (ifp->if_flags & IFF_OACTIVE) {
+ splx(s);
+ return;
+ }
+startagain:
+ /* Sneak a peek at the next packet */
+ m = ifp->if_snd.ifq_head;
+ if (m == 0) {
+ splx(s);
+ return;
+ }
+ for (len = 0, top = m; m; m = m->m_next)
+ len += m->m_len;
+
+ pad = padmap[len & 3];
+
+ /*
+ * The 3c509 automatically pads short packets to minimum ethernet length,
+ * but we drop packets that are too large. Perhaps we should truncate
+ * them instead?
+ */
+ if (len + pad > ETHER_MAX_LEN) {
+ /* packet is obviously too large: toss it */
+ ++ifp->if_oerrors;
+ IF_DEQUEUE(&ifp->if_snd, m);
+ m_freem(m);
+ goto readcheck;
+ }
+ if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) {
+ /* no room in FIFO */
+ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4));
+ ifp->if_flags |= IFF_OACTIVE;
+ splx(s);
+ return;
+ }
+ IF_DEQUEUE(&ifp->if_snd, m);
+
+ outw(BASE + EP_W1_TX_PIO_WR_1, len);
+ outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */
+
+ /* compute the Tx start threshold for this packet */
+ sc->tx_start_thresh = len =
+ (((len * (64 - sc->tx_rate)) >> 6) & ~3) + 16;
+#if 0
+ /*
+ * The following string does something strange with the card and
+ * we get a lot of output errors due to it so it's commented out
+ * and we use fixed threshold (see above)
+ */
+
+ outw(BASE + EP_COMMAND, SET_TX_START_THRESH | len);
+#endif
+
+ for (top = m; m != 0; m = m->m_next)
+ if(ep_ftst(F_ACCESS_32_BITS)) {
+ outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t),
+ m->m_len / 4);
+ if (m->m_len & 3)
+ outsb(BASE + EP_W1_TX_PIO_WR_1,
+ mtod(m, caddr_t) + (m->m_len & (~3)),
+ m->m_len & 3);
+ } else {
+ outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2);
+ if (m->m_len & 1)
+ outb(BASE + EP_W1_TX_PIO_WR_1,
+ *(mtod(m, caddr_t) + m->m_len - 1));
+ }
+
+ while (pad--)
+ outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf) {
+ bpf_mtap(ifp, top);
+ }
+#endif
+
+ ifp->if_timer=2;
+ ifp->if_opackets++;
+ m_freem(top);
+ /*
+ * Every 1024*4 packets we increment the tx_rate if we haven't had
+ * errors, that in the case it has abnormaly goten too low
+ */
+ if (!(++sc->tx_counter & (1024 * 4 - 1)) &&
+ sc->tx_rate < TX_INIT_MAX_RATE)
+ sc->tx_rate++;
+
+ /*
+ * Is another packet coming in? We don't want to overflow the tiny RX
+ * fifo.
+ */
+readcheck:
+ if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) {
+ /*
+ * we check if we have packets left, in that case we prepare to come
+ * back later
+ */
+ if (ifp->if_snd.ifq_head) {
+ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH |
+ sc->tx_start_thresh);
+ }
+ splx(s);
+ return;
+ }
+ goto startagain;
+}
+
+void
+epintr(unit)
+ int unit;
+{
+ register struct ep_softc *sc = ep_softc[unit];
+ ep_intr(sc);
+}
+
+void
+ep_intr(arg)
+ void *arg;
+{
+ struct ep_softc *sc;
+ register int status;
+ struct ifnet *ifp;
+ int x;
+
+ x=splbio();
+
+ sc = (struct ep_softc *)arg;
+
+ ifp = &sc->arpcom.ac_if;
+
+ outw(BASE + EP_COMMAND, SET_INTR_MASK); /* disable all Ints */
+
+rescan:
+
+ while ((status = inw(BASE + EP_STATUS)) & S_5_INTS) {
+
+ /* first acknowledge all interrupt sources */
+ outw(BASE + EP_COMMAND, ACK_INTR | (status & S_MASK));
+
+ if (status & (S_RX_COMPLETE | S_RX_EARLY)) {
+ epread(sc);
+ continue;
+ }
+ if (status & S_TX_AVAIL) {
+ /* we need ACK */
+ ifp->if_timer=0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ GO_WINDOW(1);
+ inw(BASE + EP_W1_FREE_TX);
+ epstart(ifp);
+ }
+ if (status & S_CARD_FAILURE) {
+ ifp->if_timer=0;
+#ifdef EP_LOCAL_STATS
+ printf("\nep%d:\n\tStatus: %x\n", sc->unit, status);
+ GO_WINDOW(4);
+ printf("\tFIFO Diagnostic: %x\n", inw(BASE + EP_W4_FIFO_DIAG));
+ printf("\tStat: %x\n", sc->stat);
+ printf("\tIpackets=%d, Opackets=%d\n",
+ ifp->if_ipackets, ifp->if_opackets);
+ printf("\tNOF=%d, NOMB=%d, BPFD=%d, RXOF=%d, RXOL=%d, TXU=%d\n",
+ sc->rx_no_first, sc->rx_no_mbuf, sc->rx_bpf_disc, sc->rx_overrunf,
+ sc->rx_overrunl, sc->tx_underrun);
+#else
+
+#ifdef DIAGNOSTIC
+ printf("ep%d: Status: %x (input buffer overflow)\n", sc->unit, status);
+#else
+ ++ifp->if_ierrors;
+#endif
+
+#endif
+ epinit(sc);
+ splx(x);
+ return;
+ }
+ if (status & S_TX_COMPLETE) {
+ ifp->if_timer=0;
+ /* we need ACK. we do it at the end */
+ /*
+ * We need to read TX_STATUS until we get a 0 status in order to
+ * turn off the interrupt flag.
+ */
+ while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) {
+ if (status & TXS_SUCCES_INTR_REQ);
+ else if (status & (TXS_UNDERRUN | TXS_JABBER | TXS_MAX_COLLISION)) {
+ outw(BASE + EP_COMMAND, TX_RESET);
+ if (status & TXS_UNDERRUN) {
+ if (sc->tx_rate > 1) {
+ sc->tx_rate--; /* Actually in steps of 1/64 */
+ sc->tx_counter = 0; /* We reset it */
+ }
+#ifdef EP_LOCAL_STATS
+ sc->tx_underrun++;
+#endif
+ } else {
+ if (status & TXS_JABBER);
+ else /* TXS_MAX_COLLISION - we shouldn't get here */
+ ++ifp->if_collisions;
+ }
+ ++ifp->if_oerrors;
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+ /*
+ * To have a tx_avail_int but giving the chance to the
+ * Reception
+ */
+ if (ifp->if_snd.ifq_head) {
+ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8);
+ }
+ }
+ outb(BASE + EP_W1_TX_STATUS, 0x0); /* pops up the next
+ * status */
+ } /* while */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ GO_WINDOW(1);
+ inw(BASE + EP_W1_FREE_TX);
+ epstart(ifp);
+ } /* end TX_COMPLETE */
+ }
+
+ outw(BASE + EP_COMMAND, C_INTR_LATCH); /* ACK int Latch */
+
+ if ((status = inw(BASE + EP_STATUS)) & S_5_INTS)
+ goto rescan;
+
+ /* re-enable Ints */
+ outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS);
+
+ splx(x);
+}
+
+static void
+epread(sc)
+ register struct ep_softc *sc;
+{
+ struct ether_header *eh;
+ struct mbuf *top, *mcur, *m;
+ struct ifnet *ifp;
+ int lenthisone;
+
+ short rx_fifo2, status;
+ register short delta;
+ register short rx_fifo;
+
+ ifp = &sc->arpcom.ac_if;
+ status = inw(BASE + EP_W1_RX_STATUS);
+
+read_again:
+
+ if (status & ERR_RX) {
+ ++ifp->if_ierrors;
+ if (status & ERR_RX_OVERRUN) {
+ /*
+ * we can think the rx latency is actually greather than we
+ * expect
+ */
+#ifdef EP_LOCAL_STATS
+ if (ep_ftst(F_RX_FIRST))
+ sc->rx_overrunf++;
+ else
+ sc->rx_overrunl++;
+#endif
+ if (sc->rx_latency < ETHERMTU)
+ sc->rx_latency += 16;
+ }
+ goto out;
+ }
+ rx_fifo = rx_fifo2 = status & RX_BYTES_MASK;
+
+ if (ep_ftst(F_RX_FIRST)) {
+ if (m = sc->mb[sc->next_mb]) {
+ sc->mb[sc->next_mb] = 0;
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ m->m_data = m->m_pktdat;
+ m->m_flags = M_PKTHDR;
+ } else {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (!m)
+ goto out;
+ }
+ sc->top = sc->mcur = top = m;
+#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
+#define EOFF (EROUND - sizeof(struct ether_header))
+ top->m_data += EOFF;
+
+ /* Read what should be the header. */
+ insw(BASE + EP_W1_RX_PIO_RD_1,
+ mtod(top, caddr_t), sizeof(struct ether_header) / 2);
+ top->m_len = sizeof(struct ether_header);
+ rx_fifo -= sizeof(struct ether_header);
+ sc->cur_len = rx_fifo2;
+ } else {
+ /* come here if we didn't have a complete packet last time */
+ top = sc->top;
+ m = sc->mcur;
+ sc->cur_len += rx_fifo2;
+ if (ep_ftst(F_RX_TRAILER))
+ /* We don't read the trailer */
+ rx_fifo -= sizeof(struct ether_header);
+ }
+
+ /* Reads what is left in the RX FIFO */
+ while (rx_fifo > 0) {
+ lenthisone = min(rx_fifo, M_TRAILINGSPACE(m));
+ if (lenthisone == 0) { /* no room in this one */
+ mcur = m;
+ if (m = sc->mb[sc->next_mb]) {
+ sc->mb[sc->next_mb] = 0;
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ } else {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (!m)
+ goto out;
+ }
+
+ if (rx_fifo >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+ m->m_len = 0;
+ mcur->m_next = m;
+ lenthisone = min(rx_fifo, M_TRAILINGSPACE(m));
+ }
+ if (ep_ftst(F_ACCESS_32_BITS)) { /* default for EISA configured cards*/
+ insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len,
+ lenthisone / 4);
+ m->m_len += (lenthisone & ~3);
+ if (lenthisone & 3)
+ insb(BASE + EP_W1_RX_PIO_RD_1,
+ mtod(m, caddr_t) + m->m_len,
+ lenthisone & 3);
+ m->m_len += (lenthisone & 3);
+ } else {
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len,
+ lenthisone / 2);
+ m->m_len += lenthisone;
+ if (lenthisone & 1)
+ *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1);
+ }
+ rx_fifo -= lenthisone;
+ }
+
+ if (ep_ftst(F_RX_TRAILER)) {/* reads the trailer */
+ if (m = sc->mb[sc->next_mb]) {
+ sc->mb[sc->next_mb] = 0;
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ m->m_data = m->m_pktdat;
+ m->m_flags = M_PKTHDR;
+ } else {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (!m)
+ goto out;
+ }
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t),
+ sizeof(struct ether_header));
+ m->m_len = sizeof(struct ether_header);
+ m->m_next = top;
+ sc->top = top = m;
+ /* XXX Accomodate for type and len from beginning of trailer */
+ sc->cur_len -= (2 * sizeof(u_short));
+ ep_frst(F_RX_TRAILER);
+ goto all_pkt;
+ }
+
+ if (status & ERR_RX_INCOMPLETE) { /* we haven't received the complete
+ * packet */
+ sc->mcur = m;
+#ifdef EP_LOCAL_STATS
+ sc->rx_no_first++; /* to know how often we come here */
+#endif
+ /*
+ * Re-compute rx_latency, the factor used is 1/4 to go up and 1/32 to
+ * go down
+ */
+ delta = rx_fifo2 - sc->rx_early_thresh; /* last latency seen LLS */
+ delta -= sc->rx_latency;/* LLS - estimated_latency */
+ if (delta >= 0)
+ sc->rx_latency += (delta / 4);
+ else
+ sc->rx_latency += (delta / 32);
+ ep_frst(F_RX_FIRST);
+ if (!((status = inw(BASE + EP_W1_RX_STATUS)) & ERR_RX_INCOMPLETE)) {
+ /* we see if by now, the packet has completly arrived */
+ goto read_again;
+ }
+ /* compute rx_early_threshold */
+ delta = (sc->rx_avg_pkt - sc->cur_len - sc->rx_latency - 16) & ~3;
+ if (delta < MIN_RX_EARLY_THRESHL)
+ delta = MIN_RX_EARLY_THRESHL;
+
+ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH |
+ (sc->rx_early_thresh = delta));
+ return;
+ }
+all_pkt:
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ /*
+ * recompute average packet's length, the factor used is 1/8 to go down
+ * and 1/32 to go up
+ */
+ delta = sc->cur_len - sc->rx_avg_pkt;
+ if (delta > 0)
+ sc->rx_avg_pkt += (delta / 32);
+ else
+ sc->rx_avg_pkt += (delta / 8);
+ delta = (sc->rx_avg_pkt - sc->rx_latency - 16) & ~3;
+ if (delta < MIN_RX_EARLY_THRESHF)
+ delta = MIN_RX_EARLY_THRESHF;
+ sc->rx_early_thresh = delta;
+ ++ifp->if_ipackets;
+ ep_fset(F_RX_FIRST);
+ ep_frst(F_RX_TRAILER);
+ top->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ top->m_pkthdr.len = sc->cur_len;
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf) {
+ bpf_mtap(ifp, top);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if there are
+ * no BPF listeners. And if we are in promiscuous mode, we have to
+ * check if this packet is really ours.
+ */
+ eh = mtod(top, struct ether_header *);
+ if ((ifp->if_flags & IFF_PROMISC) &&
+ (eh->ether_dhost[0] & 1) == 0 &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 &&
+ bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0) {
+ if (sc->top) {
+ m_freem(sc->top);
+ sc->top = 0;
+ }
+ ep_fset(F_RX_FIRST);
+ ep_frst(F_RX_TRAILER);
+#ifdef EP_LOCAL_STATS
+ sc->rx_bpf_disc++;
+#endif
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | delta);
+ return;
+ }
+ }
+#endif
+
+ eh = mtod(top, struct ether_header *);
+ m_adj(top, sizeof(struct ether_header));
+ ether_input(ifp, eh, top);
+ if (!sc->mb[sc->next_mb])
+ epmbuffill((caddr_t) sc, 0);
+ sc->top = 0;
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | delta);
+ return;
+
+out:
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ if (sc->top) {
+ m_freem(sc->top);
+ sc->top = 0;
+#ifdef EP_LOCAL_STATS
+ sc->rx_no_mbuf++;
+#endif
+ }
+ delta = (sc->rx_avg_pkt - sc->rx_latency - 16) & ~3;
+ if (delta < MIN_RX_EARLY_THRESHF)
+ delta = MIN_RX_EARLY_THRESHF;
+ ep_fset(F_RX_FIRST);
+ ep_frst(F_RX_TRAILER);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+ outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH |
+ (sc->rx_early_thresh = delta));
+}
+
+/*
+ * Look familiar?
+ */
+static int
+epioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ep_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ /* netifs are BUSY when UP */
+ sc->kdc->kdc_state=DC_BUSY;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ epinit(sc); /* before arpwhohas */
+ arp_ifinit((struct arpcom *)ifp, ifa);
+ break;
+#endif
+#ifdef IPX
+ case AF_IPX:
+ {
+ register struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
+
+ if (ipx_nullhost(*ina))
+ ina->x_host =
+ *(union ipx_host *) (sc->arpcom.ac_enaddr);
+ else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t) sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ epinit(sc);
+ break;
+ }
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *) (sc->arpcom.ac_enaddr);
+ else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t) sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ epinit(sc);
+ break;
+ }
+#endif
+ default:
+ epinit(sc);
+ break;
+ }
+ break;
+ case SIOCGIFADDR:
+ {
+ struct sockaddr *sa;
+
+ sa = (struct sockaddr *) & ifr->ifr_data;
+ bcopy((caddr_t) sc->arpcom.ac_enaddr,
+ (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
+ }
+ break;
+ case SIOCSIFFLAGS:
+ /* UP controls BUSY/IDLE */
+ sc->kdc->kdc_state= ( (ifp->if_flags & IFF_UP)
+ ? DC_BUSY
+ : DC_IDLE );
+
+ if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) {
+ ifp->if_flags &= ~IFF_RUNNING;
+ epstop(sc);
+ epmbufempty(sc);
+ break;
+ } else {
+ /* reinitialize card on any parameter change */
+ epinit(sc);
+ break;
+ }
+
+ /* NOTREACHED */
+ break;
+#ifdef notdef
+ case SIOCGHWADDR:
+ bcopy((caddr_t) sc->sc_addr, (caddr_t) & ifr->ifr_data,
+ sizeof(sc->sc_addr));
+ break;
+#endif
+ case SIOCSIFMTU:
+
+ /*
+ * Set the interface MTU.
+ */
+ if (ifr->ifr_mtu > ETHERMTU) {
+ error = EINVAL;
+ } else {
+ ifp->if_mtu = ifr->ifr_mtu;
+ }
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ /* Now this driver has no support for programmable
+ * multicast filters. If some day it will gain this
+ * support this part of code must be extended.
+ */
+ error=0;
+ break;
+ default:
+ error = EINVAL;
+ }
+
+ splx(s);
+
+ return (error);
+}
+
+static void
+epwatchdog(ifp)
+ struct ifnet *ifp;
+{
+ /*
+ printf("ep: watchdog\n");
+
+ log(LOG_ERR, "ep%d: watchdog\n", ifp->if_unit);
+ ifp->if_oerrors++;
+ */
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+ epstart(ifp);
+ ep_intr(ifp->if_softc);
+}
+
+static void
+epstop(sc)
+ struct ep_softc *sc;
+{
+ outw(BASE + EP_COMMAND, RX_DISABLE);
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+ outw(BASE + EP_COMMAND, TX_DISABLE);
+ outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK);
+ outw(BASE + EP_COMMAND, SET_INTR_MASK);
+ outw(BASE + EP_COMMAND, SET_RX_FILTER);
+}
+
+
+static int
+send_ID_sequence(port)
+ int port;
+{
+ int cx, al;
+
+ for (al = 0xff, cx = 0; cx < 255; cx++) {
+ outb(port, al);
+ al <<= 1;
+ if (al & 0x100)
+ al ^= 0xcf;
+ }
+ return (1);
+}
+
+
+/*
+ * We get eeprom data from the id_port given an offset into the eeprom.
+ * Basically; after the ID_sequence is sent to all of the cards; they enter
+ * the ID_CMD state where they will accept command requests. 0x80-0xbf loads
+ * the eeprom data. We then read the port 16 times and with every read; the
+ * cards check for contention (ie: if one card writes a 0 bit and another
+ * writes a 1 bit then the host sees a 0. At the end of the cycle; each card
+ * compares the data on the bus; if there is a difference then that card goes
+ * into ID_WAIT state again). In the meantime; one bit of data is returned in
+ * the AX register which is conveniently returned to us by inb(). Hence; we
+ * read 16 times getting one bit of data with each read.
+ */
+static int
+get_eeprom_data(id_port, offset)
+ int id_port;
+ int offset;
+{
+ int i, data = 0;
+ outb(id_port, 0x80 + offset);
+ DELAY(1000);
+ for (i = 0; i < 16; i++)
+ data = (data << 1) | (inw(id_port) & 1);
+ return (data);
+}
+
+/*
+ * We suppose this is always called inside a splimp(){...}splx() region
+ */
+static void
+epmbuffill(sp, dummy_arg)
+ caddr_t sp;
+ int dummy_arg;
+{
+ struct ep_softc *sc = (struct ep_softc *) sp;
+ int i;
+
+ i = sc->last_mb;
+ do {
+ if (sc->mb[i] == NULL)
+ MGET(sc->mb[i], M_DONTWAIT, MT_DATA);
+ if (sc->mb[i] == NULL)
+ break;
+ i = (i + 1) % MAX_MBS;
+ } while (i != sc->next_mb);
+ sc->last_mb = i;
+}
+
+static void
+epmbufempty(sc)
+ struct ep_softc *sc;
+{
+ int s, i;
+
+ s = splimp();
+ for (i = 0; i < MAX_MBS; i++) {
+ if (sc->mb[i]) {
+ m_freem(sc->mb[i]);
+ sc->mb[i] = NULL;
+ }
+ }
+ sc->last_mb = sc->next_mb = 0;
+ splx(s);
+}
+
+#endif /* NEP > 0 */
diff --git a/sys/pc98/pc98/if_epreg.h b/sys/pc98/pc98/if_epreg.h
new file mode 100644
index 0000000..3235c95
--- /dev/null
+++ b/sys/pc98/pc98/if_epreg.h
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) 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. 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.
+ *
+ * if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by:
+ *
+ October 2, 1994
+
+ Modified by: Andres Vega Garcia
+
+ INRIA - Sophia Antipolis, France
+ e-mail: avega@sophia.inria.fr
+ finger: avega@pax.inria.fr
+
+ */
+/*
+ * $Id: if_epreg.h,v 1.13 1996/02/28 17:19:04 gibbs Exp $
+ *
+ * Promiscuous mode added and interrupt logic slightly changed
+ * to reduce the number of adapter failures. Transceiver select
+ * logic changed to use value from EEPROM. Autoconfiguration
+ * features added.
+ * Done by:
+ * Serge Babkin
+ * Chelindbank (Chelyabinsk, Russia)
+ * babkin@hq.icb.chel.su
+ */
+
+/*
+ * Ethernet software status per interface.
+ */
+struct ep_softc {
+ struct arpcom arpcom; /* Ethernet common part */
+ short ep_io_addr; /* i/o bus address */
+#define MAX_MBS 8 /* # of mbufs we keep around */
+ struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */
+ int next_mb; /* Which mbuf to use next. */
+ int last_mb; /* Last mbuf. */
+ struct mbuf *top, *mcur;
+ short tx_start_thresh; /* Current TX_start_thresh. */
+ short tx_rate;
+ short tx_counter;
+ short rx_early_thresh; /* Current RX_early_thresh. */
+ short rx_latency;
+ short rx_avg_pkt;
+ short cur_len;
+ u_short ep_connectors; /* Connectors on this card. */
+ u_char ep_connector; /* Configured connector. */
+ int stat; /* some flags */
+#define F_RX_FIRST 0x1
+#define F_WAIT_TRAIL 0x2
+#define F_RX_TRAILER 0x4
+#define F_PROMISC 0x8
+
+#define F_ACCESS_32_BITS 0x100
+
+ struct ep_board *epb;
+
+ int unit;
+
+ struct kern_devconf* kdc;
+
+#ifdef EP_LOCAL_STATS
+ short tx_underrun;
+ short rx_no_first;
+ short rx_no_mbuf;
+ short rx_bpf_disc;
+ short rx_overrunf;
+ short rx_overrunl;
+#endif
+};
+
+struct ep_board {
+ int epb_addr; /* address of this board */
+ char epb_used; /* was this entry already used for configuring ? */
+ /* data from EEPROM for later use */
+ u_short eth_addr[3]; /* Ethernet address */
+ u_short prod_id; /* product ID */
+ u_short res_cfg; /* resource configuration */
+};
+
+
+/*
+ * Some global constants
+ */
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+
+#define TX_INIT_RATE 16
+#define TX_INIT_MAX_RATE 64
+#define RX_INIT_LATENCY 64
+#define RX_INIT_EARLY_THRESH 64
+#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */
+#define MIN_RX_EARLY_THRESHL 4
+
+#define EEPROMSIZE 0x40
+#define MAX_EEPROMBUSY 1000
+#define EP_LAST_TAG 0xd7
+#define EP_MAX_BOARDS 16
+#define EP_ID_PORT 0x100
+#define EP_IOSIZE 16 /* 16 bytes of I/O space used. */
+
+/*
+ * some macros to acces long named fields
+ */
+#define IS_BASE (is->id_iobase)
+#define BASE (sc->ep_io_addr)
+
+/*
+ * Commands to read/write EEPROM trough EEPROM command register (Window 0,
+ * Offset 0xa)
+ */
+#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
+#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
+#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
+#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
+
+#define EEPROM_BUSY (1<<15)
+#define EEPROM_TST_MODE (1<<14)
+
+/*
+ * Some short functions, worth to let them be a macro
+ */
+#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY)
+#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|(x))
+
+/**************************************************************************
+ * *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existence of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+
+/**************************************************************************
+ * *
+ * These are the registers for the 3Com 3c509 and their bit patterns when *
+ * applicable. They have been taken out the the "EtherLink III Parallel *
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
+ * from 3com. *
+ * *
+ **************************************************************************/
+
+#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a
+ * command reg. */
+#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status
+ * reg. */
+#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window
+ * reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+/* Write */
+#define EP_W0_EEPROM_DATA 0x0c
+#define EP_W0_EEPROM_COMMAND 0x0a
+#define EP_W0_RESOURCE_CFG 0x08
+#define EP_W0_ADDRESS_CFG 0x06
+#define EP_W0_CONFIG_CTRL 0x04
+/* Read */
+#define EP_W0_PRODUCT_ID 0x02
+#define EP_W0_MFG_ID 0x00
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+/* Write */
+#define EP_W1_TX_PIO_WR_2 0x02
+#define EP_W1_TX_PIO_WR_1 0x00
+/* Read */
+#define EP_W1_FREE_TX 0x0c
+#define EP_W1_TX_STATUS 0x0b /* byte */
+#define EP_W1_TIMER 0x0a /* byte */
+#define EP_W1_RX_STATUS 0x08
+#define EP_W1_RX_PIO_RD_2 0x02
+#define EP_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+/* Read/Write */
+#define EP_W2_ADDR_5 0x05
+#define EP_W2_ADDR_4 0x04
+#define EP_W2_ADDR_3 0x03
+#define EP_W2_ADDR_2 0x02
+#define EP_W2_ADDR_1 0x01
+#define EP_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+/* Read */
+#define EP_W3_FREE_TX 0x0c
+#define EP_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+/* Read/Write */
+#define EP_W4_MEDIA_TYPE 0x0a
+#define EP_W4_CTRLR_STATUS 0x08
+#define EP_W4_NET_DIAG 0x06
+#define EP_W4_FIFO_DIAG 0x04
+#define EP_W4_HOST_DIAG 0x02
+#define EP_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+/* Read */
+#define EP_W5_READ_0_MASK 0x0c
+#define EP_W5_INTR_MASK 0x0a
+#define EP_W5_RX_FILTER 0x08
+#define EP_W5_RX_EARLY_THRESH 0x06
+#define EP_W5_TX_AVAIL_THRESH 0x02
+#define EP_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+/* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms
+ * after issuing */
+#define WINDOW_SELECT (u_short) (0x1<<11)
+#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to
+ * determine whether
+ * this is needed. If
+ * so; wait 800 uSec
+ * before using trans-
+ * ceiver. */
+#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on
+ * power-up */
+#define RX_ENABLE (u_short) (0x4<<11)
+#define RX_RESET (u_short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11)
+#define TX_ENABLE (u_short) (0x9<<11)
+#define TX_DISABLE (u_short) (0xa<<11)
+#define TX_RESET (u_short) (0xb<<11)
+#define REQ_INTR (u_short) (0xc<<11)
+#define SET_INTR_MASK (u_short) (0xe<<11)
+#define SET_RD_0_MASK (u_short) (0xf<<11)
+#define SET_RX_FILTER (u_short) (0x10<<11)
+#define FIL_INDIVIDUAL (u_short) (0x1)
+#define FIL_GROUP (u_short) (0x2)
+#define FIL_BRDCST (u_short) (0x4)
+#define FIL_ALL (u_short) (0x8)
+#define SET_RX_EARLY_THRESH (u_short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11)
+#define SET_TX_START_THRESH (u_short) (0x13<<11)
+#define STATS_ENABLE (u_short) (0x15<<11)
+#define STATS_DISABLE (u_short) (0x16<<11)
+#define STOP_TRANSCEIVER (u_short) (0x17<<11)
+/*
+ * The following C_* acknowledge the various interrupts. Some of them don't
+ * do anything. See the manual.
+ */
+#define ACK_INTR (u_short) (0x6800)
+#define C_INTR_LATCH (u_short) (ACK_INTR|0x1)
+#define C_CARD_FAILURE (u_short) (ACK_INTR|0x2)
+#define C_TX_COMPLETE (u_short) (ACK_INTR|0x4)
+#define C_TX_AVAIL (u_short) (ACK_INTR|0x8)
+#define C_RX_COMPLETE (u_short) (ACK_INTR|0x10)
+#define C_RX_EARLY (u_short) (ACK_INTR|0x20)
+#define C_INT_RQD (u_short) (ACK_INTR|0x40)
+#define C_UPD_STATS (u_short) (ACK_INTR|0x80)
+#define C_MASK (u_short) 0xFF /* mask of C_* */
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (u_short) (0x1)
+#define S_CARD_FAILURE (u_short) (0x2)
+#define S_TX_COMPLETE (u_short) (0x4)
+#define S_TX_AVAIL (u_short) (0x8)
+#define S_RX_COMPLETE (u_short) (0x10)
+#define S_RX_EARLY (u_short) (0x20)
+#define S_INT_RQD (u_short) (0x40)
+#define S_UPD_STATS (u_short) (0x80)
+#define S_MASK (u_short) 0xFF /* mask of S_* */
+#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\
+ S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY)
+#define S_COMMAND_IN_PROGRESS (u_short) (0x1000)
+
+/* Address Config. Register.
+ * Window 0/Port 06
+ */
+
+#define ACF_CONNECTOR_BITS 14
+#define ACF_CONNECTOR_UTP 0
+#define ACF_CONNECTOR_AUI 1
+#define ACF_CONNECTOR_BNC 3
+
+/* Resource configuration register.
+ * Window 0/Port 08
+ *
+ */
+
+#define SET_IRQ(i) (((i)<<12) | 0xF00) /* set IRQ i */
+
+/*
+ * FIFO Registers.
+ * RX Status. Window 1/Port 08
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_RX_INCOMPLETE (u_short) (0x1<<15)
+#define ERR_RX (u_short) (0x1<<14)
+#define ERR_RX_OVERRUN (u_short) (0x8<<11)
+#define ERR_RX_RUN_PKT (u_short) (0xb<<11)
+#define ERR_RX_ALIGN (u_short) (0xc<<11)
+#define ERR_RX_CRC (u_short) (0xd<<11)
+#define ERR_RX_OVERSIZE (u_short) (0x9<<11)
+#define ERR_RX_DRIBBLE (u_short) (0x2<<11)
+
+/*
+ * FIFO Registers.
+ * TX Status. Window 1/Port 0B
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_SUCCES_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+/*
+ * Configuration control register.
+ * Window 0/Port 04
+ */
+/* Read */
+#define IS_AUI (1<<13)
+#define IS_BNC (1<<12)
+#define IS_UTP (1<<9)
+/* Write */
+#define ENABLE_DRQ_IRQ 0x0001
+#define W0_P4_CMD_RESET_ADAPTER 0x4
+#define W0_P4_CMD_ENABLE_ADAPTER 0x1
+/*
+ * Media type and status.
+ * Window 4/Port 0A
+ */
+#define ENABLE_UTP 0xc0
+#define DISABLE_UTP 0x0
+
+/*
+ * Misc defines for various things.
+ */
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */
+#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */
+#define PROD_ID 0x9150
+
+#define AUI 0x1
+#define BNC 0x2
+#define UTP 0x4
+
+#define ETHER_ADDR_LEN 6
+#define ETHER_MAX 1536
+#define RX_BYTES_MASK (u_short) (0x07ff)
+
+extern struct ep_board ep_board[];
+extern int ep_boards;
+extern u_long ep_unit;
+extern struct ep_softc *ep_alloc __P((int unit, struct ep_board *epb));
+extern void ep_free __P((struct ep_softc *sc));
+extern void ep_intr __P((void *sc));
+extern int ep_attach __P((struct ep_softc *sc));
+
+extern u_int16_t get_e __P((struct ep_softc *sc, int offset));
diff --git a/sys/pc98/pc98/if_fe.c b/sys/pc98/pc98/if_fe.c
new file mode 100644
index 0000000..a3dd7ac
--- /dev/null
+++ b/sys/pc98/pc98/if_fe.c
@@ -0,0 +1,3391 @@
+/*
+ * All Rights Reserved, Copyright (C) Fujitsu Limited 1995
+ *
+ * This software may be used, modified, copied, distributed, and sold, in
+ * both source and binary form provided that the above copyright, these
+ * terms and the following disclaimer are retained. The name of the author
+ * and/or the contributor 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 AND THE CONTRIBUTOR ``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 OR THE CONTRIBUTOR 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.
+ */
+
+/*
+ * $Id: if_fe.c,v 1.14 1996/04/23 18:36:55 nate Exp $
+ *
+ * Device driver for Fujitsu MB86960A/MB86965A based Ethernet cards.
+ * To be used with FreeBSD 2.x
+ * Contributed by M. Sekiguchi. <seki@sysrap.cs.fujitsu.co.jp>
+ *
+ * This version is intended to be a generic template for various
+ * MB86960A/MB86965A based Ethernet cards. It currently supports
+ * Fujitsu FMV-180 series for ISA and Allied-Telesis AT1700/RE2000
+ * series for ISA, as well as Fujitsu MBH10302 PC card.
+ * There are some currently-
+ * unused hooks embedded, which are primarily intended to support
+ * other types of Ethernet cards, but the author is not sure whether
+ * they are useful.
+ *
+ * This version also includes some alignments for
+ * RE1000/RE1000+/ME1500 support. It is incomplete, however, since the
+ * cards are not for AT-compatibles. (They are for PC98 bus -- a
+ * proprietary bus architecture available only in Japan.) Further
+ * work for PC98 version will be available as a part of FreeBSD(98)
+ * project.
+ *
+ * This software is a derivative work of if_ed.c version 1.56 by David
+ * Greenman available as a part of FreeBSD 2.0 RELEASE source distribution.
+ *
+ * The following lines are retained from the original if_ed.c:
+ *
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ */
+
+/*
+ * Modified for Allied-Telesis RE1000 series.
+ */
+
+
+/*
+ * TODO:
+ * o To support MBH10304 PC card. It is another MB8696x based
+ * PCMCIA Ethernet card by Fujitsu, which is not compatible with
+ * MBH10302.
+ * o To merge FreeBSD(98) efforts into a single source file.
+ * o To support ISA PnP auto configuration for FMV-183/184.
+ * o To reconsider mbuf usage.
+ * o To reconsider transmission buffer usage, including
+ * transmission buffer size (currently 4KB x 2) and pros-and-
+ * cons of multiple frame transmission.
+ * o To test IPX codes.
+ */
+
+#include "fe.h"
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/conf.h>
+
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/devconf.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+/* IPX code is not tested. FIXME. */
+#ifdef IPX
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+#endif
+
+/* To be used with IPv6 package of INRIA. */
+#ifdef INET6
+/* IPv6 added by shin 96.2.6 */
+#include <netinet/if_ether6.h>
+#endif
+
+/* XNS code is not tested. FIXME. */
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <machine/clock.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/icu.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+#endif
+
+/* PCCARD suport */
+#include "crd.h"
+#if NCRD > 0
+#include <sys/select.h>
+#include <pccard/card.h>
+#include <pccard/slot.h>
+#include <pccard/driver.h>
+#endif
+
+#ifdef PC98
+#include <pc98/pc98/ic/mb86960.h>
+#include <pc98/pc98/if_fereg.h>
+#else
+#include <i386/isa/ic/mb86960.h>
+#include <i386/isa/if_fereg.h>
+#endif
+
+/*
+ * This version of fe is an ISA device driver.
+ * Override the following macro to adapt it to another bus.
+ * (E.g., PC98.)
+ */
+#ifdef PC98
+#define DEVICE struct pc98_device
+#else
+#define DEVICE struct isa_device
+#endif
+
+/*
+ * Default settings for fe driver specific options.
+ * They can be set in config file by "options" statements.
+ */
+
+/*
+ * Debug control.
+ * 0: No debug at all. All debug specific codes are stripped off.
+ * 1: Silent. No debug messages are logged except emergent ones.
+ * 2: Brief. Lair events and/or important information are logged.
+ * 3: Detailed. Logs all information which *may* be useful for debugging.
+ * 4: Trace. All actions in the driver is logged. Super verbose.
+ */
+#ifndef FE_DEBUG
+#define FE_DEBUG 1
+#endif
+
+/*
+ * Transmit just one packet per a "send" command to 86960.
+ * This option is intended for performance test. An EXPERIMENTAL option.
+ */
+#ifndef FE_SINGLE_TRANSMISSION
+#define FE_SINGLE_TRANSMISSION 0
+#endif
+
+/*
+ * Device configuration flags.
+ */
+
+/* DLCR6 settings. */
+#define FE_FLAGS_DLCR6_VALUE 0x007F
+
+/* Force DLCR6 override. */
+#define FE_FLAGS_OVERRIDE_DLCR6 0x0080
+
+/* Shouldn't these be defined somewhere else such as isa_device.h? */
+#define NO_IOADDR 0xFFFFFFFF
+#define NO_IRQ 0
+
+/*
+ * Data type for a multicast address filter on 8696x.
+ */
+struct fe_filter { u_char data [ FE_FILTER_LEN ]; };
+
+/*
+ * Special filter values.
+ */
+static struct fe_filter const fe_filter_nothing = { FE_FILTER_NOTHING };
+static struct fe_filter const fe_filter_all = { FE_FILTER_ALL };
+
+/* How many registers does an fe-supported adapter have at maximum? */
+#define MAXREGISTERS 32
+
+/*
+ * fe_softc: per line info and status
+ */
+static struct fe_softc {
+
+ /* Used by "common" codes. */
+ struct arpcom arpcom; /* Ethernet common */
+
+ /* Used by config codes. */
+ struct kern_devconf kdc;/* Kernel configuration database info. */
+
+ /* Set by probe() and not modified in later phases. */
+ char * typestr; /* printable name of the interface. */
+ u_short iobase; /* base I/O address of the adapter. */
+ u_short ioaddr [ MAXREGISTERS ]; /* I/O addresses of register. */
+ u_short txb_size; /* size of TX buffer, in bytes */
+ u_char proto_dlcr4; /* DLCR4 prototype. */
+ u_char proto_dlcr5; /* DLCR5 prototype. */
+ u_char proto_dlcr6; /* DLCR6 prototype. */
+ u_char proto_dlcr7; /* DLCR7 prototype. */
+ u_char proto_bmpr13; /* BMPR13 prototype. */
+
+ /* Vendor specific hooks. */
+ void ( * init )( struct fe_softc * ); /* Just before fe_init(). */
+ void ( * stop )( struct fe_softc * ); /* Just after fe_stop(). */
+
+ /* Transmission buffer management. */
+ u_short txb_free; /* free bytes in TX buffer */
+ u_char txb_count; /* number of packets in TX buffer */
+ u_char txb_sched; /* number of scheduled packets */
+ u_char txb_padding; /* number of delayed padding bytes */
+
+ /* Multicast address filter management. */
+ u_char filter_change; /* MARs must be changed ASAP. */
+ struct fe_filter filter;/* new filter value. */
+
+} fe_softc[NFE];
+
+/* Frequently accessed members in arpcom and kdc. */
+#define sc_if arpcom.ac_if
+#define sc_unit arpcom.ac_if.if_unit
+#define sc_enaddr arpcom.ac_enaddr
+#define sc_dcstate kdc.kdc_state
+#define sc_description kdc.kdc_description
+
+/* Standard driver entry points. These can be static. */
+#ifdef PC98
+static int fe_probe ( struct pc98_device * );
+static int fe_attach ( struct pc98_device * );
+#else
+static int fe_probe ( struct isa_device * );
+static int fe_attach ( struct isa_device * );
+#endif
+static void fe_init ( int );
+static int fe_ioctl ( struct ifnet *, int, caddr_t );
+static void fe_start ( struct ifnet * );
+static void fe_reset ( int );
+static void fe_watchdog ( struct ifnet * );
+
+/* Local functions. Order of declaration is confused. FIXME. */
+static void fe_registerdev ( struct fe_softc *, DEVICE * );
+#ifdef PC98
+static int fe_probe_re1000 ( DEVICE *, struct fe_softc * );
+#else
+static int fe_probe_fmv ( DEVICE *, struct fe_softc * );
+static int fe_probe_ati ( DEVICE *, struct fe_softc * );
+static int fe_probe_mbh ( DEVICE *, struct fe_softc * );
+static void fe_init_mbh ( struct fe_softc * );
+#endif
+static int fe_get_packet ( struct fe_softc *, u_short );
+static void fe_stop ( int );
+static void fe_tint ( struct fe_softc *, u_char );
+static void fe_rint ( struct fe_softc *, u_char );
+static void fe_xmit ( struct fe_softc * );
+static void fe_write_mbufs ( struct fe_softc *, struct mbuf * );
+static struct fe_filter
+ fe_mcaf ( struct fe_softc * );
+static int fe_hash ( u_char * );
+static void fe_setmode ( struct fe_softc * );
+static void fe_loadmar ( struct fe_softc * );
+#if FE_DEBUG >= 1
+static void fe_dump ( int, struct fe_softc *, char * );
+#endif
+
+/* Ethernet constants. To be defined in if_ehter.h? FIXME. */
+#define ETHER_MIN_LEN 60 /* with header, without CRC. */
+#define ETHER_MAX_LEN 1514 /* with header, without CRC. */
+#define ETHER_ADDR_LEN 6 /* number of bytes in an address. */
+#define ETHER_TYPE_LEN 2 /* number of bytes in a data type field. */
+#define ETHER_HDR_SIZE 14 /* src addr, dst addr, and data type. */
+#define ETHER_CRC_LEN 4 /* number of bytes in CRC field. */
+
+/* Driver struct used in the config code. This must be public (external.) */
+#ifdef PC98
+struct pc98_driver fedriver =
+#else
+struct isa_driver fedriver =
+#endif
+{
+ fe_probe,
+ fe_attach,
+ "fe",
+ 1 /* It's safe to mark as "sensitive" */
+};
+
+/* Initial value for a kdc struct. */
+static struct kern_devconf const fe_kdc_template =
+{
+ 0, 0, 0,
+#ifdef PC98
+ "fe", 0, { MDDT_PC98, 0, "net" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* This is an ISA device. */
+#else
+ "fe", 0, { MDDT_ISA, 0, "net" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* This is an ISA device. */
+#endif
+ 0,
+ DC_UNCONFIGURED, /* Not yet configured. */
+ "Ethernet (MB8696x)", /* Tentative description (filled in later.) */
+ DC_CLS_NETIF /* This is a network interface. */
+};
+
+/*
+ * Fe driver specific constants which relate to 86960/86965.
+ */
+
+/* Interrupt masks */
+#define FE_TMASK ( FE_D2_COLL16 | FE_D2_TXDONE )
+#define FE_RMASK ( FE_D3_OVRFLO | FE_D3_CRCERR \
+ | FE_D3_ALGERR | FE_D3_SRTPKT | FE_D3_PKTRDY )
+
+/* Maximum number of iterations for a receive interrupt. */
+#define FE_MAX_RECV_COUNT ( ( 65536 - 2048 * 2 ) / 64 )
+ /*
+ * Maximum size of SRAM is 65536,
+ * minimum size of transmission buffer in fe is 2x2KB,
+ * and minimum amount of received packet including headers
+ * added by the chip is 64 bytes.
+ * Hence FE_MAX_RECV_COUNT is the upper limit for number
+ * of packets in the receive buffer.
+ */
+
+/*
+ * Routines to access contiguous I/O ports.
+ */
+
+static void
+inblk ( struct fe_softc * sc, int offs, u_char * mem, int len )
+{
+#ifdef PC98
+ u_short addr = sc->ioaddr[offs];
+#endif
+
+ while ( --len >= 0 ) {
+#ifdef PC98
+ *mem++ = inb( addr );
+ if (addr & 1)
+ addr+=0x1FF;
+ else
+ addr++;
+#else
+ *mem++ = inb( sc->ioaddr[ offs++ ] );
+#endif
+ }
+}
+
+static void
+outblk ( struct fe_softc * sc, int offs, u_char const * mem, int len )
+{
+#ifdef PC98
+ u_short addr = sc->ioaddr[offs];
+#endif
+
+ while ( --len >= 0 ) {
+#ifdef PC98
+ outb( addr, *mem++ );
+ if (addr & 1)
+ addr+=0x1FF;
+ else
+ addr++;
+#else
+ outb( sc->ioaddr[ offs++ ], *mem++ );
+#endif
+ }
+}
+
+/* PCCARD Support */
+#if NCRD > 0
+/*
+ * PC-Card (PCMCIA) specific code.
+ */
+static int fe_card_intr(struct pccard_dev *); /* Interrupt handler */
+static void feunload(struct pccard_dev *); /* Disable driver */
+static void fesuspend(struct pccard_dev *); /* Suspend driver */
+static int feinit(struct pccard_dev *, int); /* init device */
+
+static struct pccard_drv fe_info = {
+ "fe",
+ fe_card_intr,
+ feunload,
+ fesuspend,
+ feinit,
+ 0, /* Attributes - presently unused */
+ &net_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
+ * feinit with first=0. This is called when the user suspends
+ * the system, or the APM code suspends the system.
+ */
+static void
+fesuspend(struct pccard_dev *dp)
+{
+ printf("fe%d: suspending\n", dp->isahd.id_unit);
+}
+
+/*
+ * Initialize the device - called from Slot manager.
+ * if first is set, then initially check for
+ * the device's existence before initializing it.
+ * Once initialized, the device table may be set up.
+ */
+static int
+feinit(struct pccard_dev *dp, int first)
+{
+ /* validate unit number. */
+ if (first) {
+ if (dp->isahd.id_unit >= NFE)
+ return (ENODEV);
+ /*
+ * Probe the device. If a value is returned,
+ * the device was found at the location.
+ */
+#if FE_DEBUG >= 2
+ printf("Start Probe\n");
+#endif
+ if (fe_probe(&dp->isahd) == 0)
+ return (ENXIO);
+#if FE_DEBUG >= 2
+ printf("Start attach\n");
+#endif
+ if (fe_attach(&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);
+}
+
+/*
+ * feunload - 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
+feunload(struct pccard_dev *dp)
+{
+ printf("fe%d: unload\n", dp->isahd.id_unit);
+ fe_stop(dp->isahd.id_unit);
+}
+
+/*
+ * fe_card_intr - Shared interrupt called from
+ * front end of PC-Card handler.
+ */
+static int
+fe_card_intr(struct pccard_dev *dp)
+{
+ feintr(dp->isahd.id_unit);
+ return (1);
+}
+#endif /* NCRD > 0 */
+
+
+/*
+ * Hardware probe routines.
+ */
+
+/* How and where to probe; to support automatic I/O address detection. */
+struct fe_probe_list
+{
+ int ( * probe ) ( DEVICE *, struct fe_softc * );
+ u_short const * addresses;
+};
+
+/* Lists of possible addresses. */
+#ifdef PC98
+static u_short const fe_re1000_addr [] =
+ { 0xD0, 0xD2, 0xD4, 0xD8, 0x1D4, 0x1D6, 0x1D8, 0x1DA, 0 };
+#else
+static u_short const fe_fmv_addr [] =
+ { 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x300, 0x340, 0 };
+static u_short const fe_ati_addr [] =
+ { 0x240, 0x260, 0x280, 0x2A0, 0x300, 0x320, 0x340, 0x380, 0 };
+#endif
+
+static struct fe_probe_list const fe_probe_list [] =
+{
+#ifdef PC98
+ { fe_probe_re1000, fe_re1000_addr },
+#else
+ { fe_probe_fmv, fe_fmv_addr },
+ { fe_probe_ati, fe_ati_addr },
+ { fe_probe_mbh, NULL }, /* PCMCIAs cannot be auto-detected. */
+#endif
+ { NULL, NULL }
+};
+
+static void
+fe_registerdev ( struct fe_softc * sc, DEVICE * dev )
+{
+ /* Fill the device config data and register it. */
+ sc->kdc = fe_kdc_template;
+ sc->kdc.kdc_unit = sc->sc_unit;
+ sc->kdc.kdc_parentdata = dev;
+ dev_attach( &sc->kdc );
+}
+
+/*
+ * Determine if the device is present
+ *
+ * on entry:
+ * a pointer to an isa_device struct
+ * on exit:
+ * zero if device not found
+ * or number of i/o addresses used (if found)
+ */
+
+static int
+fe_probe ( DEVICE * dev )
+{
+#if NCRD > 0
+ static int fe_already_init;
+#endif
+ struct fe_softc * sc;
+ int u;
+ int nports;
+ struct fe_probe_list const * list;
+ u_short const * addr;
+ u_short single [ 2 ];
+
+ /* Initialize "minimum" parts of our softc. */
+ sc = &fe_softc[ dev->id_unit ];
+ sc->sc_unit = dev->id_unit;
+
+#if NCRD == 0
+#ifndef DEV_LKM
+ fe_registerdev(sc, dev);
+#endif
+#endif /* NCRD == 0 */
+
+#if NCRD > 0
+ /*
+ * If PC-Card probe required, then register driver with
+ * slot manager.
+ */
+ if (fe_already_init != 1) {
+ fe_registerdev(sc,dev);
+ pccard_add_driver(&fe_info);
+ fe_already_init = 1;
+ }
+#endif /* NCRD > 0 */
+
+ /* Probe each possibility, one at a time. */
+ for ( list = fe_probe_list; list->probe != NULL; list++ ) {
+
+ if ( dev->id_iobase != NO_IOADDR ) {
+ /* Probe one specific address. */
+ single[ 0 ] = dev->id_iobase;
+ single[ 1 ] = 0;
+ addr = single;
+ } else if ( list->addresses != NULL ) {
+ /* Auto detect. */
+ addr = list->addresses;
+ } else {
+ /* We need a list of addresses to do auto detect. */
+ continue;
+ }
+
+ /* Probe all possible addresses for the board. */
+ while ( *addr != 0 ) {
+
+ /* See if the address is already in use. */
+ for ( u = 0; u < NFE; u++ ) {
+ if ( fe_softc[u].iobase == *addr ) break;
+ }
+
+#if FE_DEBUG >= 3
+ if ( u == NFE ) {
+ log( LOG_INFO, "fe%d: probing %d at 0x%x\n",
+ sc->sc_unit, list - fe_probe_list, *addr );
+ } else if ( u == sc->sc_unit ) {
+ log( LOG_INFO, "fe%d: re-probing %d at 0x%x?\n",
+ sc->sc_unit, list - fe_probe_list, *addr );
+ } else {
+ log( LOG_INFO, "fe%d: skipping %d at 0x%x\n",
+ sc->sc_unit, list - fe_probe_list, *addr );
+ }
+#endif
+
+ /* Probe the address if it is free. */
+ if ( u == NFE || u == sc->sc_unit ) {
+
+ /* Probe an address. */
+ sc->iobase = *addr;
+ nports = list->probe( dev, sc );
+ if ( nports > 0 ) {
+ /* Found. */
+ dev->id_iobase = *addr;
+ return ( nports );
+ }
+ sc->iobase = 0;
+ }
+
+ /* Try next. */
+ addr++;
+ }
+ }
+
+ /* Probe failed. */
+ return ( 0 );
+}
+
+/*
+ * Check for specific bits in specific registers have specific values.
+ */
+struct fe_simple_probe_struct
+{
+#ifdef PC98
+ u_short port; /* Offset from the base I/O address. */
+#else
+ u_char port; /* Offset from the base I/O address. */
+#endif
+ u_char mask; /* Bits to be checked. */
+ u_char bits; /* Values to be compared against. */
+};
+
+static int
+fe_simple_probe ( struct fe_softc const * sc,
+ struct fe_simple_probe_struct const * sp )
+{
+ struct fe_simple_probe_struct const * p;
+
+ for ( p = sp; p->mask != 0; p++ ) {
+#if FE_DEBUG >=2
+ printf("Probe Port:%x,Value:%x,Mask:%x.Bits:%x\n",
+ p->port,inb(sc->ioaddr[ p->port]),p->mask,p->bits);
+#endif
+ if ( ( inb( sc->ioaddr[ p->port ] ) & p->mask ) != p->bits )
+ {
+ return ( 0 );
+ }
+ }
+ return ( 1 );
+}
+
+/*
+ * Routines to read all bytes from the config EEPROM through MB86965A.
+ * I'm not sure what exactly I'm doing here... I was told just to follow
+ * the steps, and it worked. Could someone tell me why the following
+ * code works? (Or, why all similar codes I tried previously doesn't
+ * work.) FIXME.
+ */
+
+static void
+fe_strobe_eeprom ( u_short bmpr16 )
+{
+ /*
+ * We must guarantee 800ns (or more) interval to access slow
+ * EEPROMs. The following redundant code provides enough
+ * delay with ISA timing. (Even if the bus clock is "tuned.")
+ * Some modification will be needed on faster busses.
+ */
+ outb( bmpr16, FE_B16_SELECT );
+ outb( bmpr16, FE_B16_SELECT );
+ outb( bmpr16, FE_B16_SELECT | FE_B16_CLOCK );
+ outb( bmpr16, FE_B16_SELECT | FE_B16_CLOCK );
+ outb( bmpr16, FE_B16_SELECT );
+ outb( bmpr16, FE_B16_SELECT );
+}
+
+static void
+fe_read_eeprom ( struct fe_softc * sc, u_char * data )
+{
+ u_short bmpr16 = sc->ioaddr[ FE_BMPR16 ];
+ u_short bmpr17 = sc->ioaddr[ FE_BMPR17 ];
+ u_char n, val, bit;
+
+ /* Read bytes from EEPROM; two bytes per an iteration. */
+ for ( n = 0; n < FE_EEPROM_SIZE / 2; n++ ) {
+
+ /* Reset the EEPROM interface. */
+ outb( bmpr16, 0x00 );
+ outb( bmpr17, 0x00 );
+
+ /* Start EEPROM access. */
+ outb( bmpr16, FE_B16_SELECT );
+ outb( bmpr17, FE_B17_DATA );
+ fe_strobe_eeprom( bmpr16 );
+
+ /* Pass the iteration count to the chip. */
+ val = 0x80 | n;
+ for ( bit = 0x80; bit != 0x00; bit >>= 1 ) {
+ outb( bmpr17, ( val & bit ) ? FE_B17_DATA : 0 );
+ fe_strobe_eeprom( bmpr16 );
+ }
+ outb( bmpr17, 0x00 );
+
+ /* Read a byte. */
+ val = 0;
+ for ( bit = 0x80; bit != 0x00; bit >>= 1 ) {
+ fe_strobe_eeprom( bmpr16 );
+ if ( inb( bmpr17 ) & FE_B17_DATA ) {
+ val |= bit;
+ }
+ }
+ *data++ = val;
+
+ /* Read one more byte. */
+ val = 0;
+ for ( bit = 0x80; bit != 0x00; bit >>= 1 ) {
+ fe_strobe_eeprom( bmpr16 );
+ if ( inb( bmpr17 ) & FE_B17_DATA ) {
+ val |= bit;
+ }
+ }
+ *data++ = val;
+ }
+
+ /* Reset the EEPROM interface, again. */
+ outb( bmpr16, 0x00 );
+ outb( bmpr17, 0x00 );
+
+#if FE_DEBUG >= 3
+ /* Report what we got. */
+ data -= FE_EEPROM_SIZE;
+ log( LOG_INFO, "fe%d: EEPROM:"
+ " %02x%02x%02x%02x %02x%02x%02x%02x -"
+ " %02x%02x%02x%02x %02x%02x%02x%02x -"
+ " %02x%02x%02x%02x %02x%02x%02x%02x -"
+ " %02x%02x%02x%02x %02x%02x%02x%02x\n",
+ sc->sc_unit,
+ data[ 0], data[ 1], data[ 2], data[ 3],
+ data[ 4], data[ 5], data[ 6], data[ 7],
+ data[ 8], data[ 9], data[10], data[11],
+ data[12], data[13], data[14], data[15],
+ data[16], data[17], data[18], data[19],
+ data[20], data[21], data[22], data[23],
+ data[24], data[25], data[26], data[27],
+ data[28], data[29], data[30], data[31] );
+#endif
+}
+
+/*
+ * Hardware (vendor) specific probe routines.
+ */
+
+#ifdef PC98
+/*
+ * Probe and initialization for Allied-Telesis RE1000 series.
+ */
+#if 1
+static int
+fe_probe_re1000 ( struct pc98_device * isa_dev, struct fe_softc * sc )
+{
+ int i, n, signature;
+ int dlcr6, dlcr7;
+ u_char eeprom [ FE_EEPROM_SIZE ];
+
+ static u_short const irqmap [ 4 ] =
+ { IRQ3, IRQ5, IRQ6, IRQ12 };
+ static struct fe_simple_probe_struct const probe_signature1 [] = {
+ { FE_DLCR0, 0xBF, 0x00 },
+ { FE_DLCR2, 0xFF, 0x00 },
+ { FE_DLCR4, 0x0F, 0x06 },
+ { FE_DLCR6, 0x0F, 0x06 },
+ { 0 }
+ };
+ static struct fe_simple_probe_struct const probe_signature2 [] = {
+ { FE_DLCR1, 0xFF, 0x00 },
+ { FE_DLCR3, 0xFF, 0x00 },
+ { FE_DLCR5, 0xFF, 0x41 },
+ { 0 }
+ };
+ static struct fe_simple_probe_struct const probe_table [] = {
+ { FE_DLCR2, 0x71, 0x00 },
+ { FE_DLCR4, 0x08, 0x00 },
+ { FE_DLCR5, 0x80, 0x00 },
+ { 0 }
+ };
+ static struct fe_simple_probe_struct const vendor_code [] = {
+ { FE_DLCR8, 0xFF, 0x00 },
+ { FE_DLCR9, 0xFF, 0x00 },
+ { FE_DLCR10, 0xFF, 0xF4 },
+ { 0 }
+ };
+ static struct fe_simple_probe_struct const re1000_check [] = {
+ { FE_RE1000_MAC0, 0xff, 0x00 },
+ { FE_RE1000_MAC1, 0xff, 0x00 },
+ { FE_RE1000_MAC2, 0xff, 0xf4 }, /* ATI vendor code */
+ { 0 }
+ };
+
+#if FE_DEBUG >= 3
+ log( LOG_INFO, "fe%d: probe (0x%x) for RE1000/RE1000Plus/ME1500\n", sc->sc_unit, sc->iobase );
+ fe_dump( LOG_INFO, sc, NULL );
+#endif
+
+ /* First, check the "signature" */
+ signature = 0;
+ if (fe_simple_probe(sc, probe_signature1)) {
+
+ outb(sc->iobase+FE_DLCR6, (inb(sc->iobase+FE_DLCR6) & 0xCF) | 0x16);
+ if (fe_simple_probe(sc, probe_signature2))
+ signature = 1;
+ }
+
+ /*
+ * If the "signature" not detected, 86965 *might* be previously
+ * initialized. So, check the Ethernet address here.
+ *
+ * Allied-Telesis uses 00 00 F4 ?? ?? ??.
+ */
+ if (signature == 0) {
+ /* Simple check */
+ if (!fe_simple_probe(sc, probe_table)) return 0;
+
+ /* Disable DLC */
+ dlcr6 = inb(sc->iobase + FE_DLCR6);
+ outb(sc->iobase + FE_DLCR6, dlcr6 | FE_D6_DLC_DISABLE);
+ /* Select register bank for DLCR */
+ dlcr7 = inb(sc->iobase + FE_DLCR7);
+ outb(sc->iobase + FE_DLCR7, dlcr7 & 0xF3 | FE_D7_RBS_DLCR);
+
+ /* Check the Ethernet address */
+ if (!fe_simple_probe(sc, vendor_code)) return 0;
+
+ /* Restore configuration registers */
+ DELAY(200);
+ outb(sc->iobase + FE_DLCR6, dlcr6);
+ outb(sc->iobase + FE_DLCR7, dlcr7);
+ }
+
+#if 1
+ /*
+ * This test doesn't work well for RE1000 look-alike by
+ * other vendors.
+ */
+ if ( fe_simple_probe( sc, re1000_check )){
+ /*
+ * RE1000 does not use 86965 EEPROM interface.
+ */
+ u_char c = 0;
+ c ^= sc->sc_enaddr[0] = inb(sc->iobase + FE_RE1000_MAC0);
+ c ^= sc->sc_enaddr[1] = inb(sc->iobase + FE_RE1000_MAC1);
+ c ^= sc->sc_enaddr[2] = inb(sc->iobase + FE_RE1000_MAC2);
+ c ^= sc->sc_enaddr[3] = inb(sc->iobase + FE_RE1000_MAC3);
+ c ^= sc->sc_enaddr[4] = inb(sc->iobase + FE_RE1000_MAC4);
+ c ^= sc->sc_enaddr[5] = inb(sc->iobase + FE_RE1000_MAC5);
+ c ^= inb(sc->iobase + FE_RE1000_MACCHK);
+ if (c != 0) return 0;
+
+ if ( sc->sc_enaddr[ 0 ] != 0x00
+ || sc->sc_enaddr[ 1 ] != 0x00
+ || sc->sc_enaddr[ 2 ] != 0xF4 ) return 0;
+
+ /*
+ * check interrupt configure
+ */
+ for (n=0; n<4; n++) {
+ if (isa_dev->id_irq == irqmap[n]) break;
+ }
+ if (n == 4) return 0;
+
+ /*
+ * set irq
+ */
+ c = inb(sc->iobase + FE_RE1000_IRQCONF);
+ c &= (~ FE_RE1000_IRQCONF_IRQ);
+ c |= (1 << (n + FE_RE1000_IRQCONF_IRQSHIFT));
+ outb(sc->iobase + FE_RE1000_IRQCONF, c);
+#if 0
+ PC98WAIT; PC98WAIT;
+ if (c == (inb(sc->iobase + FE_RE1000_IRQCONF)
+ & FE_RE1000_IRQCONF_IRQ)) return 0;
+#endif
+
+ sc->typestr = "RE1000";
+ sc->sc_description = "Ethernet adapter: RE1000";
+
+ } else {
+ /*
+ * We are now almost sure we have an 86965 at the given
+ * address. So, read EEPROM through 86965. We have to write
+ * into LSI registers to read from EEPROM. I want to avoid it
+ * at this stage, but I cannot test the presense of the chip
+ * any further without reading EEPROM. FIXME.
+ */
+ fe_read_eeprom( sc, eeprom );
+
+ /* Make sure that config info in EEPROM and 86965 agree. */
+ if ( eeprom[ FE_EEPROM_CONF ] != inb( sc->iobase + FE_BMPR19 ) ) {
+ return 0;
+ }
+
+ /*
+ * Initialize constants in the per-line structure.
+ */
+
+ /* Get our station address from EEPROM. */
+ bcopy( eeprom + FE_ATI_EEP_ADDR, sc->sc_enaddr, ETHER_ADDR_LEN );
+
+ sc->typestr = "RE1000Plus/ME1500";
+ sc->sc_description = "Ethernet adapter: RE1000Plus/ME1500";
+ /*
+ * Read IRQ configuration.
+ */
+ n = (inb(sc->iobase + FE_BMPR19) & FE_B19_IRQ ) >> FE_B19_IRQ_SHIFT;
+ isa_dev->id_irq = irqmap[n];
+ }
+
+#else
+ /* Make sure we got a valid station address. */
+ if ( ( sc->sc_enaddr[ 0 ] & 0x03 ) != 0x00
+ || ( sc->sc_enaddr[ 0 ] == 0x00
+ && sc->sc_enaddr[ 1 ] == 0x00
+ && sc->sc_enaddr[ 2 ] == 0x00 ) ) return 0;
+#endif
+
+ /* Should find all register prototypes here. FIXME. */
+ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; /* FIXME */
+ sc->proto_dlcr5 = 0;
+ sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC;
+
+ /*
+ * Program the 86965 as follows:
+ * SRAM: 32KB, 100ns, byte-wide access.
+ * Transmission buffer: 4KB x 2.
+ * System bus interface: 16 bits.
+ * We cannot change these values but TXBSIZE, because they
+ * are hard-wired on the board. Modifying TXBSIZE will affect
+ * the driver performance.
+ */
+ sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB
+ | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns;
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "RE1000 found" );
+#endif
+
+ /* Initialize 86965. */
+ outb( sc->iobase + FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_DISABLE );
+ DELAY(200);
+
+ /* Disable all interrupts. */
+ outb( sc->iobase + FE_DLCR2, 0 );
+ outb( sc->iobase + FE_DLCR3, 0 );
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "end of fe_probe_re1000()" );
+#endif
+
+ /*
+ * That's all. RE1000 occupies 2*16 I/O addresses, by the way.
+ */
+ return 2; /* ??? */
+}
+#else
+static int
+fe_probe_re1000 ( struct pc98_device * isa_dev, struct fe_softc * sc )
+{
+ int i, n, signature;
+ int dlcr6, dlcr7;
+ u_char eeprom [ FE_EEPROM_SIZE ];
+
+ static u_short const irqmap [ 4 ] =
+ { IRQ3, IRQ5, IRQ6, IRQ12 };
+ static struct fe_simple_probe_struct const probe_signature1 [] = {
+ { FE_DLCR0, 0xBF, 0x00 },
+ { FE_DLCR2, 0xFF, 0x00 },
+ { FE_DLCR4, 0x0F, 0x06 },
+ { FE_DLCR6, 0x0F, 0x06 },
+ { 0 }
+ };
+ static struct fe_simple_probe_struct const probe_signature2 [] = {
+ { FE_DLCR1, 0xFF, 0x00 },
+ { FE_DLCR3, 0xFF, 0x00 },
+ { FE_DLCR5, 0xFF, 0x41 },
+ { 0 }
+ };
+ static struct fe_simple_probe_struct const probe_table [] = {
+ { FE_DLCR2, 0x71, 0x00 },
+ { FE_DLCR4, 0x08, 0x00 },
+ { FE_DLCR5, 0x80, 0x00 },
+ { 0 }
+ };
+ static struct fe_simple_probe_struct const vendor_code [] = {
+ { FE_DLCR8, 0xFF, 0x00 },
+ { FE_DLCR9, 0xFF, 0x00 },
+ { FE_DLCR10, 0xFF, 0xF4 },
+ { 0 }
+ };
+ static struct fe_simple_probe_struct const re1000_check [] = {
+ { FE_RE1000_MAC0, 0xff, 0x00 },
+ { FE_RE1000_MAC1, 0xff, 0x00 },
+ { FE_RE1000_MAC2, 0xff, 0xf4 }, /* ATI vendor code */
+ { 0 }
+ };
+
+#if FE_DEBUG >= 3
+ log( LOG_INFO, "fe%d: probe (0x%x) for RE1000\n", sc->sc_unit, sc->iobase );
+ fe_dump( LOG_INFO, sc, NULL );
+#endif
+
+ /* First, check the "signature" */
+ signature = 0;
+ if (fe_simple_probe(sc, probe_signature1)) {
+
+ outb(sc->iobase+FE_DLCR6, (inb(sc->iobase+FE_DLCR6) & 0xCF) | 0x16);
+ if (fe_simple_probe(sc, probe_signature2))
+ signature = 1;
+ }
+
+ /*
+ * If the "signature" not detected, RE1000 *might* be previously
+ * initialized. So, check the Ethernet address here.
+ *
+ * Allied-Telesis uses 00 00 F4 ?? ?? ??.
+ */
+ if (signature == 0) {
+ /* Simple check */
+ if (!fe_simple_probe(sc, probe_table)) return 0;
+
+ /* Disable DLC */
+ dlcr6 = inb(sc->iobase + FE_DLCR6);
+ outb(sc->iobase + FE_DLCR6, dlcr6 | FE_D6_DLC_DISABLE);
+ /* Select register bank for DLCR */
+ dlcr7 = inb(sc->iobase + FE_DLCR7);
+ outb(sc->iobase + FE_DLCR7, dlcr7 & 0xF3 | FE_D7_RBS_DLCR);
+
+ /* Check the Ethernet address */
+ if (!fe_simple_probe(sc, vendor_code)) return 0;
+
+ /* Restore configuration registers */
+ DELAY(200);
+ outb(sc->iobase + FE_DLCR6, dlcr6);
+ outb(sc->iobase + FE_DLCR7, dlcr7);
+ }
+
+ /*
+ * Read IRQ configuration.
+ */
+ n = (inb(sc->iobase + 0x1600) & FE_B19_IRQ ) >> FE_B19_IRQ_SHIFT;
+ isa_dev->id_irq = irqmap[n];
+
+#if 1
+ /*
+ * This test doesn't work well for RE1000 look-alike by
+ * other vendors.
+ */
+ /* Make sure the vendor part is for Allied-Telesis. */
+ if ( sc->sc_enaddr[ 0 ] != 0x00
+ || sc->sc_enaddr[ 1 ] != 0x00
+ || sc->sc_enaddr[ 2 ] != 0xF4 ) return 0;
+
+#else
+ /* Make sure we got a valid station address. */
+ if ( ( sc->sc_enaddr[ 0 ] & 0x03 ) != 0x00
+ || ( sc->sc_enaddr[ 0 ] == 0x00
+ && sc->sc_enaddr[ 1 ] == 0x00
+ && sc->sc_enaddr[ 2 ] == 0x00 ) ) return 0;
+#endif
+
+ /* Should find all register prototypes here. FIXME. */
+ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; /* FIXME */
+ sc->proto_dlcr5 = 0;
+ sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC;
+
+ /*
+ * Program the 86965 as follows:
+ * SRAM: 32KB, 100ns, byte-wide access.
+ * Transmission buffer: 4KB x 2.
+ * System bus interface: 16 bits.
+ * We cannot change these values but TXBSIZE, because they
+ * are hard-wired on the board. Modifying TXBSIZE will affect
+ * the driver performance.
+ */
+ sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB
+ | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns;
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "RE1000 found" );
+#endif
+
+ /* Initialize 86965. */
+ outb( sc->iobase + FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_DISABLE );
+ DELAY(200);
+
+ /* Disable all interrupts. */
+ outb( sc->iobase + FE_DLCR2, 0 );
+ outb( sc->iobase + FE_DLCR3, 0 );
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "end of fe_probe_re1000()" );
+#endif
+
+ /*
+ * That's all. AT1700 occupies 2*16 I/O addresses, by the way.
+ */
+ return 2; /* ??? */
+}
+#endif
+#else
+/*
+ * Probe and initialization for Fujitsu FMV-180 series boards
+ */
+static int
+fe_probe_fmv ( DEVICE * dev, struct fe_softc * sc )
+{
+ int i, n;
+
+ static u_short const baseaddr [ 8 ] =
+ { 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x300, 0x340 };
+ static u_short const irqmap [ 4 ] =
+ { IRQ3, IRQ7, IRQ10, IRQ15 };
+
+ static struct fe_simple_probe_struct const probe_table [] = {
+ { FE_DLCR2, 0x70, 0x00 },
+ { FE_DLCR4, 0x08, 0x00 },
+ /* { FE_DLCR5, 0x80, 0x00 }, Doesn't work. */
+
+ { FE_FMV0, 0x78, 0x50 }, /* ERRDY+PRRDY */
+ { FE_FMV1, 0xB0, 0x00 }, /* FMV-183/184 has 0x48 bits. */
+ { FE_FMV3, 0x7F, 0x00 },
+#if 1
+ /*
+ * Test *vendor* part of the station address for Fujitsu.
+ * The test will gain reliability of probe process, but
+ * it rejects FMV-180 clone boards manufactured by other vendors.
+ * We have to turn the test off when such cards are made available.
+ */
+ { FE_FMV4, 0xFF, 0x00 },
+ { FE_FMV5, 0xFF, 0x00 },
+ { FE_FMV6, 0xFF, 0x0E },
+#else
+ /*
+ * We can always verify the *first* 2 bits (in Ethernet
+ * bit order) are "no multicast" and "no local" even for
+ * unknown vendors.
+ */
+ { FE_FMV4, 0x03, 0x00 },
+#endif
+ { 0 }
+ };
+
+ /* "Hardware revision ID" */
+ int revision;
+
+ /*
+ * See if the specified address is possible for FMV-180 series.
+ */
+ for ( i = 0; i < 8; i++ ) {
+ if ( baseaddr[ i ] == sc->iobase ) break;
+ }
+ if ( i == 8 ) return 0;
+
+ /* Setup an I/O address mapping table. */
+ for ( i = 0; i < MAXREGISTERS; i++ ) {
+ sc->ioaddr[ i ] = sc->iobase + i;
+ }
+
+ /* Simple probe. */
+ if ( !fe_simple_probe( sc, probe_table ) ) return 0;
+
+ /* Check if our I/O address matches config info. on EEPROM. */
+ n = ( inb( sc->ioaddr[ FE_FMV2 ] ) & FE_FMV2_IOS )
+ >> FE_FMV2_IOS_SHIFT;
+ if ( baseaddr[ n ] != sc->iobase ) return 0;
+
+ /* Find the "hardware revision." */
+ revision = inb( sc->ioaddr[ FE_FMV1 ] ) & FE_FMV1_REV;
+
+ /* Determine the card type. */
+ sc->typestr = NULL;
+ switch ( inb( sc->ioaddr[ FE_FMV0 ] ) & FE_FMV0_MEDIA ) {
+ case 0:
+ /* No interface? This doesn't seem to be an FMV-180... */
+ return 0;
+ case FE_FMV0_MEDIUM_T:
+ switch ( revision ) {
+ case 8:
+ sc->typestr = "FMV-183";
+ sc->sc_description = "Ethernet adapter: FMV-183";
+ break;
+ }
+ break;
+ case FE_FMV0_MEDIUM_T | FE_FMV0_MEDIUM_5:
+ switch ( revision ) {
+ case 0:
+ sc->typestr = "FMV-181";
+ sc->sc_description = "Ethernet adapter: FMV-181";
+ break;
+ case 1:
+ sc->typestr = "FMV-181A";
+ sc->sc_description = "Ethernet adapter: FMV-181A";
+ break;
+ }
+ break;
+ case FE_FMV0_MEDIUM_2:
+ switch ( revision ) {
+ case 8:
+ sc->typestr = "FMV-184 (CSR = 2)";
+ sc->sc_description = "Ethernet adapter: FMV-184";
+ break;
+ }
+ break;
+ case FE_FMV0_MEDIUM_5:
+ switch ( revision ) {
+ case 8:
+ sc->typestr = "FMV-184 (CSR = 1)";
+ sc->sc_description = "Ethernet adapter: FMV-184";
+ break;
+ }
+ break;
+ case FE_FMV0_MEDIUM_2 | FE_FMV0_MEDIUM_5:
+ switch ( revision ) {
+ case 0:
+ sc->typestr = "FMV-182";
+ sc->sc_description = "Ethernet adapter: FMV-182";
+ break;
+ case 1:
+ sc->typestr = "FMV-182A";
+ sc->sc_description = "Ethernet adapter: FMV-182A";
+ break;
+ case 8:
+ sc->typestr = "FMV-184 (CSR = 3)";
+ sc->sc_description = "Ethernet adapter: FMV-184";
+ break;
+ }
+ break;
+ }
+ if ( sc->typestr == NULL ) {
+ /* Unknown card type... Hope the driver works. */
+ sc->typestr = "unknown FMV-180 version";
+ sc->sc_description
+ = "Ethernet adapter: unknown FMV-180 version";
+ log( LOG_WARNING, "fe%d: %s: %x-%x-%x-%x\n",
+ sc->sc_unit, sc->typestr,
+ inb( sc->ioaddr[ FE_FMV0 ] ),
+ inb( sc->ioaddr[ FE_FMV1 ] ),
+ inb( sc->ioaddr[ FE_FMV2 ] ),
+ inb( sc->ioaddr[ FE_FMV3 ] ) );
+ }
+
+ /*
+ * An FMV-180 has been proved.
+ * Determine which IRQ to be used.
+ *
+ * In this version, we give a priority to the kernel config file.
+ * If the EEPROM and config don't match, say it to the user for
+ * an attention.
+ */
+ n = ( inb( sc->ioaddr[ FE_FMV2 ] ) & FE_FMV2_IRS )
+ >> FE_FMV2_IRS_SHIFT;
+ if ( dev->id_irq == NO_IRQ ) {
+ /* Just use the probed value. */
+ dev->id_irq = irqmap[ n ];
+ } else if ( dev->id_irq != irqmap[ n ] ) {
+ /* Don't match. */
+ log( LOG_WARNING,
+ "fe%d: check IRQ in config; it may be incorrect",
+ sc->sc_unit );
+ }
+
+ /*
+ * Initialize constants in the per-line structure.
+ */
+
+ /* Get our station address from EEPROM. */
+ inblk( sc, FE_FMV4, sc->sc_enaddr, ETHER_ADDR_LEN );
+
+ /* Make sure we got a valid station address. */
+ if ( ( sc->sc_enaddr[ 0 ] & 0x03 ) != 0x00
+ || ( sc->sc_enaddr[ 0 ] == 0x00
+ && sc->sc_enaddr[ 1 ] == 0x00
+ && sc->sc_enaddr[ 2 ] == 0x00 ) ) return 0;
+
+ /*
+ * Register values which (may) depend on board design.
+ *
+ * Program the 86960 as follows:
+ * SRAM: 32KB, 100ns, byte-wide access.
+ * Transmission buffer: 4KB x 2.
+ * System bus interface: 16 bits.
+ */
+ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL;
+ sc->proto_dlcr5 = 0;
+ sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB
+ | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns;
+ sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC;
+ sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO;
+
+ /*
+ * Minimum initialization of the hardware.
+ * We write into registers; hope I/O ports have no
+ * overlap with other boards.
+ */
+
+ /* Initialize ASIC. */
+ outb( sc->ioaddr[ FE_FMV3 ], 0 );
+ outb( sc->ioaddr[ FE_FMV10 ], 0 );
+
+ /* Initialize 86960. */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE );
+ DELAY( 200 );
+
+ /* Disable all interrupts. */
+ outb( sc->ioaddr[ FE_DLCR2 ], 0 );
+ outb( sc->ioaddr[ FE_DLCR3 ], 0 );
+
+ /* "Refresh" hardware configuration. FIXME. */
+ outb( sc->ioaddr[ FE_FMV2 ], inb( sc->ioaddr[ FE_FMV2 ] ) );
+
+ /* Turn the "master interrupt control" flag of ASIC on. */
+ outb( sc->ioaddr[ FE_FMV3 ], FE_FMV3_IRQENB );
+
+ /*
+ * That's all. FMV-180 occupies 32 I/O addresses, by the way.
+ */
+ return 32;
+}
+
+/*
+ * Probe and initialization for Allied-Telesis AT1700/RE2000 series.
+ */
+static int
+fe_probe_ati ( DEVICE * dev, struct fe_softc * sc )
+{
+ int i, n;
+ u_char eeprom [ FE_EEPROM_SIZE ];
+ u_char save16, save17;
+
+ static u_short const baseaddr [ 8 ] =
+ { 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300 };
+ static u_short const irqmaps [ 4 ][ 4 ] =
+ {
+ { IRQ3, IRQ4, IRQ5, IRQ9 },
+ { IRQ10, IRQ11, IRQ12, IRQ15 },
+ { IRQ3, IRQ11, IRQ5, IRQ15 },
+ { IRQ10, IRQ11, IRQ14, IRQ15 },
+ };
+ static struct fe_simple_probe_struct const probe_table [] = {
+ { FE_DLCR2, 0x70, 0x00 },
+ { FE_DLCR4, 0x08, 0x00 },
+ { FE_DLCR5, 0x80, 0x00 },
+#if 0
+ { FE_BMPR16, 0x1B, 0x00 },
+ { FE_BMPR17, 0x7F, 0x00 },
+#endif
+ { 0 }
+ };
+
+ /* Assume we have 86965 and no need to restore these. */
+ save16 = 0;
+ save17 = 0;
+
+#if FE_DEBUG >= 3
+ log( LOG_INFO, "fe%d: probe (0x%x) for ATI\n",
+ sc->sc_unit, sc->iobase );
+ fe_dump( LOG_INFO, sc, NULL );
+#endif
+
+ /*
+ * See if the specified address is possible for MB86965A JLI mode.
+ */
+ for ( i = 0; i < 8; i++ ) {
+ if ( baseaddr[ i ] == sc->iobase ) break;
+ }
+ if ( i == 8 ) goto NOTFOUND;
+
+ /* Setup an I/O address mapping table. */
+ for ( i = 0; i < MAXREGISTERS; i++ ) {
+ sc->ioaddr[ i ] = sc->iobase + i;
+ }
+
+ /*
+ * We should test if MB86965A is on the base address now.
+ * Unfortunately, it is very hard to probe it reliably, since
+ * we have no way to reset the chip under software control.
+ * On cold boot, we could check the "signature" bit patterns
+ * described in the Fujitsu document. On warm boot, however,
+ * we can predict almost nothing about register values.
+ */
+ if ( !fe_simple_probe( sc, probe_table ) ) goto NOTFOUND;
+
+ /* Check if our I/O address matches config info on 86965. */
+ n = ( inb( sc->ioaddr[ FE_BMPR19 ] ) & FE_B19_ADDR )
+ >> FE_B19_ADDR_SHIFT;
+ if ( baseaddr[ n ] != sc->iobase ) goto NOTFOUND;
+
+ /*
+ * We are now almost sure we have an AT1700 at the given
+ * address. So, read EEPROM through 86965. We have to write
+ * into LSI registers to read from EEPROM. I want to avoid it
+ * at this stage, but I cannot test the presence of the chip
+ * any further without reading EEPROM. FIXME.
+ */
+ save16 = inb( sc->ioaddr[ FE_BMPR16 ] );
+ save17 = inb( sc->ioaddr[ FE_BMPR17 ] );
+ fe_read_eeprom( sc, eeprom );
+
+ /* Make sure the EEPROM is turned off. */
+ outb( sc->ioaddr[ FE_BMPR16 ], 0 );
+ outb( sc->ioaddr[ FE_BMPR17 ], 0 );
+
+ /* Make sure that config info in EEPROM and 86965 agree. */
+ if ( eeprom[ FE_EEPROM_CONF ] != inb( sc->ioaddr[ FE_BMPR19 ] ) ) {
+ goto NOTFOUND;
+ }
+
+ /*
+ * The following model identification codes are stolen from
+ * from the NetBSD port of the fe driver. My reviewers
+ * suggested minor revision.
+ */
+
+ /* Determine the card type. */
+ switch (eeprom[FE_ATI_EEP_MODEL]) {
+ case FE_ATI_MODEL_AT1700T:
+ sc->typestr = "AT-1700T/RE2001";
+ sc->sc_description = "Ethernet adapter: AT1700T or RE2001";
+ break;
+ case FE_ATI_MODEL_AT1700BT:
+ sc->typestr = "AT-1700BT/RE2003";
+ sc->sc_description = "Ethernet adapter: AT1700BT or RE2003";
+ break;
+ case FE_ATI_MODEL_AT1700FT:
+ sc->typestr = "AT-1700FT/RE2009";
+ sc->sc_description = "Ethernet adapter: AT1700FT or RE2009";
+ break;
+ case FE_ATI_MODEL_AT1700AT:
+ sc->typestr = "AT-1700AT/RE2005";
+ sc->sc_description = "Ethernet adapter: AT1700AT or RE2005";
+ break;
+ default:
+ sc->typestr = "unknown AT-1700/RE2000 ?";
+ sc->sc_description = "Ethernet adapter: AT1700 or RE2000 ?";
+ break;
+ }
+
+ /*
+ * Try to determine IRQ settings.
+ * Different models use different ranges of IRQs.
+ */
+ if ( dev->id_irq == NO_IRQ ) {
+ n = ( inb( sc->ioaddr[ FE_BMPR19 ] ) & FE_B19_IRQ )
+ >> FE_B19_IRQ_SHIFT;
+ switch ( eeprom[ FE_ATI_EEP_REVISION ] & 0xf0 ) {
+ case 0x30:
+ dev->id_irq = irqmaps[ 3 ][ n ];
+ break;
+ case 0x10:
+ case 0x50:
+ dev->id_irq = irqmaps[ 2 ][ n ];
+ break;
+ case 0x40:
+ case 0x60:
+ if ( eeprom[ FE_ATI_EEP_MAGIC ] & 0x04 ) {
+ dev->id_irq = irqmaps[ 1 ][ n ];
+ } else {
+ dev->id_irq = irqmaps[ 0 ][ n ];
+ }
+ break;
+ default:
+ dev->id_irq = irqmaps[ 0 ][ n ];
+ break;
+ }
+ }
+
+
+ /*
+ * Initialize constants in the per-line structure.
+ */
+
+ /* Get our station address from EEPROM. */
+ bcopy( eeprom + FE_EEP_ATI_ADDR, sc->sc_enaddr, ETHER_ADDR_LEN );
+
+#if 1
+ /*
+ * This test doesn't work well for AT1700 look-alike by
+ * other vendors.
+ */
+ /* Make sure the vendor part is for Allied-Telesis. */
+ if ( sc->sc_enaddr[ 0 ] != 0x00
+ || sc->sc_enaddr[ 1 ] != 0x00
+ || sc->sc_enaddr[ 2 ] != 0xF4 ) return 0;
+
+#else
+ /* Make sure we got a valid station address. */
+ if ( ( sc->sc_enaddr[ 0 ] & 0x03 ) != 0x00
+ || ( sc->sc_enaddr[ 0 ] == 0x00
+ && sc->sc_enaddr[ 1 ] == 0x00
+ && sc->sc_enaddr[ 2 ] == 0x00 ) ) return 0;
+#endif
+
+ /*
+ * Program the 86960 as follows:
+ * SRAM: 32KB, 100ns, byte-wide access.
+ * Transmission buffer: 4KB x 2.
+ * System bus interface: 16 bits.
+ */
+ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL; /* FIXME */
+ sc->proto_dlcr5 = 0;
+ sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB
+ | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns;
+ sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_EC;
+#if 0 /* XXXX Should we use this? FIXME. */
+ sc->proto_bmpr13 = eeprom[ FE_ATI_EEP_MEDIA ];
+#else
+ sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO;
+#endif
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "ATI found" );
+#endif
+
+ /* Initialize 86965. */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE );
+ DELAY( 200 );
+
+ /* Disable all interrupts. */
+ outb( sc->ioaddr[ FE_DLCR2 ], 0 );
+ outb( sc->ioaddr[ FE_DLCR3 ], 0 );
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "end of fe_probe_ati()" );
+#endif
+
+ /*
+ * That's all. AT1700 occupies 32 I/O addresses, by the way.
+ */
+ return 32;
+
+ NOTFOUND:
+ /*
+ * We have no AT1700 at a given address.
+ * Restore BMPR16 and BMPR17 if we have destroyed them,
+ * hoping that the hardware on the address didn't get
+ * bad side effect.
+ */
+ if ( save16 != 0 | save17 != 0 ) {
+ outb( sc->ioaddr[ FE_BMPR16 ], save16 );
+ outb( sc->ioaddr[ FE_BMPR17 ], save17 );
+ }
+ return ( 0 );
+}
+
+/*
+ * Probe and initialization for Fujitsu MBH10302 PCMCIA Ethernet interface.
+ */
+static int
+fe_probe_mbh ( DEVICE * dev, struct fe_softc * sc )
+{
+ int i;
+
+ static struct fe_simple_probe_struct probe_table [] = {
+ { FE_DLCR2, 0x70, 0x00 },
+ { FE_DLCR4, 0x08, 0x00 },
+ /* { FE_DLCR5, 0x80, 0x00 }, Does not work well. */
+#if 0
+ /*
+ * Test *vendor* part of the address for Fujitsu.
+ * The test will gain reliability of probe process, but
+ * it rejects clones by other vendors, or OEM product
+ * supplied by retailer other than Fujitsu.
+ */
+ { FE_MBH10, 0xFF, 0x00 },
+ { FE_MBH11, 0xFF, 0x00 },
+ { FE_MBH12, 0xFF, 0x0E },
+#else
+ /*
+ * We can always verify the *first* 2 bits (in Ethernet
+ * bit order) are "global" and "unicast" even for
+ * unknown vendors.
+ */
+ { FE_MBH10, 0x03, 0x00 },
+#endif
+ /* Just a gap? Seems reliable, anyway. */
+ { 0x12, 0xFF, 0x00 },
+ { 0x13, 0xFF, 0x00 },
+ { 0x14, 0xFF, 0x00 },
+ { 0x15, 0xFF, 0x00 },
+ { 0x16, 0xFF, 0x00 },
+ { 0x17, 0xFF, 0x00 },
+#if 0
+ { 0x18, 0xFF, 0xFF },
+ { 0x19, 0xFF, 0xFF },
+#endif /* 0 */
+
+ { 0 }
+ };
+
+ /*
+ * We need explicit IRQ and supported address.
+ */
+ if ( dev->id_irq == NO_IRQ || ( sc->iobase & ~0x3E0 ) != 0 ) {
+ return ( 0 );
+ }
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "top of probe" );
+#endif
+
+ /* Setup an I/O address mapping table. */
+ for ( i = 0; i < MAXREGISTERS; i++ ) {
+ sc->ioaddr[ i ] = sc->iobase + i;
+ }
+
+ /*
+ * See if MBH10302 is on its address.
+ * I'm not sure the following probe code works. FIXME.
+ */
+ if ( !fe_simple_probe( sc, probe_table ) ) return 0;
+
+ /* Determine the card type. */
+ sc->typestr = "MBH10302 (PCMCIA)";
+ sc->sc_description = "Ethernet adapter: MBH10302 (PCMCIA)";
+
+ /*
+ * Initialize constants in the per-line structure.
+ */
+
+ /* Get our station address from EEPROM. */
+ inblk( sc, FE_MBH10, sc->sc_enaddr, ETHER_ADDR_LEN );
+
+ /* Make sure we got a valid station address. */
+ if ( ( sc->sc_enaddr[ 0 ] & 0x03 ) != 0x00
+ || ( sc->sc_enaddr[ 0 ] == 0x00
+ && sc->sc_enaddr[ 1 ] == 0x00
+ && sc->sc_enaddr[ 2 ] == 0x00 ) ) return 0;
+
+ /*
+ * Program the 86960 as follows:
+ * SRAM: 32KB, 100ns, byte-wide access.
+ * Transmission buffer: 4KB x 2.
+ * System bus interface: 16 bits.
+ */
+ sc->proto_dlcr4 = FE_D4_LBC_DISABLE | FE_D4_CNTRL;
+ sc->proto_dlcr5 = 0;
+ sc->proto_dlcr6 = FE_D6_BUFSIZ_32KB | FE_D6_TXBSIZ_2x4KB
+ | FE_D6_BBW_BYTE | FE_D6_SBW_WORD | FE_D6_SRAM_100ns;
+ sc->proto_dlcr7 = FE_D7_BYTSWP_LH | FE_D7_IDENT_NICE;
+ sc->proto_bmpr13 = FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO;
+
+ /* Setup hooks. We need a special initialization procedure. */
+ sc->init = fe_init_mbh;
+
+ /*
+ * Minimum initialization.
+ */
+
+ /* Minimal initialization of 86960. */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE );
+ DELAY( 200 );
+
+ /* Disable all interrupts. */
+ outb( sc->ioaddr[ FE_DLCR2 ], 0 );
+ outb( sc->ioaddr[ FE_DLCR3 ], 0 );
+
+#if 1 /* FIXME. */
+ /* Initialize system bus interface and encoder/decoder operation. */
+ outb( sc->ioaddr[ FE_MBH0 ], FE_MBH0_MAGIC | FE_MBH0_INTR_DISABLE );
+#endif
+
+ /*
+ * That's all. MBH10302 occupies 32 I/O addresses, by the way.
+ */
+ return 32;
+}
+
+/* MBH specific initialization routine. */
+static void
+fe_init_mbh ( struct fe_softc * sc )
+{
+ /* Minimal initialization of 86960. */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE );
+ DELAY( 200 );
+
+ /* Disable all interrupts. */
+ outb( sc->ioaddr[ FE_DLCR2 ], 0 );
+ outb( sc->ioaddr[ FE_DLCR3 ], 0 );
+
+ /* Enable master interrupt flag. */
+ outb( sc->ioaddr[ FE_MBH0 ], FE_MBH0_MAGIC | FE_MBH0_INTR_ENABLE );
+}
+#endif /* PC98 */
+
+/*
+ * Install interface into kernel networking data structures
+ */
+static int
+fe_attach ( DEVICE * dev )
+{
+#if NCRD > 0
+ static int already_ifattach[NFE];
+#endif
+ struct fe_softc *sc = &fe_softc[dev->id_unit];
+
+ /*
+ * Initialize ifnet structure
+ */
+ sc->sc_if.if_softc = sc;
+ sc->sc_if.if_unit = sc->sc_unit;
+ sc->sc_if.if_name = "fe";
+ sc->sc_if.if_output = ether_output;
+ sc->sc_if.if_start = fe_start;
+ sc->sc_if.if_ioctl = fe_ioctl;
+ sc->sc_if.if_watchdog = fe_watchdog;
+
+ /*
+ * Set default interface flags.
+ */
+ sc->sc_if.if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+
+ /*
+ * Set maximum size of output queue, if it has not been set.
+ * It is done here as this driver may be started after the
+ * system initialization (i.e., the interface is PCMCIA.)
+ *
+ * I'm not sure this is really necessary, but, even if it is,
+ * it should be done somewhere else, e.g., in if_attach(),
+ * since it must be a common workaround for all network drivers.
+ * FIXME.
+ */
+ if ( sc->sc_if.if_snd.ifq_maxlen == 0 ) {
+ sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen;
+ }
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "attach()" );
+#endif
+
+#if FE_SINGLE_TRANSMISSION
+ /* Override txb config to allocate minimum. */
+ sc->proto_dlcr6 &= ~FE_D6_TXBSIZ
+ sc->proto_dlcr6 |= FE_D6_TXBSIZ_2x2KB;
+#endif
+
+ /* Modify hardware config if it is requested. */
+ if ( dev->id_flags & FE_FLAGS_OVERRIDE_DLCR6 ) {
+ sc->proto_dlcr6 = dev->id_flags & FE_FLAGS_DLCR6_VALUE;
+ }
+
+ /* Find TX buffer size, based on the hardware dependent proto. */
+ switch ( sc->proto_dlcr6 & FE_D6_TXBSIZ ) {
+ case FE_D6_TXBSIZ_2x2KB: sc->txb_size = 2048; break;
+ case FE_D6_TXBSIZ_2x4KB: sc->txb_size = 4096; break;
+ case FE_D6_TXBSIZ_2x8KB: sc->txb_size = 8192; break;
+ default:
+ /* Oops, we can't work with single buffer configuration. */
+#if FE_DEBUG >= 2
+ log( LOG_WARNING, "fe%d: strange TXBSIZ config; fixing\n",
+ sc->sc_unit );
+#endif
+ sc->proto_dlcr6 &= ~FE_D6_TXBSIZ;
+ sc->proto_dlcr6 |= FE_D6_TXBSIZ_2x2KB;
+ sc->txb_size = 2048;
+ break;
+ }
+
+ /* Attach and stop the interface. */
+#if NCRD > 0
+ if (already_ifattach[dev->id_unit] != 1) {
+ if_attach(&sc->sc_if);
+ already_ifattach[dev->id_unit] = 1;
+ }
+#else
+ if_attach(&sc->sc_if);
+#endif /* NCRD > 0 */
+ fe_stop(sc->sc_unit); /* This changes the state to IDLE. */
+ ether_ifattach(&sc->sc_if);
+
+ /* Print additional info when attached. */
+ printf( "fe%d: address %6D, type %s\n", sc->sc_unit,
+ sc->sc_enaddr, ":" , sc->typestr );
+#if FE_DEBUG >= 3
+ {
+ int buf, txb, bbw, sbw, ram;
+
+ buf = txb = bbw = sbw = ram = -1;
+ switch ( sc->proto_dlcr6 & FE_D6_BUFSIZ ) {
+ case FE_D6_BUFSIZ_8KB: buf = 8; break;
+ case FE_D6_BUFSIZ_16KB: buf = 16; break;
+ case FE_D6_BUFSIZ_32KB: buf = 32; break;
+ case FE_D6_BUFSIZ_64KB: buf = 64; break;
+ }
+ switch ( sc->proto_dlcr6 & FE_D6_TXBSIZ ) {
+ case FE_D6_TXBSIZ_2x2KB: txb = 2; break;
+ case FE_D6_TXBSIZ_2x4KB: txb = 4; break;
+ case FE_D6_TXBSIZ_2x8KB: txb = 8; break;
+ }
+ switch ( sc->proto_dlcr6 & FE_D6_BBW ) {
+ case FE_D6_BBW_BYTE: bbw = 8; break;
+ case FE_D6_BBW_WORD: bbw = 16; break;
+ }
+ switch ( sc->proto_dlcr6 & FE_D6_SBW ) {
+ case FE_D6_SBW_BYTE: sbw = 8; break;
+ case FE_D6_SBW_WORD: sbw = 16; break;
+ }
+ switch ( sc->proto_dlcr6 & FE_D6_SRAM ) {
+ case FE_D6_SRAM_100ns: ram = 100; break;
+ case FE_D6_SRAM_150ns: ram = 150; break;
+ }
+ printf( "fe%d: SRAM %dKB %dbit %dns, TXB %dKBx2, %dbit I/O\n",
+ sc->sc_unit, buf, bbw, ram, txb, sbw );
+ }
+#endif
+
+#if NBPFILTER > 0
+ /* If BPF is in the kernel, call the attach for it. */
+ bpfattach( &sc->sc_if, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ return 1;
+}
+
+/*
+ * Reset interface.
+ */
+static void
+fe_reset ( int unit )
+{
+ /*
+ * Stop interface and re-initialize.
+ */
+ fe_stop(unit);
+ fe_init(unit);
+}
+
+/*
+ * Stop everything on the interface.
+ *
+ * All buffered packets, both transmitting and receiving,
+ * if any, will be lost by stopping the interface.
+ */
+static void
+fe_stop ( int unit )
+{
+ struct fe_softc *sc = &fe_softc[unit];
+ int s;
+
+ s = splimp();
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "stop()" );
+#endif
+
+ /* Disable interrupts. */
+ outb( sc->ioaddr[ FE_DLCR2 ], 0x00 );
+ outb( sc->ioaddr[ FE_DLCR3 ], 0x00 );
+
+ /* Stop interface hardware. */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE );
+ DELAY( 200 );
+
+ /* Clear all interrupt status. */
+ outb( sc->ioaddr[ FE_DLCR0 ], 0xFF );
+ outb( sc->ioaddr[ FE_DLCR1 ], 0xFF );
+
+ /* Put the chip in stand-by mode. */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR7 ], sc->proto_dlcr7 | FE_D7_POWER_DOWN );
+ DELAY( 200 );
+
+ /* Reset transmitter variables and interface flags. */
+ sc->sc_if.if_flags &= ~( IFF_OACTIVE | IFF_RUNNING );
+ sc->sc_if.if_timer = 0;
+ sc->txb_free = sc->txb_size;
+ sc->txb_count = 0;
+ sc->txb_sched = 0;
+
+ /* MAR loading can be delayed. */
+ sc->filter_change = 0;
+
+ /* Update config status also. */
+ sc->sc_dcstate = DC_IDLE;
+
+ /* Call a hook. */
+ if ( sc->stop ) sc->stop( sc );
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "end of stop()" );
+#endif
+
+ (void) splx(s);
+}
+
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ * generate an interrupt after a transmit has been started on it.
+ */
+static void
+fe_watchdog ( struct ifnet *ifp )
+{
+ struct fe_softc *sc = (struct fe_softc *)ifp;
+
+#if FE_DEBUG >= 1
+ /* A "debug" message. */
+ log( LOG_ERR, "fe%d: transmission timeout (%d+%d)%s\n",
+ ifp->if_unit, sc->txb_sched, sc->txb_count,
+ ( ifp->if_flags & IFF_UP ) ? "" : " when down" );
+#endif
+
+ /* Suggest users a possible cause. */
+ if ( ifp->if_oerrors > 0 ) {
+ log( LOG_WARNING, "fe%d: wrong IRQ setting in config?",
+ ifp->if_unit );
+ }
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, NULL );
+#endif
+
+ /* Record how many packets are lost by this accident. */
+ ifp->if_oerrors += sc->txb_sched + sc->txb_count;
+
+ /* Put the interface into known initial state. */
+ if ( ifp->if_flags & IFF_UP ) {
+ fe_reset( ifp->if_unit );
+ } else {
+ fe_stop( ifp->if_unit );
+ }
+}
+
+/*
+ * Initialize device.
+ */
+static void
+fe_init ( int unit )
+{
+ struct fe_softc *sc = &fe_softc[unit];
+ int i, s;
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "init()" );
+#endif
+
+ /* We need an address. */
+ if (sc->sc_if.if_addrlist == 0) {
+#if FE_DEBUG >= 1
+ log( LOG_ERR, "fe%d: init() without any address\n",
+ sc->sc_unit );
+#endif
+ return;
+ }
+
+#if FE_DEBUG >= 1
+ /*
+ * Make sure we have a valid station address.
+ * The following test is applicable for any Ethernet interfaces.
+ * It can be done in somewhere common to all of them. FIXME.
+ */
+ if ( ( sc->sc_enaddr[ 0 ] & 0x01 ) != 0
+ || ( sc->sc_enaddr[ 0 ] == 0x00
+ && sc->sc_enaddr[ 1 ] == 0x00
+ && sc->sc_enaddr[ 2 ] == 0x00 ) ) {
+ log( LOG_ERR, "fe%d: invalid station address (%6D)\n",
+ sc->sc_unit, sc->sc_enaddr, ":" );
+ return;
+ }
+#endif
+
+ /* Start initializing 86960. */
+ s = splimp();
+
+ /* Call a hook. */
+ if ( sc->init ) sc->init( sc );
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "after init hook" );
+#endif
+
+ /*
+ * Make sure to disable the chip, also.
+ * This may also help re-programming the chip after
+ * hot insertion of PCMCIAs.
+ */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE );
+ DELAY( 200 );
+
+ /* Power up the chip and select register bank for DLCRs. */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR7 ],
+ sc->proto_dlcr7 | FE_D7_RBS_DLCR | FE_D7_POWER_UP );
+ DELAY( 200 );
+
+ /* Feed the station address. */
+ outblk( sc, FE_DLCR8, sc->sc_enaddr, ETHER_ADDR_LEN );
+
+ /* Clear multicast address filter to receive nothing. */
+ outb( sc->ioaddr[ FE_DLCR7 ],
+ sc->proto_dlcr7 | FE_D7_RBS_MAR | FE_D7_POWER_UP );
+ outblk( sc, FE_MAR8, fe_filter_nothing.data, FE_FILTER_LEN );
+
+ /* Select the BMPR bank for runtime register access. */
+ outb( sc->ioaddr[ FE_DLCR7 ],
+ sc->proto_dlcr7 | FE_D7_RBS_BMPR | FE_D7_POWER_UP );
+
+ /* Initialize registers. */
+ outb( sc->ioaddr[ FE_DLCR0 ], 0xFF ); /* Clear all bits. */
+ outb( sc->ioaddr[ FE_DLCR1 ], 0xFF ); /* ditto. */
+ outb( sc->ioaddr[ FE_DLCR2 ], 0x00 );
+ outb( sc->ioaddr[ FE_DLCR3 ], 0x00 );
+ outb( sc->ioaddr[ FE_DLCR4 ], sc->proto_dlcr4 );
+ outb( sc->ioaddr[ FE_DLCR5 ], sc->proto_dlcr5 );
+ outb( sc->ioaddr[ FE_BMPR10 ], 0x00 );
+ outb( sc->ioaddr[ FE_BMPR11 ], FE_B11_CTRL_SKIP | FE_B11_MODE1 );
+ outb( sc->ioaddr[ FE_BMPR12 ], 0x00 );
+ outb( sc->ioaddr[ FE_BMPR13 ], sc->proto_bmpr13 );
+ outb( sc->ioaddr[ FE_BMPR14 ], 0x00 );
+ outb( sc->ioaddr[ FE_BMPR15 ], 0x00 );
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "just before enabling DLC" );
+#endif
+
+ /* Enable interrupts. */
+ outb( sc->ioaddr[ FE_DLCR2 ], FE_TMASK );
+ outb( sc->ioaddr[ FE_DLCR3 ], FE_RMASK );
+
+ /* Enable transmitter and receiver. */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_ENABLE );
+ DELAY( 200 );
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "just after enabling DLC" );
+#endif
+ /*
+ * Make sure to empty the receive buffer.
+ *
+ * This may be redundant, but *if* the receive buffer were full
+ * at this point, the driver would hang. I have experienced
+ * some strange hang-up just after UP. I hope the following
+ * code solve the problem.
+ *
+ * I have changed the order of hardware initialization.
+ * I think the receive buffer cannot have any packets at this
+ * point in this version. The following code *must* be
+ * redundant now. FIXME.
+ */
+ for ( i = 0; i < FE_MAX_RECV_COUNT; i++ ) {
+ if ( inb( sc->ioaddr[ FE_DLCR5 ] ) & FE_D5_BUFEMP ) break;
+ outb( sc->ioaddr[ FE_BMPR14 ], FE_B14_SKIP );
+ }
+#if FE_DEBUG >= 1
+ if ( i >= FE_MAX_RECV_COUNT ) {
+ log( LOG_ERR, "fe%d: cannot empty receive buffer\n",
+ sc->sc_unit );
+ }
+#endif
+#if FE_DEBUG >= 3
+ if ( i < FE_MAX_RECV_COUNT ) {
+ log( LOG_INFO, "fe%d: receive buffer emptied (%d)\n",
+ sc->sc_unit, i );
+ }
+#endif
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "after ERB loop" );
+#endif
+
+ /* Do we need this here? FIXME. */
+ outb( sc->ioaddr[ FE_DLCR0 ], 0xFF ); /* Clear all bits. */
+ outb( sc->ioaddr[ FE_DLCR1 ], 0xFF ); /* ditto. */
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "after FIXME" );
+#endif
+ /* Set 'running' flag, because we are now running. */
+ sc->sc_if.if_flags |= IFF_RUNNING;
+
+ /* Update device config status. */
+ sc->sc_dcstate = DC_BUSY;
+
+ /*
+ * At this point, the interface is running properly,
+ * except that it receives *no* packets. we then call
+ * fe_setmode() to tell the chip what packets to be
+ * received, based on the if_flags and multicast group
+ * list. It completes the initialization process.
+ */
+ fe_setmode( sc );
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "after setmode" );
+#endif
+
+ /* ...and attempt to start output queued packets. */
+ fe_start( &sc->sc_if );
+
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, "init() done" );
+#endif
+
+ (void) splx(s);
+}
+
+/*
+ * This routine actually starts the transmission on the interface
+ */
+static void
+fe_xmit ( struct fe_softc * sc )
+{
+ /*
+ * Set a timer just in case we never hear from the board again.
+ * We use longer timeout for multiple packet transmission.
+ * I'm not sure this timer value is appropriate. FIXME.
+ */
+ sc->sc_if.if_timer = 1 + sc->txb_count;
+
+ /* Update txb variables. */
+ sc->txb_sched = sc->txb_count;
+ sc->txb_count = 0;
+ sc->txb_free = sc->txb_size;
+
+ /* Start transmitter, passing packets in TX buffer. */
+ outb( sc->ioaddr[ FE_BMPR10 ], sc->txb_sched | FE_B10_START );
+}
+
+/*
+ * Start output on interface.
+ * We make two assumptions here:
+ * 1) that the current priority is set to splimp _before_ this code
+ * is called *and* is returned to the appropriate priority after
+ * return
+ * 2) that the IFF_OACTIVE flag is checked before this code is called
+ * (i.e. that the output part of the interface is idle)
+ */
+void
+fe_start ( struct ifnet *ifp )
+{
+ struct fe_softc *sc = ifp->if_softc;
+ struct mbuf *m;
+
+#if FE_DEBUG >= 1
+ /* Just a sanity check. */
+ if ( ( sc->txb_count == 0 ) != ( sc->txb_free == sc->txb_size ) ) {
+ /*
+ * Txb_count and txb_free co-works to manage the
+ * transmission buffer. Txb_count keeps track of the
+ * used potion of the buffer, while txb_free does unused
+ * potion. So, as long as the driver runs properly,
+ * txb_count is zero if and only if txb_free is same
+ * as txb_size (which represents whole buffer.)
+ */
+ log( LOG_ERR, "fe%d: inconsistent txb variables (%d, %d)\n",
+ sc->sc_unit, sc->txb_count, sc->txb_free );
+ /*
+ * So, what should I do, then?
+ *
+ * We now know txb_count and txb_free contradicts. We
+ * cannot, however, tell which is wrong. More
+ * over, we cannot peek 86960 transmission buffer or
+ * reset the transmission buffer. (In fact, we can
+ * reset the entire interface. I don't want to do it.)
+ *
+ * If txb_count is incorrect, leaving it as-is will cause
+ * sending of garbage after next interrupt. We have to
+ * avoid it. Hence, we reset the txb_count here. If
+ * txb_free was incorrect, resetting txb_count just loose
+ * some packets. We can live with it.
+ */
+ sc->txb_count = 0;
+ }
+#endif
+
+#if FE_DEBUG >= 1
+ /*
+ * First, see if there are buffered packets and an idle
+ * transmitter - should never happen at this point.
+ */
+ if ( ( sc->txb_count > 0 ) && ( sc->txb_sched == 0 ) ) {
+ log( LOG_ERR,
+ "fe%d: transmitter idle with %d buffered packets\n",
+ sc->sc_unit, sc->txb_count );
+ fe_xmit( sc );
+ }
+#endif
+
+ /*
+ * Stop accepting more transmission packets temporarily, when
+ * a filter change request is delayed. Updating the MARs on
+ * 86960 flushes the transmission buffer, so it is delayed
+ * until all buffered transmission packets have been sent
+ * out.
+ */
+ if ( sc->filter_change ) {
+ /*
+ * Filter change request is delayed only when the DLC is
+ * working. DLC soon raise an interrupt after finishing
+ * the work.
+ */
+ goto indicate_active;
+ }
+
+ for (;;) {
+
+ /*
+ * See if there is room to put another packet in the buffer.
+ * We *could* do better job by peeking the send queue to
+ * know the length of the next packet. Current version just
+ * tests against the worst case (i.e., longest packet). FIXME.
+ *
+ * When adding the packet-peek feature, don't forget adding a
+ * test on txb_count against QUEUEING_MAX.
+ * There is a little chance the packet count exceeds
+ * the limit. Assume transmission buffer is 8KB (2x8KB
+ * configuration) and an application sends a bunch of small
+ * (i.e., minimum packet sized) packets rapidly. An 8KB
+ * buffer can hold 130 blocks of 62 bytes long...
+ */
+ if ( sc->txb_free < ETHER_MAX_LEN + FE_DATA_LEN_LEN ) {
+ /* No room. */
+ goto indicate_active;
+ }
+
+#if FE_SINGLE_TRANSMISSION
+ if ( sc->txb_count > 0 ) {
+ /* Just one packet per a transmission buffer. */
+ goto indicate_active;
+ }
+#endif
+
+ /*
+ * Get the next mbuf chain for a packet to send.
+ */
+ IF_DEQUEUE( &sc->sc_if.if_snd, m );
+ if ( m == NULL ) {
+ /* No more packets to send. */
+ goto indicate_inactive;
+ }
+
+ /*
+ * Copy the mbuf chain into the transmission buffer.
+ * txb_* variables are updated as necessary.
+ */
+ fe_write_mbufs( sc, m );
+
+ /* Start transmitter if it's idle. */
+ if ( sc->txb_sched == 0 ) fe_xmit( sc );
+
+ /*
+ * Tap off here if there is a bpf listener,
+ * and the device is *not* in promiscuous mode.
+ * (86960 receives self-generated packets if
+ * and only if it is in "receive everything"
+ * mode.)
+ */
+#if NBPFILTER > 0
+ if ( sc->sc_if.if_bpf
+ && !( sc->sc_if.if_flags & IFF_PROMISC ) ) {
+ bpf_mtap( &sc->sc_if, m );
+ }
+#endif
+
+ m_freem( m );
+ }
+
+ indicate_inactive:
+ /*
+ * We are using the !OACTIVE flag to indicate to
+ * the outside world that we can accept an
+ * additional packet rather than that the
+ * transmitter is _actually_ active. Indeed, the
+ * transmitter may be active, but if we haven't
+ * filled all the buffers with data then we still
+ * want to accept more.
+ */
+ sc->sc_if.if_flags &= ~IFF_OACTIVE;
+ return;
+
+ indicate_active:
+ /*
+ * The transmitter is active, and there are no room for
+ * more outgoing packets in the transmission buffer.
+ */
+ sc->sc_if.if_flags |= IFF_OACTIVE;
+ return;
+}
+
+/*
+ * Drop (skip) a packet from receive buffer in 86960 memory.
+ */
+static void
+fe_droppacket ( struct fe_softc * sc )
+{
+ outb( sc->ioaddr[ FE_BMPR14 ], FE_B14_SKIP );
+}
+
+/*
+ * Transmission interrupt handler
+ * The control flow of this function looks silly. FIXME.
+ */
+static void
+fe_tint ( struct fe_softc * sc, u_char tstat )
+{
+ int left;
+ int col;
+
+ /*
+ * Handle "excessive collision" interrupt.
+ */
+ if ( tstat & FE_D0_COLL16 ) {
+
+ /*
+ * Find how many packets (including this collided one)
+ * are left unsent in transmission buffer.
+ */
+ left = inb( sc->ioaddr[ FE_BMPR10 ] );
+
+#if FE_DEBUG >= 2
+ log( LOG_WARNING, "fe%d: excessive collision (%d/%d)\n",
+ sc->sc_unit, left, sc->txb_sched );
+#endif
+#if FE_DEBUG >= 3
+ fe_dump( LOG_INFO, sc, NULL );
+#endif
+
+ /*
+ * Update statistics.
+ */
+ sc->sc_if.if_collisions += 16;
+ sc->sc_if.if_oerrors++;
+ sc->sc_if.if_opackets += sc->txb_sched - left;
+
+ /*
+ * Collision statistics has been updated.
+ * Clear the collision flag on 86960 now to avoid confusion.
+ */
+ outb( sc->ioaddr[ FE_DLCR0 ], FE_D0_COLLID );
+
+ /*
+ * Restart transmitter, skipping the
+ * collided packet.
+ *
+ * We *must* skip the packet to keep network running
+ * properly. Excessive collision error is an
+ * indication of the network overload. If we
+ * tried sending the same packet after excessive
+ * collision, the network would be filled with
+ * out-of-time packets. Packets belonging
+ * to reliable transport (such as TCP) are resent
+ * by some upper layer.
+ */
+ outb( sc->ioaddr[ FE_BMPR11 ],
+ FE_B11_CTRL_SKIP | FE_B11_MODE1 );
+ sc->txb_sched = left - 1;
+ }
+
+ /*
+ * Handle "transmission complete" interrupt.
+ */
+ if ( tstat & FE_D0_TXDONE ) {
+
+ /*
+ * Add in total number of collisions on last
+ * transmission. We also clear "collision occurred" flag
+ * here.
+ *
+ * 86960 has a design flaw on collision count on multiple
+ * packet transmission. When we send two or more packets
+ * with one start command (that's what we do when the
+ * transmission queue is crowded), 86960 informs us number
+ * of collisions occurred on the last packet on the
+ * transmission only. Number of collisions on previous
+ * packets are lost. I have told that the fact is clearly
+ * stated in the Fujitsu document.
+ *
+ * I considered not to mind it seriously. Collision
+ * count is not so important, anyway. Any comments? FIXME.
+ */
+
+ if ( inb( sc->ioaddr[ FE_DLCR0 ] ) & FE_D0_COLLID ) {
+
+ /* Clear collision flag. */
+ outb( sc->ioaddr[ FE_DLCR0 ], FE_D0_COLLID );
+
+ /* Extract collision count from 86960. */
+ col = inb( sc->ioaddr[ FE_DLCR4 ] );
+ col = ( col & FE_D4_COL ) >> FE_D4_COL_SHIFT;
+ if ( col == 0 ) {
+ /*
+ * Status register indicates collisions,
+ * while the collision count is zero.
+ * This can happen after multiple packet
+ * transmission, indicating that one or more
+ * previous packet(s) had been collided.
+ *
+ * Since the accurate number of collisions
+ * has been lost, we just guess it as 1;
+ * Am I too optimistic? FIXME.
+ */
+ col = 1;
+ }
+ sc->sc_if.if_collisions += col;
+#if FE_DEBUG >= 3
+ log( LOG_WARNING, "fe%d: %d collision(s) (%d)\n",
+ sc->sc_unit, col, sc->txb_sched );
+#endif
+ }
+
+ /*
+ * Update total number of successfully
+ * transmitted packets.
+ */
+ sc->sc_if.if_opackets += sc->txb_sched;
+ sc->txb_sched = 0;
+
+ /*
+ * The transmitter is no more active.
+ * Reset output active flag and watchdog timer.
+ */
+ sc->sc_if.if_flags &= ~IFF_OACTIVE;
+ sc->sc_if.if_timer = 0;
+
+ /*
+ * If more data is ready to transmit in the buffer, start
+ * transmitting them. Otherwise keep transmitter idle,
+ * even if more data is queued. This gives receive
+ * process a slight priority.
+ */
+ if ( sc->txb_count > 0 ) fe_xmit( sc );
+ }
+}
+
+/*
+ * Ethernet interface receiver interrupt.
+ */
+static void
+fe_rint ( struct fe_softc * sc, u_char rstat )
+{
+ u_short len;
+ u_char status;
+ int i;
+
+ /*
+ * Update statistics if this interrupt is caused by an error.
+ */
+ if ( rstat & ( FE_D1_OVRFLO | FE_D1_CRCERR
+ | FE_D1_ALGERR | FE_D1_SRTPKT ) ) {
+#if FE_DEBUG >= 3
+ log( LOG_WARNING,
+ "fe%d: receive error: %s%s%s%s(%02x)\n",
+ sc->sc_unit,
+ rstat & FE_D1_OVRFLO ? "OVR " : "",
+ rstat & FE_D1_CRCERR ? "CRC " : "",
+ rstat & FE_D1_ALGERR ? "ALG " : "",
+ rstat & FE_D1_SRTPKT ? "LEN " : "",
+ rstat );
+#endif
+ sc->sc_if.if_ierrors++;
+ }
+
+ /*
+ * MB86960 has a flag indicating "receive queue empty."
+ * We just loop, checking the flag, to pull out all received
+ * packets.
+ *
+ * We limit the number of iterations to avoid infinite-loop.
+ * It can be caused by a very slow CPU (some broken
+ * peripheral may insert incredible number of wait cycles)
+ * or, worse, by a broken MB86960 chip.
+ */
+ for ( i = 0; i < FE_MAX_RECV_COUNT; i++ ) {
+
+ /* Stop the iteration if 86960 indicates no packets. */
+ if ( inb( sc->ioaddr[ FE_DLCR5 ] ) & FE_D5_BUFEMP ) break;
+
+ /*
+ * Extract A receive status byte.
+ * As our 86960 is in 16 bit bus access mode, we have to
+ * use inw() to get the status byte. The significant
+ * value is returned in lower 8 bits.
+ */
+ status = ( u_char )inw( sc->ioaddr[ FE_BMPR8 ] );
+#if FE_DEBUG >= 4
+ log( LOG_INFO, "fe%d: receive status = %04x\n",
+ sc->sc_unit, status );
+#endif
+
+ /*
+ * If there was an error, update statistics and drop
+ * the packet, unless the interface is in promiscuous
+ * mode.
+ */
+ if ( ( status & 0xF0 ) != 0x20 ) {
+ if ( !( sc->sc_if.if_flags & IFF_PROMISC ) ) {
+ sc->sc_if.if_ierrors++;
+ fe_droppacket(sc);
+ continue;
+ }
+ }
+
+ /*
+ * Extract the packet length.
+ * It is a sum of a header (14 bytes) and a payload.
+ * CRC has been stripped off by the 86960.
+ */
+ len = inw( sc->ioaddr[ FE_BMPR8 ] );
+
+ /*
+ * MB86965 checks the packet length and drop big packet
+ * before passing it to us. There are no chance we can
+ * get big packets through it, even if they are actually
+ * sent over a line. Hence, if the length exceeds
+ * the specified limit, it means some serious failure,
+ * such as out-of-sync on receive buffer management.
+ *
+ * Is this statement true? FIXME.
+ */
+ if ( len > ETHER_MAX_LEN || len < ETHER_HDR_SIZE ) {
+#if FE_DEBUG >= 2
+ log( LOG_WARNING,
+ "fe%d: received a %s packet? (%u bytes)\n",
+ sc->sc_unit,
+ len < ETHER_HDR_SIZE ? "partial" : "big",
+ len );
+#endif
+ sc->sc_if.if_ierrors++;
+ fe_droppacket( sc );
+ continue;
+ }
+
+ /*
+ * Check for a short (RUNT) packet. We *do* check
+ * but do nothing other than print a message.
+ * Short packets are illegal, but does nothing bad
+ * if it carries data for upper layer.
+ */
+#if FE_DEBUG >= 2
+ if ( len < ETHER_MIN_LEN ) {
+ log( LOG_WARNING,
+ "fe%d: received a short packet? (%u bytes)\n",
+ sc->sc_unit, len );
+ }
+#endif
+
+ /*
+ * Go get a packet.
+ */
+ if ( fe_get_packet( sc, len ) < 0 ) {
+ /* Skip a packet, updating statistics. */
+#if FE_DEBUG >= 2
+ log( LOG_WARNING, "%s%d: out of mbuf;"
+ " dropping a packet (%u bytes)\n",
+ sc->sc_unit, len );
+#endif
+ sc->sc_if.if_ierrors++;
+ fe_droppacket( sc );
+
+ /*
+ * We stop receiving packets, even if there are
+ * more in the buffer. We hope we can get more
+ * mbuf next time.
+ */
+ return;
+ }
+
+ /* Successfully received a packet. Update stat. */
+ sc->sc_if.if_ipackets++;
+ }
+}
+
+/*
+ * Ethernet interface interrupt processor
+ */
+void
+feintr ( int unit )
+{
+ struct fe_softc *sc = &fe_softc[unit];
+ u_char tstat, rstat;
+
+ /*
+ * Loop until there are no more new interrupt conditions.
+ */
+ for (;;) {
+
+#if FE_DEBUG >= 4
+ fe_dump( LOG_INFO, sc, "intr()" );
+#endif
+
+ /*
+ * Get interrupt conditions, masking unneeded flags.
+ */
+ tstat = inb( sc->ioaddr[ FE_DLCR0 ] ) & FE_TMASK;
+ rstat = inb( sc->ioaddr[ FE_DLCR1 ] ) & FE_RMASK;
+ if ( tstat == 0 && rstat == 0 ) break;
+
+ /*
+ * Reset the conditions we are acknowledging.
+ */
+ outb( sc->ioaddr[ FE_DLCR0 ], tstat );
+ outb( sc->ioaddr[ FE_DLCR1 ], rstat );
+
+ /*
+ * Handle transmitter interrupts. Handle these first because
+ * the receiver will reset the board under some conditions.
+ */
+ if ( tstat ) {
+ fe_tint( sc, tstat );
+ }
+
+ /*
+ * Handle receiver interrupts
+ */
+ if ( rstat ) {
+ fe_rint( sc, rstat );
+ }
+
+ /*
+ * Update the multicast address filter if it is
+ * needed and possible. We do it now, because
+ * we can make sure the transmission buffer is empty,
+ * and there is a good chance that the receive queue
+ * is empty. It will minimize the possibility of
+ * packet loss.
+ */
+ if ( sc->filter_change
+ && sc->txb_count == 0 && sc->txb_sched == 0 ) {
+ fe_loadmar(sc);
+ sc->sc_if.if_flags &= ~IFF_OACTIVE;
+ }
+
+ /*
+ * If it looks like the transmitter can take more data,
+ * attempt to start output on the interface. This is done
+ * after handling the receiver interrupt to give the
+ * receive operation priority.
+ *
+ * BTW, I'm not sure in what case the OACTIVE is on at
+ * this point. Is the following test redundant?
+ *
+ * No. This routine polls for both transmitter and
+ * receiver interrupts. 86960 can raise a receiver
+ * interrupt when the transmission buffer is full.
+ */
+ if ( ( sc->sc_if.if_flags & IFF_OACTIVE ) == 0 ) {
+ fe_start( &sc->sc_if );
+ }
+
+ }
+}
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ * pretty ugly.
+ */
+static int
+fe_ioctl ( struct ifnet * ifp, int command, caddr_t data )
+{
+ struct fe_softc *sc = ifp->if_softc;
+ int s, error = 0;
+
+#if FE_DEBUG >= 3
+ log( LOG_INFO, "fe%d: ioctl(%x)\n", sc->sc_unit, command );
+#endif
+
+ s = splimp();
+
+ switch (command) {
+
+ case SIOCSIFADDR:
+ {
+ struct ifaddr * ifa = ( struct ifaddr * )data;
+
+ sc->sc_if.if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ fe_init( sc->sc_unit ); /* before arp_ifinit */
+ arp_ifinit( &sc->arpcom, ifa );
+ break;
+#endif
+#ifdef IPX
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_IPX:
+ {
+ register struct ipx_addr *ina
+ = &(IA_SIPX(ifa)->sipx_addr);
+
+ if (ipx_nullhost(*ina))
+ ina->x_host =
+ *(union ipx_host *) (sc->sc_enaddr); else {
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t) sc->sc_enaddr,
+ sizeof(sc->sc_enaddr));
+ }
+
+ /*
+ * Set new address
+ */
+ fe_init(sc->sc_unit);
+ break;
+ }
+#endif
+#ifdef INET6
+ case AF_INET6:
+ /* IPV6 added by shin 96.2.6 */
+ fe_init(sc->sc_unit);
+ ndp6_ifinit(&sc->arpcom, ifa);
+ break;
+#endif
+#ifdef NS
+
+ /*
+ * XXX - This code is probably wrong
+ */
+ case AF_NS:
+ {
+ register struct ns_addr *ina
+ = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *) (sc->sc_enaddr);
+ else {
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t) sc->sc_enaddr,
+ sizeof(sc->sc_enaddr));
+ }
+
+ /*
+ * Set new address
+ */
+ fe_init(sc->sc_unit);
+ break;
+ }
+#endif
+ default:
+ fe_init( sc->sc_unit );
+ break;
+ }
+ break;
+ }
+
+#ifdef SIOCGIFADDR
+ case SIOCGIFADDR:
+ {
+ struct ifreq * ifr = ( struct ifreq * )data;
+ struct sockaddr * sa = ( struct sockaddr * )&ifr->ifr_data;
+
+ bcopy((caddr_t)sc->sc_enaddr,
+ (caddr_t)sa->sa_data, ETHER_ADDR_LEN);
+ break;
+ }
+#endif
+
+#ifdef SIOCGIFPHYSADDR
+ case SIOCGIFPHYSADDR:
+ {
+ struct ifreq * ifr = ( struct ifreq * )data;
+
+ bcopy((caddr_t)sc->sc_enaddr,
+ (caddr_t)&ifr->ifr_data, ETHER_ADDR_LEN);
+ break;
+ }
+#endif
+
+#ifdef notdef
+#ifdef SIOCSIFPHYSADDR
+ case SIOCSIFPHYSADDR:
+ {
+ /*
+ * Set the physical (Ethernet) address of the interface.
+ * When and by whom is this command used? FIXME.
+ */
+ struct ifreq * ifr = ( struct ifreq * )data;
+
+ bcopy((caddr_t)&ifr->ifr_data,
+ (caddr_t)sc->sc_enaddr, ETHER_ADDR_LEN);
+ fe_setlinkaddr( sc );
+ break;
+ }
+#endif
+#endif /* notdef */
+
+#ifdef SIOCSIFFLAGS
+ case SIOCSIFFLAGS:
+ {
+ /*
+ * Switch interface state between "running" and
+ * "stopped", reflecting the UP flag.
+ */
+ if ( sc->sc_if.if_flags & IFF_UP ) {
+ if ( ( sc->sc_if.if_flags & IFF_RUNNING ) == 0 ) {
+ fe_init( sc->sc_unit );
+ }
+ } else {
+ if ( ( sc->sc_if.if_flags & IFF_RUNNING ) != 0 ) {
+ fe_stop( sc->sc_unit );
+ }
+ }
+
+ /*
+ * Promiscuous and/or multicast flags may have changed,
+ * so reprogram the multicast filter and/or receive mode.
+ */
+ fe_setmode( sc );
+
+#if FE_DEBUG >= 1
+ /* "ifconfig fe0 debug" to print register dump. */
+ if ( sc->sc_if.if_flags & IFF_DEBUG ) {
+ fe_dump( LOG_DEBUG, sc, "SIOCSIFFLAGS(DEBUG)" );
+ }
+#endif
+ break;
+ }
+#endif
+
+#ifdef SIOCADDMULTI
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ {
+ /*
+ * Update out multicast list.
+ */
+ struct ifreq * ifr = ( struct ifreq * )data;
+
+ error = ( command == SIOCADDMULTI )
+ ? ether_addmulti( ifr, &sc->arpcom )
+ : ether_delmulti( ifr, &sc->arpcom );
+
+ if ( error == ENETRESET ) {
+ /*
+ * Multicast list has changed; set the hardware filter
+ * accordingly.
+ */
+ fe_setmode( sc );
+ error = 0;
+ }
+
+ break;
+ }
+#endif
+
+#ifdef SIOCSIFMTU
+ case SIOCSIFMTU:
+ {
+ /*
+ * Set the interface MTU.
+ */
+ struct ifreq * ifr = ( struct ifreq * )data;
+
+ if ( ifr->ifr_mtu > ETHERMTU ) {
+ error = EINVAL;
+ } else {
+ sc->sc_if.if_mtu = ifr->ifr_mtu;
+ }
+ break;
+ }
+#endif
+
+ default:
+ error = EINVAL;
+ }
+
+ (void) splx(s);
+ return (error);
+}
+
+/*
+ * Retrieve packet from receive buffer and send to the next level up via
+ * ether_input(). If there is a BPF listener, give a copy to BPF, too.
+ * Returns 0 if success, -1 if error (i.e., mbuf allocation failure).
+ */
+static int
+fe_get_packet ( struct fe_softc * sc, u_short len )
+{
+ struct ether_header *eh;
+ struct mbuf *m;
+
+ /*
+ * NFS wants the data be aligned to the word (4 byte)
+ * boundary. Ethernet header has 14 bytes. There is a
+ * 2-byte gap.
+ */
+#define NFS_MAGIC_OFFSET 2
+
+ /*
+ * This function assumes that an Ethernet packet fits in an
+ * mbuf (with a cluster attached when necessary.) On FreeBSD
+ * 2.0 for x86, which is the primary target of this driver, an
+ * mbuf cluster has 4096 bytes, and we are happy. On ancient
+ * BSDs, such as vanilla 4.3 for 386, a cluster size was 1024,
+ * however. If the following #error message were printed upon
+ * compile, you need to rewrite this function.
+ */
+#if ( MCLBYTES < ETHER_MAX_LEN + NFS_MAGIC_OFFSET )
+#error "Too small MCLBYTES to use fe driver."
+#endif
+
+ /*
+ * Our strategy has one more problem. There is a policy on
+ * mbuf cluster allocation. It says that we must have at
+ * least MINCLSIZE (208 bytes on FreeBSD 2.0 for x86) to
+ * allocate a cluster. For a packet of a size between
+ * (MHLEN - 2) to (MINCLSIZE - 2), our code violates the rule...
+ * On the other hand, the current code is short, simple,
+ * and fast, however. It does no harmful thing, just waists
+ * some memory. Any comments? FIXME.
+ */
+
+ /* Allocate an mbuf with packet header info. */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if ( m == NULL ) return -1;
+
+ /* Attach a cluster if this packet doesn't fit in a normal mbuf. */
+ if ( len > MHLEN - NFS_MAGIC_OFFSET ) {
+ MCLGET( m, M_DONTWAIT );
+ if ( !( m->m_flags & M_EXT ) ) {
+ m_freem( m );
+ return -1;
+ }
+ }
+
+ /* Initialize packet header info. */
+ m->m_pkthdr.rcvif = &sc->sc_if;
+ m->m_pkthdr.len = len;
+
+ /* Set the length of this packet. */
+ m->m_len = len;
+
+ /* The following silliness is to make NFS happy */
+ m->m_data += NFS_MAGIC_OFFSET;
+
+ /* Get a packet. */
+ insw( sc->ioaddr[ FE_BMPR8 ], m->m_data, ( len + 1 ) >> 1 );
+
+ /* Get (actually just point to) the header part. */
+ eh = mtod( m, struct ether_header *);
+
+#define ETHER_ADDR_IS_MULTICAST(A) (*(char *)(A) & 1)
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a BPF listener on this interface.
+ * If it is, hand off the raw packet to bpf.
+ */
+ if ( sc->sc_if.if_bpf ) {
+ bpf_mtap( &sc->sc_if, m );
+ }
+#endif
+
+ /*
+ * Make sure this packet is (or may be) directed to us.
+ * That is, the packet is either unicasted to our address,
+ * or broad/multi-casted. If any other packets are
+ * received, it is an indication of an error -- probably
+ * 86960 is in a wrong operation mode.
+ * Promiscuous mode is an exception. Under the mode, all
+ * packets on the media must be received. (We must have
+ * programmed the 86960 so.)
+ */
+
+ if ( ( sc->sc_if.if_flags & IFF_PROMISC )
+ && !ETHER_ADDR_IS_MULTICAST( eh->ether_dhost )
+ && bcmp( eh->ether_dhost, sc->sc_enaddr, ETHER_ADDR_LEN ) != 0 ) {
+ /*
+ * The packet was not for us. This is normal since
+ * we are now in promiscuous mode. Just drop the packet.
+ */
+ m_freem( m );
+ return 0;
+ }
+
+#if FE_DEBUG >= 3
+ if ( !ETHER_ADDR_IS_MULTICAST( eh->ether_dhost )
+ && bcmp( eh->ether_dhost, sc->sc_enaddr, ETHER_ADDR_LEN ) != 0 ) {
+ /*
+ * This packet was not for us. We can't be in promiscuous
+ * mode since the case was handled by above test.
+ * We found an error (of this driver.)
+ */
+ log( LOG_WARNING,
+ "fe%d: got an unwanted packet, dst = %6D\n",
+ sc->sc_unit, eh->ether_dhost , ":" );
+ m_freem( m );
+ return 0;
+ }
+#endif
+
+ /* Strip off the Ethernet header. */
+ m->m_pkthdr.len -= sizeof ( struct ether_header );
+ m->m_len -= sizeof ( struct ether_header );
+ m->m_data += sizeof ( struct ether_header );
+
+ /* Feed the packet to upper layer. */
+ ether_input( &sc->sc_if, eh, m );
+ return 0;
+}
+
+/*
+ * Write an mbuf chain to the transmission buffer memory using 16 bit PIO.
+ * Returns number of bytes actually written, including length word.
+ *
+ * If an mbuf chain is too long for an Ethernet frame, it is not sent.
+ * Packets shorter than Ethernet minimum are legal, and we pad them
+ * before sending out. An exception is "partial" packets which are
+ * shorter than mandatory Ethernet header.
+ *
+ * I wrote a code for an experimental "delayed padding" technique.
+ * When employed, it postpones the padding process for short packets.
+ * If xmit() occurred at the moment, the padding process is omitted, and
+ * garbage is sent as pad data. If next packet is stored in the
+ * transmission buffer before xmit(), write_mbuf() pads the previous
+ * packet before transmitting new packet. This *may* gain the
+ * system performance (slightly).
+ */
+static void
+fe_write_mbufs ( struct fe_softc *sc, struct mbuf *m )
+{
+ u_short addr_bmpr8 = sc->ioaddr[ FE_BMPR8 ];
+ u_short length, len;
+ short pad;
+ struct mbuf *mp;
+ u_char *data;
+ u_short savebyte; /* WARNING: Architecture dependent! */
+#define NO_PENDING_BYTE 0xFFFF
+
+#if FE_DEBUG >= 2
+ /* First, count up the total number of bytes to copy */
+ length = 0;
+ for ( mp = m; mp != NULL; mp = mp->m_next ) {
+ length += mp->m_len;
+ }
+ /* Check if this matches the one in the packet header. */
+ if ( length != m->m_pkthdr.len ) {
+ log( LOG_WARNING, "fe%d: packet length mismatch? (%d/%d)\n",
+ sc->sc_unit, length, m->m_pkthdr.len );
+ }
+#else
+ /* Just use the length value in the packet header. */
+ length = m->m_pkthdr.len;
+#endif
+
+#if FE_DEBUG >= 1
+ /*
+ * Should never send big packets. If such a packet is passed,
+ * it should be a bug of upper layer. We just ignore it.
+ * ... Partial (too short) packets, neither.
+ */
+ if ( length > ETHER_MAX_LEN || length < ETHER_HDR_SIZE ) {
+ log( LOG_ERR,
+ "fe%d: got a %s packet (%u bytes) to send\n",
+ sc->sc_unit,
+ length < ETHER_HDR_SIZE ? "partial" : "big", length );
+ sc->sc_if.if_oerrors++;
+ return;
+ }
+#endif
+
+ /*
+ * Put the length word for this frame.
+ * Does 86960 accept odd length? -- Yes.
+ * Do we need to pad the length to minimum size by ourselves?
+ * -- Generally yes. But for (or will be) the last
+ * packet in the transmission buffer, we can skip the
+ * padding process. It may gain performance slightly. FIXME.
+ */
+ outw( addr_bmpr8, max( length, ETHER_MIN_LEN ) );
+
+ /*
+ * Update buffer status now.
+ * Truncate the length up to an even number, since we use outw().
+ */
+ length = ( length + 1 ) & ~1;
+ sc->txb_free -= FE_DATA_LEN_LEN + max( length, ETHER_MIN_LEN );
+ sc->txb_count++;
+
+ /*
+ * Transfer the data from mbuf chain to the transmission buffer.
+ * MB86960 seems to require that data be transferred as words, and
+ * only words. So that we require some extra code to patch
+ * over odd-length mbufs.
+ */
+ savebyte = NO_PENDING_BYTE;
+ for ( mp = m; mp != 0; mp = mp->m_next ) {
+
+ /* Ignore empty mbuf. */
+ len = mp->m_len;
+ if ( len == 0 ) continue;
+
+ /* Find the actual data to send. */
+ data = mtod(mp, caddr_t);
+
+ /* Finish the last byte. */
+ if ( savebyte != NO_PENDING_BYTE ) {
+ outw( addr_bmpr8, savebyte | ( *data << 8 ) );
+ data++;
+ len--;
+ savebyte = NO_PENDING_BYTE;
+ }
+
+ /* output contiguous words */
+ if (len > 1) {
+ outsw( addr_bmpr8, data, len >> 1);
+ data += len & ~1;
+ len &= 1;
+ }
+
+ /* Save a remaining byte, if there is one. */
+ if ( len > 0 ) {
+ savebyte = *data;
+ }
+ }
+
+ /* Spit the last byte, if the length is odd. */
+ if ( savebyte != NO_PENDING_BYTE ) {
+ outw( addr_bmpr8, savebyte );
+ }
+}
+
+/*
+ * Compute hash value for an Ethernet address
+ */
+static int
+fe_hash ( u_char * ep )
+{
+#define FE_HASH_MAGIC_NUMBER 0xEDB88320L
+
+ u_long hash = 0xFFFFFFFFL;
+ int i, j;
+ u_char b;
+ u_long m;
+
+ for ( i = ETHER_ADDR_LEN; --i >= 0; ) {
+ b = *ep++;
+ for ( j = 8; --j >= 0; ) {
+ m = hash;
+ hash >>= 1;
+ if ( ( m ^ b ) & 1 ) hash ^= FE_HASH_MAGIC_NUMBER;
+ b >>= 1;
+ }
+ }
+ return ( ( int )( hash >> 26 ) );
+}
+
+/*
+ * Compute the multicast address filter from the
+ * list of multicast addresses we need to listen to.
+ */
+static struct fe_filter
+fe_mcaf ( struct fe_softc *sc )
+{
+ int index;
+ struct fe_filter filter;
+ struct ether_multi *enm;
+ struct ether_multistep step;
+
+ filter = fe_filter_nothing;
+ ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
+ while ( enm != NULL) {
+ if ( bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) ) {
+ return ( fe_filter_all );
+ }
+ index = fe_hash( enm->enm_addrlo );
+#if FE_DEBUG >= 4
+ log( LOG_INFO, "fe%d: hash(%6D) == %d\n",
+ sc->sc_unit, enm->enm_addrlo , ":", index );
+#endif
+
+ filter.data[index >> 3] |= 1 << (index & 7);
+ ETHER_NEXT_MULTI(step, enm);
+ }
+ return ( filter );
+}
+
+/*
+ * Calculate a new "multicast packet filter" and put the 86960
+ * receiver in appropriate mode.
+ */
+static void
+fe_setmode ( struct fe_softc *sc )
+{
+ int flags = sc->sc_if.if_flags;
+
+ /*
+ * If the interface is not running, we postpone the update
+ * process for receive modes and multicast address filter
+ * until the interface is restarted. It reduces some
+ * complicated job on maintaining chip states. (Earlier versions
+ * of this driver had a bug on that point...)
+ *
+ * To complete the trick, fe_init() calls fe_setmode() after
+ * restarting the interface.
+ */
+ if ( !( flags & IFF_RUNNING ) ) return;
+
+ /*
+ * Promiscuous mode is handled separately.
+ */
+ if ( flags & IFF_PROMISC ) {
+ /*
+ * Program 86960 to receive all packets on the segment
+ * including those directed to other stations.
+ * Multicast filter stored in MARs are ignored
+ * under this setting, so we don't need to update it.
+ *
+ * Promiscuous mode in FreeBSD 2 is used solely by
+ * BPF, and BPF only listens to valid (no error) packets.
+ * So, we ignore erroneous ones even in this mode.
+ * (Older versions of fe driver mistook the point.)
+ */
+ outb( sc->ioaddr[ FE_DLCR5 ],
+ sc->proto_dlcr5 | FE_D5_AFM0 | FE_D5_AFM1 );
+ sc->filter_change = 0;
+
+#if FE_DEBUG >= 3
+ log( LOG_INFO, "fe%d: promiscuous mode\n", sc->sc_unit );
+#endif
+ return;
+ }
+
+ /*
+ * Turn the chip to the normal (non-promiscuous) mode.
+ */
+ outb( sc->ioaddr[ FE_DLCR5 ], sc->proto_dlcr5 | FE_D5_AFM1 );
+
+ /*
+ * Find the new multicast filter value.
+ * I'm not sure we have to handle modes other than MULTICAST.
+ * Who sets ALLMULTI? Who turns MULTICAST off? FIXME.
+ */
+ if ( flags & IFF_ALLMULTI ) {
+ sc->filter = fe_filter_all;
+ } else if ( flags & IFF_MULTICAST ) {
+ sc->filter = fe_mcaf( sc );
+ } else {
+ sc->filter = fe_filter_nothing;
+ }
+ sc->filter_change = 1;
+
+#if FE_DEBUG >= 3
+ log( LOG_INFO, "fe%d: address filter: [%8D]\n",
+ sc->sc_unit, sc->filter.data, " " );
+#endif
+
+ /*
+ * We have to update the multicast filter in the 86960, A.S.A.P.
+ *
+ * Note that the DLC (Data Link Control unit, i.e. transmitter
+ * and receiver) must be stopped when feeding the filter, and
+ * DLC trashes all packets in both transmission and receive
+ * buffers when stopped.
+ *
+ * ... Are the above sentences correct? I have to check the
+ * manual of the MB86960A. FIXME.
+ *
+ * To reduce the packet loss, we delay the filter update
+ * process until buffers are empty.
+ */
+ if ( sc->txb_sched == 0 && sc->txb_count == 0
+ && !( inb( sc->ioaddr[ FE_DLCR1 ] ) & FE_D1_PKTRDY ) ) {
+ /*
+ * Buffers are (apparently) empty. Load
+ * the new filter value into MARs now.
+ */
+ fe_loadmar(sc);
+ } else {
+ /*
+ * Buffers are not empty. Mark that we have to update
+ * the MARs. The new filter will be loaded by feintr()
+ * later.
+ */
+#if FE_DEBUG >= 4
+ log( LOG_INFO, "fe%d: filter change delayed\n", sc->sc_unit );
+#endif
+ }
+}
+
+/*
+ * Load a new multicast address filter into MARs.
+ *
+ * The caller must have splimp'ed before fe_loadmar.
+ * This function starts the DLC upon return. So it can be called only
+ * when the chip is working, i.e., from the driver's point of view, when
+ * a device is RUNNING. (I mistook the point in previous versions.)
+ */
+static void
+fe_loadmar ( struct fe_softc * sc )
+{
+ /* Stop the DLC (transmitter and receiver). */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_DISABLE );
+ DELAY( 200 );
+
+ /* Select register bank 1 for MARs. */
+ outb( sc->ioaddr[ FE_DLCR7 ],
+ sc->proto_dlcr7 | FE_D7_RBS_MAR | FE_D7_POWER_UP );
+
+ /* Copy filter value into the registers. */
+ outblk( sc, FE_MAR8, sc->filter.data, FE_FILTER_LEN );
+
+ /* Restore the bank selection for BMPRs (i.e., runtime registers). */
+ outb( sc->ioaddr[ FE_DLCR7 ],
+ sc->proto_dlcr7 | FE_D7_RBS_BMPR | FE_D7_POWER_UP );
+
+ /* Restart the DLC. */
+ DELAY( 200 );
+ outb( sc->ioaddr[ FE_DLCR6 ], sc->proto_dlcr6 | FE_D6_DLC_ENABLE );
+ DELAY( 200 );
+
+ /* We have just updated the filter. */
+ sc->filter_change = 0;
+
+#if FE_DEBUG >= 3
+ log( LOG_INFO, "fe%d: address filter changed\n", sc->sc_unit );
+#endif
+}
+
+#if FE_DEBUG >= 1
+static void
+fe_dump ( int level, struct fe_softc * sc, char * message )
+{
+ log( level, "fe%d: %s,"
+ " DLCR = %02x %02x %02x %02x %02x %02x %02x %02x,"
+ " BMPR = xx xx %02x %02x %02x %02x %02x %02x,"
+ " asic = %02x %02x %02x %02x %02x %02x %02x %02x"
+ " + %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ sc->sc_unit, message ? message : "registers",
+ inb( sc->ioaddr[ FE_DLCR0 ] ), inb( sc->ioaddr[ FE_DLCR1 ] ),
+ inb( sc->ioaddr[ FE_DLCR2 ] ), inb( sc->ioaddr[ FE_DLCR3 ] ),
+ inb( sc->ioaddr[ FE_DLCR4 ] ), inb( sc->ioaddr[ FE_DLCR5 ] ),
+ inb( sc->ioaddr[ FE_DLCR6 ] ), inb( sc->ioaddr[ FE_DLCR7 ] ),
+ inb( sc->ioaddr[ FE_BMPR10 ] ), inb( sc->ioaddr[ FE_BMPR11 ] ),
+ inb( sc->ioaddr[ FE_BMPR12 ] ), inb( sc->ioaddr[ FE_BMPR13 ] ),
+ inb( sc->ioaddr[ FE_BMPR14 ] ), inb( sc->ioaddr[ FE_BMPR15 ] ),
+ inb( sc->ioaddr[ 0x10 ] ), inb( sc->ioaddr[ 0x11 ] ),
+ inb( sc->ioaddr[ 0x12 ] ), inb( sc->ioaddr[ 0x13 ] ),
+ inb( sc->ioaddr[ 0x14 ] ), inb( sc->ioaddr[ 0x15 ] ),
+ inb( sc->ioaddr[ 0x16 ] ), inb( sc->ioaddr[ 0x17 ] ),
+ inb( sc->ioaddr[ 0x18 ] ), inb( sc->ioaddr[ 0x19 ] ),
+ inb( sc->ioaddr[ 0x1A ] ), inb( sc->ioaddr[ 0x1B ] ),
+ inb( sc->ioaddr[ 0x1C ] ), inb( sc->ioaddr[ 0x1D ] ),
+ inb( sc->ioaddr[ 0x1E ] ), inb( sc->ioaddr[ 0x1F ] ) );
+}
+#endif
diff --git a/sys/pc98/pc98/if_fereg.h b/sys/pc98/pc98/if_fereg.h
new file mode 100644
index 0000000..63c4216
--- /dev/null
+++ b/sys/pc98/pc98/if_fereg.h
@@ -0,0 +1,149 @@
+/*
+ * Hardware specification of various 8696x based Ethernet cards.
+ * Contributed by M. Sekiguchi <seki@sysrap.cs.fujitsu.co.jp>
+ *
+ * All Rights Reserved, Copyright (C) Fujitsu Limited 1995
+ *
+ * This software may be used, modified, copied, distributed, and sold,
+ * in both source and binary form provided that the above copyright,
+ * these terms and the following disclaimer are retained. The name of
+ * the author and/or the contributor 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 AND THE CONTRIBUTOR ``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 OR THE CONTRIBUTOR 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.
+ */
+
+/* $Id: if_fereg.h,v 1.3 1996/03/17 08:36:38 jkh Exp $ */
+
+/*
+ * Registers on FMV-180 series' ISA bus interface ASIC.
+ * I'm not sure the following register names are appropriate.
+ * Doesn't it look silly, eh? FIXME.
+ */
+
+#define FE_FMV0 16 /* Card status register #0 */
+#define FE_FMV1 17 /* Card status register #1 */
+#define FE_FMV2 18 /* Card config register #0 */
+#define FE_FMV3 19 /* Card config register #1 */
+#define FE_FMV4 20 /* Station address #1 */
+#define FE_FMV5 21 /* Station address #2 */
+#define FE_FMV6 22 /* Station address #3 */
+#define FE_FMV7 23 /* Station address #4 */
+#define FE_FMV8 24 /* Station address #5 */
+#define FE_FMV9 25 /* Station address #6 */
+#define FE_FMV10 26 /* Buffer RAM control register */
+#define FE_FMV11 27 /* Buffer RAM data register */
+
+/*
+ * FMV-180 series' ASIC register values.
+ */
+
+/* FMV0: Card status register #0: Misc info? */
+#define FE_FMV0_MEDIA 0x07 /* Supported physical media. */
+#define FE_FMV0_PRRDY 0x10 /* ??? */
+#define FE_FMV0_PRERR 0x20 /* ??? */
+#define FE_FMV0_ERRDY 0x40 /* ??? */
+#define FE_FMV0_IREQ 0x80 /* ??? */
+
+#define FE_FMV0_MEDIUM_5 0x01 /* 10base5/Dsub */
+#define FE_FMV0_MEDIUM_2 0x02 /* 10base2/BNC */
+#define FE_FMV0_MEDIUM_T 0x04 /* 10baseT/RJ45 */
+
+/* Card status register #1: Hardware revision. */
+#define FE_FMV1_REV 0x0F /* Card revision */
+#define FE_FMV1_UPPER 0xF0 /* Usage unknown */
+
+/* Card config register #0: I/O port address assignment. */
+#define FE_FMV2_IOS 0x07 /* I/O selection. */
+#define FE_FMV2_MES 0x38 /* ??? boot ROM? */
+#define FE_FMV2_IRS 0xC0 /* IRQ selection. */
+
+#define FE_FMV2_IOS_SHIFT 0
+#define FE_FMV2_MES_SHIFT 3
+#define FE_FMV2_IRS_SHIFT 6
+
+/* Card config register #1: IRQ enable */
+#define FE_FMV3_IRQENB 0x80 /* IRQ enable. */
+
+/*
+ * Register(?) specific to AT1700/RE2000.
+ */
+
+#define FE_ATI_RESET 0x1F /* Write to reset the 86965. */
+
+/* EEPROM allocation (offsets) of AT1700/RE2000. */
+#define FE_ATI_EEP_ADDR 0x08 /* Station address. (8-13) */
+#define FE_ATI_EEP_MEDIA 0x18 /* Media type. */
+#define FE_ATI_EEP_MAGIC 0x19 /* XXX Magic. */
+#define FE_ATI_EEP_MODEL 0x1e /* Hardware type. */
+#define FE_ATI_EEP_REVISION 0x1f /* Hardware revision. */
+
+/* Value for FE_ATI_EEP_MODEL. */
+#define FE_ATI_MODEL_AT1700T 0x00
+#define FE_ATI_MODEL_AT1700BT 0x01
+#define FE_ATI_MODEL_AT1700FT 0x02
+#define FE_ATI_MODEL_AT1700AT 0x03
+
+/*
+ * Registers on MBH10302.
+ */
+
+#define FE_MBH0 0x10 /* ??? Including interrupt. */
+#define FE_MBH1 0x11 /* ??? */
+#define FE_MBH10 0x1A /* Station address. (10 - 15) */
+
+/* Values to be set in MBH0 register. */
+#define FE_MBH0_MAGIC 0x0D /* Just a magic constant? */
+#define FE_MBH0_INTR 0x10 /* Master interrupt control. */
+
+#define FE_MBH0_INTR_ENABLE 0x10 /* Enable interrupts. */
+#define FE_MBH0_INTR_DISABLE 0x00 /* Disable interrupts. */
+
+/*
+ * Registers on RE1000. (*NOT* on RE1000 Plus.)
+ */
+
+/* IRQ configuration. */
+#ifdef PC98
+#define FE_RE1000_IRQCONF 0x1000
+#else
+#define FE_RE1000_IRQCONF 0x10
+#endif
+#define FE_RE1000_IRQCONF_IRQ 0xf0
+#define FE_RE1000_IRQCONF_IRQSHIFT 4
+
+/* MAC (station) address. */
+#ifdef PC98
+#define FE_RE1000_MAC0 0x1001
+#define FE_RE1000_MAC1 0x1201
+#define FE_RE1000_MAC2 0x1401
+#define FE_RE1000_MAC3 0x1601
+#define FE_RE1000_MAC4 0x1801
+#define FE_RE1000_MAC5 0x1a01
+#else
+#define FE_RE1000_MAC0 0x11
+#define FE_RE1000_MAC1 0x13
+#define FE_RE1000_MAC2 0x15
+#define FE_RE1000_MAC3 0x17
+#define FE_RE1000_MAC4 0x19
+#define FE_RE1000_MAC5 0x1B
+#endif
+
+/* "Check sum" -- an xor of MAC0 through MAC5 */
+#ifdef PC98
+#define FE_RE1000_MACCHK 0x1c01 /* xor data MAC0 through MAC5 */
+#else
+#define FE_RE1000_MACCHK 0x1D
+#endif
+
diff --git a/sys/pc98/pc98/if_zp.c b/sys/pc98/pc98/if_zp.c
new file mode 100644
index 0000000..1b32c6b
--- /dev/null
+++ b/sys/pc98/pc98/if_zp.c
@@ -0,0 +1,1198 @@
+/*
+ * This code is based on
+ * (1) FreeBSD implementation on ISA/EISA Ethelink III by Herb Peyerl
+ * (2) Linux implementation on PCMCIA Etherlink III by Devid Hinds
+ * (3) FreeBSD implementation on PCMCIA IBM Ethernet Card I/II
+ * by David Greenman
+ * (4) RT-Mach implementation on PCMCIA/ISA/EISA Etherlink III
+ * by Seiji Murata
+ *
+ * Copyright (c) by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp>
+ * Copyright (c) by Seiji Murata <seiji@mt.cs.keio.ac.jp>
+ */
+/*
+ * Copyright (c) 1993 Herb Peyerl <hpeyerl@novatel.ca>
+ * 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. 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.
+ *
+ * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $
+ * $Id: if_zp.c,v 1.19 1996/06/04 21:41:01 nate Exp $
+ */
+/*-
+ * TODO:
+ * [1] integrate into current if_ed.c
+ * [2] parse tuples to find out where to map the shared memory buffer,
+ * and what to write into the configuration register
+ * [3] move pcic-specific code into a separate module.
+ *
+ * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet,
+ * if_ze.c
+ *
+ * Based on the Device driver for National Semiconductor DS8390 ethernet
+ * adapters by David Greenman. Modifications for PCMCIA by Keith Moore.
+ * Adapted for FreeBSD 1.1.5 by Jordan Hubbard.
+ *
+ * Currently supports only the IBM Credit Card Adapter for Ethernet, but
+ * could probably work with other PCMCIA cards also, if it were modified
+ * to get the locations of the PCMCIA configuration option register (COR)
+ * by parsing the configuration tuples, rather than by hard-coding in
+ * the value expected by IBM's card.
+ *
+ * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver:
+ *
+ * [1] _Local Area Network Credit Card Adapters Technical Reference_,
+ * IBM Corp., SC30-3585-00, part # 33G9243.
+ * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan.
+ * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel
+ * Order Number 290423-002
+ * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network
+ * Interface Controller for Twisted Pair data sheet.
+ *
+ *
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ */
+/*======================================================================
+
+ A PCMCIA ethernet driver for the 3com 3c589 card.
+
+ Written by David Hinds, dhinds@allegro.stanford.edu
+
+ The network driver code is based on Donald Becker's 3c589 code:
+
+ Written 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+ Donald Becker may be reached at becker@cesdis1.gsfc.nasa.gov
+
+======================================================================*/
+/*
+ * I doubled delay loops in this file because it is not enough for some
+ * laptop machines' PCIC (especially, on my Chaplet ILFA 350 ^^;).
+ * HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp>
+ */
+/*
+ * Very small patch for IBM Ethernet PCMCIA Card II and IBM ThinkPad230Cs.
+ * ETO, Toshihisa <eto@osl.fujitsu.co.jp>
+ */
+
+#include "zp.h"
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#if defined(__FreeBSD__)
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef IPX
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <machine/clock.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/if_zpreg.h>
+#include <pc98/pc98/pcic.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/if_zpreg.h>
+#include <i386/isa/pcic.h>
+#endif
+
+#include "apm.h"
+#if NAPM > 0
+#include <machine/apm_bios.h>
+#endif /* NAPM > 0 */
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+
+
+
+/*****************************************************************************
+ * Driver for Ethernet Adapter *
+ *****************************************************************************/
+/*
+ * zp_softc: per line info and status
+ */
+static struct zp_softc {
+ struct arpcom arpcom; /* Ethernet common part */
+#define MAX_MBS 8 /* # of mbufs we keep around */
+ struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */
+ int next_mb; /* Which mbuf to use next. */
+ int last_mb; /* Last mbuf. */
+ short ep_io_addr; /* i/o bus address */
+ char ep_connectors; /* Connectors on this card. */
+ int tx_start_thresh;/* Current TX_start_thresh. */
+ char bus32bit; /* 32bit access possible */
+ u_short if_port;
+ u_char last_alive; /* information for reconfiguration */
+ u_char last_up; /* information for reconfiguration */
+ int slot; /* PCMCIA slot */
+#if NAPM > 0
+ struct apmhook s_hook; /* reconfiguration support */
+ struct apmhook r_hook; /* reconfiguration support */
+#endif /* NAPM > 0 */
+} zp_softc[NZP];
+
+#ifdef PC98
+static int zpprobe __P((struct pc98_device *));
+static int zpattach __P((struct pc98_device *));
+#else
+static int zpprobe __P((struct isa_device *));
+static int zpattach __P((struct isa_device *));
+#endif
+static int zp_suspend __P((void *visa_dev));
+static int zp_resume __P((void *visa_dev));
+static int zpioctl __P((struct ifnet * ifp, int, caddr_t));
+static u_short read_eeprom_data __P((int, int));
+
+static void zpinit __P((int));
+static void zpmbuffill __P((void *));
+static void zpmbufempty __P((struct zp_softc *));
+static void zpread __P((struct zp_softc *));
+static void zpreset __P((int));
+static void zpstart __P((struct ifnet *));
+static void zpstop __P((int));
+static void zpwatchdog __P((struct ifnet *));
+
+#ifdef PC98
+struct pc98_driver zpdriver = {
+#else
+struct isa_driver zpdriver = {
+#endif
+ zpprobe,
+ zpattach,
+ "zp"
+};
+#define CARD_INFO "3Com Corporation~3C589"
+
+static unsigned char card_info[256];
+
+/*
+ * scan the card information structure looking for the version/product info
+ * tuple. when we find it, compare it to the string we are looking for.
+ * return 1 if we find it, 0 otherwise.
+ */
+
+static int
+zp_check_cis(unsigned char *scratch)
+{
+ int i, j, k;
+
+ card_info[0] = '\0';
+ i = 0;
+ while (scratch[i] != 0xff && i < 1024) {
+ unsigned char link = scratch[i + 2];
+
+ if (scratch[i] == 0x15) {
+ /* level 1 version/product info copy to card_info,
+ * translating '\0' to '~' */
+ k = 0;
+ for (j = i + 8; scratch[j] != 0xff; j += 2)
+ card_info[k++] = scratch[j] == '\0' ? '~' : scratch[j];
+ card_info[k++] = '\0';
+ return (bcmp(card_info, CARD_INFO, sizeof(CARD_INFO) - 1) == 0);
+ }
+ i += 4 + 2 * link;
+ }
+ return 0;
+}
+/*
+ * Probe each slot looking for an IBM Credit Card Adapter for Ethernet
+ * For each card that we find, map its card information structure
+ * into system memory at 'scratch' and see whether it's one of ours.
+ * Return the slot number if we find a card, or -1 otherwise.
+ *
+ * Side effects:
+ * + On success, leaves CIS mapped into memory at 'scratch';
+ * caller must free it.
+ * + On success, leaves ethernet address in enet_addr.
+ * + Leaves product/vendor id of last card probed in 'card_info'
+ */
+
+static int prev_slot = 0;
+
+static int
+zp_find_adapter(unsigned char *scratch, int reconfig)
+{
+ int slot;
+
+ for (slot = prev_slot; slot < MAXSLOT; ++slot) {
+ /* see if there's a PCMCIA controller here Intel PCMCIA
+ * controllers use 0x82 and 0x83 IBM clone chips use 0x88 and
+ * 0x89, apparently */
+ /* IBM ThinkPad230Cs use 0x84. */
+ unsigned char idbyte = pcic_getb(slot, PCIC_ID_REV);
+
+ if (idbyte != 0x82 && idbyte != 0x83 &&
+ idbyte != 0x84 && /* for IBM ThinkPad 230Cs */
+ idbyte != 0x88 && idbyte != 0x89) {
+ continue;
+ }
+ if ((pcic_getb(slot, PCIC_STATUS) & PCIC_CD) != PCIC_CD) {
+ if (!reconfig) {
+ printf("zp: slot %d: no card in slot\n", slot);
+ } else {
+ log(LOG_NOTICE, "zp: slot %d: no card in slot\n", slot);
+ }
+ /* no card in slot */
+ continue;
+ }
+ pcic_power_on(slot);
+ pcic_reset(slot);
+ /* map the card's attribute memory and examine its card
+ * information structure tuples for something we recognize. */
+ pcic_map_memory(slot, 0, kvtop(scratch), 0L,
+ 0xFFFL, ATTRIBUTE, 1);
+
+ if ((zp_check_cis(scratch)) > 0) {
+ /* found it */
+ if (!reconfig) {
+ printf("zp: found card in slot %d\n", slot);
+ } else {
+ log(LOG_NOTICE, "zp: found card in slot %d\n", slot);
+ }
+ prev_slot = (prev_slot == MAXSLOT - 1) ? 0 : prev_slot + 1;
+
+ return slot;
+ } else {
+ if (!reconfig) {
+ printf("zp: pcmcia slot %d: %s\n", slot, card_info);
+ } else {
+ log(LOG_NOTICE, "zp: pcmcia slot %d: %s\n", slot, card_info);
+ }
+ }
+ pcic_unmap_memory(slot, 0);
+ }
+ prev_slot = 0;
+ return -1;
+}
+
+
+/*
+ * macros to handle casting unsigned long to (char *) so we can
+ * read/write into physical memory space.
+ */
+
+#define PEEK(addr) (*((unsigned char *)(addr)))
+#define POKE(addr,val) do { PEEK(addr) = (val); } while (0)
+
+/*
+ * Determine if the device is present
+ *
+ * on entry:
+ * a pointer to an isa_device struct
+ * on exit:
+ * NULL if device not found
+ * or # of i/o addresses used (if found)
+ */
+static int
+#ifdef PC98
+zpprobe(struct pc98_device * isa_dev)
+#else
+zpprobe(struct isa_device * isa_dev)
+#endif
+{
+ struct zp_softc *sc = &zp_softc[isa_dev->id_unit];
+ int slot;
+ u_short k;
+ int re_init_flag;
+
+ if ((slot = zp_find_adapter(isa_dev->id_maddr, isa_dev->id_reconfig)) < 0)
+ return NULL;
+
+ /* okay, we found a card, so set it up */
+ /* Inhibit 16 bit memory delay. POINTETH.SYS apparently does this, for
+ * what reason I don't know. */
+ pcic_putb(slot, PCIC_CDGC,
+ pcic_getb(slot, PCIC_CDGC) | PCIC_16_DL_INH);
+ /* things to map (1) card's EEPROM is already mapped by the
+ * find_adapter routine but we still need to get the card's ethernet
+ * address. after that we unmap that part of attribute memory. (2)
+ * card configuration registers need to be mapped in so we can set the
+ * configuration and socket # registers. (3) shared memory packet
+ * buffer (4) i/o ports (5) IRQ */
+#ifdef notdef
+ /* Sigh. Location of the ethernet address isn't documented in [1]. It
+ * was derived by doing a hex dump of all of attribute memory and
+ * looking for the IBM vendor prefix. */
+ enet_addr[0] = PEEK(isa_dev->id_maddr + 0xff0);
+ enet_addr[1] = PEEK(isa_dev->id_maddr + 0xff2);
+ enet_addr[2] = PEEK(isa_dev->id_maddr + 0xff4);
+ enet_addr[3] = PEEK(isa_dev->id_maddr + 0xff6);
+ enet_addr[4] = PEEK(isa_dev->id_maddr + 0xff8);
+ enet_addr[5] = PEEK(isa_dev->id_maddr + 0xffa);
+#endif
+ re_init_flag = 0;
+re_init:
+ /* (2) map card configuration registers. these are offset in card
+ * memory space by 0x20000. normally we could get this offset from
+ * the card information structure, but I'm too lazy and am not quite
+ * sure if I understand the CIS anyway.
+ *
+ * XXX IF YOU'RE TRYING TO PORT THIS DRIVER FOR A DIFFERENT PCMCIA CARD,
+ * the most likely thing to change is the constant 0x20000 in the next
+ * statement. Oh yes, also change the card id string that we probe
+ * for. */
+ pcic_map_memory(slot, 0, kvtop(isa_dev->id_maddr), 0x10000, 8L,
+ ATTRIBUTE, 1);
+#if OLD_3C589B_CARDS
+ POKE(isa_dev->id_maddr, 0x80); /* reset the card (how long?) */
+ DELAY(40000);
+#endif
+ /* Set the configuration index. According to [1], the adapter won't
+ * respond to any i/o signals until we do this; it uses the Memory
+ * Only interface (whatever that is; it's not documented). Also turn
+ * on "level" (not pulse) interrupts.
+ *
+ * XXX probably should init the socket and copy register also, so that we
+ * can deal with multiple instances of the same card. */
+ POKE(isa_dev->id_maddr, 0x41);
+ pcic_unmap_memory(slot, 0);
+
+ /* (4) map i/o ports.
+ *
+ * XXX is it possible that the config file leaves this unspecified, in
+ * which case we have to pick one?
+ *
+ * At least one PCMCIA device driver I'v seen maps a block of 32
+ * consecutive i/o ports as two windows of 16 ports each. Maybe some
+ * other pcic chips are restricted to 16-port windows; the 82365SL
+ * doesn't seem to have that problem. But since we have an extra
+ * window anyway... */
+ pcic_map_io(slot, 0, isa_dev->id_iobase, 16, 2);
+
+ /* (5) configure the card for the desired interrupt
+ *
+ * XXX is it possible that the config file leaves this unspecified? */
+ pcic_map_irq(slot, ffs(isa_dev->id_irq) - 1);
+
+ /* tell the PCIC that this is an I/O card (not memory) */
+ pcic_putb(slot, PCIC_INT_GEN,
+ pcic_getb(slot, PCIC_INT_GEN) | PCIC_CARDTYPE);
+
+ sc->ep_io_addr = isa_dev->id_iobase;
+ GO_WINDOW(0);
+ k = read_eeprom_data(BASE, EEPROM_ADDR_CFG); /* get addr cfg */
+ sc->if_port = k >> 14;
+ k = (k & 0x1f) * 0x10 + 0x200; /* decode base addr. */
+ if (k != (u_short) isa_dev->id_iobase) {
+ if (!re_init_flag) {
+ re_init_flag++;
+ goto re_init;
+ }
+ return (0);
+ }
+ k = read_eeprom_data(BASE, EEPROM_RESOURCE_CFG);
+
+ k >>= 12;
+
+ if (isa_dev->id_irq != (1 << ((k == 2) ? 9 : k)))
+ return (0);
+
+ outb(BASE, ACTIVATE_ADAPTER_TO_CONFIG);
+
+
+ /* information for reconfiguration */
+ sc->last_alive = 0;
+ sc->last_up = 0;
+ sc->slot = slot;
+
+ return (0x10); /* 16 bytes of I/O space used. */
+}
+#if NAPM > 0
+static int
+zp_suspend(visa_dev)
+ void *visa_dev;
+{
+#ifdef PC98
+ struct pc98_device *isa_dev = visa_dev;
+#else
+ struct isa_device *isa_dev = visa_dev;
+#endif
+ struct zp_softc *sc = &zp_softc[isa_dev->id_unit];
+
+ pcic_power_off(sc->slot);
+ return 0;
+}
+
+static int
+zp_resume(visa_dev)
+ void *visa_dev;
+{
+#ifdef PC98
+ struct pc98_device *isa_dev = visa_dev;
+#else
+ struct isa_device *isa_dev = visa_dev;
+#endif
+
+ prev_slot = 0;
+ reconfig_isadev(isa_dev, &net_imask);
+ return 0;
+}
+#endif /* NAPM > 0 */
+
+
+/*
+ * Install interface into kernel networking data structures
+ */
+
+static int
+zpattach(isa_dev)
+#ifdef PC98
+ struct pc98_device *isa_dev;
+#else
+ struct isa_device *isa_dev;
+#endif
+{
+ struct zp_softc *sc = &zp_softc[isa_dev->id_unit];
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ u_short i;
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ int pl;
+
+ /* PCMCIA card can be offlined. Reconfiguration is required */
+ if (isa_dev->id_reconfig) {
+ if (!isa_dev->id_alive && sc->last_alive) {
+ pl = splimp();
+ sc->last_up = (ifp->if_flags & IFF_UP);
+ if_down(ifp);
+ splx(pl);
+ sc->last_alive = 0;
+ }
+ if (isa_dev->id_alive && !sc->last_alive) {
+ zpreset(isa_dev->id_unit);
+ if (sc->last_up) {
+ pl = splimp();
+ if_up(ifp);
+ splx(pl);
+ }
+ sc->last_alive = 1;
+ }
+ return 1;
+ } else {
+ sc->last_alive = 1;
+ }
+
+
+ sc->ep_io_addr = isa_dev->id_iobase;
+ printf("zp%d: ", isa_dev->id_unit);
+
+ sc->ep_connectors = 0;
+
+ i = inw(isa_dev->id_iobase + EP_W0_CONFIG_CTRL);
+
+ if (i & IS_AUI) {
+ printf("aui");
+ sc->ep_connectors |= AUI;
+ }
+ if (i & IS_BNC) {
+ if (sc->ep_connectors)
+ printf("/");
+ printf("bnc");
+ sc->ep_connectors |= BNC;
+ }
+ if (i & IS_UTP) {
+ if (sc->ep_connectors)
+ printf("/");
+ printf("utp");
+ sc->ep_connectors |= UTP;
+ }
+ if (!sc->ep_connectors)
+ printf("no connectors!");
+
+ GO_WINDOW(0);
+ {
+ short tmp_addr[3];
+ int i;
+ for (i = 0; i < 3; i++) {
+ tmp_addr[i] = htons(read_eeprom_data(BASE, i));
+ }
+ bcopy(tmp_addr, sc->arpcom.ac_enaddr, 6);
+ }
+
+ printf(" address %6D\n", sc->arpcom.ac_enaddr, ":");
+
+ ifp->if_softc = sc;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ ifp->if_unit = isa_dev->id_unit;
+ ifp->if_name = "zp";
+ ifp->if_output = ether_output;
+ ifp->if_start = zpstart;
+ ifp->if_ioctl = zpioctl;
+ ifp->if_watchdog = zpwatchdog;
+ /* Select connector according to board setting. */
+ ifp->if_flags |= IFF_LINK0;
+
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+#if NBPFILTER > 0
+ bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+#if NAPM > 0
+ sc->s_hook.ah_fun = zp_suspend;
+ sc->s_hook.ah_arg = (void *) isa_dev;
+ sc->s_hook.ah_name = "3Com PCMCIA Etherlink III 3C589";
+ sc->s_hook.ah_order = APM_MID_ORDER;
+ apm_hook_establish(APM_HOOK_SUSPEND, &sc->s_hook);
+ sc->r_hook.ah_fun = zp_resume;
+ sc->r_hook.ah_arg = (void *) isa_dev;
+ sc->r_hook.ah_name = "3Com PCMCIA Etherlink III 3C589";
+ sc->r_hook.ah_order = APM_MID_ORDER;
+ apm_hook_establish(APM_HOOK_RESUME, &sc->r_hook);
+#endif /* NAPM > 0 */
+ return 1;
+}
+/*
+ * The order in here seems important. Otherwise we may not receive
+ * interrupts. ?!
+ */
+static void
+zpinit(unit)
+ int unit;
+{
+ register struct zp_softc *sc = &zp_softc[unit];
+ register struct ifnet *ifp = &sc->arpcom.ac_if;
+ int s, i;
+
+ if (ifp->if_addrlist == (struct ifaddr *) 0)
+ return;
+
+ s = splimp();
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+
+ GO_WINDOW(0);
+
+ /* Disable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, 0);
+
+ /* Enable the card */
+ outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
+
+ GO_WINDOW(2);
+
+ /* Reload the ether_addr. */
+ for (i = 0; i < 6; i++)
+ outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]);
+
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+
+ /* Window 1 is operating window */
+ GO_WINDOW(1);
+ for (i = 0; i < 31; i++)
+ inb(BASE + EP_W1_TX_STATUS);
+
+ /* get rid of stray intr's */
+ outw(BASE + EP_COMMAND, ACK_INTR | 0xff);
+
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL);
+ outw(BASE + EP_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL);
+
+#ifndef IFF_MULTICAST
+#define IFF_MULTICAST 0x10000
+#endif
+
+ outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
+ ((sc->arpcom.ac_if.if_flags & IFF_MULTICAST) ? FIL_GROUP : 0) |
+ FIL_BRDCST |
+ ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) ? FIL_ALL : 0));
+ /* you can `ifconfig (link0|-link0) ep0' to get the following
+ * behaviour: -link0 disable AUI/UTP. enable BNC. link0 disable
+ * BNC. enable AUI. if the card has a UTP connector, that is enabled
+ * too. not sure, but it seems you have to be careful to not plug
+ * things into both AUI & UTP. */
+
+ if (!(ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & BNC)) {
+ GO_WINDOW(0);
+ /* set the xcvr */
+ outw(BASE + EP_W0_ADDRESS_CFG, 3 << 14);
+ GO_WINDOW(2);
+ outw(BASE + EP_COMMAND, START_TRANSCEIVER);
+ GO_WINDOW(1);
+ }
+#if defined(__NetBSD__) || defined(__FreeBSD__)
+ if ((ifp->if_flags & IFF_LINK0) && (sc->ep_connectors & UTP)) {
+#else
+ if ((ifp->if_flags & IFF_ALTPHYS) && (sc->ep_connectors & UTP)) {
+#endif
+ GO_WINDOW(4);
+ outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
+ GO_WINDOW(1);
+ }
+ outw(BASE + EP_COMMAND, RX_ENABLE);
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE; /* just in case */
+ sc->tx_start_thresh = 20; /* probably a good starting point. */
+ /* Store up a bunch of mbuf's for use later. (MAX_MBS). First we free
+ * up any that we had in case we're being called from intr or
+ * somewhere else. */
+ sc->last_mb = 0;
+ sc->next_mb = 0;
+ zpmbuffill(sc);
+ zpstart(ifp);
+ splx(s);
+}
+
+static const char padmap[] = {0, 3, 2, 1};
+static void
+zpstart(ifp)
+ struct ifnet *ifp;
+{
+ register struct zp_softc *sc = ifp->if_softc;
+ struct mbuf *m, *top;
+
+ int s, len, pad;
+
+ s = splimp();
+
+ if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) {
+ splx(s);
+ return;
+ }
+startagain:
+
+ /* Sneak a peek at the next packet */
+ m = sc->arpcom.ac_if.if_snd.ifq_head;
+ if (m == 0) {
+ splx(s);
+ return;
+ }
+ for (len = 0, top = m; m; m = m->m_next)
+ len += m->m_len;
+
+ pad = padmap[len & 3];
+
+ /* The 3c509 automatically pads short packets to minimum ethernet
+ * length, but we drop packets that are too large. Perhaps we should
+ * truncate them instead? */
+ if (len + pad > ETHER_MAX_LEN) {
+ /* packet is obviously too large: toss it */
+ ++sc->arpcom.ac_if.if_oerrors;
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+ m_freem(m);
+ goto readcheck;
+ }
+ if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) {
+ /* no room in FIFO */
+ outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4));
+ sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+ splx(s);
+
+ return;
+ }
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+
+ if (m == 0) { /* not really needed */
+ splx(s);
+ return;
+ }
+ outw(BASE + EP_COMMAND, SET_TX_START_THRESH |
+ (len / 4 + sc->tx_start_thresh));
+
+ outw(BASE + EP_W1_TX_PIO_WR_1, len);
+ outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */
+
+ for (top = m; m != 0; m = m->m_next) {
+ if (sc->bus32bit) {
+ outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t),
+ m->m_len / 4);
+ if (m->m_len & 3)
+ outsb(BASE + EP_W1_TX_PIO_WR_1,
+ mtod(m, caddr_t) + (m->m_len & (~3)),
+ m->m_len & 3);
+ } else {
+ outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 2);
+ if (m->m_len & 1)
+ outb(BASE + EP_W1_TX_PIO_WR_1,
+ *(mtod(m, caddr_t) + m->m_len - 1));
+ }
+ }
+ while (pad--)
+ outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */
+
+#if NBPFILTER > 0
+ if (sc->arpcom.ac_if.if_bpf) {
+ bpf_mtap(&sc->arpcom.ac_if, top);
+ }
+#endif
+
+ m_freem(top);
+ ++sc->arpcom.ac_if.if_opackets;
+ /* Is another packet coming in? We don't want to overflow the tiny RX
+ * fifo. */
+readcheck:
+ if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) {
+ splx(s);
+ return;
+ }
+ goto startagain;
+}
+void
+zpintr(unit)
+ int unit;
+{
+ int status, i;
+ register struct zp_softc *sc = &zp_softc[unit];
+
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+
+ status = 0;
+checkintr:
+ status = inw(BASE + EP_STATUS) &
+ (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE | S_CARD_FAILURE);
+checkintr2:
+ if (status == 0) {
+ /* No interrupts. */
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+
+ status = inw(BASE + EP_STATUS) &
+ (S_TX_COMPLETE | S_TX_AVAIL | S_RX_COMPLETE |
+ S_CARD_FAILURE);
+ if (status)
+ goto checkintr2;
+
+ return;
+ }
+ /* important that we do this first. */
+ outw(BASE + EP_COMMAND, ACK_INTR | status);
+
+ if (status & S_TX_AVAIL) {
+ status &= ~S_TX_AVAIL;
+ inw(BASE + EP_W1_FREE_TX);
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+ zpstart(&sc->arpcom.ac_if);
+
+ }
+ if (status & S_RX_COMPLETE) {
+ status &= ~S_RX_COMPLETE;
+ zpread(sc);
+ }
+ if (status & S_CARD_FAILURE) {
+ printf("zp%d: reset (status: %x)\n", unit, status);
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ zpinit(unit);
+ return;
+ }
+ if (status & S_TX_COMPLETE) {
+ status &= ~S_TX_COMPLETE;
+ /* We need to read TX_STATUS until we get a 0 status in order
+ * to turn off the interrupt flag. */
+ while ((i = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) {
+ outw(BASE + EP_W1_TX_STATUS, 0x0);
+ if (i & (TXS_MAX_COLLISION | TXS_JABBER | TXS_UNDERRUN)) {
+ if (i & TXS_MAX_COLLISION)
+ ++sc->arpcom.ac_if.if_collisions;
+ if (i & (TXS_JABBER | TXS_UNDERRUN)) {
+ outw(BASE + EP_COMMAND, TX_RESET);
+ if (i & TXS_UNDERRUN) {
+ if (sc->tx_start_thresh < ETHER_MAX_LEN) {
+ sc->tx_start_thresh += 20;
+ outw(BASE + EP_COMMAND,
+ SET_TX_START_THRESH |
+ sc->tx_start_thresh);
+ }
+ }
+ }
+ outw(BASE + EP_COMMAND, TX_ENABLE);
+ ++sc->arpcom.ac_if.if_oerrors;
+ }
+ }
+ zpstart(ifp);
+ }
+ goto checkintr;
+}
+
+static void
+zpread(sc)
+ register struct zp_softc *sc;
+{
+ struct ether_header *eh;
+ struct mbuf *mcur, *m, *m0, *top;
+ int totlen, lenthisone;
+ int save_totlen;
+ int off;
+
+
+ totlen = inw(BASE + EP_W1_RX_STATUS);
+ off = 0;
+ top = 0;
+
+ if (totlen & ERR_RX) {
+ ++sc->arpcom.ac_if.if_ierrors;
+ goto out;
+ }
+ save_totlen = totlen &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */
+
+ m = sc->mb[sc->next_mb];
+ sc->mb[sc->next_mb] = 0;
+
+ if (m == 0) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ goto out;
+ } else {
+ /* Convert one of our saved mbuf's */
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ m->m_data = m->m_pktdat;
+ m->m_flags = M_PKTHDR;
+ }
+
+ top = m0 = m; /* We assign top so we can "goto out" */
+#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
+#define EOFF (EROUND - sizeof(struct ether_header))
+ m0->m_data += EOFF;
+ /* Read what should be the header. */
+ insw(BASE + EP_W1_RX_PIO_RD_1,
+ mtod(m0, caddr_t), sizeof(struct ether_header) / 2);
+ m->m_len = sizeof(struct ether_header);
+ totlen -= sizeof(struct ether_header);
+ /* mostly deal with trailer here. (untested) We do this in a couple
+ * of parts. First we check for a trailer, if we have one we convert
+ * the mbuf back to a regular mbuf and set the offset and subtract
+ * sizeof(struct ether_header) from the pktlen. After we've read the
+ * packet off the interface (all except for the trailer header, we
+ * then get a header mbuf, read the trailer into it, and fix up the
+ * mbuf pointer chain. */
+ eh = mtod(m, struct ether_header *);
+ while (totlen > 0) {
+ lenthisone = min(totlen, M_TRAILINGSPACE(m));
+ if (lenthisone == 0) { /* no room in this one */
+ mcur = m;
+ m = sc->mb[sc->next_mb];
+ sc->mb[sc->next_mb] = 0;
+ if (!m) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ goto out;
+ } else {
+ timeout(zpmbuffill, sc, 0);
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ }
+ if (totlen >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+ m->m_len = 0;
+ mcur->m_next = m;
+ lenthisone = min(totlen, M_TRAILINGSPACE(m));
+ }
+ if (sc->bus32bit) {
+ insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len,
+ lenthisone / 4);
+ m->m_len += (lenthisone & ~3);
+ if (lenthisone & 3)
+ insb(BASE + EP_W1_RX_PIO_RD_1,
+ mtod(m, caddr_t) + m->m_len,
+ lenthisone & 3);
+ m->m_len += (lenthisone & 3);
+ } else {
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len,
+ lenthisone / 2);
+ m->m_len += lenthisone;
+ if (lenthisone & 1)
+ *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1);
+ }
+ totlen -= lenthisone;
+ }
+ if (off) {
+ top = sc->mb[sc->next_mb];
+ sc->mb[sc->next_mb] = 0;
+ if (top == 0) {
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (top == 0) {
+ top = m0;
+ goto out;
+ }
+ } else {
+ /* Convert one of our saved mbuf's */
+ sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
+ top->m_data = top->m_pktdat;
+ top->m_flags = M_PKTHDR;
+ }
+ insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t),
+ sizeof(struct ether_header));
+ top->m_next = m0;
+ top->m_len = sizeof(struct ether_header);
+ /* XXX Accomodate for type and len from beginning of trailer */
+ top->m_pkthdr.len = save_totlen - (2 * sizeof(u_short));
+ } else {
+ top = m0;
+ top->m_pkthdr.len = save_totlen;
+ }
+
+ top->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+ ++sc->arpcom.ac_if.if_ipackets;
+#if NBPFILTER > 0
+ if (sc->arpcom.ac_if.if_bpf) {
+ bpf_mtap(&sc->arpcom.ac_if, top);
+
+ /* Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours. */
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ (eh->ether_dhost[0] & 1) == 0 &&
+ bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0 &&
+ bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0) {
+ m_freem(top);
+ return;
+ }
+ }
+#endif
+ m_adj(top, sizeof(struct ether_header));
+ ether_input(&sc->arpcom.ac_if, eh, top);
+ return;
+
+out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+ if (top)
+ m_freem(top);
+
+}
+
+
+/*
+ * Look familiar?
+ */
+static int
+zpioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *) data;
+ struct zp_softc *sc = ifp->if_softc;
+ int error = 0;
+
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ zpinit(ifp->if_unit); /* before arpwhohas */
+ arp_ifinit((struct arpcom *) ifp, ifa);
+ break;
+#endif
+#ifdef IPX
+ case AF_IPX:
+ {
+ register struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
+
+ if (ipx_nullhost(*ina))
+ ina->x_host =
+ *(union ipx_host *) (sc->arpcom.ac_enaddr);
+ else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t) sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ zpinit(ifp->if_unit);
+ break;
+ }
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *) (sc->arpcom.ac_enaddr);
+ else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t) ina->x_host.c_host,
+ (caddr_t) sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+ }
+ zpinit(ifp->if_unit);
+ break;
+ }
+#endif
+ default:
+ zpinit(ifp->if_unit);
+ break;
+ }
+ break;
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) {
+ ifp->if_flags &= ~IFF_RUNNING;
+ zpstop(ifp->if_unit);
+ zpmbufempty(sc);
+ break;
+ }
+ if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0)
+ zpinit(ifp->if_unit);
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+static void
+zpreset(unit)
+ int unit;
+{
+ int s = splimp();
+
+ zpstop(unit);
+ zpinit(unit);
+ splx(s);
+}
+
+static void
+zpwatchdog(ifp)
+ struct ifnet *ifp;
+{
+ log(LOG_ERR, "zp%d: watchdog\n", ifp->if_unit);
+ ifp->if_oerrors++;
+ zpreset(ifp->if_unit);
+}
+
+static void
+zpstop(unit)
+ int unit;
+{
+ struct zp_softc *sc = &zp_softc[unit];
+
+ outw(BASE + EP_COMMAND, RX_DISABLE);
+ outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
+ outw(BASE + EP_COMMAND, TX_DISABLE);
+ outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
+ outw(BASE + EP_COMMAND, RX_RESET);
+ outw(BASE + EP_COMMAND, TX_RESET);
+ outw(BASE + EP_COMMAND, C_INTR_LATCH);
+ outw(BASE + EP_COMMAND, SET_RD_0_MASK);
+ outw(BASE + EP_COMMAND, SET_INTR_MASK);
+ outw(BASE + EP_COMMAND, SET_RX_FILTER);
+}
+
+
+
+static u_short
+read_eeprom_data(id_port, offset)
+ int id_port;
+ int offset;
+{
+
+ outb(id_port + 10, 0x80 + offset);
+ DELAY(1000);
+ return inw(id_port + 12);
+}
+
+
+
+
+static void
+zpmbuffill(sp)
+ void *sp;
+{
+ struct zp_softc *sc = (struct zp_softc *) sp;
+ int s, i;
+
+ s = splimp();
+ i = sc->last_mb;
+ do {
+ if (sc->mb[i] == NULL)
+ MGET(sc->mb[i], M_DONTWAIT, MT_DATA);
+ if (sc->mb[i] == NULL)
+ break;
+ i = (i + 1) % MAX_MBS;
+ } while (i != sc->next_mb);
+ sc->last_mb = i;
+ splx(s);
+}
+
+static void
+zpmbufempty(sc)
+ struct zp_softc *sc;
+{
+ int s, i;
+
+ s = splimp();
+ for (i = 0; i < MAX_MBS; i++) {
+ if (sc->mb[i]) {
+ m_freem(sc->mb[i]);
+ sc->mb[i] = NULL;
+ }
+ }
+ sc->last_mb = sc->next_mb = 0;
+ untimeout(zpmbuffill, sc);
+ splx(s);
+}
diff --git a/sys/pc98/pc98/if_zpreg.h b/sys/pc98/pc98/if_zpreg.h
new file mode 100644
index 0000000..2e214d8
--- /dev/null
+++ b/sys/pc98/pc98/if_zpreg.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca)
+ * 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. 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.
+ *
+ * $Id: if_zpreg.h,v 1.3 1996/01/30 22:55:55 mpp Exp $
+ */
+/**************************************************************************
+ * *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existence of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+
+/**************************************************************************
+ * *
+ * These are the registers for the 3Com 3c509 and their bit patterns when *
+ * applicable. They have been taken out the the "EtherLink III Parallel *
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
+ * from 3com. *
+ * *
+ **************************************************************************/
+
+#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a command reg. */
+#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status reg. */
+#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+ /* Write */
+#define EP_W0_EEPROM_DATA 0x0c
+#define EP_W0_EEPROM_COMMAND 0x0a
+#define EP_W0_RESOURCE_CFG 0x08
+#define EP_W0_ADDRESS_CFG 0x06
+#define EP_W0_CONFIG_CTRL 0x04
+ /* Read */
+#define EP_W0_PRODUCT_ID 0x02
+#define EP_W0_MFG_ID 0x00
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+ /* Write */
+#define EP_W1_TX_PIO_WR_2 0x02
+#define EP_W1_TX_PIO_WR_1 0x00
+ /* Read */
+#define EP_W1_FREE_TX 0x0c
+#define EP_W1_TX_STATUS 0x0b /* byte */
+#define EP_W1_TIMER 0x0a /* byte */
+#define EP_W1_RX_STATUS 0x08
+#define EP_W1_RX_PIO_RD_2 0x02
+#define EP_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+ /* Read/Write */
+#define EP_W2_ADDR_5 0x05
+#define EP_W2_ADDR_4 0x04
+#define EP_W2_ADDR_3 0x03
+#define EP_W2_ADDR_2 0x02
+#define EP_W2_ADDR_1 0x01
+#define EP_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+ /* Read */
+#define EP_W3_FREE_TX 0x0c
+#define EP_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+ /* Read/Write */
+#define EP_W4_MEDIA_TYPE 0x0a
+#define EP_W4_CTRLR_STATUS 0x08
+#define EP_W4_NET_DIAG 0x06
+#define EP_W4_FIFO_DIAG 0x04
+#define EP_W4_HOST_DIAG 0x02
+#define EP_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+ /* Read */
+#define EP_W5_READ_0_MASK 0x0c
+#define EP_W5_INTR_MASK 0x0a
+#define EP_W5_RX_FILTER 0x08
+#define EP_W5_RX_EARLY_THRESH 0x06
+#define EP_W5_TX_AVAIL_THRESH 0x02
+#define EP_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+ /* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms after issuing */
+#define WINDOW_SELECT (u_short) (0x1<<11)
+#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to determine
+ whether this is needed. If so;
+ wait 800 uSec before using trans-
+ ceiver. */
+#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on power-up */
+#define RX_ENABLE (u_short) (0x4<<11)
+#define RX_RESET (u_short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11)
+#define TX_ENABLE (u_short) (0x9<<11)
+#define TX_DISABLE (u_short) (0xa<<11)
+#define TX_RESET (u_short) (0xb<<11)
+#define REQ_INTR (u_short) (0xc<<11)
+ /*
+ * The following C_* acknowledge the various interrupts.
+ * Some of them don't do anything. See the manual.
+ */
+#define ACK_INTR (u_short) (0x6800)
+# define C_INTR_LATCH (u_short) (ACK_INTR|0x1)
+# define C_CARD_FAILURE (u_short) (ACK_INTR|0x2)
+# define C_TX_COMPLETE (u_short) (ACK_INTR|0x4)
+# define C_TX_AVAIL (u_short) (ACK_INTR|0x8)
+# define C_RX_COMPLETE (u_short) (ACK_INTR|0x10)
+# define C_RX_EARLY (u_short) (ACK_INTR|0x20)
+# define C_INT_RQD (u_short) (ACK_INTR|0x40)
+# define C_UPD_STATS (u_short) (ACK_INTR|0x80)
+#define SET_INTR_MASK (u_short) (0xe<<11)
+#define SET_RD_0_MASK (u_short) (0xf<<11)
+#define SET_RX_FILTER (u_short) (0x10<<11)
+# define FIL_INDIVIDUAL (u_short) (0x1)
+# define FIL_GROUP (u_short) (0x2)
+# define FIL_BRDCST (u_short) (0x4)
+# define FIL_ALL (u_short) (0x8)
+#define SET_RX_EARLY_THRESH (u_short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11)
+#define SET_TX_START_THRESH (u_short) (0x13<<11)
+#define STATS_ENABLE (u_short) (0x15<<11)
+#define STATS_DISABLE (u_short) (0x16<<11)
+#define STOP_TRANSCEIVER (u_short) (0x17<<11)
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (u_short) (0x1)
+#define S_CARD_FAILURE (u_short) (0x2)
+#define S_TX_COMPLETE (u_short) (0x4)
+#define S_TX_AVAIL (u_short) (0x8)
+#define S_RX_COMPLETE (u_short) (0x10)
+#define S_RX_EARLY (u_short) (0x20)
+#define S_INT_RQD (u_short) (0x40)
+#define S_UPD_STATS (u_short) (0x80)
+#define S_COMMAND_IN_PROGRESS (u_short) (0x1000)
+
+/*
+ * FIFO Registers. RX Status.
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_INCOMPLETE (u_short) (0x8000)
+#define ERR_RX (u_short) (0x4000)
+#define ERR_RX_PACKET (u_short) (0x2000)
+#define ERR_OVERRUN (u_short) (0x1000)
+#define ERR_RUNT (u_short) (0x1300)
+#define ERR_ALIGNMENT (u_short) (0x1400)
+#define ERR_CRC (u_short) (0x1500)
+#define ERR_OVERSIZE (u_short) (0x1100)
+#define ERR_DRIBBLE (u_short) (0x200)
+
+/*
+ * TX Status
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+/*
+ * Misc defines for various things.
+ */
+#define TAG_ADAPTER_0 0xd0
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff
+#define ENABLE_DRQ_IRQ 0x0001
+#define MFG_ID 0x6d50
+#define PROD_ID 0x9150
+#define BASE sc->ep_io_addr
+#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|x)
+#define AUI 0x1
+#define BNC 0x2
+#define UTP 0x4
+#define IS_AUI (1<<13)
+#define IS_BNC (1<<12)
+#define IS_UTP (1<<9)
+#define EEPROM_BUSY (1<<15)
+#define EEPROM_TST_MODE (1<<14)
+#define READ_EEPROM (1<<7)
+#define ETHER_ADDR_LEN 6
+#define ETHER_MAX 1536
+#define ENABLE_UTP 0xc0
+#define DISABLE_UTP 0x0
+#define RX_BYTES_MASK (u_short) (0x07ff)
diff --git a/sys/pc98/pc98/kbd.h b/sys/pc98/pc98/kbd.h
new file mode 100644
index 0000000..8a963e4
--- /dev/null
+++ b/sys/pc98/pc98/kbd.h
@@ -0,0 +1,15 @@
+/*
+ * Keyboard definitions
+ */
+
+#ifndef _PC98_PC98_KBD_H_
+#define _PC98_PC98_KBD_H_ 1
+
+/* commands and responses */
+#define KBC_RESET 0xFF /* Reset the keyboard */
+#define KBC_STSIND 0xED /* set keyboard status indicators */
+#define KBR_OVERRUN 0xFE /* Keyboard flooded */
+#define KBR_RESEND 0xFE /* Keyboard needs resend of command */
+#define KBR_ACK 0xFA /* Keyboard did receive command */
+#define KBR_RSTDONE 0xAA /* Keyboard reset complete */
+#endif /* _PC98_PC98_KBD_H_ */
diff --git a/sys/pc98/pc98/kbdtables.h b/sys/pc98/pc98/kbdtables.h
new file mode 100644
index 0000000..36832a3
--- /dev/null
+++ b/sys/pc98/pc98/kbdtables.h
@@ -0,0 +1,1039 @@
+/*-
+ * Copyright (c) 1992-1994 Sen Schmidt
+ * 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
+ * in this position and unchanged.
+ * 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 withough 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.
+ *
+ * $Id: kbdtables.h,v 1.31 1996/01/25 16:37:20 ache Exp $
+ */
+
+#define SET8 0x80 /* set eight bit on */
+
+/*
+ * modified for PC98 by kuribo@isl.melco.co.jp
+ */
+
+#ifdef PC98 /* PC98 keyboard truetype by kuribo */
+static keymap_t key_map = { 0x80, /* PC98 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=01 */ '1', '!', '!', '!', '1', '!', '!', '!', 0x00, 0x00,
+/* sc=02 */ '2', '\"', 0x1A, 0x1A, '2', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=03 */ '3', '#', 0x1B, 0x1B, '3', '#', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=04 */ '4', '$', 0x1C, 0x1C, '4', '$', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=05 */ '5', '%', 0x1D, 0x1D, '5', '%', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=06 */ '6', '&', 0x1E, 0x1E, '6', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=07 */ '7', '\'', 0x1F, 0x1F, '7', '&', '&', '&', 0x00, 0x00,
+/* sc=08 */ '8', '(', 0x7F, 0x7F, '8', '*', 0x08, 0x08, 0x00, 0x00,
+/* sc=09 */ '9', ')', '9', '9', '9', '(', '(', '(', 0x00, 0x00,
+/* sc=0a */ '0', NOP, '0', '0', '0', ')', ')', ')', 0x40, 0x00,
+/* sc=0b */ '-', '=', '-', '-', '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=0c */ '^', '`', 0x1E, 0x1E, '=', '+', '+', '+', 0x00, 0x00,
+/* sc=0d */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+/* sc=0f */ '\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t', 0x00, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ '@', '~', 0x00, 0x00, '[', '{', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=1b */ '[', '{', 0x1B, 0x1B, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ '\r', '\r', '\n', '\n', '\r', '\r', '\n', '\n', 0x00, 0x00,
+/* sc=1d */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1e */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=1f */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=20 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=21 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=22 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=23 */ 'j', 'J', '\n', '\n', 'j', 'J', '\n', '\n', 0x00, 0x01,
+/* sc=24 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=25 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=26 */ ';', '+', ';', ';', ';', ':', ';', ';', 0x00, 0x00,
+/* sc=27 */ ':', '*', ':', ':', '\'', '\"', '\'', '\'', 0x00, 0x00,
+/* sc=28 */ ']', '}', 0x1D, 0x1D, '`', '~', '~', '~', 0x00, 0x00,
+/* sc=29 */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2a */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2b */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2c */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=2d */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=2e */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=2f */ 'm', 'M', '\r', '\r', 'm', 'M', '\r', '\r', 0x00, 0x01,
+/* sc=30 */ ',', '<', '<', '<', ',', '<', '<', '<', 0x00, 0x00,
+/* sc=31 */ '.', '>', '>', '>', '.', '>', '>', '>', 0x00, 0x00,
+/* sc=32 */ '/', '?', 0x7F, 0x7F, '/', '?', 0x7F, 0x7F, 0x00, 0x00,
+/* sc=33 */ NOP, '_', 0x1F, 0x1F, '\\', '|', 0x1C, 0x1C, 0x80, 0x00,
+/* sc=34 */ ' ', ' ', 0x00, 0x00, ' ', ' ', 0x00, 0x00, 0x00, 0x00,
+/* sc=35 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00,
+/* sc=36 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=37 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=38 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=39 */ 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=3a */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=3b */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=3c */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=3d */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=3e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=3f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=40 */ '-', '-', '-', '-', '-', '-', '-', '-', 0x00, 0x00,
+/* sc=41 */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00,
+/* sc=42 */ '7', '7', '7', '7', '7', '7', '7', '7', 0x00, 0x00,
+/* sc=43 */ '8', '8', '8', '8', '8', '8', '8', '8', 0x00, 0x00,
+/* sc=44 */ '9', '9', '9', '9', '9', '9', '9', '9', 0x00, 0x00,
+/* sc=45 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x00,
+/* sc=46 */ '4', '4', '4', '4', '4', '4', '4', '4', 0x00, 0x00,
+/* sc=47 */ '5', '5', '5', '5', '5', '5', '5', '5', 0x00, 0x00,
+/* sc=48 */ '6', '6', '6', '6', '6', '6', '6', '6', 0x00, 0x00,
+/* sc=49 */ '+', '+', '+', '+', '+', '+', '+', '+', 0x00, 0x00,
+/* sc=4a */ '1', '1', '1', '1', '1', '1', '1', '1', 0x00, 0x00,
+/* sc=4b */ '2', '2', '2', '2', '2', '2', '2', '2', 0x00, 0x00,
+/* sc=4c */ '3', '3', '3', '3', '3', '3', '3', '3', 0x00, 0x00,
+/* sc=4d */ '=', '=', '=', '=', '=', '=', '=', '=', 0x00, 0x00,
+/* sc=4e */ '0', '0', '0', '0', '0', '0', '0', '0', 0x00, 0x00,
+/* sc=4f */ ',', ',', ',', ',', ',', ',', ',', ',', 0x00, 0x00,
+/* sc=50 */ '.', '.', '.', '.', '.', '.', '.', '.', 0x00, 0x00,
+/* sc=51 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00,
+/* sc=52 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=53 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=54 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=58 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=59 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=60 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=61 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=62 */ F( 1), F(13), F(25), F(37), S( 1), S( 1), S( 1), S( 1), 0xFF, 0x00,
+/* sc=63 */ F( 2), F(14), F(26), F(38), S( 2), S( 2), S( 2), S( 2), 0xFF, 0x00,
+/* sc=64 */ F( 3), F(15), F(27), F(39), S( 3), S( 3), S( 3), S( 3), 0xFF, 0x00,
+/* sc=65 */ F( 4), F(16), F(28), F(40), S( 4), S( 4), S( 4), S( 4), 0xFF, 0x00,
+/* sc=66 */ F( 5), F(17), F(29), F(41), S( 5), S( 5), S( 5), S( 5), 0xFF, 0x00,
+/* sc=67 */ F( 6), F(18), F(30), F(42), S( 6), S( 6), S( 6), S( 6), 0xFF, 0x00,
+/* sc=68 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=69 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=6a */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=6b */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=6c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=70 */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=71 */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=72 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=73 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=74 */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=75 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=76 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=77 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=78 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=79 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+};
+#endif
+
+#ifdef DKKEYMAP
+static keymap_t key_map = { 0x6C, /* DK iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, 0x9E, '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', 0xA4, NOP, NOP, '$', 0xA4, NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '&', NOP, NOP, '6', '&', NOP, NOP, 0x33, 0x00,
+/* sc=08 */ '7', '/', NOP, NOP, '{', '/', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '(', 0x1B, 0x1B, '[', '(', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ')', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '}', '=', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '+', '?', NOP, NOP, '+', '?', NOP, NOP, 0x33, 0x00,
+/* sc=0d */ '\'', '`', NOP, NOP, '|', '`', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, BTAB, NOP, NOP, 0x09, BTAB, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xE5, 0xC5, NOP, NOP, 0x86, 0x8F, NOP, NOP, 0x33, 0x01,
+/* sc=1b */ '"', '^', 0x1E, 0x1E, '~', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xE6, 0xC6, NOP, NOP, 0x91, 0x92, NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xF8, 0xD8, NOP, NOP, 0x9B, 0x9D, NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xBD, 0xA7, NOP, NOP, 0xBD, 0xA7, NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\'', '*', NOP, NOP, '\'', '*', NOP, NOP, 0x33, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', '*', '*', '*', '*', '*', '*', 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', 0x00, ' ', ' ', ' ', SUSP, ' ', 0x02, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', '.', '.', '.', '.', RBT, RBT, 0x03, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '<', '>', 0x1C, 0x1C, '\\', '>', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62), 0xFF, 0x00,
+/* sc=6a */ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63), 0xFF, 0x00,
+/* sc=6b */ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64), 0xFF, 0x00,
+};
+#endif
+
+#ifdef UKKEYMAP
+static keymap_t key_map = { 0x6C, /* uk iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', 0xA3, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, '[', '[', 0x1B, 0x1B, 0x30, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, ']', ']', 0x1D, 0x1D, 0x30, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, '|', '|', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, '}', '}', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, BTAB, NOP, NOP, 0x09, BTAB, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '@', 0x00, 0x00, '\'', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=29 */ '\\', '|', 0x1C, 0x1C, '\\', '\\', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '#', '~', NOP, NOP, '~', '~', NOP, NOP, 0x33, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', 0x00, ' ', ' ', ' ', SUSP, ' ', 0x02, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', '.', '.', '.', '.', RBT, RBT, 0x03, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62), 0xFF, 0x00,
+/* sc=6a */ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63), 0xFF, 0x00,
+/* sc=6b */ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64), 0xFF, 0x00,
+};
+#endif
+
+#ifdef GRKEYMAP
+static keymap_t key_map = { 0x6C, /* german iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '`', '`', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', 0xA7, NOP, NOP, '#', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '&', 0x1E, 0x1E, '^', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '/', 0x1B, 0x1B, '[', '[', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=09 */ '8', '(', NOP, NOP, '8', '(', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', ')', 0x1D, 0x1D, ']', ']', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '{', '{', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ 0xDF, '?', NOP, NOP, '|', '|', NOP, NOP, 0x33, 0x00,
+/* sc=0d */ 0x92, 0x93, NOP, NOP, '\'', '`', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, BTAB, NOP, NOP, 0x09, BTAB, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xFC, 0xDC, 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x01,
+/* sc=1b */ '+', '*', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xF6, 0xD6, NOP, NOP, 0xF6, 0xD6, NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xE4, 0xC4, NOP, NOP, 0xE4, 0xC4, NOP, NOP, 0x33, 0x01,
+/* sc=29 */ '<', '>', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '#', '^', 0x1E, 0x1E, '`', '~', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=2c */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, ',', ';', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, '.', ':', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', 0x00, ' ', ' ', ' ', SUSP, ' ', 0x02, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', '.', '.', '.', '.', RBT, RBT, 0x03, 0x02,
+/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62), 0xFF, 0x00,
+/* sc=6a */ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63), 0xFF, 0x00,
+/* sc=6b */ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64), 0xFF, 0x00,
+};
+#endif
+
+#ifdef SWKEYMAP
+static keymap_t key_map = { 0x6C, /* swedish iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=03 */ '2', '"', 0x00, 0x00, '@', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, 0xA3, NOP, NOP, NOP, 0x37, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, 0xA4, NOP, NOP, NOP, 0x37, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=07 */ '6', '&', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=08 */ '7', '/', NOP, NOP, '{', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=09 */ '8', '(', NOP, NOP, '[', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0a */ '9', ')', NOP, NOP, ']', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0b */ '0', '=', NOP, NOP, '}', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=0c */ '+', '?', NOP, NOP, '\\', NOP, 0x1C, NOP, 0x35, 0x00,
+/* sc=0d */ 0x180, '`', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, BTAB, NOP, NOP, 0x09, BTAB, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '}', ']', NOP, NOP, 0x33, 0x01,
+/* sc=1b */ 0xA8, '^', NOP, NOP, '~', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ 0xF6, 0xD6, NOP, NOP, '|', '\\', NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xE4, 0xC4, NOP, NOP, '{', '[', NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xA7, 0xBD, NOP, NOP, '\\', '|', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\'', '*', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', ';', NOP, NOP, NOP, '<', NOP, NOP, 0x3B, 0x00,
+/* sc=34 */ '.', ':', NOP, NOP, NOP, '>', NOP, NOP, 0x3B, 0x00,
+/* sc=35 */ '-', '_', 0x1F, NOP, '/', '?', NOP, NOP, 0x13, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', 0x00, ' ', ' ', ' ', SUSP, ' ', 0x02, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, 0x13, 0x13, NLK, NLK, 0x13, 0x13, 0xCC, 0x00,
+/* sc=46 */ SLK, SLK, 0x7F, 0x7F, SLK, SLK, 0x7F, 0x7F, 0xCC, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', 0x1F, 0x1F, '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', 0x1E, 0x1E, '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', '.', '.', '.', '.', RBT, RBT, 0x03, 0x02,
+/* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ '<', '>', NOP, NOP, '|', NOP, NOP, NOP, 0x37, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x02,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62), 0xFF, 0x00,
+/* sc=6a */ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63), 0xFF, 0x00,
+/* sc=6b */ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64), 0xFF, 0x00,
+};
+#endif
+
+#ifdef RUKEYMAP
+static keymap_t key_map = { 0xEC, /* keys number */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * -------------------------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '@', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, BTAB, NOP, NOP, SET8|0x09, BTAB, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '"', NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x00,
+/* sc=29 */ '`', '~', NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', 0x00, ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00,
+/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', '.', '.', SET8|'.', SET8|'.', RBT, RBT, 0x03, 0x02,
+/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62), 0xFF, 0x00,
+/* sc=6a */ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63), 0xFF, 0x00,
+/* sc=6b */ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64), 0xFF, 0x00,
+/* sc=6c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=6f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=70 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=71 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=72 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=73 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=74 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=75 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=76 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=77 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=78 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=79 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7a */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7b */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7c */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7d */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7e */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=7f */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* extended (ALTGR LOCK keys) */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, NOP, NOP, SET8|0x1B, SET8|0x1B, DBG, NOP, 0x33, 0x00,
+/* sc=02 */ '!', '1', NOP, NOP, SET8|'1', SET8|'!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '"', '2', 0x00, 0x00, SET8|'2', SET8|'@', SET8|0x00, SET8|0x00, 0x00, 0x00,
+/* sc=04 */ '\'', '3', NOP, NOP, SET8|'3', SET8|'#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '*', '4', NOP, NOP, SET8|'4', SET8|'$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ ':', '5', NOP, NOP, SET8|'5', SET8|'%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ ',', '6', 0x1E, 0x1E, SET8|'6', SET8|'^', SET8|0x1E, SET8|0x1E, 0x00, 0x00,
+/* sc=08 */ '.', '7', NOP, NOP, SET8|'7', SET8|'&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ ';', '8', NOP, NOP, SET8|'8', SET8|'*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '(', '9', NOP, NOP, SET8|'9', SET8|'(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ ')', '0', NOP, NOP, SET8|'0', SET8|')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, SET8|'-', SET8|'_', SET8|0x1F, SET8|0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, SET8|'=', SET8|'+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, SET8|0x08, SET8|0x08, SET8|0x7F, SET8|0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, BTAB, NOP, NOP, SET8|0x09, BTAB, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 0xca, 0xea, 0x11, 0x11, SET8|'q', SET8|'Q', SET8|0x11, SET8|0x11, 0x00, 0x01,
+/* sc=11 */ 0xc3, 0xe3, 0x17, 0x17, SET8|'w', SET8|'W', SET8|0x17, SET8|0x17, 0x00, 0x01,
+/* sc=12 */ 0xd5, 0xf5, 0x05, 0x05, SET8|'e', SET8|'E', SET8|0x05, SET8|0x05, 0x00, 0x01,
+/* sc=13 */ 0xcb, 0xeb, 0x12, 0x12, SET8|'r', SET8|'R', SET8|0x12, SET8|0x12, 0x00, 0x01,
+/* sc=14 */ 0xc5, 0xe5, 0x14, 0x14, SET8|'t', SET8|'T', SET8|0x14, SET8|0x14, 0x00, 0x01,
+/* sc=15 */ 0xce, 0xee, 0x19, 0x19, SET8|'y', SET8|'Y', SET8|0x19, SET8|0x19, 0x00, 0x01,
+/* sc=16 */ 0xc7, 0xe7, 0x15, 0x15, SET8|'u', SET8|'U', SET8|0x15, SET8|0x15, 0x00, 0x01,
+/* sc=17 */ 0xdb, 0xfb, 0x09, 0x09, SET8|'i', SET8|'I', SET8|0x09, SET8|0x09, 0x00, 0x01,
+/* sc=18 */ 0xdd, 0xfd, 0x0F, 0x0F, SET8|'o', SET8|'O', SET8|0x0F, SET8|0x0F, 0x00, 0x01,
+/* sc=19 */ 0xda, 0xfa, 0x10, 0x10, SET8|'p', SET8|'P', SET8|0x10, SET8|0x10, 0x00, 0x01,
+/* sc=1a */ 0xc8, 0xe8, 0x1B, 0x1B, SET8|'[', SET8|'{', SET8|0x1B, SET8|0x1B, 0x00, 0x01,
+/* sc=1b */ 0xdf, 0xff, 0x1D, 0x1D, SET8|']', SET8|'}', SET8|0x1D, SET8|0x1D, 0x00, 0x01,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 0xc6, 0xe6, 0x01, 0x01, SET8|'a', SET8|'A', SET8|0x01, SET8|0x01, 0x00, 0x01,
+/* sc=1f */ 0xd9, 0xf9, 0x13, 0x13, SET8|'s', SET8|'S', SET8|0x13, SET8|0x13, 0x00, 0x01,
+/* sc=20 */ 0xd7, 0xf7, 0x04, 0x04, SET8|'d', SET8|'D', SET8|0x04, SET8|0x04, 0x00, 0x01,
+/* sc=21 */ 0xc1, 0xe1, 0x06, 0x06, SET8|'f', SET8|'F', SET8|0x06, SET8|0x06, 0x00, 0x01,
+/* sc=22 */ 0xd0, 0xf0, 0x07, 0x07, SET8|'g', SET8|'G', SET8|0x07, SET8|0x07, 0x00, 0x01,
+/* sc=23 */ 0xd2, 0xf2, 0x08, 0x08, SET8|'h', SET8|'H', SET8|0x08, SET8|0x08, 0x00, 0x01,
+/* sc=24 */ 0xcf, 0xef, 0x0A, 0x0A, SET8|'j', SET8|'J', SET8|0x0A, SET8|0x0A, 0x00, 0x01,
+/* sc=25 */ 0xcc, 0xec, 0x0B, 0x0B, SET8|'k', SET8|'K', SET8|0x0B, SET8|0x0B, 0x00, 0x01,
+/* sc=26 */ 0xc4, 0xe4, 0x0C, 0x0C, SET8|'l', SET8|'L', SET8|0x0C, SET8|0x0C, 0x00, 0x01,
+/* sc=27 */ 0xd6, 0xf6, NOP, NOP, SET8|';', SET8|':', NOP, NOP, 0x33, 0x01,
+/* sc=28 */ 0xdc, 0xfc, NOP, NOP, SET8|'\'', SET8|'"', NOP, NOP, 0x33, 0x01,
+/* sc=29 */ 0xa3, 0xb3, NOP, NOP, SET8|'`', SET8|'~', NOP, NOP, 0x33, 0x01,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, SET8|'\\', SET8|'|', SET8|0x1C, SET8|0x1C, 0x00, 0x00,
+/* sc=2c */ 0xd1, 0xf1, 0x1A, 0x1A, SET8|'z', SET8|'Z', SET8|0x1A, SET8|0x1A, 0x00, 0x01,
+/* sc=2d */ 0xde, 0xfe, 0x18, 0x18, SET8|'x', SET8|'X', SET8|0x18, SET8|0x18, 0x00, 0x01,
+/* sc=2e */ 0xd3, 0xf3, 0x03, 0x03, SET8|'c', SET8|'C', SET8|0x03, SET8|0x03, 0x00, 0x01,
+/* sc=2f */ 0xcd, 0xed, 0x16, 0x16, SET8|'v', SET8|'V', SET8|0x16, SET8|0x16, 0x00, 0x01,
+/* sc=30 */ 0xc9, 0xe9, 0x02, 0x02, SET8|'b', SET8|'B', SET8|0x02, SET8|0x02, 0x00, 0x01,
+/* sc=31 */ 0xd4, 0xf4, 0x0E, 0x0E, SET8|'n', SET8|'N', SET8|0x0E, SET8|0x0E, 0x00, 0x01,
+/* sc=32 */ 0xd8, 0xf8, 0x0D, 0x0D, SET8|'m', SET8|'M', SET8|0x0D, SET8|0x0D, 0x00, 0x01,
+/* sc=33 */ 0xc2, 0xe2, NOP, NOP, SET8|',', SET8|'<', NOP, NOP, 0x33, 0x01,
+/* sc=34 */ 0xc0, 0xe0, NOP, NOP, SET8|'.', SET8|'>', NOP, NOP, 0x33, 0x01,
+/* sc=35 */ '/', '?', NOP, NOP, SET8|'/', SET8|'?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, SET8|'*', SET8|'*', SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', 0x00, ' ', SET8|' ', SET8|' ', SET8|' ', SET8|' ', 0x00, 0x00,
+/* sc=3a */ ALK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', SET8|'7', SET8|'7', SET8|'7', SET8|'7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', SET8|'8', SET8|'8', SET8|'8', SET8|'8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', SET8|'9', SET8|'9', SET8|'9', SET8|'9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', SET8|'-', SET8|'-', SET8|'-', SET8|'-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', SET8|'4', SET8|'4', SET8|'4', SET8|'4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', SET8|'5', SET8|'5', SET8|'5', SET8|'5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', SET8|'6', SET8|'6', SET8|'6', SET8|'6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', SET8|'+', SET8|'+', SET8|'+', SET8|'+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', SET8|'1', SET8|'1', SET8|'1', SET8|'1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', '.', '.', SET8|'.', SET8|'.', RBT, RBT, 0x03, 0x02,
+/* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0A, 0x0A, SET8|0x0D, SET8|0x0D, SET8|0x0A, SET8|0x0A, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', NOP, NOP, SET8|'/', SET8|'/', NOP, NOP, 0x33, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62), 0xFF, 0x00,
+/* sc=6a */ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63), 0xFF, 0x00,
+/* sc=6b */ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64), 0xFF, 0x00,
+};
+
+#endif
+
+#if !defined(DKKEYMAP) && !defined(UKKEYMAP) && !defined(GRKEYMAP) && !defined(SWKEYMAP) && !defined(RUKEYMAP) && !defined(PC98)
+static keymap_t key_map = { 0x6C, /* US iso8859 keymap */
+/* alt
+ * scan cntrl alt alt cntrl
+ * code base shift cntrl shift alt shift cntrl shift spcl flgs
+ * ---------------------------------------------------------------------------
+ */
+/* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00,
+/* sc=02 */ '1', '!', NOP, NOP, '1', '!', NOP, NOP, 0x33, 0x00,
+/* sc=03 */ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, 0x00, 0x00,
+/* sc=04 */ '3', '#', NOP, NOP, '3', '#', NOP, NOP, 0x33, 0x00,
+/* sc=05 */ '4', '$', NOP, NOP, '4', '$', NOP, NOP, 0x33, 0x00,
+/* sc=06 */ '5', '%', NOP, NOP, '5', '%', NOP, NOP, 0x33, 0x00,
+/* sc=07 */ '6', '^', 0x1E, 0x1E, '6', '^', 0x1E, 0x1E, 0x00, 0x00,
+/* sc=08 */ '7', '&', NOP, NOP, '7', '&', NOP, NOP, 0x33, 0x00,
+/* sc=09 */ '8', '*', NOP, NOP, '8', '*', NOP, NOP, 0x33, 0x00,
+/* sc=0a */ '9', '(', NOP, NOP, '9', '(', NOP, NOP, 0x33, 0x00,
+/* sc=0b */ '0', ')', NOP, NOP, '0', ')', NOP, NOP, 0x33, 0x00,
+/* sc=0c */ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00,
+/* sc=0d */ '=', '+', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00,
+/* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00,
+/* sc=0f */ 0x09, BTAB, NOP, NOP, 0x09, BTAB, NOP, NOP, 0x77, 0x00,
+/* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01,
+/* sc=11 */ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, 0x00, 0x01,
+/* sc=12 */ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, 0x00, 0x01,
+/* sc=13 */ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, 0x00, 0x01,
+/* sc=14 */ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, 0x00, 0x01,
+/* sc=15 */ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, 0x00, 0x01,
+/* sc=16 */ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, 0x00, 0x01,
+/* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01,
+/* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01,
+/* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01,
+/* sc=1a */ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, 0x00, 0x00,
+/* sc=1b */ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, 0x00, 0x00,
+/* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00,
+/* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00,
+/* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01,
+/* sc=1f */ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, 0x00, 0x01,
+/* sc=20 */ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, 0x00, 0x01,
+/* sc=21 */ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, 0x00, 0x01,
+/* sc=22 */ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, 0x00, 0x01,
+/* sc=23 */ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, 0x00, 0x01,
+/* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01,
+/* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01,
+/* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01,
+/* sc=27 */ ';', ':', NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x00,
+/* sc=28 */ '\'', '"', NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x00,
+/* sc=29 */ '`', '~', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00,
+/* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00,
+/* sc=2b */ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, 0x00, 0x00,
+/* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01,
+/* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01,
+/* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01,
+/* sc=2f */ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, 0x00, 0x01,
+/* sc=30 */ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, 0x00, 0x01,
+/* sc=31 */ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, 0x00, 0x01,
+/* sc=32 */ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, 0x00, 0x01,
+/* sc=33 */ ',', '<', NOP, NOP, ',', '<', NOP, NOP, 0x33, 0x00,
+/* sc=34 */ '.', '>', NOP, NOP, '.', '>', NOP, NOP, 0x33, 0x00,
+/* sc=35 */ '/', '?', NOP, NOP, '/', '?', NOP, NOP, 0x33, 0x00,
+/* sc=36 */ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, 0xFF, 0x00,
+/* sc=37 */ '*', '*', 0x0A, 0x0A, '*', '*', 0x0A, 0x0A, 0x33, 0x00,
+/* sc=38 */ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, 0xFF, 0x00,
+/* sc=39 */ ' ', ' ', 0x00, ' ', ' ', ' ', SUSP, ' ', 0x02, 0x00,
+/* sc=3a */ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, 0xFF, 0x00,
+/* sc=3b */ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11), 0xFF, 0x00,
+/* sc=3c */ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12), 0xFF, 0x00,
+/* sc=3d */ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13), 0xFF, 0x00,
+/* sc=3e */ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14), 0xFF, 0x00,
+/* sc=3f */ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15), 0xFF, 0x00,
+/* sc=40 */ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16), 0xFF, 0x00,
+/* sc=41 */ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7), 0xFF, 0x00,
+/* sc=42 */ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8), 0xFF, 0x00,
+/* sc=43 */ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9), 0xFF, 0x00,
+/* sc=44 */ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10), 0xFF, 0x00,
+/* sc=45 */ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, 0xFF, 0x00,
+/* sc=46 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=47 */ F(49), '7', '7', '7', '7', '7', '7', '7', 0x80, 0x02,
+/* sc=48 */ F(50), '8', '8', '8', '8', '8', '8', '8', 0x80, 0x02,
+/* sc=49 */ F(51), '9', '9', '9', '9', '9', '9', '9', 0x80, 0x02,
+/* sc=4a */ F(52), '-', '-', '-', '-', '-', '-', '-', 0x80, 0x02,
+/* sc=4b */ F(53), '4', '4', '4', '4', '4', '4', '4', 0x80, 0x02,
+/* sc=4c */ F(54), '5', '5', '5', '5', '5', '5', '5', 0x80, 0x02,
+/* sc=4d */ F(55), '6', '6', '6', '6', '6', '6', '6', 0x80, 0x02,
+/* sc=4e */ F(56), '+', '+', '+', '+', '+', '+', '+', 0x80, 0x02,
+/* sc=4f */ F(57), '1', '1', '1', '1', '1', '1', '1', 0x80, 0x02,
+/* sc=50 */ F(58), '2', '2', '2', '2', '2', '2', '2', 0x80, 0x02,
+/* sc=51 */ F(59), '3', '3', '3', '3', '3', '3', '3', 0x80, 0x02,
+/* sc=52 */ F(60), '0', '0', '0', '0', '0', '0', '0', 0x80, 0x02,
+/* sc=53 */ 0x7F, '.', '.', '.', '.', '.', RBT, RBT, 0x03, 0x02,
+/* sc=54 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00,
+/* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00,
+/* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x00, 0x00,
+/* sc=5a */ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, 0xFF, 0x00,
+/* sc=5b */ '/', '/', '/', '/', '/', '/', '/', '/', 0x00, 0x00,
+/* sc=5c */ NEXT, NOP, DBG, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00,
+/* sc=5d */ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, 0xFF, 0x00,
+/* sc=5e */ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49), 0xFF, 0x00,
+/* sc=5f */ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50), 0xFF, 0x00,
+/* sc=60 */ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51), 0xFF, 0x00,
+/* sc=61 */ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53), 0xFF, 0x00,
+/* sc=62 */ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55), 0xFF, 0x00,
+/* sc=63 */ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57), 0xFF, 0x00,
+/* sc=64 */ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58), 0xFF, 0x00,
+/* sc=65 */ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59), 0xFF, 0x00,
+/* sc=66 */ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60), 0xFF, 0x00,
+/* sc=67 */ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61), 0xFF, 0x00,
+/* sc=68 */ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, 0xFF, 0x00,
+/* sc=69 */ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62), 0xFF, 0x00,
+/* sc=6a */ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63), 0xFF, 0x00,
+/* sc=6b */ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64), 0xFF, 0x00,
+};
+
+#endif
+
+static fkeytab_t fkey_tab[96] = {
+/* 01-04 */ {"\033[M", 3}, {"\033[N", 3}, {"\033[O", 3}, {"\033[P", 3},
+/* 05-08 */ {"\033[Q", 3}, {"\033[R", 3}, {"\033[S", 3}, {"\033[T", 3},
+/* 09-12 */ {"\033[U", 3}, {"\033[V", 3}, {"\033[W", 3}, {"\033[X", 3},
+/* 13-16 */ {"\033[Y", 3}, {"\033[Z", 3}, {"\033[a", 3}, {"\033[b", 3},
+/* 17-20 */ {"\033[c", 3}, {"\033[d", 3}, {"\033[e", 3}, {"\033[f", 3},
+/* 21-24 */ {"\033[g", 3}, {"\033[h", 3}, {"\033[i", 3}, {"\033[j", 3},
+/* 25-28 */ {"\033[k", 3}, {"\033[l", 3}, {"\033[m", 3}, {"\033[n", 3},
+/* 29-32 */ {"\033[o", 3}, {"\033[p", 3}, {"\033[q", 3}, {"\033[r", 3},
+/* 33-36 */ {"\033[s", 3}, {"\033[t", 3}, {"\033[u", 3}, {"\033[v", 3},
+/* 37-40 */ {"\033[w", 3}, {"\033[x", 3}, {"\033[y", 3}, {"\033[z", 3},
+/* 41-44 */ {"\033[@", 3}, {"\033[[", 3}, {"\033[\\",3}, {"\033[]", 3},
+/* 45-48 */ {"\033[^", 3}, {"\033[_", 3}, {"\033[`", 3}, {"\033[{", 3},
+/* 49-52 */ {"\033[H", 3}, {"\033[A", 3}, {"\033[I", 3}, {"-" , 1},
+/* 53-56 */ {"\033[D", 3}, {"\033[E", 3}, {"\033[C", 3}, {"+" , 1},
+/* 57-60 */ {"\033[F", 3}, {"\033[B", 3}, {"\033[G", 3}, {"\033[L", 3},
+/* 61-64 */ {"\177", 1}, {"\033[J", 3}, {"\033[~", 3}, {"\033[}", 3},
+/* 65-68 */ {"", 0} , {"", 0} , {"", 0} , {"", 0} ,
+/* 69-72 */ {"", 0} , {"", 0} , {"", 0} , {"", 0} ,
+/* 73-76 */ {"", 0} , {"", 0} , {"", 0} , {"", 0} ,
+/* 77-80 */ {"", 0} , {"", 0} , {"", 0} , {"", 0} ,
+/* 81-84 */ {"", 0} , {"", 0} , {"", 0} , {"", 0} ,
+/* 85-88 */ {"", 0} , {"", 0} , {"", 0} , {"", 0} ,
+/* 89-92 */ {"", 0} , {"", 0} , {"", 0} , {"", 0} ,
+/* 93-96 */ {"", 0} , {"", 0} , {"", 0} , {"", 0}
+};
diff --git a/sys/pc98/pc98/lpt.c b/sys/pc98/pc98/lpt.c
new file mode 100644
index 0000000..33a3822
--- /dev/null
+++ b/sys/pc98/pc98/lpt.c
@@ -0,0 +1,1488 @@
+/*
+ * Copyright (c) 1990 William F. Jolitz, TeleMuse
+ * 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 software is a component of "386BSD" developed by
+ * William F. Jolitz, TeleMuse.
+ * 4. Neither the name of the developer nor the name "386BSD"
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
+ * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
+ * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
+ * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
+ * NOT MAKE USE OF THIS WORK.
+ *
+ * FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
+ * BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
+ * REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
+ * (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
+ * JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
+ * LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
+ * ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
+ * OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``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 DEVELOPER 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: unknown origin, 386BSD 0.1
+ * $Id: lpt.c,v 1.53 1996/04/04 12:28:36 joerg Exp $
+ */
+
+/*
+ * Device Driver for AT parallel printer port
+ * Written by William Jolitz 12/18/90
+ */
+
+/*
+ * Parallel port TCP/IP interfaces added. I looked at the driver from
+ * MACH but this is a complete rewrite, and btw. incompatible, and it
+ * should perform better too. I have never run the MACH driver though.
+ *
+ * This driver sends two bytes (0x08, 0x00) in front of each packet,
+ * to allow us to distinguish another format later.
+ *
+ * Now added an Linux/Crynwr compatibility mode which is enabled using
+ * IF_LINK0 - Tim Wilkinson.
+ *
+ * TODO:
+ * Make HDLC/PPP mode, use IF_LLC1 to enable.
+ *
+ * Connect the two computers using a Laplink parallel cable to use this
+ * feature:
+ *
+ * +----------------------------------------+
+ * |A-name A-End B-End Descr. Port/Bit |
+ * +----------------------------------------+
+ * |DATA0 2 15 Data 0/0x01 |
+ * |-ERROR 15 2 1/0x08 |
+ * +----------------------------------------+
+ * |DATA1 3 13 Data 0/0x02 |
+ * |+SLCT 13 3 1/0x10 |
+ * +----------------------------------------+
+ * |DATA2 4 12 Data 0/0x04 |
+ * |+PE 12 4 1/0x20 |
+ * +----------------------------------------+
+ * |DATA3 5 10 Strobe 0/0x08 |
+ * |-ACK 10 5 1/0x40 |
+ * +----------------------------------------+
+ * |DATA4 6 11 Data 0/0x10 |
+ * |BUSY 11 6 1/~0x80 |
+ * +----------------------------------------+
+ * |GND 18-25 18-25 GND - |
+ * +----------------------------------------+
+ *
+ * Expect transfer-rates up to 75 kbyte/sec.
+ *
+ * If GCC could correctly grok
+ * register int port asm("edx")
+ * the code would be cleaner
+ *
+ * Poul-Henning Kamp <phk@freebsd.org>
+ */
+
+#include "lpt.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/devconf.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /*DEVFS*/
+
+#include <machine/clock.h>
+#include <machine/lpt.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/lptreg.h>
+#else /* !PC98 */
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/lptreg.h>
+#endif /* PC98 */
+
+#ifdef INET
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+#endif /* INET */
+
+
+#define LPINITRDY 4 /* wait up to 4 seconds for a ready */
+#define LPTOUTINITIAL 10 /* initial timeout to wait for ready 1/10 s */
+#define LPTOUTMAX 1 /* maximal timeout 1 s */
+#define LPPRI (PZERO+8)
+#define BUFSIZE 1024
+
+#ifdef INET
+#ifndef LPMTU /* MTU for the lp# interfaces */
+#define LPMTU 1500
+#endif
+
+#ifndef LPMAXSPIN1 /* DELAY factor for the lp# interfaces */
+#define LPMAXSPIN1 8000 /* Spinning for remote intr to happen */
+#endif
+
+#ifndef LPMAXSPIN2 /* DELAY factor for the lp# interfaces */
+#define LPMAXSPIN2 500 /* Spinning for remote handshake to happen */
+#endif
+
+#ifndef LPMAXERRS /* Max errors before !RUNNING */
+#define LPMAXERRS 100
+#endif
+
+#define CLPIPHDRLEN 14 /* We send dummy ethernet addresses (two) + packet type in front of packet */
+#define CLPIP_SHAKE 0x80 /* This bit toggles between nibble reception */
+#define MLPIPHDRLEN CLPIPHDRLEN
+
+#define LPIPHDRLEN 2 /* We send 0x08, 0x00 in front of packet */
+#define LPIP_SHAKE 0x40 /* This bit toggles between nibble reception */
+#if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN
+#define MLPIPHDRLEN LPIPHDRLEN
+#endif
+
+#define LPIPTBLSIZE 256 /* Size of octet translation table */
+
+#endif /* INET */
+
+#ifndef PC98
+/* BIOS printer list - used by BIOS probe*/
+#define BIOS_LPT_PORTS 0x408
+#define BIOS_PORTS (short *)(KERNBASE+BIOS_LPT_PORTS)
+#define BIOS_MAX_LPT 4
+#endif
+
+
+#ifndef DEBUG
+#define lprintf (void)
+#else
+#define lprintf if (lptflag) printf
+int lptflag = 1;
+#endif
+
+#define LPTUNIT(s) ((s)&0x03)
+#define LPTFLAGS(s) ((s)&0xfc)
+
+static struct lpt_softc {
+ short sc_port;
+ short sc_state;
+ /* default case: negative prime, negative ack, handshake strobe,
+ prime once */
+ u_char sc_control;
+ char sc_flags;
+#define LP_POS_INIT 0x04 /* if we are a postive init signal */
+#define LP_POS_ACK 0x08 /* if we are a positive going ack */
+#define LP_NO_PRIME 0x10 /* don't prime the printer at all */
+#define LP_PRIMEOPEN 0x20 /* prime on every open */
+#define LP_AUTOLF 0x40 /* tell printer to do an automatic lf */
+#define LP_BYPASS 0x80 /* bypass printer ready checks */
+ struct buf *sc_inbuf;
+ short sc_xfercnt ;
+ char sc_primed;
+ char *sc_cp ;
+ u_char sc_irq ; /* IRQ status of port */
+#define LP_HAS_IRQ 0x01 /* we have an irq available */
+#define LP_USE_IRQ 0x02 /* we are using our irq */
+#define LP_ENABLE_IRQ 0x04 /* enable IRQ on open */
+ u_char sc_backoff ; /* time to call lptout() again */
+
+#ifdef INET
+ struct ifnet sc_if;
+ u_char *sc_ifbuf;
+ int sc_iferrs;
+#endif
+#ifdef DEVFS
+ void *devfs_token;
+ void *devfs_token_ctl;
+#endif
+} lpt_sc[NLPT] ;
+
+/* bits for state */
+#define OPEN (1<<0) /* device is open */
+#define ASLP (1<<1) /* awaiting draining of printer */
+#define ERROR (1<<2) /* error was received from printer */
+#define OBUSY (1<<3) /* printer is busy doing output */
+#define LPTOUT (1<<4) /* timeout while not selected */
+#define TOUT (1<<5) /* timeout while not selected */
+#define INIT (1<<6) /* waiting to initialize for open */
+#define INTERRUPTED (1<<7) /* write call was interrupted */
+
+
+/* status masks to interrogate printer status */
+#define RDY_MASK (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR) /* ready ? */
+#define LP_READY (LPS_SEL|LPS_NBSY|LPS_NERR)
+
+/* Printer Ready condition - from lpa.c */
+/* Only used in polling code */
+#ifdef PC98
+#define NOT_READY(x) ((inb(x) & LPS_NBSY) != LPS_NBSY)
+#else /* IBM-PC */
+#define LPS_INVERT (LPS_NBSY | LPS_NACK | LPS_SEL | LPS_NERR)
+#define LPS_MASK (LPS_NBSY | LPS_NACK | LPS_OUT | LPS_SEL | LPS_NERR)
+#define NOT_READY(x) ((inb(x)^LPS_INVERT)&LPS_MASK)
+#endif
+
+#define MAX_SLEEP (hz*5) /* Timeout while waiting for device ready */
+#define MAX_SPIN 20 /* Max delay for device ready in usecs */
+
+static void lptout (struct lpt_softc * sc);
+#ifdef PC98
+static int lptprobe (struct pc98_device *dvp);
+static int lptattach (struct pc98_device *isdp);
+#else
+static int lptprobe (struct isa_device *dvp);
+static int lptattach (struct isa_device *isdp);
+#endif
+
+#ifdef INET
+
+/* Tables for the lp# interface */
+static u_char *txmith;
+#define txmitl (txmith+(1*LPIPTBLSIZE))
+#define trecvh (txmith+(2*LPIPTBLSIZE))
+#define trecvl (txmith+(3*LPIPTBLSIZE))
+
+static u_char *ctxmith;
+#define ctxmitl (ctxmith+(1*LPIPTBLSIZE))
+#define ctrecvh (ctxmith+(2*LPIPTBLSIZE))
+#define ctrecvl (ctxmith+(3*LPIPTBLSIZE))
+
+/* Functions for the lp# interface */
+static void lpattach(struct lpt_softc *,int);
+static int lpinittables(void);
+static int lpioctl(struct ifnet *, int, caddr_t);
+static int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
+ struct rtentry *);
+static void lpintr(int);
+#endif /* INET */
+
+#ifdef PC98
+#ifndef PC98_LPT_INTR
+void lptintr(int unit);
+#endif
+
+struct pc98_driver lptdriver = {
+#else
+struct isa_driver lptdriver = {
+#endif
+ lptprobe, lptattach, "lpt"
+};
+
+static d_open_t lptopen;
+static d_close_t lptclose;
+static d_write_t lptwrite;
+static d_ioctl_t lptioctl;
+
+#define CDEV_MAJOR 16
+static struct cdevsw lpt_cdevsw =
+ { lptopen, lptclose, noread, lptwrite, /*16*/
+ lptioctl, nullstop, nullreset, nodevtotty,/* lpt */
+ seltrue, nommap, nostrat, "lpt", NULL, -1 };
+
+
+static struct kern_devconf kdc_lpt[NLPT] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ "lpt", 0, { MDDT_PC98, 0, "tty" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "lpt", 0, { MDDT_ISA, 0, "tty" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "Parallel printer adapter",
+ DC_CLS_PARALLEL | DC_CLS_NETIF /* class */
+} };
+
+#ifdef PC98
+static inline void
+lpt_registerdev(struct pc98_device *id)
+#else
+static inline void
+lpt_registerdev(struct isa_device *id)
+#endif
+{
+ if(id->id_unit)
+ kdc_lpt[id->id_unit] = kdc_lpt[0];
+ kdc_lpt[id->id_unit].kdc_unit = id->id_unit;
+#ifdef PC98
+ kdc_lpt[id->id_unit].kdc_pc98 = id;
+#else
+ kdc_lpt[id->id_unit].kdc_isa = id;
+#endif
+ dev_attach(&kdc_lpt[id->id_unit]);
+}
+
+/*
+ * Internal routine to lptprobe to do port tests of one byte value
+ */
+static int
+lpt_port_test (short port, u_char data, u_char mask)
+{
+ int temp, timeout;
+
+ data = data & mask;
+ outb(port, data);
+ timeout = 10000;
+ do {
+ DELAY(10);
+ temp = inb(port) & mask;
+ }
+ while (temp != data && --timeout);
+ lprintf("Port 0x%x\tout=%x\tin=%x\ttout=%d\n",
+ port, data, temp, timeout);
+ return (temp == data);
+}
+
+/*
+ * New lpt port probe Geoff Rehmet - Rhodes University - 14/2/94
+ * Based partially on Rod Grimes' printer probe
+ *
+ * Logic:
+ * 1) If no port address was given, use the bios detected ports
+ * and autodetect what ports the printers are on.
+ * 2) Otherwise, probe the data port at the address given,
+ * using the method in Rod Grimes' port probe.
+ * (Much code ripped off directly from Rod's probe.)
+ *
+ * Comments from Rod's probe:
+ * Logic:
+ * 1) You should be able to write to and read back the same value
+ * to the data port. Do an alternating zeros, alternating ones,
+ * walking zero, and walking one test to check for stuck bits.
+ *
+ * 2) You should be able to write to and read back the same value
+ * to the control port lower 5 bits, the upper 3 bits are reserved
+ * per the IBM PC technical reference manauls and different boards
+ * do different things with them. Do an alternating zeros, alternating
+ * ones, walking zero, and walking one test to check for stuck bits.
+ *
+ * Some printers drag the strobe line down when the are powered off
+ * so this bit has been masked out of the control port test.
+ *
+ * XXX Some printers may not like a fast pulse on init or strobe, I
+ * don't know at this point, if that becomes a problem these bits
+ * should be turned off in the mask byte for the control port test.
+ *
+ * We are finally left with a mask of 0x14, due to some printers
+ * being adamant about holding other bits high ........
+ *
+ * Before probing the control port, we write a 0 to the data port -
+ * If not, some printers chuck out garbage when the strobe line
+ * gets toggled.
+ *
+ * 3) Set the data and control ports to a value of 0
+ *
+ * This probe routine has been tested on Epson Lx-800, HP LJ3P,
+ * Epson FX-1170 and C.Itoh 8510RM
+ * printers.
+ * Quick exit on fail added.
+ */
+
+#ifdef PC98
+int
+lptprobe(struct pc98_device *dvp)
+{
+ return 8;
+}
+#else
+int
+lptprobe(struct isa_device *dvp)
+{
+ short port;
+ static short next_bios_lpt = 0;
+ int status;
+ u_char data;
+ u_char mask;
+ int i;
+
+ lpt_registerdev(dvp);
+
+ /*
+ * Make sure there is some way for lptopen to see that
+ * the port is not configured
+ * This 0 will remain if the port isn't attached
+ */
+ (lpt_sc + dvp->id_unit)->sc_port = 0;
+
+ status = IO_LPTSIZE;
+ /* If port not specified, use bios list */
+ if(dvp->id_iobase < 0) { /* port? */
+ if((next_bios_lpt < BIOS_MAX_LPT) &&
+ (*(BIOS_PORTS+next_bios_lpt) != 0) ) {
+ dvp->id_iobase = *(BIOS_PORTS+next_bios_lpt++);
+ goto end_probe;
+ } else
+ return (0);
+ }
+
+ /* Port was explicitly specified */
+ /* This allows probing of ports unknown to the BIOS */
+
+ port = dvp->id_iobase + lpt_data;
+ mask = 0xff;
+ data = 0x55; /* Alternating zeros */
+ if (!lpt_port_test(port, data, mask))
+ { status = 0 ; goto end_probe ; }
+
+ data = 0xaa; /* Alternating ones */
+ if (!lpt_port_test(port, data, mask))
+ { status = 0 ; goto end_probe ; }
+
+ for (i = 0; i < 8; i++) { /* Walking zero */
+ data = ~(1 << i);
+ if (!lpt_port_test(port, data, mask))
+ { status = 0 ; goto end_probe ; }
+ }
+
+ for (i = 0; i < 8; i++) { /* Walking one */
+ data = (1 << i);
+ if (!lpt_port_test(port, data, mask))
+ { status = 0 ; goto end_probe ; }
+ }
+
+end_probe:
+ /* write 0's to control and data ports */
+ outb(dvp->id_iobase+lpt_data, 0);
+ outb(dvp->id_iobase+lpt_control, 0);
+
+ return (status);
+}
+#endif
+
+/* XXX Todo - try and detect if interrupt is working */
+#ifdef PC98
+int
+lptattach(struct pc98_device *isdp)
+#else
+int
+lptattach(struct isa_device *isdp)
+#endif
+{
+ struct lpt_softc *sc;
+ int unit;
+
+ unit = isdp->id_unit;
+ sc = lpt_sc + unit;
+ sc->sc_port = isdp->id_iobase;
+ sc->sc_primed = 0; /* not primed yet */
+#ifdef PC98
+ outb(sc->sc_port+lpt_pstb_ctrl, LPC_DIS_PSTB); /* PSTB disable */
+ outb(sc->sc_port+lpt_control, LPC_MODE8255); /* 8255 mode set */
+ outb(sc->sc_port+lpt_control, LPC_NIRQ8); /* IRQ8 inactive */
+ outb(sc->sc_port+lpt_control, LPC_NPSTB); /* PSTB inactive */
+ outb(sc->sc_port+lpt_pstb_ctrl, LPC_EN_PSTB); /* PSTB enable */
+#else
+ outb(sc->sc_port+lpt_control, LPC_NINIT);
+#endif
+
+ /* check if we can use interrupt */
+ lprintf("oldirq %x\n", sc->sc_irq);
+ if (isdp->id_irq) {
+ sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ;
+ printf("lpt%d: Interrupt-driven port\n", unit);
+#ifdef INET
+ lpattach(sc, unit);
+#endif
+ } else {
+ sc->sc_irq = 0;
+ lprintf("lpt%d: Polled port\n", unit);
+ }
+ lprintf("irq %x\n", sc->sc_irq);
+
+ kdc_lpt[unit].kdc_state = DC_IDLE;
+
+#ifdef DEVFS
+ /* XXX what to do about the flags in the minor number? */
+ sc->devfs_token = devfs_add_devswf(&lpt_cdevsw,
+ unit, DV_CHR,
+ UID_ROOT, GID_WHEEL, 0600, "lpt%d", unit);
+ sc->devfs_token_ctl = devfs_add_devswf(&lpt_cdevsw,
+ unit | LP_BYPASS, DV_CHR,
+ UID_ROOT, GID_WHEEL, 0600, "lpctl%d", unit);
+#endif
+ return (1);
+}
+
+/*
+ * lptopen -- reset the printer, then wait until it's selected and not busy.
+ * If LP_BYPASS flag is selected, then we do not try to select the
+ * printer -- this is just used for passing ioctls.
+ */
+
+static int
+lptopen (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct lpt_softc *sc;
+ int s;
+ int trys, port;
+ u_int unit = LPTUNIT(minor(dev));
+
+ sc = lpt_sc + unit;
+ if ((unit >= NLPT) || (sc->sc_port == 0))
+ return (ENXIO);
+
+#ifdef INET
+ if (sc->sc_if.if_flags & IFF_UP)
+ return(EBUSY);
+#endif
+
+ if (sc->sc_state) {
+ lprintf("lp: still open %x\n", sc->sc_state);
+ return(EBUSY);
+ } else
+ sc->sc_state |= INIT;
+
+ sc->sc_flags = LPTFLAGS(minor(dev));
+
+ /* Check for open with BYPASS flag set. */
+ if (sc->sc_flags & LP_BYPASS) {
+ sc->sc_state = OPEN;
+ return(0);
+ }
+
+ s = spltty();
+ lprintf("lp flags 0x%x\n", sc->sc_flags);
+ port = sc->sc_port;
+
+ /* set IRQ status according to ENABLE_IRQ flag */
+ if (sc->sc_irq & LP_ENABLE_IRQ)
+ sc->sc_irq |= LP_USE_IRQ;
+ else
+ sc->sc_irq &= ~LP_USE_IRQ;
+
+ /* init printer */
+#ifndef PC98
+ if ((sc->sc_flags & LP_NO_PRIME) == 0) {
+ if((sc->sc_flags & LP_PRIMEOPEN) || sc->sc_primed == 0) {
+ outb(port+lpt_control, 0);
+ sc->sc_primed++;
+ DELAY(500);
+ }
+ }
+
+ outb (port+lpt_control, LPC_SEL|LPC_NINIT);
+
+ /* wait till ready (printer running diagnostics) */
+ trys = 0;
+ do {
+ /* ran out of waiting for the printer */
+ if (trys++ >= LPINITRDY*4) {
+ splx(s);
+ sc->sc_state = 0;
+ lprintf ("status %x\n", inb(port+lpt_status) );
+ return (EBUSY);
+ }
+
+ /* wait 1/4 second, give up if we get a signal */
+ if (tsleep ((caddr_t)sc, LPPRI|PCATCH, "lptinit", hz/4) !=
+ EWOULDBLOCK) {
+ sc->sc_state = 0;
+ splx(s);
+ return (EBUSY);
+ }
+
+ /* is printer online and ready for output */
+ } while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
+ (LPS_SEL|LPS_NBSY|LPS_NERR));
+
+ sc->sc_control = LPC_SEL|LPC_NINIT;
+ if (sc->sc_flags & LP_AUTOLF)
+ sc->sc_control |= LPC_AUTOL;
+
+ /* enable interrupt if interrupt-driven */
+ if (sc->sc_irq & LP_USE_IRQ)
+ sc->sc_control |= LPC_ENA;
+
+ outb(port+lpt_control, sc->sc_control);
+#endif
+
+ sc->sc_state = OPEN;
+ kdc_lpt[unit].kdc_state = DC_BUSY;
+ sc->sc_inbuf = geteblk(BUFSIZE);
+ sc->sc_xfercnt = 0;
+ splx(s);
+
+ /* only use timeout if using interrupt */
+ lprintf("irq %x\n", sc->sc_irq);
+ if (sc->sc_irq & LP_USE_IRQ) {
+ sc->sc_state |= TOUT;
+ timeout ((timeout_func_t)lptout, (caddr_t)sc,
+ (sc->sc_backoff = hz/LPTOUTINITIAL));
+ }
+
+ lprintf("opened.\n");
+ return(0);
+}
+
+static void
+lptout (struct lpt_softc * sc)
+{ int pl;
+
+ lprintf ("T %x ", inb(sc->sc_port+lpt_status));
+ if (sc->sc_state & OPEN) {
+ sc->sc_backoff++;
+ if (sc->sc_backoff > hz/LPTOUTMAX)
+ sc->sc_backoff = sc->sc_backoff > hz/LPTOUTMAX;
+ timeout ((timeout_func_t)lptout, (caddr_t)sc, sc->sc_backoff);
+ } else
+ sc->sc_state &= ~TOUT;
+
+ if (sc->sc_state & ERROR)
+ sc->sc_state &= ~ERROR;
+
+ /*
+ * Avoid possible hangs do to missed interrupts
+ */
+ if (sc->sc_xfercnt) {
+ pl = spltty();
+ lptintr(sc - lpt_sc);
+ splx(pl);
+ } else {
+ sc->sc_state &= ~OBUSY;
+ wakeup((caddr_t)sc);
+ }
+}
+
+/*
+ * lptclose -- close the device, free the local line buffer.
+ *
+ * Check for interrupted write call added.
+ */
+
+static int
+lptclose(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
+ int port = sc->sc_port;
+
+ if(sc->sc_flags & LP_BYPASS)
+ goto end_close;
+
+ sc->sc_state &= ~OPEN;
+ kdc_lpt[minor(dev)].kdc_state = DC_IDLE;
+
+#ifndef PC98
+ /* if the last write was interrupted, don't complete it */
+ if((!(sc->sc_state & INTERRUPTED)) && (sc->sc_irq & LP_USE_IRQ))
+ while ((inb(port+lpt_status) & (LPS_SEL|LPS_OUT|LPS_NBSY|LPS_NERR)) !=
+ (LPS_SEL|LPS_NBSY|LPS_NERR) || sc->sc_xfercnt)
+ /* wait 1/4 second, give up if we get a signal */
+ if (tsleep ((caddr_t)sc, LPPRI|PCATCH,
+ "lpclose", hz) != EWOULDBLOCK)
+ break;
+
+ outb(sc->sc_port+lpt_control, LPC_NINIT);
+#endif
+ brelse(sc->sc_inbuf);
+
+end_close:
+ sc->sc_state = 0;
+ sc->sc_xfercnt = 0;
+ lprintf("closed.\n");
+ return(0);
+}
+
+/*
+ * pushbytes()
+ * Workhorse for actually spinning and writing bytes to printer
+ * Derived from lpa.c
+ * Originally by ?
+ *
+ * This code is only used when we are polling the port
+ */
+static int
+pushbytes(struct lpt_softc * sc)
+{
+ int spin, err, tic;
+ char ch;
+ int port = sc->sc_port;
+
+ lprintf("p");
+ /* loop for every character .. */
+ while (sc->sc_xfercnt > 0) {
+ /* printer data */
+ ch = *(sc->sc_cp);
+ sc->sc_cp++;
+ sc->sc_xfercnt--;
+
+ /*
+ * Wait for printer ready.
+ * Loop 20 usecs testing BUSY bit, then sleep
+ * for exponentially increasing timeout. (vak)
+ */
+ for (spin=0; NOT_READY(port+lpt_status) && spin<MAX_SPIN; ++spin)
+ DELAY(1); /* XXX delay is NOT this accurate! */
+ if (spin >= MAX_SPIN) {
+ tic = 0;
+ while (NOT_READY(port+lpt_status)) {
+ /*
+ * Now sleep, every cycle a
+ * little longer ..
+ */
+ tic = tic + tic + 1;
+ /*
+ * But no more than 10 seconds. (vak)
+ */
+ if (tic > MAX_SLEEP)
+ tic = MAX_SLEEP;
+ err = tsleep((caddr_t)sc, LPPRI,
+ "lptpoll", tic);
+ if (err != EWOULDBLOCK) {
+ return (err);
+ }
+ }
+ }
+
+ /* output data */
+ outb(port+lpt_data, ch);
+#ifdef PC98
+ DELAY(1);
+ outb(port+lpt_control, LPC_PSTB);
+ DELAY(1);
+ outb(port+lpt_control, LPC_NPSTB);
+#else
+ /* strobe */
+ outb(port+lpt_control, sc->sc_control|LPC_STB);
+ outb(port+lpt_control, sc->sc_control);
+#endif
+
+ }
+ return(0);
+}
+
+/*
+ * lptwrite --copy a line from user space to a local buffer, then call
+ * putc to get the chars moved to the output queue.
+ *
+ * Flagging of interrupted write added.
+ */
+
+static int
+lptwrite(dev_t dev, struct uio * uio, int ioflag)
+{
+ register unsigned n;
+ int pl, err;
+ struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev));
+
+ if(sc->sc_flags & LP_BYPASS) {
+ /* we can't do writes in bypass mode */
+ return(EPERM);
+ }
+
+ sc->sc_state &= ~INTERRUPTED;
+ while ((n = min(BUFSIZE, uio->uio_resid)) != 0) {
+ sc->sc_cp = sc->sc_inbuf->b_un.b_addr ;
+ uiomove(sc->sc_cp, n, uio);
+ sc->sc_xfercnt = n ;
+ while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) {
+ lprintf("i");
+ /* if the printer is ready for a char, */
+ /* give it one */
+ if ((sc->sc_state & OBUSY) == 0){
+ lprintf("\nC %d. ", sc->sc_xfercnt);
+ pl = spltty();
+ lptintr(sc - lpt_sc);
+ (void) splx(pl);
+ }
+ lprintf("W ");
+ if (sc->sc_state & OBUSY)
+ if ((err = tsleep ((caddr_t)sc,
+ LPPRI|PCATCH, "lpwrite", 0))) {
+ sc->sc_state |= INTERRUPTED;
+ return(err);
+ }
+ }
+ /* check to see if we must do a polled write */
+ if(!(sc->sc_irq & LP_USE_IRQ) && (sc->sc_xfercnt)) {
+ lprintf("p");
+ if((err = pushbytes(sc)))
+ return(err);
+ }
+ }
+ return(0);
+}
+
+/*
+ * lptintr -- handle printer interrupts which occur when the printer is
+ * ready to accept another char.
+ *
+ * do checking for interrupted write call.
+ */
+
+void
+lptintr(int unit)
+{
+ struct lpt_softc *sc = lpt_sc + unit;
+ int port = sc->sc_port, sts;
+ int i;
+
+#ifdef INET
+ if(sc->sc_if.if_flags & IFF_UP) {
+ lpintr(unit);
+ return;
+ }
+#endif /* INET */
+
+#ifndef PC98
+ /*
+ * Is printer online and ready for output?
+ *
+ * Avoid falling back to lptout() too quickly. First spin-loop
+ * to see if the printer will become ready ``really soon now''.
+ */
+ for (i = 0;
+ i < 100 &&
+ ((sts=inb(port+lpt_status)) & RDY_MASK) != LP_READY;
+ i++) ;
+ if ((sts & RDY_MASK) == LP_READY) {
+ sc->sc_state = (sc->sc_state | OBUSY) & ~ERROR;
+ sc->sc_backoff = hz/LPTOUTINITIAL;
+
+ if (sc->sc_xfercnt) {
+ /* send char */
+ /*lprintf("%x ", *sc->sc_cp); */
+ outb(port+lpt_data, *sc->sc_cp++) ;
+ outb(port+lpt_control, sc->sc_control|LPC_STB);
+ /* DELAY(X) */
+ outb(port+lpt_control, sc->sc_control);
+
+ /* any more data for printer */
+ if(--(sc->sc_xfercnt) > 0) return;
+ }
+
+ /*
+ * No more data waiting for printer.
+ * Wakeup is not done if write call was interrupted.
+ */
+ sc->sc_state &= ~OBUSY;
+ if(!(sc->sc_state & INTERRUPTED))
+ wakeup((caddr_t)sc);
+ lprintf("w ");
+ return;
+ } else { /* check for error */
+ if(((sts & (LPS_NERR | LPS_OUT) ) != LPS_NERR) &&
+ (sc->sc_state & OPEN))
+ sc->sc_state |= ERROR;
+ /* lptout() will jump in and try to restart. */
+ }
+#endif
+ lprintf("sts %x ", sts);
+}
+
+static int
+lptioctl(dev_t dev, int cmd, caddr_t data, int flags, struct proc *p)
+{
+ int error = 0;
+ struct lpt_softc *sc;
+ u_int unit = LPTUNIT(minor(dev));
+ u_char old_sc_irq; /* old printer IRQ status */
+
+ sc = lpt_sc + unit;
+
+ switch (cmd) {
+ case LPT_IRQ :
+ if(sc->sc_irq & LP_HAS_IRQ) {
+ /*
+ * NOTE:
+ * If the IRQ status is changed,
+ * this will only be visible on the
+ * next open.
+ *
+ * If interrupt status changes,
+ * this gets syslog'd.
+ */
+ old_sc_irq = sc->sc_irq;
+ if(*(int*)data == 0)
+ sc->sc_irq &= (~LP_ENABLE_IRQ);
+ else
+ sc->sc_irq |= LP_ENABLE_IRQ;
+ if (old_sc_irq != sc->sc_irq )
+ log(LOG_NOTICE, "lpt%c switched to %s mode\n",
+ (char)unit+'0',
+ (sc->sc_irq & LP_ENABLE_IRQ)?
+ "interrupt-driven":"polled");
+ } else /* polled port */
+ error = EOPNOTSUPP;
+ break;
+ default:
+ error = ENODEV;
+ }
+
+ return(error);
+}
+
+#ifdef INET
+
+static void
+lpattach (struct lpt_softc *sc, int unit)
+{
+ struct ifnet *ifp = &sc->sc_if;
+
+ ifp->if_softc = sc;
+ ifp->if_name = "lp";
+ ifp->if_unit = unit;
+ ifp->if_mtu = LPMTU;
+ ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
+ ifp->if_ioctl = lpioctl;
+ ifp->if_output = lpoutput;
+ ifp->if_type = IFT_PARA;
+ ifp->if_hdrlen = 0;
+ ifp->if_addrlen = 0;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+ if_attach(ifp);
+ printf("lp%d: TCP/IP capable interface\n", unit);
+
+#if NBPFILTER > 0
+ bpfattach(ifp, DLT_NULL, LPIPHDRLEN);
+#endif
+}
+/*
+ * Build the translation tables for the LPIP (BSD unix) protocol.
+ * We don't want to calculate these nasties in our tight loop, so we
+ * precalculate them when we initialize.
+ */
+static int
+lpinittables (void)
+{
+ int i;
+
+ if (!txmith)
+ txmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
+
+ if (!txmith)
+ return 1;
+
+ if (!ctxmith)
+ ctxmith = malloc(4*LPIPTBLSIZE, M_DEVBUF, M_NOWAIT);
+
+ if (!ctxmith)
+ return 1;
+
+ for (i=0; i < LPIPTBLSIZE; i++) {
+ ctxmith[i] = (i & 0xF0) >> 4;
+ ctxmitl[i] = 0x10 | (i & 0x0F);
+ ctrecvh[i] = (i & 0x78) << 1;
+ ctrecvl[i] = (i & 0x78) >> 3;
+ }
+
+ for (i=0; i < LPIPTBLSIZE; i++) {
+ txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08;
+ txmitl[i] = ((i & 0x08) << 1) | (i & 0x07);
+ trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1);
+ trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3);
+ }
+
+ return 0;
+}
+
+/*
+ * Process an ioctl request.
+ */
+
+static int
+lpioctl (struct ifnet *ifp, int cmd, caddr_t data)
+{
+ struct lpt_softc *sc = lpt_sc + ifp->if_unit;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+ u_char *ptr;
+
+ switch (cmd) {
+
+ case SIOCSIFDSTADDR:
+ case SIOCAIFADDR:
+ case SIOCSIFADDR:
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ return EAFNOSUPPORT;
+ ifp->if_flags |= IFF_UP;
+ /* FALLTHROUGH */
+ case SIOCSIFFLAGS:
+ if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) {
+ outb(sc->sc_port + lpt_control, 0x00);
+ ifp->if_flags &= ~IFF_RUNNING;
+ break;
+ }
+ if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) {
+ if (lpinittables())
+ return ENOBUFS;
+ sc->sc_ifbuf = malloc(sc->sc_if.if_mtu + MLPIPHDRLEN,
+ M_DEVBUF, M_WAITOK);
+ if (!sc->sc_ifbuf)
+ return ENOBUFS;
+
+ outb(sc->sc_port + lpt_control, LPC_ENA);
+ ifp->if_flags |= IFF_RUNNING;
+ }
+ break;
+
+ case SIOCSIFMTU:
+ ptr = sc->sc_ifbuf;
+ sc->sc_ifbuf = malloc(ifr->ifr_mtu+MLPIPHDRLEN, M_DEVBUF, M_NOWAIT);
+ if (!sc->sc_ifbuf) {
+ sc->sc_ifbuf = ptr;
+ return ENOBUFS;
+ }
+ if (ptr)
+ free(ptr,M_DEVBUF);
+ sc->sc_if.if_mtu = ifr->ifr_mtu;
+ break;
+
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = sc->sc_if.if_mtu;
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifr == 0) {
+ return EAFNOSUPPORT; /* XXX */
+ }
+ switch (ifr->ifr_addr.sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ break;
+#endif
+
+ default:
+ return EAFNOSUPPORT;
+ }
+ break;
+
+ default:
+ lprintf("LP:ioctl(0x%x)\n",cmd);
+ return EINVAL;
+ }
+ return 0;
+}
+
+static inline int
+clpoutbyte (u_char byte, int spin, int data_port, int status_port)
+{
+ outb(data_port, ctxmitl[byte]);
+ while (inb(status_port) & CLPIP_SHAKE)
+ if (--spin == 0) {
+ return 1;
+ }
+ outb(data_port, ctxmith[byte]);
+ while (!(inb(status_port) & CLPIP_SHAKE))
+ if (--spin == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static inline int
+clpinbyte (int spin, int data_port, int status_port)
+{
+ int c, cl;
+
+ while((inb(status_port) & CLPIP_SHAKE))
+ if(!--spin) {
+ return -1;
+ }
+ cl = inb(status_port);
+ outb(data_port, 0x10);
+
+ while(!(inb(status_port) & CLPIP_SHAKE))
+ if(!--spin) {
+ return -1;
+ }
+ c = inb(status_port);
+ outb(data_port, 0x00);
+
+ return (ctrecvl[cl] | ctrecvh[c]);
+}
+
+static void
+lpintr (int unit)
+{
+ struct lpt_softc *sc = lpt_sc + unit;
+ register int lpt_data_port = sc->sc_port + lpt_data;
+ register int lpt_stat_port = sc->sc_port + lpt_status;
+ int lpt_ctrl_port = sc->sc_port + lpt_control;
+ int len, s, j;
+ u_char *bp;
+ u_char c, cl;
+ struct mbuf *top;
+
+ s = splhigh();
+
+ if (sc->sc_if.if_flags & IFF_LINK0) {
+
+ /* Ack. the request */
+ outb(lpt_data_port, 0x01);
+
+ /* Get the packet length */
+ j = clpinbyte(LPMAXSPIN2, lpt_data_port, lpt_stat_port);
+ if (j == -1)
+ goto err;
+ len = j;
+ j = clpinbyte(LPMAXSPIN2, lpt_data_port, lpt_stat_port);
+ if (j == -1)
+ goto err;
+ len = len + (j << 8);
+ if (len > sc->sc_if.if_mtu + MLPIPHDRLEN)
+ goto err;
+
+ bp = sc->sc_ifbuf;
+
+ while (len--) {
+ j = clpinbyte(LPMAXSPIN2, lpt_data_port, lpt_stat_port);
+ if (j == -1) {
+ goto err;
+ }
+ *bp++ = j;
+ }
+ /* Get and ignore checksum */
+ j = clpinbyte(LPMAXSPIN2, lpt_data_port, lpt_stat_port);
+ if (j == -1) {
+ goto err;
+ }
+
+ len = bp - sc->sc_ifbuf;
+ if (len <= CLPIPHDRLEN)
+ goto err;
+
+ sc->sc_iferrs = 0;
+
+ if (IF_QFULL(&ipintrq)) {
+ lprintf("DROP");
+ IF_DROP(&ipintrq);
+ goto done;
+ }
+ len -= CLPIPHDRLEN;
+ sc->sc_if.if_ipackets++;
+ sc->sc_if.if_ibytes += len;
+ top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, &sc->sc_if, 0);
+ if (top) {
+ IF_ENQUEUE(&ipintrq, top);
+ schednetisr(NETISR_IP);
+ }
+ goto done;
+ }
+ while ((inb(lpt_stat_port) & LPIP_SHAKE)) {
+ len = sc->sc_if.if_mtu + LPIPHDRLEN;
+ bp = sc->sc_ifbuf;
+ while (len--) {
+
+ cl = inb(lpt_stat_port);
+ outb(lpt_data_port, 8);
+
+ j = LPMAXSPIN2;
+ while((inb(lpt_stat_port) & LPIP_SHAKE))
+ if(!--j) goto err;
+
+ c = inb(lpt_stat_port);
+ outb(lpt_data_port, 0);
+
+ *bp++= trecvh[cl] | trecvl[c];
+
+ j = LPMAXSPIN2;
+ while (!((cl=inb(lpt_stat_port)) & LPIP_SHAKE)) {
+ if (cl != c &&
+ (((cl = inb(lpt_stat_port)) ^ 0xb8) & 0xf8) ==
+ (c & 0xf8))
+ goto end;
+ if (!--j) goto err;
+ }
+ }
+
+ end:
+ len = bp - sc->sc_ifbuf;
+ if (len <= LPIPHDRLEN)
+ goto err;
+
+ sc->sc_iferrs = 0;
+
+ if (IF_QFULL(&ipintrq)) {
+ lprintf("DROP");
+ IF_DROP(&ipintrq);
+ goto done;
+ }
+#if NBPFILTER > 0
+ if (sc->sc_if.if_bpf) {
+ bpf_tap(&sc->sc_if, sc->sc_ifbuf, len);
+ }
+#endif
+ len -= LPIPHDRLEN;
+ sc->sc_if.if_ipackets++;
+ sc->sc_if.if_ibytes += len;
+ top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, &sc->sc_if, 0);
+ if (top) {
+ IF_ENQUEUE(&ipintrq, top);
+ schednetisr(NETISR_IP);
+ }
+ }
+ goto done;
+
+ err:
+ outb(lpt_data_port, 0);
+ lprintf("R");
+ sc->sc_if.if_ierrors++;
+ sc->sc_iferrs++;
+
+ /*
+ * We are not able to send receive anything for now,
+ * so stop wasting our time
+ */
+ if (sc->sc_iferrs > LPMAXERRS) {
+ printf("lp%d: Too many errors, Going off-line.\n", unit);
+ outb(lpt_ctrl_port, 0x00);
+ sc->sc_if.if_flags &= ~IFF_RUNNING;
+ sc->sc_iferrs=0;
+ }
+
+ done:
+ splx(s);
+ return;
+}
+
+static inline int
+lpoutbyte (u_char byte, int spin, int data_port, int status_port)
+{
+ outb(data_port, txmith[byte]);
+ while (!(inb(status_port) & LPIP_SHAKE))
+ if (--spin == 0)
+ return 1;
+ outb(data_port, txmitl[byte]);
+ while (inb(status_port) & LPIP_SHAKE)
+ if (--spin == 0)
+ return 1;
+ return 0;
+}
+
+static int
+lpoutput (struct ifnet *ifp, struct mbuf *m,
+ struct sockaddr *dst, struct rtentry *rt)
+{
+ register int lpt_data_port = lpt_sc[ifp->if_unit].sc_port + lpt_data;
+ register int lpt_stat_port = lpt_sc[ifp->if_unit].sc_port + lpt_status;
+ int lpt_ctrl_port = lpt_sc[ifp->if_unit].sc_port + lpt_control;
+
+ int s, err;
+ struct mbuf *mm;
+ u_char *cp = "\0\0";
+ u_char chksum = 0;
+ int count = 0;
+ int i;
+ int spin;
+
+ /* We need a sensible value if we abort */
+ cp++;
+ ifp->if_flags |= IFF_RUNNING;
+
+ err = 1; /* assume we're aborting because of an error */
+
+ s = splhigh();
+
+ /* Suspend (on laptops) or receive-errors might have taken us offline */
+ outb(lpt_ctrl_port, LPC_ENA);
+
+ if (ifp->if_flags & IFF_LINK0) {
+
+ if (!(inb(lpt_stat_port) & CLPIP_SHAKE)) {
+ lprintf("&");
+ lptintr(ifp->if_unit);
+ }
+
+ /* Alert other end to pending packet */
+ spin = LPMAXSPIN1;
+ outb(lpt_data_port, 0x08);
+ while ((inb(lpt_stat_port) & 0x08) == 0)
+ if (--spin == 0) {
+ goto nend;
+ }
+
+ /* Calculate length of packet, then send that */
+
+ count += 14; /* Ethernet header len */
+
+ mm = m;
+ for (mm = m; mm; mm = mm->m_next) {
+ count += mm->m_len;
+ }
+ if (clpoutbyte(count & 0xFF, LPMAXSPIN1, lpt_data_port, lpt_stat_port))
+ goto nend;
+ if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, lpt_data_port, lpt_stat_port))
+ goto nend;
+
+ /* Send dummy ethernet header */
+ for (i = 0; i < 12; i++) {
+ if (clpoutbyte(i, LPMAXSPIN1, lpt_data_port, lpt_stat_port))
+ goto nend;
+ chksum += i;
+ }
+
+ if (clpoutbyte(0x08, LPMAXSPIN1, lpt_data_port, lpt_stat_port))
+ goto nend;
+ if (clpoutbyte(0x00, LPMAXSPIN1, lpt_data_port, lpt_stat_port))
+ goto nend;
+ chksum += 0x08 + 0x00; /* Add into checksum */
+
+ mm = m;
+ do {
+ cp = mtod(mm, u_char *);
+ while (mm->m_len--) {
+ chksum += *cp;
+ if (clpoutbyte(*cp++, LPMAXSPIN2, lpt_data_port, lpt_stat_port))
+ goto nend;
+ }
+ } while ((mm = mm->m_next));
+
+ /* Send checksum */
+ if (clpoutbyte(chksum, LPMAXSPIN2, lpt_data_port, lpt_stat_port))
+ goto nend;
+
+ /* Go quiescent */
+ outb(lpt_data_port, 0);
+
+ err = 0; /* No errors */
+
+ nend:
+ if (err) { /* if we didn't timeout... */
+ ifp->if_oerrors++;
+ lprintf("X");
+ } else {
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_pkthdr.len;
+ }
+
+ m_freem(m);
+
+ if (!(inb(lpt_stat_port) & CLPIP_SHAKE)) {
+ lprintf("^");
+ lptintr(ifp->if_unit);
+ }
+ (void) splx(s);
+ return 0;
+ }
+
+ if (inb(lpt_stat_port) & LPIP_SHAKE) {
+ lprintf("&");
+ lptintr(ifp->if_unit);
+ }
+
+ if (lpoutbyte(0x08, LPMAXSPIN1, lpt_data_port, lpt_stat_port))
+ goto end;
+ if (lpoutbyte(0x00, LPMAXSPIN2, lpt_data_port, lpt_stat_port))
+ goto end;
+
+ mm = m;
+ do {
+ cp = mtod(mm,u_char *);
+ while (mm->m_len--)
+ if (lpoutbyte(*cp++, LPMAXSPIN2, lpt_data_port, lpt_stat_port))
+ goto end;
+ } while ((mm = mm->m_next));
+
+ err = 0; /* no errors were encountered */
+
+ end:
+ --cp;
+ outb(lpt_data_port, txmitl[*cp] ^ 0x17);
+
+ if (err) { /* if we didn't timeout... */
+ ifp->if_oerrors++;
+ lprintf("X");
+ } else {
+ ifp->if_opackets++;
+ ifp->if_obytes += m->m_pkthdr.len;
+#if NBPFILTER > 0
+ if (ifp->if_bpf) {
+ /*
+ * We need to prepend the packet type as
+ * a two byte field. Cons up a dummy header
+ * to pacify bpf. This is safe because bpf
+ * will only read from the mbuf (i.e., it won't
+ * try to free it or keep a pointer to it).
+ */
+ struct mbuf m0;
+ u_short hdr = 0x800;
+
+ m0.m_next = m;
+ m0.m_len = 2;
+ m0.m_data = (char *)&hdr;
+
+ bpf_mtap(ifp, &m0);
+ }
+#endif
+ }
+
+ m_freem(m);
+
+ if (inb(lpt_stat_port) & LPIP_SHAKE) {
+ lprintf("^");
+ lptintr(ifp->if_unit);
+ }
+
+ (void) splx(s);
+ return 0;
+}
+
+#endif /* INET */
+
+static lpt_devsw_installed = 0;
+
+static void lpt_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! lpt_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev,&lpt_cdevsw, NULL);
+ lpt_devsw_installed = 1;
+ }
+}
+
+SYSINIT(lptdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,lpt_drvinit,NULL)
+
diff --git a/sys/pc98/pc98/lptreg.h b/sys/pc98/pc98/lptreg.h
new file mode 100644
index 0000000..7c3693f
--- /dev/null
+++ b/sys/pc98/pc98/lptreg.h
@@ -0,0 +1,59 @@
+/*-
+ * 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.
+ *
+ * form: @(#)lptreg.h 1.1 (Berkeley) 12/19/90
+ * $Id: lptreg.h,v 1.2 1993/10/16 13:46:12 rgrimes Exp $
+ */
+
+/*
+ * AT Parallel Port (for lineprinter)
+ * Interface port and bit definitions
+ * Written by William Jolitz 12/18/90
+ * Copyright (C) William Jolitz 1990
+ */
+#if 0
+static char rcsid[] = "$Id: lptreg.h,v 1.1 1992/11/01 20:09:14 ukai Exp $";
+#endif
+/*
+ * modified for PC9801 by A.Kojima
+ * Kyoto University Microcomputer Club (KMC)
+ */
+
+#ifdef PC98
+#define lpt_pstb_ctrl (-9) /* PSTB enable control */
+#define LPC_EN_PSTB 0xc /* PSTB enable */
+#define LPC_DIS_PSTB 0xd /* PSTB disable */
+
+#define lpt_data 0 /* Data to/from printer (R/W) */
+
+#define lpt_status 2 /* Status of printer (R) */
+#define LPS_NBSY 0x4 /* printer no ack of data */
+
+#define lpt_control 6 /* Control printer (W) */
+#define LPC_MODE8255 0x82 /* 8255 mode */
+#define LPC_IRQ8 0x6 /* IRQ8 active */
+#define LPC_NIRQ8 0x7 /* IRQ8 inactive */
+#define LPC_PSTB 0xe /* PSTB active */
+#define LPC_NPSTB 0xf /* PSTB inactive */
+
+#else /* IBM-PC */
+#define lpt_data 0 /* Data to/from printer (R/W) */
+
+#define lpt_status 1 /* Status of printer (R) */
+#define LPS_NERR 0x08 /* printer no error */
+#define LPS_SEL 0x10 /* printer selected */
+#define LPS_OUT 0x20 /* printer out of paper */
+#define LPS_NACK 0x40 /* printer no ack of data */
+#define LPS_NBSY 0x80 /* printer no ack of data */
+
+#define lpt_control 2 /* Control printer (R/W) */
+#define LPC_STB 0x01 /* strobe data to printer */
+#define LPC_AUTOL 0x02 /* automatic linefeed */
+#define LPC_NINIT 0x04 /* initialize printer */
+#define LPC_SEL 0x08 /* printer selected */
+#endif
+#define LPC_ENA 0x10 /* printer out of paper */
diff --git a/sys/pc98/pc98/matcd/TODO b/sys/pc98/pc98/matcd/TODO
new file mode 100644
index 0000000..138f470
--- /dev/null
+++ b/sys/pc98/pc98/matcd/TODO
@@ -0,0 +1,42 @@
+Things to do for the matcd driver 4-Jul-95
+
+1. Someone wants to switch all drivers from disklabel and
+ its assorted mechanisms over to disk slicing and its mechanisms,
+ but I was unable to find any useful documentation on how to
+ implement the changes for a read-only, single-partition,
+ removable (ie, partition can change size) device.
+ So this will have to wait until after 2.1.
+
+2. Support for reading R-W subcodes while playing audio. This would be
+ useful if you have any CD+G or CD+MIDI discs, but the demand for this
+ is pretty low, unless you like Karaoke. Someone will also have to
+ write a CD+G viewer for X. The code for the driver to add this is
+ pretty minor but there aren't any precedents on how to handle the
+ data transfer to the application.
+
+3. Support for reading the ISBN and UPC labels. The ioctl structures
+ for these appear to be defined but no other driver seems to do this.
+
+4. Multi-session support. There are two forms of this; what
+ Philips defined and what Kodak uses. This will be quite
+ complicated and will probably require changes in the filesystem
+ layer. The drive support for Kodak multi-session is known to work.
+
+5. Multiple data tracks. My vision here was to add an ioctl
+ that caused a track offset to be inserted into block requests,
+ effectively shifting the base to the specified track. Very
+ easy to add but not a big deal since I have only two discs
+ in my collection that have multiple data tracks and I mastered
+ one of them.
+
+6. A curses-based CD-Player app (ie, not X). I will probably do this
+ mainly for its value as a debugging tool. It was pretty annoying
+ not finding a single application that actually issued all the
+ defined ioctls, let alone any new ones.
+
+If you feel the urge to work on one or more of these remaining items,
+please contact the author first at bsdmail@nemesis.lonestar.org
+to make sure the work hasn't already been done or started.
+
+ Frank Durda IV
+
diff --git a/sys/pc98/pc98/matcd/audio.c b/sys/pc98/pc98/matcd/audio.c
new file mode 100644
index 0000000..b8cf6bf
--- /dev/null
+++ b/sys/pc98/pc98/matcd/audio.c
@@ -0,0 +1,514 @@
+/*audio.c--------------------------------------------------------------------
+
+ Matsushita(Panasonic) / Creative CD-ROM Driver (matcd)
+ Authored by Frank Durda IV
+
+ Copyright 1994, 1995 Frank Durda IV. All rights reserved.
+ "FDIV" is a trademark of Frank Durda IV.
+
+
+ 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 positioned at the very beginning of this file without
+ modification, all copyright strings, all related programming
+ codes that display the copyright strings, this list of
+ conditions and the following disclaimer.
+ 2. Redistributions in binary form must contain all copyright strings
+ and related programming code that display the copyright strings.
+ 3. 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.
+ 4. All advertising materials mentioning features or use of this
+ software must display the following acknowledgement:
+ "The Matsushita/Panasonic CD-ROM driver was developed
+ by Frank Durda IV for use with "FreeBSD" and similar
+ operating systems."
+ "Similar operating systems" includes mainly non-profit oriented
+ systems for research and education, including but not restricted
+ to "NetBSD", "386BSD", and "Mach" (by CMU). The wording of the
+ acknowledgement (in electronic form or printed text) may not be
+ changed without permission from the author.
+ 5. Absolutely no warranty of function, fitness or purpose is made
+ by the author Frank Durda IV.
+ 6. Neither the name of the author nor the name "FreeBSD" may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ (The author can be reached at bsdmail@nemesis.lonestar.org)
+ 7. The product containing this software must meet all of these
+ conditions even if it is unsupported, not a complete system
+ and/or does not contain compiled code.
+ 8. These conditions will be in force for the full life of the
+ copyright.
+ 9. If all the above conditions are met, modifications to other
+ parts of this file may be freely made, although any person
+ or persons making changes do not receive the right to add their
+ name or names to the copyright strings and notices in this
+ software. Persons making changes are encouraged to insert edit
+ history in matcd.c and to put your name and details of the
+ change there.
+ 10. You must have prior written permission from the author to
+ deviate from these terms.
+
+ Vendors who produce product(s) containing this code are encouraged
+ (but not required) to provide copies of the finished product(s) to
+ the author and to correspond with the author about development
+ activity relating to this code. Donations of development hardware
+ and/or software are also welcome. (This is one of the faster ways
+ to get a driver developed for a device.)
+
+ THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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 DEVELOPER(S) 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.
+
+
+-----No changes are allowed above this line------------------------------------
+
+ The following functions are related to the audio playback
+ capabilities of the drive. They can be omitted from the
+ finished driver using the FULLDRIVER conditional.
+
+ The full set of features the drive is capable of are currently
+ not implemented but will be added in upcoming releases.
+-----------------------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------
+ matcd_playtracks - Plays one or more audio tracks
+-----------------------------------------------------------------------------*/
+
+static int matcd_playtracks(int ldrive, int cdrive, int controller,
+ struct ioc_play_track *pt)
+{
+ struct matcd_data *cd;
+ int start,end;
+ int i,port;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+ if ((cd->flags & MATCDLABEL)==0)
+ return(EIO); /*Refuse after chg error*/
+
+ start=pt->start_track;
+ end=pt->end_track;
+
+ if (start < 1 || /*Starting track valid?*/
+ end < 1 || /*Ending track valid?*/
+ start > end || /*Start higher than end?*/
+ end > cd->volinfo.trk_high) /*End track higher than disc size?*/
+ return(ESPIPE); /*Track out of range*/
+
+ lockbus(controller, ldrive); /*<16>Request bus*/
+ i=matcd_setmode(ldrive, MODE_DA);/*Force drive into audio mode*/
+ unlockbus(controller, ldrive); /*<16>Release bus*/
+ if (i!=0) {
+ return(i); /*Not legal for this media?*/
+ }
+ zero_cmd(cmd);
+ cmd[0]=PLAYTRKS; /*Play Audio Track/Index*/
+ cmd[1]=start;
+ cmd[2]=pt->start_index;
+ cmd[3]=end;
+ cmd[4]=pt->end_index;
+ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/
+#ifdef DEBUGIOCTL
+ printf("matcd%d: Play track results %d \n",ldrive,i);
+#endif /*DEBUGIOCTL*/
+ if (i==0) cd->status=CD_AS_PLAY_IN_PROGRESS; /*<14>*/
+ return(i);
+}
+
+
+/*-----------------------------------------------------------------------------
+ matcd_playmsf - Plays between a range of blocks
+-----------------------------------------------------------------------------*/
+
+static int matcd_playmsf(int ldrive, int cdrive, int controller,
+ struct ioc_play_msf *pt)
+{
+ struct matcd_data *cd;
+ int i,port;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+#ifdef DEBUGIOCTL
+ printf("matcd%d: playmsf %2x %2x %2x -> %2x %2x %2x\n",
+ ldrive,pt->start_m, pt->start_s, pt->start_f, pt->end_m,
+ pt->end_s,pt->end_f);
+#endif /*DEBUGIOCTL*/
+
+ if ((cd->flags & MATCDLABEL)==0)
+ return(EIO); /*Refuse after chg error*/
+
+ if ((cd->volinfo.vol_msf[0]==0 &&
+ cd->volinfo.vol_msf[1]<2) || /*Must be after 0'1"75F*/
+ msf_to_blk((char *)&pt->start_m) >
+ msf_to_blk((char *)&cd->volinfo.vol_msf)) {
+#ifdef DEBUGIOCTL
+ printf("matcd%d: Invalid block combination\n",ldrive);
+#endif /*DEBUGIOCTL*/
+ return(ESPIPE); /*Track out of range*/
+ }
+
+
+ lockbus(controller, ldrive); /*<16>Request bus*/
+ i=matcd_setmode(ldrive, MODE_DA);/*Force drive into audio mode*/
+ unlockbus(controller, ldrive); /*<16>Release bus*/
+ if (i!=0) {
+ return(i); /*Not legal for this media?*/
+ }
+ zero_cmd(cmd);
+ cmd[0]=PLAYBLOCKS; /*Play Audio Blocks*/
+ cmd[1]=pt->start_m;
+ cmd[2]=pt->start_s;
+ cmd[3]=pt->start_f;
+ cmd[4]=pt->end_m;
+ cmd[5]=pt->end_s;
+ cmd[6]=pt->end_f;
+ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/
+ if (i==0) cd->status=CD_AS_PLAY_IN_PROGRESS; /*<14>*/
+ return(i);
+}
+
+
+/*-----------------------------------------------------------------------------
+ matcd_pause - Pause or Resume audio playback
+-----------------------------------------------------------------------------*/
+
+static int matcd_pause(int ldrive, int cdrive, int controller, int action)
+{
+ struct matcd_data *cd;
+ int i,z,port;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+ if ((cd->flags & MATCDLABEL)==0)
+ return(EIO); /*Refuse after chg error*/
+
+ zero_cmd(cmd);
+ cmd[0]=NOP; /*<14>Just find out whats going on*/
+
+ lockbus(controller, ldrive); /*<16>Request bus*/
+ matcd_slowcmd(port,ldrive,cdrive,cmd); /*<14>*/
+ i=waitforit(10*TICKRES,DTEN,port,"matpau"); /*<25>*/
+ z=get_stat(port,ldrive); /*<14>Read status byte*/
+ if ((z & MATCD_ST_ERROR)) { /*<14>Something went wrong*/
+ i=get_error(port, ldrive, cdrive); /*<14>*/
+ unlockbus(controller, ldrive); /*<16>Release bus*/
+ return(EIO); /*<14>*/
+ } /*<14>*/
+ unlockbus(controller, ldrive); /*<16>Release bus*/
+
+ if (z & MATCD_ST_AUDIOBSY==0 && /*<14>If drive is idle*/
+ cd->status==CD_AS_PLAY_IN_PROGRESS) { /*<14>but was playing*/
+ cd->status=CD_AS_PLAY_COMPLETED; /*<14>then its done*/
+ return(0);
+ }
+
+ if (action) { /*<14>Set state for subq ioctl*/
+#ifndef KRYTEN
+ if (cd->status==CD_AS_PLAY_IN_PROGRESS) {/*<14>Don't resume*/
+ return(0); /*<14>if already playing*/
+ } /*<14>Max Headroom sound occurs*/
+#endif /*KRYTEN*/
+ cd->status=CD_AS_PLAY_IN_PROGRESS; /*<14>to read*/
+ } else { /*<14>There is no way to ask the*/
+ cd->status=CD_AS_PLAY_PAUSED;/*<14>drive if it is paused*/
+ } /*<14>*/
+
+ cmd[0]=PAUSE; /*Pause or Resume playing audio*/
+ cmd[1]=action;
+ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/
+#ifdef DEBUGIOCTL
+ printf("matcd%d: Pause / Resume results %d \n",ldrive,i);
+#endif /*DEBUGIOCTL*/
+ return(i);
+}
+
+
+
+/*-----------------------------------------------------------------------------
+ matcd_stop - Stop audio playback
+-----------------------------------------------------------------------------*/
+
+static int matcd_stop(int ldrive, int cdrive, int controller)
+{
+ struct matcd_data *cd;
+ int i,port;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+ if ((cd->flags & MATCDLABEL)==0)
+ return(EIO); /*Refuse after chg error*/
+
+ zero_cmd(cmd);
+ cmd[0]=ABORT; /*Abort playing audio*/
+ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/
+#ifdef DEBUGIOCTL
+ printf("matcd%d: Abort results %d \n",ldrive,i);
+#endif /*DEBUGIOCTL*/
+ cd->status=CD_AS_PLAY_COMPLETED;/*<14>the drive if it is paused*/
+ return(i);
+}
+
+
+/*-----------------------------------------------------------------------------
+ matcd_level - Read or set the audio levels
+<12> New for Edit 12
+-----------------------------------------------------------------------------*/
+
+static int matcd_level(int ldrive, int cdrive, int controller,
+ struct ioc_vol * level, int action)
+{
+ struct matcd_data *cd;
+ int i,z,port;
+ unsigned char c;
+ unsigned char cmd[MAXCMDSIZ];
+ unsigned char data[5];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+ zero_cmd(cmd);
+ if (action==CDIOCSETVOL) { /*We are setting new volume settings*/
+
+/* Here we set the volume levels. Note that the same command
+ also sets the patching (routing) of audio, so we have to rely
+ on previously-stored settings to fill in these fields.
+*/
+ cmd[0]=MODESELECT; /*Write drive settings*/
+ cmd[1]=AUDIOPARM; /*Audio/routing settings*/
+
+/* Although the drive allows a left and right channel volume to be
+ specified separately, the drive refuses the settings if the
+ values are different.
+*/
+ c=level->vol[0] | level->vol[1]; /*Or them together*/
+
+ cmd[4]=cd->volume[0]=c; /*Channel 0 (Left) volume*/
+ cmd[6]=cd->volume[1]=c; /*Channel 1 (Right) volume*/
+ cmd[3]=cd->patch[0]; /*Channel 0 (Left) patching*/
+ cmd[5]=cd->patch[1]; /*Channel 1 (Right) patching*/
+ i=docmd(cmd,ldrive,cdrive,controller,port);/*Issue cmd*/
+#ifdef DEBUGIOCTL
+ printf("matcd%d: Volume set %d\n",ldrive,i);
+#endif /*DEBUGIOCTL*/
+ return(i);
+ } else { /*Read existing settings*/
+
+
+/* This code reads the settings for the drive back - note that
+ volume and patching are both returned so we have to keep
+ both internally.
+*/
+ cmd[0]=MODESENSE; /*Read drive settings*/
+ cmd[1]=AUDIOPARM; /*Audio/routing settings*/
+ lockbus(controller, ldrive); /*<16>Request bus*/
+ matcd_slowcmd(port,ldrive,cdrive,cmd);
+ i=waitforit(10*TICKRES,DTEN,port,"matlvl"); /*<25>*/
+ matcd_pread(port, 5, data); /*Read data returned*/
+ z=get_stat(port,ldrive);/*Read status byte*/
+ unlockbus(controller, ldrive); /*<16>Release bus*/
+#ifdef DEBUGIOCTL
+ printf("matcd%d: Data got was %x %x %x %x %x ",ldrive,
+ data[0],data[1],data[2], data[3],data[4]);
+ printf("status byte %x\n",z);
+#endif /*DEBUGIOCTL*/
+ cd->volume[0]=level->vol[0]= /*Channel 0 (Left) volume*/
+ data[2];
+ cd->volume[1]=level->vol[1]= /*Channel 1 (Right) volume*/
+ data[4];
+ level->vol[2]=level->vol[3]=0; /*Channel 2 & 3 not avail*/
+
+ cd->patch[0]=data[1]; /*Channel 0 (Left) patching*/
+ cd->patch[1]=data[3]; /*Channel 1 (Right) patching*/
+
+ return(0);
+ }
+
+}
+
+
+/*-----------------------------------------------------------------------------
+ matcd_routing - Set the audio routing (patching)
+<12> New for Edit 12
+-----------------------------------------------------------------------------*/
+
+static int matcd_route(int ldrive, int cdrive, int controller,
+ int command)
+{
+ struct matcd_data *cd;
+ int i,port;
+ unsigned char l,r;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+ zero_cmd(cmd);
+ switch (command) {
+ case CDIOCSETMUTE:
+ l=r=0;
+ break;
+
+ case CDIOCSETLEFT:
+ l=r=OUTLEFT;
+ break;
+
+ case CDIOCSETRIGHT:
+ l=r=OUTRIGHT;
+ break;
+
+ default:
+ case CDIOCSETSTEREO:
+ l=OUTLEFT;
+ r=OUTRIGHT;
+ break;
+ }
+
+/* Here we set the volume levels. Note that the same command
+ also sets the patching (routing) of audio, so we have to rely
+ on previously-stored settings to fill in these fields.
+*/
+ cmd[0]=MODESELECT; /*Write drive settings*/
+ cmd[1]=AUDIOPARM; /*Audio/routing settings*/
+
+
+/* Although the drive allows a left and right channel volume to be
+ specified separately, the drive refuses the settings if the
+ values are different.
+*/
+ cmd[4]=cd->volume[0]; /*Channel 0 (Left) volume*/
+ cmd[6]=cd->volume[1]; /*Channel 1 (Right) volume*/
+ cmd[3]=cd->patch[0]=l; /*Channel 0 (Left) patching*/
+ cmd[5]=cd->patch[1]=r; /*Channel 1 (Right) patching*/
+ i=docmd(cmd,ldrive,cdrive,controller,port);/*Issue cmd*/
+#ifdef DEBUGIOCTL
+ printf("matcd%d: Routing set %d\n",ldrive,i);
+#endif /*DEBUGIOCTL*/
+ return(i);
+
+}
+
+
+/*-----------------------------------------------------------------------------
+ matcd_patch - Set the audio routing (patching)
+<12> New for Edit 12
+-----------------------------------------------------------------------------*/
+
+static int matcd_patch(int ldrive, int cdrive, int controller,
+ struct ioc_patch * routing)
+{
+ struct matcd_data *cd;
+ int i,port;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+ zero_cmd(cmd);
+
+/* Here we set the volume levels. Note that the same command
+ also sets the patching (routing) of audio, so we have to rely
+ on previously-stored settings to fill in these fields.
+*/
+ cmd[0]=MODESELECT; /*Write drive settings*/
+ cmd[1]=AUDIOPARM; /*Audio/routing settings*/
+
+
+/* Although the drive allows a left and right channel volume to be
+ specified separately, the drive refuses the settings if the
+ values are different.
+*/
+ cmd[4]=cd->volume[0]; /*Channel 0 (Left) volume*/
+ cmd[6]=cd->volume[1]; /*Channel 1 (Right) volume*/
+ cmd[3]=cd->patch[0]= /*Channel 0 (Left) patching*/
+ (routing->patch[0] & 0x03);
+ cmd[5]=cd->patch[1]= /*Channel 1 (Right) patching*/
+ (routing->patch[1] & 0x03);
+ i=docmd(cmd,ldrive,cdrive,controller,port);/*Issue cmd*/
+#ifdef DEBUGIOCTL
+ printf("matcd%d: Routing set %d\n",ldrive,i);
+#endif /*DEBUGIOCTL*/
+ return(i);
+
+}
+
+/*-----------------------------------------------------------------------------
+ matcd_pitch - Change audio playback rate
+ Apart from making things sound funny, the only
+ other application might be Karaoke. Ugh.
+<12> New for Edit 12
+-----------------------------------------------------------------------------*/
+
+static int matcd_pitch(int ldrive, int cdrive, int controller,
+ struct ioc_pitch * speed)
+{
+ struct matcd_data *cd;
+ short i;
+ int z,port;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+ zero_cmd(cmd);
+
+/* This function sets the audio playback rate. In SCSI devices this is
+ referred to as the logical block addresses per second parameter.
+ Uh huh. Sounds like they didn't want anyone to find it.
+ Anyway, a study found that no one else has implemented this ioctl
+ but the capability does exist in the SCSI standard so I am following
+ the SCSI scheme even though it really doesn't fit this drive well.
+
+ I define the parameter to this ioctl as -32767 to -1 being
+ "play slower", 0x0000 flat and 1 to 32767 being "play faster"
+ within the scale allowed by the device. The value is scaled to fit
+ the allowed by the device and any excess is treated as being
+ the positive or negative limit. No ioctl input value is considered
+ invalid.
+
+ This device has a +/- 13% playback pitch specified by a range
+ -130 to +130. The drive does a hard enforcement on this.
+
+ SCSI defines a 16 bit LBAS count, and a "multiplier" that
+ is either x1 or x(1/256). The Matsushita drive only provides
+ 10 bits total for indicating pitch so the LSbits are discarded.
+*/
+
+ cmd[0]=MODESELECT; /*Write drive settings*/
+ cmd[1]=SPEEDPARM; /*Audio speed settings*/
+
+ i=speed->speed>>7; /*Scale down to our usable range*/
+
+ if (i!=0) { /*Real pitch value*/
+ if (i < -130) i=-130; /*Force into range we support*/
+ else if (i > 130) i=130;
+ cmd[3]=((i>>8)&0x03) | 0x04; /*Get upper bits*/
+ cmd[4]=(i & 0xff); /*Set lower bits*/
+ }
+ z=docmd(cmd,ldrive,cdrive,controller,port);/*Issue cmd*/
+#ifdef DEBUGIOCTL
+ printf("matcd%d: Pitch set %d\n",ldrive,i);
+#endif /*DEBUGIOCTL*/
+ return(z);
+}
+
+/*End of audio.c*/
+
diff --git a/sys/pc98/pc98/matcd/creative.h b/sys/pc98/pc98/matcd/creative.h
new file mode 100644
index 0000000..72a12ad
--- /dev/null
+++ b/sys/pc98/pc98/matcd/creative.h
@@ -0,0 +1,142 @@
+/*creative.h-------------------------------------------------------------------
+
+ Matsushita(Panasonic) / Creative CD-ROM Driver (matcd)
+ Authored by Frank Durda IV
+
+ Copyright 1994, 1995 Frank Durda IV. All rights reserved.
+ "FDIV" is a trademark of Frank Durda IV.
+
+
+ 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 positioned at the very beginning of this file without
+ modification, all copyright strings, all related programming
+ codes that display the copyright strings, this list of
+ conditions and the following disclaimer.
+ 2. Redistributions in binary form must contain all copyright strings
+ and related programming code that display the copyright strings.
+ 3. 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.
+ 4. All advertising materials mentioning features or use of this
+ software must display the following acknowledgement:
+ "The Matsushita/Panasonic CD-ROM driver was developed
+ by Frank Durda IV for use with "FreeBSD" and similar
+ operating systems."
+ "Similar operating systems" includes mainly non-profit oriented
+ systems for research and education, including but not restricted
+ to "NetBSD", "386BSD", and "Mach" (by CMU). The wording of the
+ acknowledgement (in electronic form or printed text) may not be
+ changed without permission from the author.
+ 5. Absolutely no warranty of function, fitness or purpose is made
+ by the author Frank Durda IV.
+ 6. Neither the name of the author nor the name "FreeBSD" may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ (The author can be reached at bsdmail@nemesis.lonestar.org)
+ 7. The product containing this software must meet all of these
+ conditions even if it is unsupported, not a complete system
+ and/or does not contain compiled code.
+ 8. These conditions will be in force for the full life of the
+ copyright.
+ 9. If all the above conditions are met, modifications to other
+ parts of this file may be freely made, although any person
+ or persons making changes do not receive the right to add their
+ name or names to the copyright strings and notices in this
+ software. Persons making changes are encouraged to insert edit
+ history in matcd.c and to put your name and details of the
+ change there.
+ 10. You must have prior written permission from the author to
+ deviate from these terms.
+
+ Vendors who produce product(s) containing this code are encouraged
+ (but not required) to provide copies of the finished product(s) to
+ the author and to correspond with the author about development
+ activity relating to this code. Donations of development hardware
+ and/or software are also welcome. (This is one of the faster ways
+ to get a driver developed for a device.)
+
+ THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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 DEVELOPER(S) 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.
+
+
+-----No changes are allowed above this line------------------------------------
+
+See matcd.c for Edit History
+
+ These are the I/O port mapping offsets and bit assignments used
+ by Creative Labs in their implementation of the host interface for
+ the Matsushita CD-ROM drive. These may be different in the adapter
+ cards (including sound cards) made by other vendors.
+ It is unknown if the Creative interface is based on a reference design
+ provided by Matsushita (other interface vendors would similar or
+ identical if this was the case).
+
+ The drive is actually capable of some things that the Creative
+ interface doesn't implement, such as DMA and interrupts.
+
+ See matcd.h for defines related to the Matsushita drive itself.
+*/
+
+
+/* Creative Labs (and compatible) I/O port mapping offsets
+*/
+
+#define NUMPORTS 4 /*Four ports are decoded by the i/f*/
+
+#define CMD 0 /*Write - commands*/
+#define DATA 0 /*Read - data/status from drive*/
+#ifdef PC98
+#define PHASE 0x100 /*Write - switch between data/status*/
+#define STATUS 0x100 /*Read - bus status */
+#define RESET 0x200 /*Write - reset all attached drives*/
+#define ALTDATA 0x200 /*<20>Read - data on non Creative bds.*/
+#define SELECT 0x300 /*Write - drive select*/
+#else /* !PC98 */
+#define PHASE 1 /*Write - switch between data/status*/
+#define STATUS 1 /*Read - bus status*/
+#define RESET 2 /*Write - reset all attached drives*/
+ /*Any value written will reset*/
+#define ALTDATA 2 /*<20>Read - data on non Creative bds.*/
+#define SELECT 3 /*Write - drive select*/
+#endif /*PC98*/
+
+/* Creative PHASE port bit assignments
+*/
+
+#define PHASENA 1 /*Access data bytes instead of status*/
+
+
+/* Creative STATUS port register bits
+*/
+
+#define DTEN 2 /*When low, in data xfer phase*/
+#define STEN 4 /*When low, in status phase*/
+#define TEST 1 /*Function is unknown*/
+
+
+/* Creative drive SELECT port bit assignments
+ Note that in the Creative interface, DS0==Bit 1 and
+ DS1==Bit 0 (DS is Drive Select).
+*/
+
+#define CRDRIVE0 0x00
+#define CRDRIVE1 0x02
+#define CRDRIVE2 0x01
+#define CRDRIVE3 0x03
+
+/*End of creative.h*/
+
+
diff --git a/sys/pc98/pc98/matcd/matcd.c b/sys/pc98/pc98/matcd/matcd.c
new file mode 100644
index 0000000..20a968b
--- /dev/null
+++ b/sys/pc98/pc98/matcd/matcd.c
@@ -0,0 +1,2748 @@
+/*matcd.c--------------------------------------------------------------------
+
+ Matsushita(Panasonic) / Creative CD-ROM Driver (matcd)
+ Authored by Frank Durda IV
+
+ Copyright 1994, 1995 Frank Durda IV. All rights reserved.
+ "FDIV" is a trademark of Frank Durda IV.
+
+
+ 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 positioned at the very beginning of this file without
+ modification, all copyright strings, all related programming
+ codes that display the copyright strings, this list of
+ conditions and the following disclaimer.
+ 2. Redistributions in binary form must contain all copyright strings
+ and related programming code that display the copyright strings.
+ 3. 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.
+ 4. All advertising materials mentioning features or use of this
+ software must display the following acknowledgement:
+ "The Matsushita/Panasonic CD-ROM driver was developed
+ by Frank Durda IV for use with "FreeBSD" and similar
+ operating systems."
+ "Similar operating systems" includes mainly non-profit oriented
+ systems for research and education, including but not restricted
+ to "NetBSD", "386BSD", and "Mach" (by CMU). The wording of the
+ acknowledgement (in electronic form or printed text) may not be
+ changed without permission from the author.
+ 5. Absolutely no warranty of function, fitness or purpose is made
+ by the author Frank Durda IV.
+ 6. Neither the name of the author nor the name "FreeBSD" may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ (The author can be reached at bsdmail@nemesis.lonestar.org)
+ 7. The product containing this software must meet all of these
+ conditions even if it is unsupported, not a complete system
+ and/or does not contain compiled code.
+ 8. These conditions will be in force for the full life of the
+ copyright.
+ 9. If all the above conditions are met, modifications to other
+ parts of this file may be freely made, although any person
+ or persons making changes do not receive the right to add their
+ name or names to the copyright strings and notices in this
+ software. Persons making changes are encouraged to insert edit
+ history in matcd.c and to put your name and details of the
+ change there.
+ 10. You must have prior written permission from the author to
+ deviate from these terms.
+
+ Vendors who produce product(s) containing this code are encouraged
+ (but not required) to provide copies of the finished product(s) to
+ the author and to correspond with the author about development
+ activity relating to this code. Donations of development hardware
+ and/or software are also welcome. (This is one of the faster ways
+ to get a driver developed for a device.)
+
+ THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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 DEVELOPER(S) 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.
+
+
+-----------------------------------------------------------------------------
+Dedicated to: My family, my Grandfather,
+ and Max, my Golden Retriever
+
+Thanks to: Jordon Hubbard (jkh) for getting me ramped-up to 2.x system
+ quickly enough to make the 2.1 release. He put up with
+ plenty of silly questions and might get the post of
+ ambassador some day.
+
+and The people who donated equipment and other material to make
+ development of this driver possible. Donations and
+ sponsors for projects are appreciated.
+
+
+-----No changes are allowed above this line------------------------------------
+
+Edit History - (should be in sync with any source control log entries)
+
+ Never seen one of these before? Ok, here is how it works.
+ Every time you change the code, you increment the edit number,
+ that number over there in the <%d> and in the (%d) in the
+ version string. You never set this number lower than it is.
+ Near, or preferably on lines that change, insert the edit
+ number. If there is a number there already, you can replace it
+ with a newer one. This makes searches for code changes very fast.
+
+ In the edit history, start with the edit number, and a good
+ description of what changes were made. Then follow it with
+ the date, your name and an EMAIL address where you can be reached.
+
+ Please follow this practice; it helps leave understandable code in
+ your wake.
+
+ FYI, you have major and minor release codes. Major releases numbered
+ 1 thru n. Major feature additions should get a new major release
+ number. Minor releases start with a null and then letters
+ A thru Z. So 3A(456) is Major release 3, Minor release 1,
+ Edit 456 (in Microsoft-ese that would be 03.01.456), and 5(731)
+ is Major release 5, Minor release 0, Edit 731. Typically only the
+ author will change the major and minor release codes in small
+ projects.
+
+ EDIT edit Edit HISTORY history History
+
+<1> This initial version is to get basic filesystem I/O working
+ using the SoundBlaster 16 interface. The stand-alone adapter
+ card doesn't work yet.
+ December 1994 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<2> Corrections to resolve a race condition when multiple drives
+ on the same controller was active. Fixed drive 1 & 2 swap
+ problem. See selectdrive().
+ 21-Jan-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<3> Added automatic probing and support for all Creative Labs sound
+ cards with the Creative/Panasonic interface and the stand-alone
+ interface adapters. See AUTOHUNT and FULLCONFIG conditionals
+ for more information.
+ 21-Jan-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<4> Rebundled debug conditionals.
+ 14-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<5> Changes needed to work on FreeBSD 2.1. Also added draincmd
+ since some conditions cause the drive to produce surprise data.
+ See setmode and draincmd
+ 19-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<6> Got rid of some redundant error code by creating chk_error().
+ Also built a nice generic bus-lock function.
+ 20-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<7> Improved comments, general structuring.
+ Fixed a problem with disc eject not working if LOCKDRIVE was set.
+ Apparently the drive will reject an EJECT command if the drive
+ is LOCKED.
+ 21-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+Edit number code marking begins here - earlier edits were during development.
+
+<8> Final device name selected and actually made to compile under
+ >2.0. For newer systems, it is "matcd", for older it is "mat".
+ 24-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<9> Added some additional disk-related ioctl functions that didn't
+ make it into earlier versions.
+ 26-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<10> Updated some conditionals so the code will compile under
+ 1.1.5.1, although this is not the supported platform.
+ Also found that some other devices probe code was changing the
+ settings for the port 0x302 debug board, so added code to set it
+ to a sane state before we use it.
+ 26-Feb-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<11> The Copyright and Use statement has been replaced in all files
+ with a new version.
+ 1-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<12> Added ioctls having to do with audio volume, routing and playback
+ speed. Also added some code I think is for dynamic loading.
+ 12-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<13> Added ioctls to return TOC headers and entries.
+ 19-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<14> More ioctls to finish out general audio support and some clean-up.
+ Also fixed a bug in open where CD label information would not
+ always be cleared after a disc change.
+
+ Added a check to block attempts to resume audio if already playing.
+ The resulting sound is a cross between Kryten and Max Headroom.
+ But, if you *want* this "feature", enable #define KRYTEN
+ in options.h.
+
+ So it is not BSD-ish enough, eh? What, too many comments? :-)
+ 21-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<15> LOCKDRIVE has been modified so that a new series of minor
+ numbers are created. When these are opened, the selected
+ drive will have its door locked and the device must be completely
+ closed to unlock the media. The EJECT ioctl will be refused
+ when the drive is locked this way. This is useful for
+ servers and other places where the media needs to remain in the
+ drive. Bit 7 of the minor number controls locking.
+
+ As of this edit, the code compiles with no warnings with -Wall set.
+ 22-Mar-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<16> Added a new check in the probe code that looks for the drive
+ interface being in an idle state after issuing a reset. If this
+ isn't the case, then the device at this location isn't a
+ Matsushita CD-ROM drive. This will prevent hangs in draincmd later.
+ Added the tray close ioctl. This required modifications to open
+ to allow the character devices to be "partially" opened so that
+ the close ioctl could be issued when the open would otherwise fail.
+ Close also delays slightly after completing because the drive
+ doesn't update its disc and media status instantly.
+ Also created the capability ioctl that lets an application find out
+ up front what things a drive can do.
+ Fixed a global spelling error.
+ Changed matcddriver structure to simply say "matcd". The original
+ string "matcd interface " broke the kernel -c boot mechanism.
+ Updated the #includes in response to a complaint in first release.
+ Updated and tested conditionals so that driver will still compile
+ under FreeBSD 1.1.5.1 as well as 2.0 and early 2.1.
+ 4-Apr-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<17> The function matcd_toc_entries which is executed in response to
+ the CDIOREADTOCENTRYS ioctl didn't cope with programs that only
+ requested part of the TOC. This change is based on code submitted
+ by Doug Robson (dfr@render.com).
+ (This change was introduced out of order and exists in FreeBSD
+ 2.0.5 without the version stamp being updated. I.N.M.F.)
+ 1-Jun-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<18> While working on the TEAC CD-ROM driver (teaccd) that is reusing
+ chunks of code from this driver, I discovered several functions,
+ arrays and other things that should have been declared 'static'.
+ These changes are necessary if the TEAC CD-ROM driver is to be
+ present at the same time as matcd.
+ Also fixed the residual buss vs bus symbols and strings.
+ There are no functional code changes in this edit.
+ 2-May-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<19> Creative has changed the Status port slightly in their
+ sound boards based on the Vibra-16 (and probably the Vibra-16S)
+ chipset. This change masks some unused bits that were formally
+ on all the time and are doing different things in this design.
+ The changes are transparent to all other supported boards.
+ 20-Jun-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<20> Code was added to detect non-Creative (SoundBlaster) host
+ interfaces, and the driver will switch to code compatible with the
+ detected host interface. This should add support for MediaVision,
+ IBM, Reveal, and other compatible adapters with split
+ data/status-ports. This code allows a mix of SoundBlaster (Type 0)
+ and non-SoundBlaster (Type 1) boards in the same system with no
+ special configuration.
+
+ I also updated the attach code to display the interface type and
+ changed the host interface probe messages to reflect the "c" for
+ controller in controller-specific messages as the existing messages
+ were confusing when a second card was in place . The kernel -c
+ tables have been updated accordingly, so you now have a matcdc%d
+ controller to change settings on.
+ 24-Jun-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<21> Added interface handling code in two of those "this should not
+ happen" routines, draincmd and get_stat. Since these routines are
+ called by functions during probing that may not know what type
+ interface is out there, the code assumes that a given adapter is
+ both a type 0 and a type 1 adapter at the same time. Plus,
+ this code gets executed once in a very long time so the cost of
+ assuming both host adapter types is not significant.
+ 4-Jul-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<22> Four external interface prototypes were altered by someone else.
+ I believe these changes are for making GCC and/or the linker shut-up
+ when building some other part of the system since matcd already
+ compiles -Wall with no warnings...
+ 8-Sep-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<23> This change implements the ioctls for preventing media removal
+ and allowing media removal.
+ Currently, these calls will work according to the following rules:
+ No "l" devs opened Any "l" dev open
+ CDALLOW accepted always rejected always
+ CDPREVENT accepted always accepted always
+
+ One refinement might be to allow CDALLOW/CDPREVENT to always
+ work if UID 0 issued the ioctl, but that will wait for later.
+
+ I also made a change to the information that the toc_entry code
+ returns so that xcdplayer won't malfunction. (It would not play
+ the last track on a non-mixed mode audio CD.) Unlike cdplayer,
+ xcdplayer asks for track information one track at a time, and
+ calls for information on the lead-out track by its official
+ number (0xaa), rather than referring to the "after last" (n+1) track
+ as cdplayer does. Anyway, this change should make both players
+ happy.
+ 16-Sep-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<24> In Edit 15 when the extra devs were created for selective locking,
+ the door locking was broken if a non-locking dev on the drive is
+ closed. The problem was caused by not tracking locked devs and
+ non-locking devs as being different partitions. The change is to
+ simply use the locking dev bit to flag a set of shadow partitions
+ when it comes to lock operations. All other operations treat the
+ locked and unlocked partitions as being identical.
+ 18-Sep-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<25> During work on Edit 23, I noted that on slow and very busy systems,
+ sometimes the driver would go to sleep forever. The problem appears
+ to have been a race condition caused by doing separate timeout/sleep
+ calls without using SPL first. The change here is to use tsleep
+ which provides the equivalent of timeout/sleep timeout/tsleep if the
+ last paremeter is tsleep is set to the time value that would have been
+ given to timeout.
+ I also fixed some duplicate location strings in the tsleep calls.
+ 24-Sep-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<26> Moved a function declaration that generated two warnings with
+ the FULLCONFIG/FULLDRIVER conditionals disabled.
+ Updated the igot function so that it correctly reports limited
+ functions when a sub-set driver is compiled.
+ Eliminated FULLCONFIG conditional and now set controller counts
+ based on the NMATCD #define produced by the config process.
+ Also, disable the audio-related ioctls based on the BOOTMFS
+ conditional to help make the boot floppy kernel smaller.
+ 18-Oct-95 Frank Durda IV bsdmail@nemesis.lonestar.org
+
+<27> Incorporated changes needed to move the cdevsw and bdevsw
+ entries into the drivers (including this one). Also
+ include a quick first pass cut at DEVFS suppport.
+
+---------------------------------------------------------------------------*/
+
+/*Match this format: Version_dc(d)__dd-mmm-yy */
+static char MATCDVERSION[]="Version 1(26) 18-Oct-95";
+
+/* The following strings may not be changed*/
+static char MATCDCOPYRIGHT[] = "Matsushita CD-ROM driver, Copr. 1994,1995 Frank Durda IV";
+/* The proceeding strings may not be changed*/
+
+/* $Id: matcd.c,v 1.18 1996/06/08 09:17:51 bde Exp $ */
+
+/*---------------------------------------------------------------------------
+ Include declarations
+---------------------------------------------------------------------------*/
+
+#include "matcd.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <sys/buf.h>
+#include <sys/dkbad.h>
+#include <sys/cdio.h>
+#include <sys/disklabel.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+
+#ifdef PC98
+#include "pc98/pc98/matcd/options.h" /*Conditional compile options
+ and probe port hints*/
+#include "pc98/pc98/matcd/matcddrv.h" /*Drive-related defs & strings*/
+#include "pc98/pc98/matcd/creative.h" /*Host interface related defs*/
+#else
+#include "i386/isa/matcd/options.h" /*Conditional compile options
+ and probe port hints*/
+#include "i386/isa/matcd/matcddrv.h" /*Drive-related defs & strings*/
+#include "i386/isa/matcd/creative.h" /*Host interface related defs*/
+#endif
+
+#include <sys/devconf.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /*DEVFS*/
+
+/*---------------------------------------------------------------------------
+ Defines and structures
+---------------------------------------------------------------------------*/
+
+#define DRIVESPERC 4 /*This is a constant*/
+#define TOTALDRIVES NUMCTRLRS*DRIVESPERC /*Max possible drives*/
+
+#define TICKRES 10 /*Our coarse timer resolution*/
+#define ISABUSKHZ 8330 /*Number of IN/OUT ISA/sec*/
+#define MAXTRKS 101 /*Maximum possible tracks*/
+
+#define RAW_DEVICE 46 /*<16>Dev number for raw device*/
+
+
+#define MATCDBLK 2048 /*Standard block size*/
+#define MATCDRBLK 2352 /*Raw and/or DA block size*/
+#define MATCD_RETRYS 5 /*Number of retries for read ops*/
+#define MATCD_READ_1 0x80 /*Read state machine defines*/
+#define MATCD_READ_2 0x90 /*Read state machine defines*/
+
+struct matcd_volinfo {
+ unsigned char type; /*00 CD-DA or CD-ROM
+ 10 CD-I
+ 20 XA */
+ unsigned char trk_low; /*Normally 1*/
+ unsigned char trk_high; /*Highest track number*/
+ unsigned char vol_msf[3]; /*Size of disc in min/sec/frame*/
+};
+
+
+struct matcd_mbx {
+ short controller;
+ short ldrive;
+ short partition;
+ short port;
+ short iftype; /*<20>Host interface type*/
+ short retry;
+ short nblk;
+ int sz;
+ u_long skip;
+ struct buf *bp;
+ int p_offset;
+ short count;
+};
+
+static struct matcd_data {
+ short drivemode; /*Last state drive was set to*/
+ short flags;
+ short status; /*Last audio-related function*/
+ int blksize;
+ u_long disksize;
+ short iobase;
+ short iftype; /*<20>Host interface type*/
+ struct disklabel dlabel;
+ unsigned int partflags[MAXPARTITIONS];
+ unsigned int openflags;
+ struct matcd_volinfo volinfo;
+ struct matcd_mbx mbx;
+ u_char patch[2]; /*<12>Last known audio routing*/
+ u_char volume[2]; /*<12>Last known volume setting*/
+#ifdef DEVFS
+ void *ra_devfs_token; /* handle for devfs entry */
+ void *rc_devfs_token;
+ void *a_devfs_token;
+ void *c_devfs_token;
+ void *rla_devfs_token;
+ void *rlc_devfs_token;
+ void *la_devfs_token;
+ void *lc_devfs_token;
+#endif DEVFS
+} matcd_data[TOTALDRIVES];
+
+
+/* Bit equates for matcd_data.flags*/
+
+#define MATCDINIT 0x0001 /*Probe ran on host adapter*/
+#define MATCDLABEL 0x0004 /*Valid TOC exists*/
+#define MATCDLOCK 0x0008 /*<15>Drive door is locked*/
+#define MATCDWARN 0x0020 /*Have reported an open disc change*/
+
+
+/* Bit equates for matcd_data.partflags*/
+
+#define MATCDOPEN 0x0001
+#define MATCDREADRAW 0x0002
+
+
+/* Error classes returned by chk_error()*/
+
+#define ERR_RETRY 1 /*A retry might recover this*/
+#define ERR_INIT 2 /*A retry certainly will get this*/
+#define ERR_FATAL 3 /*This cannot be recovered from*/
+
+
+static struct buf_queue_head request_head[NUMCTRLRS]; /*<18>A queue for each host interface*/
+static int nextcontroller=0; /*<18>Number of interface units found*/
+static int drivepresent=0; /*<18>Don't change this - see license*/
+static int iftype; /*<20>Probe/Attach i.f. type relay*/
+
+static unsigned char if_state[4]={0,0,0,0}; /*<18>State of the host I/F and bus*/
+
+/* Flags in the if_state array
+*/
+
+#define BUSBUSY 0x01 /*<18>Bus is already busy*/
+
+
+struct matcd_read2 {
+ unsigned char start_msf[3];
+ unsigned char end_msf[3];
+};
+
+
+/* This mystery structure is supposed to make dynamic driver
+ loading possible.
+*/
+
+static struct kern_devconf kdc_matcd[TOTALDRIVES] = { {
+ 0,0,0, /*Filled in by dev_attach*/
+#ifdef PC98
+ "matcdc",0,{MDDT_PC98,0,"bio"},
+ pc98_generic_externalize,0,0,PC98_EXTERNALLEN,
+ &kdc_nec0, /*<12>Parent*/
+#else
+ "matcdc",0,{MDDT_ISA,0,"bio"},
+ isa_generic_externalize,0,0,ISA_EXTERNALLEN,
+ &kdc_isa0, /*<12>Parent*/
+#endif
+ 0, /*<12>Parent Data*/
+ DC_IDLE, /*<12>Status*/
+ "Matsushita CD-ROM Controller" /*<12>This is the description*/
+} }; /*<12>*/
+
+
+
+
+/*---------------------------------------------------------------------------
+ These macros take apart the minor number and yield the
+ partition, drive on controller, and controller.
+ This must match the settings in /dev/MAKEDEV.
+---------------------------------------------------------------------------*/
+
+#define matcd_partition(dev) ((minor(dev)) & 0x07)
+#define matcd_ldrive(dev) (((minor(dev)) & 0x78) >> 3)
+#define matcd_cdrive(dev) (((minor(dev)) & 0x18) >> 3)
+#define matcd_controller(dev) (((minor(dev)) & 0x60) >> 5)
+#ifdef LOCKDRIVE
+#define matcd_lockable(dev) (((minor(dev)) & 0x80) >> 5)
+#endif /*LOCKDRIVE*/
+
+
+
+
+/*---------------------------------------------------------------------------
+ Entry points and other connections to/from kernel - see also conf.h
+ --- not any more :)
+---------------------------------------------------------------------------*/
+
+#ifdef PC98
+static int matcd_probe(struct pc98_device *dev);
+static int matcd_attach(struct pc98_device *dev);
+struct pc98_driver matcddriver={matcd_probe, matcd_attach,
+ "matcdc"};
+#else
+static int matcd_probe(struct isa_device *dev);
+static int matcd_attach(struct isa_device *dev);
+struct isa_driver matcddriver={matcd_probe, matcd_attach,
+ "matcdc"};
+#endif
+
+
+static d_open_t matcdopen;
+static d_close_t matcdclose;
+static d_ioctl_t matcdioctl;
+static d_psize_t matcdsize;
+static d_strategy_t matcdstrategy;
+
+#define CDEV_MAJOR 46
+#define BDEV_MAJOR 17
+
+extern struct cdevsw matcd_cdevsw;
+static struct bdevsw matcd_bdevsw =
+ { matcdopen, matcdclose, matcdstrategy, matcdioctl, /*17*/
+ nodump, matcdsize, 0, "matcd",
+ &matcd_cdevsw, -1 };
+
+static struct cdevsw matcd_cdevsw =
+ { matcdopen, matcdclose, rawread, nowrite, /*46*/
+ matcdioctl, nostop, nullreset, nodevtotty,/* SB cd */
+ seltrue, nommap, matcdstrategy, "matcd",
+ &matcd_bdevsw, -1};
+
+
+
+/*---------------------------------------------------------------------------
+ Internal function declarations
+---------------------------------------------------------------------------*/
+
+static void matcd_drvinit(void *unused);
+static void matcd_start(int controller);
+static void zero_cmd(char *);
+static void matcd_pread(int port, int count, unsigned char * data);
+static int matcd_fastcmd(int port,int ldrive,int cdrive,
+ unsigned char * cp);
+static void matcd_slowcmd(int port,int ldrive,int cdrive,
+ unsigned char * cp);
+static void matcd_blockread(int state);
+static void selectdrive(int port,int drive);
+static void doreset(int port,int cdrive);
+static int doprobe(int port,int cdrive);
+static void lockbus(int controller, int ldrive);
+static void unlockbus(int controller, int ldrive);
+static int matcd_volinfo(int ldrive);
+static void draincmd(int port,int cdrive,int ldrive);
+static int get_error(int port, int ldrive, int cdrive);
+static int chk_error(int errnum);
+static int msf_to_blk(unsigned char * cd);
+#ifdef FULLDRIVER
+static int matcd_playtracks(int ldrive, int cdrive, int controller,
+ struct ioc_play_track *pt);
+static int matcd_playmsf(int ldrive, int cdrive, int controller,
+ struct ioc_play_msf *pt);
+static int matcd_pause(int ldrive, int cdrive, int controller,
+ int action);
+static int matcd_stop(int ldrive, int cdrive, int controller);
+static int matcd_level(int ldrive, int cdrive, int controller,
+ struct ioc_vol * volume, int action);
+static int matcd_patch(int ldrive, int cdrive, int controller,
+ struct ioc_patch * routing);
+static int matcd_route(int ldrive, int cdrive, int controller,
+ int command);
+static int matcd_pitch(int ldrive, int cdrive, int controller,
+ struct ioc_pitch * speed);
+#endif /*FULLDRIVER*/
+static int matcd_toc_header(int ldrive, int cdrive, int controller,
+ struct ioc_toc_header * toc);
+static int matcd_toc_entries(int ldrive, int cdrive,
+ int controller,
+ struct ioc_read_toc_entry *ioc_entry);
+static int matcd_read_subq(int ldrive, int cdrive, int controller,
+ struct ioc_read_subchannel * sqp);
+static int matcd_igot(struct ioc_capability * sqp);
+static int waitforit(int timelimit, int state, int port,
+ char * where);
+static int get_stat(int port, int ldrive);
+static int media_chk(struct matcd_data *cd,int errnum,
+ int ldrive,int test);
+static int matcd_eject(int ldrive, int cdrive, int controller);
+static int matcd_doorclose(int ldrive, int cdrive, int controller);
+static int matcd_dlock(int ldrive, int cdrive,
+ int controller, int action);
+static int docmd(char * cmd, int ldrive, int cdrive,
+ int controller, int port);
+
+
+/*---------------------------------------------------------------------------
+ matcdopen - Open the device
+
+ This routine actually gets called every time anybody opens
+ any partition on a drive. But the first call is the one that
+ does all the work.
+
+<15> If LOCKDRIVE is enabled, additional minor number devices allow
+<15> the drive to be locked while being accessed.
+---------------------------------------------------------------------------*/
+int matcdopen(dev_t dev, int flags, int fmt,
+ struct proc *p)
+{
+ int cdrive,ldrive,partition,controller,lock;
+ struct matcd_data *cd;
+ int i,z,port;
+ unsigned char cmd[MAXCMDSIZ];
+
+ ldrive=matcd_ldrive(dev);
+ cdrive=matcd_cdrive(dev);
+ partition=matcd_partition(dev);
+ controller=matcd_controller(dev);
+ lock=matcd_lockable(dev);
+ cd= &matcd_data[ldrive];
+ port=cd->iobase; /*and port#*/
+
+ if (ldrive >= TOTALDRIVES) return(ENXIO);
+
+
+#ifdef DEBUGOPEN
+ printf("matcd%d: Open: dev %x partition %x controller %x flags %x cdrive %x\n",
+ ldrive,(int)dev,partition,controller,cd->flags,
+ matcd_cdrive(dev));
+#endif /*DEBUGOPEN*/
+
+ if (!(cd->flags & MATCDINIT)) { /*Did probe find this drive*/
+ return(ENXIO);
+ }
+
+ if (!(cd->flags & MATCDLABEL) &&
+ cd->openflags) { /*Has drive completely closed?*/
+ return(ENXIO); /*No, all partitions must close*/
+ }
+
+
+/* Now, test to see if the media is ready
+*/
+
+ lockbus(controller,ldrive);
+ zero_cmd(cmd);
+ cmd[0]=NOP; /*Test drive*/
+ matcd_slowcmd(port,ldrive,cdrive,cmd);
+ i=waitforit(10*TICKRES,DTEN,port,"matopen");
+ z=get_stat(port,ldrive); /*Read status byte*/
+#ifdef DEBUGOPEN
+ printf("matcd%d Result of NOP is %x %x\n",ldrive,i,z);
+#endif /*DEBUGOPEN*/
+ if ((z & MATCD_ST_DSKIN)==0) { /*Is there a disc in the drive?*/
+#ifdef DEBUGOPEN
+ printf("matcd%d: No Disc in open\n",ldrive);
+#endif /*DEBUGOPEN*/
+ unlockbus(controller, ldrive); /*Release bus lock*/
+ cd->flags &= ~MATCDLABEL; /*<16>Mark label as invalid*/
+ if (major(dev)==RAW_DEVICE) { /*<16>Is the char device?*/
+ return(0); /*<16>Allow Semi open*/
+ }
+ else {
+ return(ENXIO); /*<16>Normally blow off*/
+ }
+ }
+ if (z & MATCD_ST_ERROR) { /*Was there an error*/
+ i=get_error(port,ldrive,cdrive);/*Find out what it was*/
+#ifdef DEBUGOPEN
+ printf("matcd%d NOP Error was %x\n",ldrive,i);
+#endif /*DEBUGOPEN*/
+ if (cd->openflags) { /*Any parts open?*/
+ if (media_chk(cd,i,ldrive,0)) { /*<14>Was it a disc chg?*/
+#ifdef DEBUGOPEN
+ printf("matcd%d: Disc change detected i %x z %x\n",
+ ldrive,i,z);
+#endif /*DEBUGOPEN*/
+ unlockbus(controller, ldrive); /*Release bus lock*/
+ return(ENOTTY);
+ }
+
+ } else {
+ media_chk(cd,i,ldrive,1);/*<14>Was it a disc chg?*/
+ /*<14>Clear volume info*/
+ }
+ }
+ unlockbus(controller, ldrive); /*Release bus lock*/
+
+/* Here we fill in the disklabel structure although most is
+ hardcoded.
+*/
+
+ if ((cd->flags & MATCDLABEL)==0) {
+ bzero(&cd->dlabel,sizeof(struct disklabel));
+
+
+/* Now we query the drive for the actual size of the media.
+ This is where we find out of there is any media or if the
+ media isn't a Mode 1 or Mode 2/XA disc.
+ See version information about Mode 2/XA support.
+*/
+ lockbus(controller,ldrive);
+ i=matcdsize(dev);
+ unlockbus(controller, ldrive); /*Release bus lock*/
+#ifdef DEBUGOPEN
+ printf("matcd%d: Bus unlocked in open\n",ldrive);
+#endif /*DEBUGOPEN*/
+ if (i < 0) {
+ printf("matcd%d: Could not read the disc size\n",ldrive);
+ return(ENXIO);
+ } /*matcdsize filled in rest of dlabel*/
+
+/* Based on the results, fill in the variable entries in the disklabel
+*/
+ cd->dlabel.d_secsize=cd->blksize;
+ cd->dlabel.d_ncylinders=(cd->disksize/100)+1;
+ cd->dlabel.d_secperunit=cd->disksize;
+ cd->dlabel.d_partitions[0].p_size=cd->disksize;
+ cd->dlabel.d_checksum=dkcksum(&cd->dlabel);
+
+
+/* Now fill in the hardcoded section
+*/
+ /*123456789012345678*/
+ strncpy(cd->dlabel.d_typename,"Matsushita CDR ",16);
+ strncpy(cd->dlabel.d_packname,"(c) 1994, fdiv ",16);
+ cd->dlabel.d_magic=DISKMAGIC;
+ cd->dlabel.d_magic2=DISKMAGIC;
+ cd->dlabel.d_nsectors=100;
+ cd->dlabel.d_secpercyl=100;
+ cd->dlabel.d_ntracks=1;
+ cd->dlabel.d_interleave=1;
+ cd->dlabel.d_rpm=300;
+ cd->dlabel.d_npartitions=1; /*See note below*/
+ cd->dlabel.d_partitions[0].p_offset=0;
+ cd->dlabel.d_partitions[0].p_fstype=9;
+ cd->dlabel.d_flags=D_REMOVABLE;
+
+/* I originally considered allowing the partition match tracks or
+ sessions on the media, but since you are allowed up to 99
+ tracks in the RedBook world, this would not fit in with the
+ BSD fixed partition count scheme. So ioctls will be used to shift
+ the track to be accessed into partition 1.
+*/
+
+ cd->flags |= MATCDLABEL; /*Mark drive as having TOC*/
+ }
+
+#ifdef DEBUGOPEN
+ printf("matcd%d open2: partition=%d disksize=%d blksize=%x flags=%x\n",
+ ldrive,partition,(int)cd->disksize,cd->blksize,cd->flags);
+#endif /*DEBUGOPEN*/
+
+#ifdef LOCKDRIVE
+ if (cd->openflags==0 && lock) {
+ zero_cmd(cmd);
+ cmd[0]=LOCK; /*Lock drive*/
+ cmd[1]=1;
+ docmd(cmd,ldrive,cdrive,controller,port);/*<15>Issue cmd*/
+ cd->flags |= MATCDLOCK; /*<15>Drive is now locked*/
+ }
+#endif /*LOCKDRIVE*/
+ cd->openflags |= (1<<(partition+lock));/*<24>Mark partition open*/
+
+ if (partition==RAW_PART ||
+ (partition < cd->dlabel.d_npartitions &&
+ cd->dlabel.d_partitions[partition].p_fstype != FS_UNUSED)) {
+ cd->partflags[partition] |= MATCDOPEN;
+ if (partition == RAW_PART) {
+ cd->partflags[partition] |= MATCDREADRAW;
+ }
+#ifdef DEBUGOPEN
+ printf("matcd%d: Open is complete - openflags %x\n",
+ ldrive,cd->openflags);
+#endif /*DEBUGOPEN*/
+ return(0);
+ }
+#ifdef DEBUGOPEN
+ printf("matcd%d: Open FAILED\n",ldrive);
+#endif /*DEBUGOPEN*/
+ return(ENXIO);
+}
+
+
+/*---------------------------------------------------------------------------
+ matcdclose - Close the device
+
+ Close may not do much other than clear some driver settings.
+ Note that audio playback will continue.
+
+<15> If you define LOCKDRIVE, and the drive has been opened using
+<15> one of the locking minor numbers, code in close will unlock
+<15> the drive.
+---------------------------------------------------------------------------*/
+
+int matcdclose(dev_t dev, int flags, int fmt,
+ struct proc *p)
+{
+ int ldrive,cdrive,port,partition,controller,lock;
+ struct matcd_data *cd;
+#ifdef LOCKDRIVE
+ unsigned char cmd[MAXCMDSIZ];
+#endif /*LOCKDRIVE*/
+
+ ldrive=matcd_ldrive(dev);
+ cdrive=matcd_cdrive(dev);
+ lock=matcd_lockable(dev);
+ cd=matcd_data+ldrive;
+ port=cd->iobase; /*and port#*/
+
+ if (ldrive >= TOTALDRIVES)
+ return(ENXIO);
+
+ partition = matcd_partition(dev);
+ controller=matcd_controller(dev);
+#ifdef DEBUGOPEN
+ printf("matcd%d: Close partition=%d flags %x openflags %x partflags %x\n",
+ ldrive,partition,cd->flags,cd->openflags,
+ cd->partflags[partition]);
+#endif /*DEBUGOPEN*/
+
+ if (!(cd->flags & MATCDINIT))
+ return(ENXIO);
+
+ cd->partflags[partition] &= ~(MATCDOPEN|MATCDREADRAW);
+ cd->openflags &= ~(1<<(partition+lock));
+ if (cd->openflags==0) { /*<24>Really last close?*/
+#ifdef LOCKDRIVE
+ if (cd->flags & MATCDLOCK) { /*<24>Was drive locked?*/
+ zero_cmd(cmd); /*Yes, so unlock it*/
+ cmd[0]=LOCK; /*Unlock drive*/
+ docmd(cmd,ldrive,cdrive,controller,port);
+ }
+#endif /*LOCKDRIVE*/
+ cd->flags &= ~(MATCDWARN|MATCDLOCK);
+ /*<15>Clear warning flag*/
+ }
+ return(0);
+}
+
+
+/*---------------------------------------------------------------------------
+ matcdstrategy - Accepts I/O requests from kernel for processing
+
+ This routine accepts a read request block pointer (historically
+ but somewhat inaccurately called *bp for buffer pointer).
+ Various sanity checks are performed on the request.
+ When we are happy with the request and the state of the device,
+ the request is added to the queue of requests for the interface
+ that the drive is connected to. We support multiple interfaces
+ so there are multiple queues. Once the request is added, we
+ call the matcd_start routine to start the device in case it isn't
+ doing something already. All I/O including ioctl requests
+ rely on the current request starting the next one before exiting.
+---------------------------------------------------------------------------*/
+
+void matcdstrategy(struct buf *bp)
+{
+ struct matcd_data *cd;
+ int s;
+ int ldrive,controller;
+
+ ldrive=matcd_ldrive(bp->b_dev);
+ controller=matcd_controller(bp->b_dev);
+ cd= &matcd_data[ldrive];
+
+#ifdef DEBUGIO
+ printf("matcd%d: Strategy: buf=0x%lx, block#=%ld bcount=%ld\n",
+ ldrive,(unsigned long)bp,bp->b_blkno,bp->b_bcount);
+#endif /*DEBUGIO*/
+
+
+ if (ldrive >= TOTALDRIVES || bp->b_blkno < 0) {
+ printf("matcd%d: Bogus parameters received - kernel may be corrupted\n",ldrive);
+ bp->b_error=EINVAL;
+ goto bad;
+ }
+
+ if (!(cd->flags & MATCDLABEL)) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+
+ if (!(bp->b_flags & B_READ)) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+
+ if (bp->b_bcount==0) /*Request is zero-length - all done*/
+ goto done;
+
+ if (matcd_partition(bp->b_dev) != RAW_PART) {
+ if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
+ goto done;
+ }
+ } else {
+ bp->b_pblkno=bp->b_blkno;
+ bp->b_resid=0;
+ }
+
+ s=splbio(); /*Make sure we don't get intr'ed*/
+ tqdisksort(&request_head[controller], bp);/*Add new request (bp) to queue (dp
+ and sort the requests in a way that
+ may not be ideal for CD-ROM media*/
+
+
+ matcd_start(controller); /*Ok, with our newly sorted queue,
+ see if we can start an I/O operation
+ right now*/
+ splx(s); /*Return priorities to normal*/
+ return; /*All done*/
+
+bad: bp->b_flags |= B_ERROR; /*Request bad in some way*/
+done: bp->b_resid = bp->b_bcount; /*Show amount of data un read*/
+ biodone(bp); /*Signal we have done all we plan to*/
+ return;
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_start - Pull a request from the queue and consider doing it.
+---------------------------------------------------------------------------*/
+
+static void matcd_start(int controller)
+{
+ struct matcd_data *cd;
+ struct buf *bp;
+ struct partition *p;
+ int part,ldrive;
+
+ bp = TAILQ_FIRST(&request_head[controller]);
+ if (bp == NULL) { /*Nothing on read queue to do?*/
+ wakeup((caddr_t)&matcd_data->status); /*Wakeup any blocked*/
+ return; /* opens, ioctls, etc*/
+ }
+
+ ldrive=matcd_ldrive(bp->b_dev); /*Get logical drive#*/
+ cd=&matcd_data[ldrive]; /*Get pointer to data for this drive*/
+#ifdef DEBUGIO
+ printf("matcd%d: In start controller %d\n",ldrive,controller);
+#endif /*DEBUGIO*/
+
+ if (if_state[controller] & BUSBUSY) {
+#ifdef DEBUGIO
+ printf("matcd%d: Dropping thread in start, controller %d\n",
+ ldrive,controller);
+#endif /*DEBUGIO*/
+ return;
+ }
+
+/* Ok, the controller is idle (not necessarily the drive) and so
+ get the command to do and issue it
+*/
+
+ TAILQ_REMOVE(&request_head[controller], bp, b_act);
+
+ part=matcd_partition(bp->b_dev);
+ p=cd->dlabel.d_partitions + part;
+
+ if_state[controller] |= BUSBUSY;/*<18>Mark bus as busy*/
+ cd->mbx.ldrive=ldrive; /*Save current logical drive*/
+ cd->mbx.controller=controller; /*and controller*/
+ cd->mbx.partition=part; /*and partition (2048 vs 2532)*/
+ cd->mbx.port=cd->iobase; /*and port#*/
+ cd->mbx.iftype=cd->iftype; /*<20>interface type*/
+ cd->mbx.retry=MATCD_RETRYS; /*and the retry count*/
+ cd->mbx.bp=bp; /*and the bp*/
+ cd->mbx.p_offset=p->p_offset; /*and where the data will go*/
+ matcd_blockread(MATCD_READ_1+ldrive); /*Actually start the read*/
+ return; /*Dropping thread. matcd_blockread
+ must have scheduled a timeout or
+ we will go to sleep forever*/
+}
+
+
+/*---------------------------------------------------------------------------
+ matcdioctl - Process things that aren't block reads
+
+ In this driver, ioctls are used mainly to change
+ the mode the drive is running in, play audio and other
+ things that don't fit into the block read scheme of things.
+---------------------------------------------------------------------------*/
+
+int matcdioctl(dev_t dev, int command, caddr_t addr,
+ int flags, struct proc *p)
+{
+ struct matcd_data *cd;
+ int ldrive,cdrive,partition;
+ int port, controller;
+#ifdef DEBUGIOCTL
+ int i;
+#endif /*DEBUGIOCTL*/
+
+ ldrive=matcd_ldrive(dev);
+ cdrive=matcd_cdrive(dev);
+ partition=matcd_partition(dev);
+ controller=ldrive>>2;
+ cd = &matcd_data[ldrive];
+ port=cd->iobase;
+
+#ifdef DEBUGIOCTL
+ printf("matcd%d: ioctl %x cdrive %x parms ",ldrive,command,cdrive);
+ for (i=0;i<10;i++) {
+ printf("%02x ",(unsigned int)addr[i]);
+ }
+ printf(" flags %x\n",cd->flags);
+#endif /*DEBUGIOCTL*/
+
+ if (command==CDIOCCLOSE) /*<16>Allow close if door open*/
+ return(matcd_doorclose(ldrive, cdrive, controller));
+
+ if (!(cd->flags & MATCDLABEL)) /*Did we read TOC OK?*/
+ return(EIO); /*<16>then drive really isn't ready*/
+
+ switch(command) {
+ case DIOCSBAD:
+ return(EINVAL);
+
+ case DIOCGDINFO:
+ *(struct disklabel *) addr = cd->dlabel;
+ return(0);
+
+ case DIOCGPART:
+ ((struct partinfo *) addr)->disklab=&cd->dlabel;
+ ((struct partinfo *) addr)->part=
+ &cd->dlabel.d_partitions[matcd_partition(dev)];
+ return(0);
+
+ case DIOCWDINFO:
+ case DIOCSDINFO:
+ if ((flags & FWRITE) == 0) {
+ return(EBADF);
+ }
+ else {
+ return setdisklabel(&cd->dlabel,
+ (struct disklabel *) addr, 0);
+ }
+ case DIOCWLABEL:
+ return(EBADF);
+
+ case CDIOCEJECT:
+ return(matcd_eject(ldrive, cdrive, controller));
+
+ case CDIOCALLOW:
+ return(matcd_dlock(ldrive, cdrive,
+ controller,0));
+
+ case CDIOCPREVENT:
+ return(matcd_dlock(ldrive, cdrive,
+ controller, MATCDLOCK));
+
+#ifdef FULLDRIVER
+ case CDIOCPLAYTRACKS:
+ return(matcd_playtracks(ldrive, cdrive, controller,
+ (struct ioc_play_track *) addr));
+
+ case CDIOCPLAYMSF:
+ return(matcd_playmsf(ldrive, cdrive, controller,
+ (struct ioc_play_msf *) addr));
+
+ case CDIOCRESUME:
+ return(matcd_pause(ldrive, cdrive, controller,RESUME));
+
+ case CDIOCPAUSE:
+ return(matcd_pause(ldrive, cdrive, controller,0));
+
+ case CDIOCSTOP:
+ return(matcd_stop(ldrive, cdrive, controller));
+
+ case CDIOCGETVOL:
+ case CDIOCSETVOL:
+ return(matcd_level(ldrive, cdrive, controller,
+ (struct ioc_vol *) addr, command));
+
+ case CDIOCSETMONO: /*<12>This drive can't do mono*/
+ return(EINVAL); /*<12>but it looks like it should*/
+
+ /*<12>SRC OUT SRC OUT*/
+ case CDIOCSETSTEREO: /*<12>0 -> L 1 -> R*/
+ case CDIOCSETMUTE: /*<12>0 -> NULL 1 -> NULL*/
+ case CDIOCSETLEFT: /*<12>0 -> L&R 1 -> NULL*/
+ case CDIOCSETRIGHT: /*<12>0 -> NULL 1 -> L&R*/
+ /*<12>Adjust audio routing*/
+ return(matcd_route(ldrive, cdrive, controller,
+ command));
+
+ case CDIOCSETPATCH: /*<12>Allow precise routing*/
+ return(matcd_patch(ldrive, cdrive, controller,
+ (struct ioc_patch *) addr));
+
+ case CDIOCPITCH: /*<12>Adjust playback speed*/
+ return(matcd_pitch(ldrive, cdrive, controller,
+ (struct ioc_pitch *) addr));
+
+ case CDIOCSTART: /*<12>Only reason this isn't*/
+ return(EINVAL); /*<12>implemented is I can't find out*/
+ /*<12>what it should do!*/
+#endif /*FULLDRIVER*/
+
+ case CDIOREADTOCHEADER:
+ return(matcd_toc_header(ldrive, cdrive, controller,
+ (struct ioc_toc_header *) addr));
+
+ case CDIOREADTOCENTRYS:
+ return(matcd_toc_entries(ldrive, cdrive, controller,
+ (struct ioc_read_toc_entry *) addr));
+
+ case CDIOCREADSUBCHANNEL:
+ return(matcd_read_subq(ldrive, cdrive, controller,
+ (struct ioc_read_subchannel *) addr));
+
+ case CDIOCCAPABILITY: /*<16>Request drive/driver capability*/
+ return(matcd_igot((struct ioc_capability *) addr));
+
+ case CDIOCRESET: /*<12>There is no way to hard reset*/
+ return(EINVAL); /*<12>just one drive*/
+
+ default:
+ return(ENOTTY);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ matcdsize - Reports how many blocks exist on the disc.
+---------------------------------------------------------------------------*/
+
+int matcdsize(dev_t dev)
+{
+ int size,blksize;
+ int ldrive,part;
+ struct matcd_data *cd;
+
+ ldrive=matcd_ldrive(dev);
+ part=matcd_partition(dev);
+ if (part==RAW_PART)
+ blksize=MATCDRBLK; /*2352*/
+ else
+ blksize=MATCDBLK; /*2048*/
+
+ cd = &matcd_data[ldrive];
+
+ if (matcd_volinfo(ldrive) >= 0) {
+ cd->blksize=blksize;
+ size=msf_to_blk((char * )&cd->volinfo.vol_msf);
+
+ cd->disksize=size*(blksize/DEV_BSIZE);
+#ifdef DEBUGOPEN
+ printf("matcd%d: Media size %d\n",ldrive,size);
+#endif /*DEBUGOPEN*/
+ return(0);
+ }
+ return(-1);
+}
+
+/*---------------------------------------------------------------------------
+ matcd_probe - Search for host interface/adapters
+
+ The probe routine hunts for the first drive on the interface since
+ there is no way to locate just the adapter. It also resets the
+ entire drive chain while it is there. matcd_attach() takes care of
+ the rest of the initialization.
+
+ The probe routine can be compiled two ways. In AUTOHUNT mode,
+ the kernel config file can say "port?" and we will check all ports
+ listed in the port_hint array (see above).
+
+ Without AUTOHUNT set, the config file must list a specific port
+ address to check.
+
+ Note that specifying the explicit addresses makes boot-up a lot
+ faster.
+
+ The probe will locate Panasonic/Creative interface on the following
+ Creative adapter boards:
+ #1330A Sound Blaster PRO
+ #1730 Sound Blaster 16
+ #1740 Sound Blaster 16 (cost reduced)
+ #2230 Sound Blaster 16 (cost reduced)
+ #2770 Sound Blaster 16 Value (cost reduced)
+ #1810 omniCD upgrade kit adapter card (stand-alone CD)
+ #3100 PhoneBlaster SB16 + Sierra 14.4K modem combo
+ Creative releases a newer and cheaper-to-make Sound Blaster
+ board every few months, so by the original release date of this
+ software, there are probably 8 different board models called
+ Sound Blaster 16. These include "Vibra", "Value", etc.
+
+ Please report additional part numbers and board descriptions
+ and new port numbers that work to the author.
+
+---------------------------------------------------------------------------*/
+
+static int
+#ifdef PC98
+matcd_probe(struct pc98_device *dev)
+#else
+matcd_probe(struct isa_device *dev)
+#endif
+{
+ int i,cdrive;
+ unsigned char y;
+ int port = dev->id_iobase; /*Take port hint from config file*/
+
+ cdrive=nextcontroller; /*Controller defined by pass for now*/
+ if (nextcontroller==NUMCTRLRS) {
+ printf("matcdc%d: - Too many interfaces specified in config\n",
+ nextcontroller);
+ return(0);
+ }
+ if (nextcontroller==0) { /*Very first time to be called*/
+ for (i=0; i<TOTALDRIVES; i++) {
+ matcd_data[i].drivemode=MODE_UNKNOWN;
+ matcd_data[i].flags=0;
+ }
+ }
+
+ i=nextcontroller*DRIVESPERC; /*Precompute controller offset*/
+ for (y=0; y<DRIVESPERC; y++) {
+ matcd_data[i+y].flags=0;
+ }
+
+#ifdef DEBUGPROBE
+ printf("matcdc%d: In probe i %d y %d port %x\n",
+ nextcontroller,i,y,port);
+#endif /*DEBUGPROBE*/
+#ifdef AUTOHUNT
+#ifdef DEBUGPROBE
+ printf("matcd%d: size of port_hints %d\n",
+ nextcontroller,sizeof(port_hints));
+#endif /*DEBUGPROBE*/
+ if (port==-1) {
+ for(i=0;i<(sizeof(port_hints)/sizeof(short));i++) {
+ port=port_hints[i];
+#ifdef DEBUGPROBE
+ printf("matcdc%d: Port hint %x\n",nextcontroller,port);
+#endif /*DEBUGPROBE*/
+ if (port==-1) {
+ dev->id_iobase=-1; /*Put port ? back*/
+ return(0);/*Nothing left to try*/
+ }
+ if (port!=0) { /*Untested port found*/
+ dev->id_iobase=port;
+ port_hints[i]=0;/*Don't use that port again*/
+ if (doprobe(port,cdrive)==0) return(NUMPORTS);
+ }
+ }
+ dev->id_iobase=-1; /*Put port ? back as it was*/
+ return(0); /*Interface not found*/
+
+ } else { /*Config specified a port*/
+ i=0; /*so eliminate it from the hint list*/
+ for(i=0;;i++) { /*or we might try to assign it again*/
+ if (port_hints[i]== -1) break; /*End of list*/
+ if (port_hints[i]==port) {
+ port_hints[i]=0; /*Clear duplicate*/
+ break;
+ }
+ }
+ if (doprobe(port,cdrive)==0) return(NUMPORTS);
+ else return(0);
+ }
+#else /*AUTOHUNT*/
+ if (port==-1) {
+ printf("matcdc%d: AUTOHUNT disabled but port? specified in config\n",
+ nextcontroller);
+ return(0);
+ }
+ if (doprobe(port,cdrive)==0) return(NUMPORTS);
+ else return(0);
+#endif /*AUTOHUNT*/
+}
+
+/*---------------------------------------------------------------------------
+ doprobe - Common probe code that actually checks the ports we
+ have decided to test.
+
+<20> Edit 20 changes adds code to determine if the host interface
+ is one that behaves like the Creative SoundBlaster cards,
+ or whether the host interface like those used by some boards
+ made by Media Vision and a version known as Lasermate.
+---------------------------------------------------------------------------*/
+
+int doprobe(int port,int cdrive)
+{
+ unsigned char cmd[MAXCMDSIZ];
+ int i;
+
+#ifdef RESETONBOOT
+ doreset(port,cdrive); /*Reset what might be our device*/
+#endif /*RESETONBOOT*/
+ outb(port+PHASE,0); /*<16>Guarantee status phase*/
+ zero_cmd(cmd);
+ cmd[0]=NOP; /*A reasonably harmless command.
+ This command will fail after
+ power-up or after reset. That's OK*/
+#ifdef RESETONBOOT
+ if (((inb(port+STATUS) & (DTEN|STEN)) != (DTEN|STEN)) ||
+ (inb(port+DATA) != 0xff))
+ return(-1); /*<20>Something detected but it isn't
+ the device we wanted*/
+#endif /*RESETONBOOT*/
+ if (matcd_fastcmd(port,0,0,cmd)==0) {/*Issue command*/
+ outb(port+PHASE,1); /*<20>Switch to Creative Data phase*/
+ i=inb(port+CMD); /*<20>Read a byte in data phase*/
+ outb(port+PHASE,0); /*<20>Switch to Creative Status phase*/
+ if ((inb(port+STATUS) & (DTEN|STEN))
+ == (DTEN|STEN)) { /*<20>Drive went idle*/
+ iftype=1; /*<20>It is not a Creative interface.*/
+ } else { /*<20>Status byte still available*/
+ iftype=0;
+ inb(port+CMD); /*<20>Read status byte*/
+ }
+#ifdef DEBUGPROBE
+ printf("matcdc%d: Probe found something\n",nextcontroller);
+#endif /*DEBUGPROBE*/
+/*------Don't change anything below this line - see license -----------------*/
+ if (drivepresent==0) { /*Don't change*/
+ printf("matcd - Matsushita (Panasonic) CD-ROM Driver by FDIV, %s\n",
+ /*Don't change*/
+ MATCDVERSION); /*Don't change*/
+ drivepresent++; /*Don't change*/
+ if (drivepresent==0) /*Don't change - make LINT happy*/
+ printf("%s\n",MATCDCOPYRIGHT); /*Don't change*/
+ } /*Don't change*/
+/*------Don't change anything above this line - see license -----------------*/
+ return(0); /*Drive 0 detected*/
+ }
+#ifdef DEBUGPROBE
+ printf("matcdc%d: Probe DID NOT find something\n",nextcontroller);
+#endif /*DEBUGPROBE*/
+ return(1);
+}
+
+
+/*---------------------------------------------------------------------------
+<12> matcd_register - Something to handle dynamic driver loading.
+ Sorry for the lousy description but no one could point
+ me to anything that explained what it is for either.
+ Added in Edit 12.
+---------------------------------------------------------------------------*/
+
+#ifdef PC98
+static inline void matcd_register(struct pc98_device *id)
+#else
+static inline void matcd_register(struct isa_device *id)
+#endif
+{
+ if(id->id_unit) {
+ kdc_matcd[id->id_unit]=kdc_matcd[0];
+ }
+ kdc_matcd[id->id_unit].kdc_unit=id->id_unit;
+#ifdef PC98
+ kdc_matcd[id->id_unit].kdc_pc98=id;
+#else
+ kdc_matcd[id->id_unit].kdc_isa=id;
+#endif
+ dev_attach(&kdc_matcd[id->id_unit]);
+ return;
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_attach - Locates drives on the adapters that were located.
+ If we got here, we located an interface and at least one
+ drive. Now we figure out how many drives are under that
+ interface. The Panasonic interface is too simple to call
+ it a controller, but in the existing PDP model, that is
+ what it would be.
+---------------------------------------------------------------------------*/
+
+static int
+#ifdef PC98
+matcd_attach(struct pc98_device *dev)
+#else
+matcd_attach(struct isa_device *dev)
+#endif
+{
+ int i;
+ unsigned int z,cdrive;
+ unsigned char cmd[MAXCMDSIZ];
+ unsigned char data[12];
+ struct matcd_data *cd;
+ int port = dev->id_iobase; /*Take port ID selected in probe()*/
+
+#ifdef DEBUGPROBE
+ printf("matcdc: Attach dev %x id_unit %d\n",
+ (unsigned int)dev,dev->id_unit);
+#endif /*DEBUGPROBE*/
+ printf("matcdc%d Host interface type %d\n",
+ nextcontroller,iftype);
+ TAILQ_INIT(&request_head[nextcontroller]);
+ for (cdrive=0; cdrive<4; cdrive++) { /*We're hunting drives...*/
+ zero_cmd(cmd);
+ cmd[0]=NOP; /*A reasonably harmless command.
+ This command will fail after
+ power-up or after reset. It's OK*/
+ i=cdrive+(DRIVESPERC*nextcontroller);
+ if (matcd_fastcmd(port,i,cdrive,cmd)==0) { /*Issue cmd*/
+ z=get_stat(port,cdrive);/*Read status byte*/
+ if ((z & MATCD_ST_ERROR)) { /*If there was an error,
+ we must ask for error info
+ or subsequent cmds fail*/
+ zero_cmd(cmd);
+ cmd[0]=READERROR; /*Inquire*/
+ matcd_fastcmd(port,i,cdrive,cmd);
+ matcd_pread(port,8,data);/*Read data returned*/
+ z=get_stat(port,i);/*Read status byte*/
+#ifdef DEBUGPROBE
+ printf("matcd%d: Status byte %x ",i,z);
+#endif /*DEBUGPROBE*/
+ }
+ zero_cmd(cmd);
+ cmd[0]=READID; /*Get drive ID*/
+ matcd_fastcmd(port,i,cdrive,cmd);
+ matcd_pread(port,10,data);/*Read Drive Parm*/
+ get_stat(port,i); /*Read and toss status byte*/
+ data[10]=0; /*Build ASCIZ string*/
+ printf("matcd%d: [%s] ",i,data);
+ cd=&matcd_data[i];
+ cd->flags |= MATCDINIT;
+ cd->iobase=dev->id_iobase;
+ cd->iftype=iftype;
+ cd->openflags=0;
+ cd->volume[0]=cd->volume[1]=DEFVOL;
+ /*<12>Match volume drive resets to*/
+ cd->patch[0]=0x01; /*<12>Channel 0 to Left*/
+ cd->patch[1]=0x02; /*<12>Channel 1 to Right*/
+ cd->status=CD_AS_NO_STATUS;
+ for (i=0; i<MAXPARTITIONS; i++) {
+ cd->partflags[i]=0;
+ }
+#ifdef DEVFS
+ cd->ra_devfs_token = devfs_add_devswf(&matcd_cdevsw,
+ dkmakeminor(i, 0, 0), DV_CHR,
+ UID_ROOT, GID_OPERATOR, 0640, "rmatcd%da", i);
+ cd->rc_devfs_token = devfs_add_devswf(&matcd_cdevsw,
+ dkmakeminor(i, 0, RAW_PART), DV_CHR,
+ UID_ROOT, GID_OPERATOR, 0640, "rmatcd%dc", i);
+ cd->a_devfs_token = devfs_add_devswf(&matcd_bdevsw,
+ dkmakeminor(i, 0, 0), DV_BLK,
+ UID_ROOT, GID_OPERATOR, 0640, "matcd%da", i);
+ cd->c_devfs_token = devfs_add_devswf(&matcd_bdevsw,
+ dkmakeminor(i, 0, RAW_PART), DV_BLK,
+ UID_ROOT, GID_OPERATOR, 0640, "matcd%dc", i);
+ cd->rla_devfs_token = devfs_add_devswf(&matcd_cdevsw,
+ 0x80 | dkmakeminor(i, 0, 0), DV_CHR,
+ UID_ROOT, GID_OPERATOR, 0640, "rmatcd%dla", i);
+ cd->rlc_devfs_token = devfs_add_devswf(&matcd_cdevsw,
+ 0x80 | dkmakeminor(i, 0, RAW_PART), DV_CHR,
+ UID_ROOT, GID_OPERATOR, 0640, "rmatcd%dc", i);
+ cd->la_devfs_token = devfs_add_devswf(&matcd_bdevsw,
+ 0x80 | dkmakeminor(i, 0, 0), DV_BLK,
+ UID_ROOT, GID_OPERATOR, 0640, "matcd%dla", i);
+ cd->lc_devfs_token = devfs_add_devswf(&matcd_bdevsw,
+ 0x80 | dkmakeminor(i, 0, RAW_PART), DV_BLK,
+ UID_ROOT, GID_OPERATOR, 0640, "matcd%dlc", i);
+#endif
+ }
+ }
+ nextcontroller++; /*Bump ctlr assign to next number*/
+ printf("\n"); /*End line of drive reports*/
+
+ return(1);
+}
+
+
+/*---------------------------------------------------------------------------
+ zero_cmd - Initialize command buffer
+---------------------------------------------------------------------------*/
+
+void zero_cmd(char * lcmd)
+{
+ int i;
+
+ for (i=0; i<MAXCMDSIZ; lcmd[i++]=0);
+ return;
+}
+
+
+/*---------------------------------------------------------------------------
+ doreset - Resets all the drives connected to a interface
+---------------------------------------------------------------------------*/
+
+void doreset(int port,int cdrive)
+{
+ register int i,z;
+ outb(port+RESET,0); /*Reset what might be our device*/
+ /*Although this ensures a known
+ state, it does close the drive
+ door (if open) and aborts any
+ audio playback in progress. */
+ for (i=0;i<(125*ISABUSKHZ);i++){/*DELAY 500msec minimum. Worst
+ case is door open and none or
+ unreadable media */
+ z=inb(port+CMD); /*This makes the loop run at a
+ known speed. This value is ok
+ for 8.33MHz bus*/
+ }
+ for (i=0;i<4;i++) {
+ matcd_data[(cdrive*4)+i].drivemode=MODE_UNKNOWN;
+ }
+ return;
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_fastcmd - Send a command to a drive
+
+ This routine executed commands that return instantly (or reasonably
+ quick), such as RESET, NOP, READ ERROR, etc. The only difference
+ between it and handling for slower commands, is the slower commands
+ will invoke a timeout/sleep if they don't get an instant response.
+
+ Fastcmd is mainly used in probe(), attach() and error related
+ functions. Every attempt should be made to NOT use this
+ function for any command that might be executed when the system
+ is up.
+---------------------------------------------------------------------------*/
+
+int matcd_fastcmd(int port,int ldrive,int cdrive,unsigned char * cp)
+{
+ unsigned int i;
+ unsigned char z;
+ int level;
+#ifdef DEBUGCMD
+ unsigned char *cx;
+#endif /*DEBUGCMD*/
+
+
+
+ draincmd(port,cdrive,ldrive); /*Make sure bus is really idle*/
+#ifdef DEBUGCMD
+ cx=cp;
+ printf("matcd%d: Fast Send port %x sel %d command %x %x %x %x %x %x %x\n",
+ ldrive,port,cdrive,cx[0],cx[1],cx[2],cx[3],cx[4],cx[5],cx[6]);
+#endif /*DEBUGCMD*/
+ selectdrive(port,cdrive); /*Enable the desired target drive*/
+ level=splhigh(); /*----------------------------------------*/
+ for (i=0; i<7; i++) { /*The seven bytes of the command*/
+ outb(port+CMD,*cp++); /*must be sent within 10msec or*/
+ } /*the drive will ignore the cmd*/
+ splx(level); /*------------------------------------------------*/
+
+/* Now we wait a maximum of 240msec for a response.
+ Only in a few rare cases does it take this long.
+ If it is longer, the command should probably be slept on
+ rather than increasing the timing value
+*/
+
+ for (i=0; i<(60*ISABUSKHZ); i++) {
+ z = (inb(port+STATUS)) & (DTEN|STEN);
+ if (z != (DTEN|STEN)) break;
+ }
+
+/* We are now either in a data or status phase, OR we timed-out.*/
+
+ if (z == (DTEN|STEN)) {
+#ifdef DEBUGCMD
+ printf("matcd%d: Command time-out\n",ldrive);
+#endif /*DEBUGCMD*/
+ return(-1);
+ }
+ if (z != DTEN) {
+ return(1);
+ }
+ return(0);
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_slowcmd - Issue a command to the drive
+
+ This routine is for commands that might take a long time, such
+ as a read or seek. The caller must determine if the command
+ completes instantly or schedule a poll later on.
+---------------------------------------------------------------------------*/
+
+void matcd_slowcmd(int port,int ldrive,int cdrive,unsigned char * cp)
+{
+ unsigned int i;
+ int level,size;
+#ifdef DEBUGCMD
+ unsigned char *cx;
+#endif /*DEBUGCMD*/
+
+
+ draincmd(port,cdrive,ldrive); /*Make sure bus is really idle*/
+
+#ifdef DEBUGCMD
+ cx=cp;
+ printf("matcd%d: Slow Send port %x sel %d command %x %x %x %x %x %x %x\n",
+ ldrive,port,cdrive,cx[0],cx[1],cx[2],cx[3],cx[4],cx[5],cx[6]);
+#endif /*DEBUGCMD*/
+ selectdrive(port,cdrive); /*Enable the desired target drive*/
+ if (*cp==ABORT) size=1;
+ else size=7;
+ level=splhigh(); /*----------------------------------------*/
+ for (i=0; i<size; i++) { /*The seven bytes of the command*/
+ outb(port+CMD,*cp++); /*must be sent within 10msec or*/
+ } /*the drive will ignore the cmd*/
+ splx(level); /*------------------------------------------------*/
+ return;
+}
+
+
+/*---------------------------------------------------------------------------
+ draincmd - Makes certain the bus is idle and throws away
+ any residual data from the drive if there is any.
+ Called as preface to most commands.
+ Added in Edit 5.
+
+ This was added because switching drive modes causes
+ the drive to emit buffers that were meant to be sent
+ to the D-to-A to be sent to the host. See setmode.
+---------------------------------------------------------------------------*/
+void draincmd(int port,int cdrive,int ldrive)
+{
+ int i,z;
+
+ i=inb(port+STATUS);
+ if ((i & (DTEN|STEN)) == (DTEN|STEN)) return;
+
+ printf("matcd%d: in draincmd: bus not idle %x - trying to fix\n",
+ ldrive,inb(port+STATUS));
+ if ((i & (DTEN|STEN)) == STEN) {
+#ifdef DEBUGCMD
+ printf("matcd%d: Data present READING - ",ldrive);
+#endif /*DEBUGCMD*/
+ i=0;
+ outb(port+PHASE,1); /*<16>Enable data read*/
+ while ((inb(port+STATUS) & (DTEN|STEN)) == STEN) {
+ inb(port+DATA); /*<21>Ok for Creative*/
+ inb(port+ALTDATA); /*<21>Ok for others*/
+ i++;
+ }
+ outb(port+PHASE,0);
+#ifdef DEBUGCMD
+ printf("%d bytes read\n",i);
+#endif /*DEBUGCMD*/
+ }
+#ifdef DEBUGCMD
+ printf("matcd%d: Now read status: ",ldrive);
+#endif /*DEBUGCMD*/
+ i=get_stat(port,ldrive); /*Read status byte*/
+ z=inb(port+STATUS); /*Read bus status*/
+#ifdef DEBUGCMD
+ printf("Data byte %x and status is now %x\n",i,z);
+#endif /*DEBUGCMD*/
+ if ((z & (DTEN|STEN)) != (DTEN|STEN)) {
+ printf("matcd%d: Bus not idle %x - resetting\n",
+ cdrive,inb(port+STATUS));
+ doreset(port,cdrive);
+ }
+ return;
+}
+
+
+/*---------------------------------------------------------------------------
+ selectdrive - Swaps drive select bits
+
+ On Creative SB/SB16/stand-alone adapters, possibly to make them
+ hard to reverse engineer, the drive select signals are swapped.
+---------------------------------------------------------------------------*/
+
+void selectdrive(int port,int drive)
+{
+ switch(drive) {
+ case 0: /*0x00 -> 0x00*/
+ outb(port+SELECT,CRDRIVE0);
+ break;
+ case 1: /*0x01 -> 0x02*/
+ outb(port+SELECT,CRDRIVE1);
+ break;
+ case 2: /*0x02 -> 0x01*/
+ outb(port+SELECT,CRDRIVE2);
+ break;
+ case 3: /*0x03 -> 0x03*/
+ outb(port+SELECT,CRDRIVE3);
+ break;
+ }
+ return;
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_pread - Read small blocks of control data from a drive
+---------------------------------------------------------------------------*/
+
+void matcd_pread(int port, int count, unsigned char * data)
+{
+ int i;
+
+ for (i=0; i<count; i++) {
+ *data++ = inb(port+CMD);
+ }
+ return;
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_setmode - Configures disc to run in the desired data mode
+
+ This routine assumes the drive is already idle.
+
+NOTE - Undocumented action of hardware: If you change (or reaffirm) data
+ modes with MODESELECT + BLOCKPARAM immediately after a command was
+ issued that aborted a DA play operation, the drive will unexpectedly
+ return 2532 bytes of data in a data phase on the first or second
+ subsequent command.
+
+ Original Symptom: drive will refuse to go idle after reading data
+ and status expected for a command. State mechanics for this are
+ not fully understood.
+---------------------------------------------------------------------------*/
+
+static int
+matcd_setmode(int ldrive, int mode)
+{
+ struct matcd_data *cd;
+ int retries;
+ int i,port,cdrive;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd = matcd_data + ldrive;
+ retries=3;
+ cdrive=ldrive&0x03;
+ port=cd->iobase;
+ if (cd->drivemode==mode) {
+ return(0); /*Drive already set*/
+ }
+
+/* The drive is not in the right mode, so we need to set it.
+*/
+
+ zero_cmd(cmd);
+ cmd[0]=MODESELECT; /*Set drive transfer modes*/
+/* cmd[1]=BLOCKPARAM; BLOCKPARAM==0*/
+ cmd[2]=mode;
+ switch(mode) {
+ case MODE_DATA:
+ cmd[3]=0x08; /*2048 bytes*/
+ break;
+ case MODE_USER:
+ cmd[3]=0x09; /*2352 bytes*/
+ cmd[4]=0x30;
+ break;
+ case MODE_DA:
+ cmd[3]=0x09; /*2352 bytes*/
+ cmd[4]=0x30;
+ break;
+ }
+ i=0;
+ while(retries-- > 0) {
+ i=matcd_fastcmd(port,ldrive,cdrive,cmd);
+ get_stat(port,ldrive); /*Read and toss status byte*/
+ if (i==0) {
+ cd->drivemode=mode; /*Set new mode*/
+ return(i);
+ }
+ get_error(port,ldrive,cdrive);
+ }
+ cd->drivemode=MODE_UNKNOWN; /*We failed*/
+ return(i);
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_volinfo - Read information from disc Table of Contents
+---------------------------------------------------------------------------*/
+
+static int matcd_volinfo(int ldrive)
+{
+ struct matcd_data *cd;
+ int port,i;
+ int z,cdrive;
+ int retry;
+ unsigned char cmd[MAXCMDSIZ];
+ unsigned char data[12];
+
+ retry=10; /*<16>This may take a long time*/
+ cd = &matcd_data[ldrive];
+ cdrive=ldrive&0x03;
+ port=cd->iobase;
+
+#ifdef DEBUGOPEN
+ printf("matcd%d: In volinfo, port %x\n",ldrive,port);
+#endif /*DEBUGOPEN*/
+
+ while(retry>0) {
+ zero_cmd(cmd);
+ cmd[0]=READDINFO; /*Read Disc Info*/
+ matcd_slowcmd(port,ldrive,cdrive,cmd);
+ i=waitforit(10*TICKRES,DTEN,port,"matvinf");
+ if (i) { /*THIS SHOULD NOT HAPPEN*/
+ z=get_stat(port,ldrive);/*Read status byte*/
+ printf("matcd%d: command failed, status %x\n",
+ ldrive,z);
+ return(-1);
+ }
+ matcd_pread(port, 6, data); /*Read data returned*/
+ z=get_stat(port,ldrive);/*Read status byte*/
+#ifdef DEBUGOPEN
+ printf("matcd%d: Data got was %x %x %x %x %x %x ",ldrive,
+ data[0],data[1],data[2], data[3],data[4],data[5]);
+ printf("status byte %x\n",z);
+#endif /*DEBUGOPEN*/
+ if ((z & MATCD_ST_ERROR)==0)
+ break; /*No Error*/
+
+/* If media change or other error, you have to read error data or
+ the drive will reject subsequent commands.
+*/
+
+ if (chk_error(get_error(port, ldrive, cdrive))==ERR_FATAL) {
+#ifdef DEBUGOPEN
+ printf("matcd%d: command failed, status %x\n",
+ ldrive,z);
+#endif /*DEBUGOPEN*/
+ return(-1);
+ }
+ tsleep((caddr_t)&nextcontroller, PRIBIO, "matvi2", hz);
+ if ((--retry)==0) return(-1);
+#ifdef DEBUGOPEN
+ printf("matcd%d: Retrying",ldrive);
+#endif /*DEBUGOPEN*/
+ }
+#ifdef DEBUGOPEN
+ printf("matcd%d: Status port %x \n",ldrive,inb(port+STATUS));
+#endif /*DEBUGOPEN*/
+
+ cd->volinfo.type=data[0];
+ cd->volinfo.trk_high=data[2];
+ cd->volinfo.trk_low=data[1];
+ cd->volinfo.vol_msf[0]=data[3];
+ cd->volinfo.vol_msf[1]=data[4];
+ cd->volinfo.vol_msf[2]=data[5];
+
+ if (cd->volinfo.trk_low + cd->volinfo.trk_high) {
+ cd->flags |= MATCDLABEL;
+ return(0);
+ }
+ return(-1);
+}
+
+
+/*---------------------------------------------------------------------------
+ blk_to_msf - Convert block numbers into CD disk block ids
+---------------------------------------------------------------------------*/
+
+static void blk_to_msf(int blk, unsigned char *msf)
+{
+ blk=blk+150; /*2 seconds skip required to
+ reach ISO data*/
+ msf[0]=blk/4500;
+ blk=blk%4500;
+ msf[1]=blk/75;
+ msf[2]=blk%75;
+ return;
+}
+
+
+/*---------------------------------------------------------------------------
+ msf_to_blk - Convert CD disk block ids into block numbers
+---------------------------------------------------------------------------*/
+
+static int msf_to_blk(unsigned char * cd)
+{
+ return(((cd[0]*60) /*Convert MSF to*/
+ +cd[1])*75 /*Blocks minus 2*/
+ +cd[2]-150); /*seconds*/
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_blockread - Performs actual background disc I/O operations
+
+ This routine is handed the block number to read, issues the
+ command to the drive, waits for it to complete, reads the
+ data or error, retries if needed, and returns the results
+ to the host.
+---------------------------------------------------------------------------*/
+
+static void matcd_blockread(int state)
+{
+ struct matcd_mbx *mbx;
+ int ldrive,cdrive;
+ int port, controller;
+ short iftype;
+ struct buf *bp;
+ struct matcd_data *cd;
+ int i;
+ struct matcd_read2 rbuf;
+ int blknum;
+ caddr_t addr;
+ int status;
+ int errtyp;
+ int phase;
+ unsigned char cmd[MAXCMDSIZ];
+
+ mbx = &matcd_data[state & 0x0f].mbx;
+ ldrive=mbx->ldrive; /*ldrive is logical drive #*/
+ cdrive=ldrive & 0x03; /*cdrive is drive # on a controller*/
+ port=mbx->port; /*port is base port for i/f*/
+ iftype=mbx->iftype;
+ bp= mbx->bp;
+ cd=&matcd_data[ldrive];
+ controller = cd->mbx.controller;
+
+#ifdef DEBUGIO
+ printf("matcd%d: Show state %x cdrive %d partition %d\n",
+ ldrive,state,cdrive,mbx->partition);
+#endif /*DEBUGIO*/
+
+loop:
+#ifdef DEBUGIO
+ printf("matcd%d: Top dp %x\n",ldrive,(unsigned int)dp);
+#endif /*DEBUGIO*/
+ switch (state & 0xf0) {
+ case MATCD_READ_1:
+#ifdef DEBUGIO
+ printf("matcd%d: State 1 cd->flags %x\n",ldrive,cd->flags);
+#endif /*DEBUGIO*/
+ /* to check for raw/cooked mode */
+ if (cd->partflags[mbx->partition] & MATCDREADRAW) {
+ mbx->sz = MATCDRBLK;
+ i=matcd_setmode(ldrive, MODE_DA);
+#ifdef DEBUGIO
+ printf("matcd%d: Set MODE_DA result %d\n",ldrive,i);
+#endif /*DEBUGIO*/
+ } else {
+ mbx->sz = cd->blksize;
+ i=matcd_setmode(ldrive, MODE_DATA);
+#ifdef DEBUGIO
+ printf("matcd%d: Set MODE_DATA result %d\n",ldrive,i);
+#endif /*DEBUGIO*/
+ }
+ /*for first block*/
+#ifdef DEBUGIO
+ printf("matcd%d: A mbx %x bp %x b_bcount %x sz %x\n",
+ ldrive,(unsigned int)mbx,(unsigned int)bp,
+ (unsigned int)bp->b_bcount,mbx->sz);
+#endif /*DEBUGIO*/
+ mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
+ mbx->skip=0;
+nextblock:
+#ifdef DEBUGIO
+ printf("matcd%d: at Nextblock b_blkno %d\n",
+ ldrive,(unsigned int)bp->b_blkno);
+#endif /*DEBUGIO*/
+
+ blknum=(bp->b_blkno / (mbx->sz/DEV_BSIZE))
+ + mbx->p_offset + mbx->skip/mbx->sz;
+
+ blk_to_msf(blknum,rbuf.start_msf);
+
+ zero_cmd(cmd);
+ cmd[0]=READ; /*Get drive ID*/
+ cmd[1]=rbuf.start_msf[0];
+ cmd[2]=rbuf.start_msf[1];
+ cmd[3]=rbuf.start_msf[2];
+ cmd[6]=1; /*Xfer only one block*/
+ matcd_slowcmd(port,ldrive,cdrive,cmd);
+
+/* Now that we have issued the command, check immediately to
+ see if data is ready. The drive has read-ahead caching, so
+ it is possible the data is already in the drive buffer.
+
+ If the data is not ready, schedule a wakeup and later on this
+ code will run again to see if the data is ready then.
+*/
+
+ case MATCD_READ_2:
+ state=MATCD_READ_2+ldrive;
+ phase = (inb(port+STATUS)) & (DTEN|STEN);
+#ifdef DEBUGIO
+ printf("matcd%d: In state 2 status %x ",ldrive,phase);
+#endif /*DEBUGIO*/
+ switch(phase) {
+ case (DTEN|STEN): /*DTEN==H STEN==H*/
+#ifdef DEBUGIO
+ printf("matcd%d: Sleeping\n",ldrive);
+#endif /*DEBUGIO*/
+ timeout((timeout_func_t)matcd_blockread,
+ (caddr_t)MATCD_READ_2+ldrive,hz/100);
+ return;
+
+
+ case STEN: /*DTEN=L STEN=H*/
+ case 0: /*DTEN=L STEN=L*/
+#ifdef DEBUGIO
+ printf("matcd%d: Data Phase\n",ldrive);
+#endif /*DEBUGIO*/
+ addr=bp->b_un.b_addr + mbx->skip;
+#ifdef DEBUGIO
+ printf("matcd%d: Xfer Addr %x size %x",
+ ldrive,(unsigned int)addr,mbx->sz);
+ i=0; /*<20>Reset read count*/
+#endif /*DEBUGIO*/
+ if (iftype==0) { /*<20>Creative host I/F*/
+ outb(port+PHASE,1); /*Enable data read*/
+ while((inb(port+STATUS) &
+ (DTEN|STEN))==STEN) {
+ *addr++=inb(port+DATA);
+#ifdef DEBUGIO
+ i++;
+#endif /*DEBUGIO*/
+ }
+ outb(port+PHASE,0); /*Disable read*/
+ } else { /*<20>Not Creative interface*/
+ while((inb(port+STATUS) &
+ (DTEN|STEN))==STEN) {
+ *addr++=inb(port+ALTDATA);
+#ifdef DEBUGIO
+ i++;
+#endif /*DEBUGIO*/
+ }
+ }
+#ifdef DEBUGIO
+ printf("matcd%d: Read %d bytes\n",ldrive,i);
+#endif /*DEBUGIO*/
+
+
+/* Now, wait for the Status phase to arrive. This will also
+ tell us if any went wrong with the request.
+*/
+ while((inb(port+STATUS)&(DTEN|STEN)) != DTEN);
+ status=get_stat(port,ldrive); /*Read status byte*/
+#ifdef DEBUGIO
+ printf("matcd%d: Status port %x byte %x ",
+ ldrive,i,status);
+#endif /*DEBUGIO*/
+ if (status & MATCD_ST_ERROR) {
+ i=get_error(port,ldrive,cdrive);
+ printf("matcd%d: %s while reading block %d [Soft]\n",
+ ldrive,matcderrors[i],(int)bp->b_blkno);
+ media_chk(cd,i,ldrive,0);/*<14>was wrong place*/
+ }
+
+ if (--mbx->nblk > 0) {
+ mbx->skip += mbx->sz;
+ goto nextblock; /*Oooooh, you flunk the course*/
+ }
+ bp->b_resid=0;
+ biodone(bp); /*Signal transfer complete*/
+
+ unlockbus(ldrive>>2, ldrive); /*Release bus lock*/
+ matcd_start(controller);/*See if other drives have work*/
+ return;
+
+/* Here we skipped the data phase and went directly to status.
+ This indicates a hard error.
+*/
+
+ case DTEN: /*DTEN=H STEN=L*/
+ status=get_stat(port,ldrive); /*Read status byte*/
+#ifdef DEBUGIO
+ printf("matcd%d: error, status was %x\n",
+ ldrive,status);
+#endif /*DEBUGIO*/
+
+/* Ok, we need more details, so read error. This is needed to issue
+ any further commands anyway
+*/
+
+ errtyp=get_error(port,ldrive,cdrive);
+ printf("matcd%d: %s while reading block %d\n",
+ ldrive,matcderrors[errtyp],(int)bp->b_blkno);
+
+ if (media_chk(cd,errtyp,ldrive,0)==0) {
+ errtyp=chk_error(errtyp);
+ if (errtyp==ERR_RETRY) {/*<14>We can retry*/
+ /*<14>this error but the drive*/
+ /*<14>probably has already*/
+ if (mbx->retry-- > 0 ) {
+ state=MATCD_READ_1+ldrive;
+#ifdef DEBUGIO
+ printf("matcd%d: Attempting retry\n",
+ ldrive);
+#endif /*DEBUGIO*/
+ goto loop;
+ }
+ }
+ }
+/*<14> The other error types are either something very bad or the media
+<14> has been removed by the user. In both cases there is no retry
+<14> for this call. We will invalidate the label in both cases.
+*/
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ unlockbus(ldrive>>2, ldrive);
+ matcd_start(controller);
+ return;
+ }
+ }
+}
+
+
+/*---------------------------------------------------------------------------
+ docmd - Get the bus, do the command, wait for completion,
+ attempt retries, give up the bus.
+ For commands that do not return data.
+---------------------------------------------------------------------------*/
+
+int docmd(char * cmd, int ldrive, int cdrive, int controller, int port)
+{
+ int retries,i,z;
+
+ lockbus(controller, ldrive); /*Request bus*/
+ retries=3;
+ while(retries-- > 0) {
+ matcd_slowcmd(port,ldrive,cdrive,cmd);
+ i=waitforit(80*TICKRES,DTEN,port,"matcmd");
+ z=get_stat(port,ldrive);/*Read status byte*/
+ if ((z & MATCD_ST_ERROR)==0) break;
+ i=chk_error(get_error(port,ldrive,cdrive));
+ if (i!=ERR_INIT) {
+ unlockbus(controller, ldrive); /*Release bus*/
+ return(EFAULT);
+ }
+ }
+ unlockbus(controller, ldrive); /*Release bus*/
+ return(i);
+}
+
+
+/*---------------------------------------------------------------------------
+ get_error - Read the error that aborted a command.
+ Created in Edit 6
+---------------------------------------------------------------------------*/
+
+int get_error(int port, int ldrive, int cdrive)
+{
+ int status,errnum;
+ unsigned char cmd1[MAXCMDSIZ];
+ unsigned char data[12];
+
+ zero_cmd(cmd1);
+ cmd1[0]=READERROR; /*Enquire*/
+ matcd_fastcmd(port,ldrive,cdrive,cmd1);
+ matcd_pread(port, 8, data); /*Read data returned*/
+ errnum=data[2]; /*Caller wants it classified*/
+ status=get_stat(port,ldrive); /*Read status byte*/
+
+#ifdef DEBUGCMD
+ printf("matcd%d: Chkerror found %x on command %x addrval %x statusdata %x statusport %x\n",
+ ldrive,errnum,data[1],data[0],status,inb(port+STATUS));
+#endif /*DEBUGCMD*/
+ return(errnum);
+}
+
+
+/*---------------------------------------------------------------------------
+ chk_error - Classify the error that the drive reported
+ Created in Edit 6
+---------------------------------------------------------------------------*/
+
+int chk_error(int errnum)
+{
+ switch(errnum) {
+/* These are errors we can attempt a retry for, although the drive
+ has already done so.
+*/
+ case UNRECV_ERROR:
+ case SEEK_ERROR:
+ case TRACK_ERROR:
+ case FOCUS_ERROR:
+ case CLV_ERROR:
+ case DATA_ERROR:
+ case MODE_ERROR: /*<16>Make this retryable*/
+ return(ERR_RETRY);
+
+/* These errors usually indicate the user took the media from the
+ drive while the dev was open. We will invalidate the unit
+ until it closes when we see this.
+*/
+ case NOT_READY:
+ case MEDIA_CHANGED:
+ case DISC_OUT:
+ case HARD_RESET:
+ return (ERR_INIT);
+
+/* These errors indicate the system is confused about the drive
+ or media, and point to bugs in the driver or OS. These errors
+ cannot be retried since you will always get the same error.
+*/
+
+ case RAM_ERROR:
+ case DIAG_ERROR:
+ case CDB_ERROR:
+ case END_ADDRESS:
+ case ILLEGAL_REQ:
+ case ADDRESS_ERROR:
+ default:
+ return (ERR_FATAL);
+ }
+}
+
+
+/*---------------------------------------------------------------------------
+ get_stat - Reads status byte
+
+ This routine should be totally unnecessary, performing the
+ task with a single line of in-line code. However in special
+ cases, the drives return blocks of data that are not associated
+ with the command in question. This appears to be at least one
+ firmware error and the rest of the driver makes an effort to avoid
+ triggering the fault. However, reading and throwing this
+ bogus data is faster and less destructive than resetting all
+ the drives on a given controller, plus it leaves the other drives
+ unaffected.
+---------------------------------------------------------------------------*/
+
+int get_stat(int port,int ldrive)
+{
+ int status,busstat;
+
+ status=inb(port+DATA); /*Read status byte, last step of cmd*/
+ busstat=inb(port+STATUS); /*<16>Get bus status - should be 0xff*/
+ while ((busstat & (DTEN|STEN)) != (DTEN|STEN)) {
+ printf("matcd%d: get_stat: After reading status byte, bus didn't go idle %x %x %x\n",ldrive,status,busstat,port);
+ if (( busstat & (DTEN|STEN)) == STEN) {
+ int k;
+ k=0;
+#ifdef DEBUGCMD
+ printf("matcd%d: DATA PRESENT!!!! DISCARDING\n",ldrive);
+#endif /*DEBUGCMD*/
+ outb(port+PHASE,1); /*Enable data read*/
+ while ((inb(port+STATUS) & (DTEN|STEN)) == STEN) {
+ inb(port+DATA);
+ inb(port+ALTDATA);
+/* printf("%2x ",inb(port+DATA));*/
+ k++;
+ }
+ outb(port+PHASE,0);
+#ifdef DEBUGCMD
+ printf("\nmatcd%d: BYTES READ IN DATA was %d\n",
+ ldrive,k);
+#endif /*DEBUGCMD*/
+ }
+ status=inb(port+DATA); /*Read the status byte again*/
+#ifdef DEBUGCMD
+ printf("matcd%d: Next status byte is %x\n",ldrive,status);
+#endif /*DEBUGCMD*/
+ busstat=inb(port+STATUS);
+ }
+ return(status);
+}
+
+
+/*---------------------------------------------------------------------------
+ waitforit - Waits for a command started by slowcmd to complete.
+---------------------------------------------------------------------------*/
+
+int waitforit(int timelimit, int state, int port, char * where)
+{
+ int i,j;
+
+ j=i=0;
+#ifdef DEBUGCMD
+ printf("matcd: waitforit port %x timelimit %x hz %x\n",
+ port,timelimit,hz);
+#endif /*DEBUGCMD*/
+ while (i<timelimit) {
+ j=inb(port+STATUS) & (STEN|DTEN); /*Read status*/
+ if (j!=(STEN|DTEN)) break;
+ tsleep((caddr_t)&nextcontroller, PRIBIO, where, hz/100);
+ i++;
+ }
+#ifdef DEBUGCMD
+ printf("matcd: Count was %d\n",i);
+#endif /*DEBUGCMD*/
+ if (j==state) return(0); /*Command complete*/
+#ifdef DEBUGCMD
+ printf("matcd: Timeout!");
+#endif /*DEBUGCMD*/
+ return(1); /*Timeout occurred*/
+}
+
+
+/*---------------------------------------------------------------------------
+ lockbus - Wait for the bus on the requested driver interface
+ to go idle and acquire it.
+ Created in Edit 6
+---------------------------------------------------------------------------*/
+
+void lockbus(int controller, int ldrive)
+{
+ while ((if_state[controller] & BUSBUSY)) {
+#ifdef DEBUGSLEEP
+ printf("matcd%d: Can't do it now - going to sleep\n",
+ ldrive);
+#endif /*DEBUGSLEEP*/
+ tsleep((caddr_t)&matcd_data->status, PRIBIO,
+ "matlck", 0);
+ }
+ if_state[controller] |= BUSBUSY; /*<18>It's ours NOW*/
+#ifdef DEBUGSLEEP
+ printf("matcd%d: BUS locked in lockbus\n",ldrive);
+#endif /*DEBUGSLEEP*/
+}
+
+
+/*---------------------------------------------------------------------------
+ unlockbus - Release the host interface bus we already have so
+ someone else can use it.
+ Created in Edit 6
+---------------------------------------------------------------------------*/
+
+void unlockbus(int controller, int ldrive)
+{
+ if_state[controller] &= ~BUSBUSY;
+#ifdef DEBUGSLEEP
+ printf("matcd%d: bus unlocked\n",ldrive);
+#endif /*DEBUGSLEEP*/
+ wakeup((caddr_t)&matcd_data->status); /*Wakeup other users*/
+ matcd_start(controller); /*Wake up any block I/O*/
+}
+
+
+/*---------------------------------------------------------------------------
+ media_chk - Checks error for types related to media
+ changes.
+---------------------------------------------------------------------------*/
+
+int media_chk(struct matcd_data *cd,int errnum,int ldrive,int test)
+{
+ if (errnum==NOT_READY ||
+ errnum==MEDIA_CHANGED ||
+ errnum==HARD_RESET ||
+ errnum==DISC_OUT) {
+ cd->flags &= ~MATCDLABEL; /*Mark label as invalid*/
+ if (test==0) { /*<14>Do warn by default*/
+
+ if ((cd->flags & MATCDWARN)==0) {/*<14>Msg already?*/
+ printf("matcd%d: Media changed - Further I/O aborted until device closed\n",ldrive);
+ cd->flags |= MATCDWARN;
+ }
+ }
+ return(1);
+ }
+ if (errnum==MODE_ERROR) /*<16>Maybe the setting is*/
+ cd->drivemode=MODE_UNKNOWN; /*<16>wrong so force a reset*/
+ return(0);
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_eject - Open drive tray
+---------------------------------------------------------------------------*/
+
+int matcd_eject(int ldrive, int cdrive, int controller)
+{
+ int i,port;
+ struct matcd_data *cd;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase; /*Get I/O port base*/
+
+#ifdef LOCKDRIVE
+ if (cd->flags & MATCDLOCK) { /*<15>Drive was locked via open*/
+ return(EINVAL); /*<15>so don't allow the eject*/
+ }
+#endif /*LOCKDRIVE*/
+ zero_cmd(cmd); /*Initialize command buffer*/
+ cmd[0]=LOCK; /*Unlock drive*/
+ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/
+ cmd[0]=DOOROPEN; /*Open Door*/
+ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/
+ cd->flags &= ~(MATCDLABEL|MATCDLOCK); /*<15>Mark vol info invalid*/
+ return(i); /*Return result we got*/
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_doorclose - Close drive tray
+<16> Added in Edit 16
+---------------------------------------------------------------------------*/
+
+int matcd_doorclose(int ldrive, int cdrive, int controller)
+{
+ int i,port;
+ struct matcd_data *cd;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase; /*Get I/O port base*/
+
+ zero_cmd(cmd); /*Initialize command buffer*/
+ cmd[0]=DOORCLOSE; /*Open Door*/
+ i=docmd(cmd,ldrive,cdrive,controller,port); /*Issue command*/
+ cd->flags &= ~(MATCDLABEL|MATCDLOCK); /*Mark vol info invalid*/
+ tsleep((caddr_t)&nextcontroller, PRIBIO, "matclos", hz);
+ return(i); /*Return result we got*/
+}
+
+
+/*---------------------------------------------------------------------------
+<23> matcd_dlock - Honor/Reject drive tray requests
+---------------------------------------------------------------------------*/
+
+int matcd_dlock(int ldrive, int cdrive, int controller, int action)
+{
+ int i,port;
+ struct matcd_data *cd;
+ unsigned char cmd[MAXCMDSIZ];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase; /*<23>Get I/O port base*/
+
+ zero_cmd(cmd); /*<23>Initialize command buffer*/
+ cmd[0]=LOCK; /*<23>Unlock drive*/
+
+ if (action) { /*<23>They want to lock the door?*/
+ cd->flags |= MATCDLOCK; /*<23>Remember we did this*/
+ cmd[1]=1; /*<23>Lock Door command*/
+ } else {
+ cd->flags &= ~MATCDLOCK;/*<23>Remember we did this*/
+ /*<23>Unlock Door command*/
+ }
+ i=docmd(cmd,ldrive,cdrive,controller,port); /*<23>Issue command*/
+ return(i); /*<23>Return result we got*/
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_toc_header - Return Table of Contents header to caller
+<13> New for Edit 13
+---------------------------------------------------------------------------*/
+
+static int matcd_toc_header(int ldrive, int cdrive, int controller,
+ struct ioc_toc_header * toc)
+{
+ struct matcd_data *cd;
+
+ cd=&matcd_data[ldrive];
+ if ((cd->flags & MATCDLABEL)==0)
+ return(EIO); /*Refuse after chg error*/
+
+ toc->len=msf_to_blk(cd->volinfo.vol_msf); /*In frames*/
+ toc->starting_track=cd->volinfo.trk_low; /*1*/
+ toc->ending_track=cd->volinfo.trk_high; /*Last track*/
+
+ return(0);
+
+}
+
+
+/*---------------------------------------------------------------------------
+ matcd_toc_entries - Read all of the TOC entries
+
+ These entries are cached by the drive, but it might be worth
+ the space investment to have the driver cache these as well.
+ For a disc with 40 tracks, it means 41 command calls to get
+ this information from the drive.
+<13> New for Edit 13
+---------------------------------------------------------------------------*/
+
+static int matcd_toc_entries(int ldrive, int cdrive, int controller,
+ struct ioc_read_toc_entry * ioc_entry)
+{
+ struct matcd_data *cd;
+ struct cd_toc_entry entries[MAXTRKS];
+ struct cd_toc_entry *from;
+ struct cd_toc_entry *to;
+ int len,trk,i,z,port;
+ unsigned char cmd[MAXCMDSIZ];
+ unsigned char data[5];
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+ if ((cd->flags & MATCDLABEL)==0)
+ return(EIO); /*Refuse after chg error*/
+
+ zero_cmd(cmd);
+ cmd[0]=READTOC;
+
+ for(trk=cd->volinfo.trk_low-1; trk<cd->volinfo.trk_high; trk++) {
+ cmd[2]=trk+1;
+ lockbus(controller, ldrive); /*Request bus*/
+ matcd_slowcmd(port,ldrive,cdrive,cmd);
+ i=waitforit(10*TICKRES,DTEN,port,"mats1");
+ matcd_pread(port, 8, data); /*Read data returned*/
+ z=get_stat(port,ldrive); /*Read status byte*/
+ if ((z & MATCD_ST_ERROR)) { /*Something went wrong*/
+ i=get_error(port, ldrive, cdrive);
+ unlockbus(controller, ldrive); /*Release bus*/
+ return(EIO);
+ }
+ unlockbus(controller, ldrive); /*Release bus*/
+
+#ifdef DEBUGIOCTL
+ printf("Track %d addr/ctrl %x m %x s %x f %x\n",data[2],
+ data[1],data[4],data[5],data[6]);
+#endif /*DEBUGIOCTL*/
+
+ entries[trk].control=data[1]; /*Track type*/
+ entries[trk].addr_type=ioc_entry->address_format;/*Type*/
+ entries[trk].track=data[2]; /*Track #, can be Out of Order*/
+ if (ioc_entry->address_format == CD_MSF_FORMAT) {
+ entries[trk].addr.msf.unused=0;
+ entries[trk].addr.msf.minute=data[4]; /*Min*/
+ entries[trk].addr.msf.second=data[5]; /*Sec*/
+ entries[trk].addr.msf.frame=data[6]; /*Frame*/
+ }
+ }
+ entries[trk].control=data[2]; /*Copy from last valid track*/
+ entries[trk].track=0xaa; /*<23>Lead-out*/
+ entries[trk].addr.msf.unused=0; /*Fill*/
+ entries[trk].addr.msf.minute=cd->volinfo.vol_msf[0];
+ entries[trk].addr.msf.second=cd->volinfo.vol_msf[1];
+ entries[trk].addr.msf.frame=cd->volinfo.vol_msf[2];
+ trk++; /*Bump to include leadout track*/
+
+
+/* Now that we have read all the data from the drive, copy the
+ array from the kernel address space into the user address space
+*/
+
+ len=ioc_entry->data_len;
+ i=ioc_entry->starting_track; /*<23>What did they want?*/
+ if (i==0xaa) i=trk-1; /*<23>Give them lead-out info*/
+ else i=ioc_entry->starting_track - 1; /*<23>start where they asked*/
+ from = &entries[i];
+ to = ioc_entry->data;
+
+ while (i < trk && len >= sizeof(struct cd_toc_entry)) {
+ if (copyout(from,to,sizeof(struct cd_toc_entry))
+ != 0) {
+ return (EFAULT);
+ }
+ i++;
+ len -= sizeof(struct cd_toc_entry);
+ from++;
+ to++;
+ }
+ return(0);
+
+}
+
+/*---------------------------------------------------------------------------
+ matcd_subq - Read the Sub-Q packet - (where are we?)
+
+ This call gives a snapshot state of where the optical
+ pick-up is when the command is issued.
+<14> New for Edit 14
+---------------------------------------------------------------------------*/
+
+static int matcd_read_subq(int ldrive, int cdrive, int controller,
+ struct ioc_read_subchannel * sqp)
+{
+ struct matcd_data *cd;
+ int i,z,port;
+ unsigned char cmd[MAXCMDSIZ];
+ unsigned char data[12];
+ struct cd_sub_channel_info subq; /*Build result here*/
+
+ cd=&matcd_data[ldrive];
+ port=cd->iobase;
+
+ if ((cd->flags & MATCDLABEL)==0)
+ return(EIO); /*Refuse after chg error*/
+
+/* We only support the ioctl functions we could get information
+ on, so test for the things we can do
+*/
+
+ if (sqp->data_format!=CD_CURRENT_POSITION ||
+ sqp->address_format!=CD_MSF_FORMAT) {
+ return(EINVAL);
+ }
+
+ zero_cmd(cmd);
+ cmd[0]=READSUBQ;
+ lockbus(controller, ldrive); /*Request bus*/
+ matcd_slowcmd(port,ldrive,cdrive,cmd);
+
+/* While we wait, fill in the hard-coded entries of the table*/
+
+ subq.what.position.data_format=CD_MSF_FORMAT;
+ subq.what.position.absaddr.msf.unused=0;
+ subq.what.position.reladdr.msf.unused=0;
+
+ i=waitforit(10*TICKRES,DTEN,port,"mats2");
+ matcd_pread(port, 11, data); /*Read data returned*/
+ z=get_stat(port,ldrive); /*Read status byte*/
+ if ((z & MATCD_ST_ERROR)) { /*Something went wrong*/
+ i=get_error(port, ldrive, cdrive);
+ unlockbus(controller, ldrive); /*Release bus*/
+ return(EIO);
+ }
+ unlockbus(controller, ldrive); /*Release bus*/
+
+#ifdef DEBUGIOCTL
+ printf("Subq track %d index %d adr/ctl %x abs %d:%2d:%2d rel %d:%2d:%2d UPC %x\n",
+ data[2],data[3],data[1],data[4],data[5],data[6],
+ data[7],data[8],data[9],data[10]);
+#endif /*DEBUGIOCTL*/
+
+ if (z & MATCD_ST_AUDIOBSY) { /*Drive playing or paused*/
+ if (cd->status==CD_AS_PLAY_PAUSED) { /*Have we issued*/
+ i=cd->status; /*a pause command?*/
+ } else {
+ i=CD_AS_PLAY_IN_PROGRESS;/*No, we really are playing*/
+ }
+ } else {
+ if (cd->status==CD_AS_PLAY_IN_PROGRESS) {/*It was playing*/
+ i=CD_AS_PLAY_COMPLETED; /*so it finished*/
+ } else { /*Any other status reported*/
+ i=cd->status; /*as we get it*/
+ }
+ }
+
+ subq.header.audio_status=cd->status=i; /*Store status we selected*/
+
+ subq.what.position.track_number=data[2];
+ subq.what.position.index_number=data[3];
+
+ subq.what.position.absaddr.msf.minute=data[4];
+ subq.what.position.absaddr.msf.second=data[5];
+ subq.what.position.absaddr.msf.frame=data[6];
+
+ subq.what.position.reladdr.msf.minute=data[7];
+ subq.what.position.reladdr.msf.second=data[8];
+ subq.what.position.reladdr.msf.frame=data[9];
+
+/* Ok, now copy our nicely-built structure from the kernel address
+ space into the user address space (we hope)
+*/
+
+ if (copyout(&subq, sqp->data,
+ min(sizeof(struct cd_sub_channel_info), sqp->data_len))!=0) {
+ return(EFAULT);
+ }
+ return(0);
+}
+
+/*---------------------------------------------------------------------------
+ matcd_igot - Like the song, report the capabilities that the
+ drive/driver has available.
+
+ This call returns a structure of flags indicating what
+ functions are available so that the application can offer
+ only the functions the drive is actually capable of.
+<16> New for Edit 16
+---------------------------------------------------------------------------*/
+
+static int matcd_igot(struct ioc_capability * sqp)
+{
+
+#ifdef FULLDRIVER
+ sqp->play_function=(CDDOPLAYTRK | /*Can play trks/indx*/
+ CDDOPLAYMSF | /*Can play msf to msf*/
+ CDDOPAUSE | /*Can pause playback*/
+ CDDORESUME | /*Can resume playback*/
+ CDDOSTOP | /*Can stop playback*/
+ CDDOPITCH); /*Can change play pitch*/
+
+ sqp->routing_function=(CDREADVOLUME | /*Can read volume*/
+ CDSETVOLUME | /*Can set volume*/
+ CDSETSTEREO | /*Can select stereo play*/
+ CDSETLEFT | /*Can select left-only*/
+ CDSETRIGHT | /*Can select right-only*/
+ CDSETMUTE | /*Can mute audio*/
+ CDSETPATCH); /*Direct patch settings*/
+#else /*FULLDRIVER*/
+ sqp->play_function=0; /*No audio capability*/
+ sqp->routing_function=0; /*No audio capability*/
+#endif /*FULLDRIVER*/
+
+ sqp->special_function=(CDDOEJECT | /*Door can be opened*/
+ CDDOCLOSE | /*Door can be closed*/
+ CDDOLOCK | /*Door can be locked*/
+ CDREADSUBQ | /*Can read subchannel*/
+ CDREADENTRIES | /*Can read TOC entries*/
+ CDREADHEADER); /*Can read TOC*/
+ return(0);
+}
+
+
+#ifdef FULLDRIVER
+#ifdef PC98
+#include "pc98/pc98/matcd/audio.c" /*<15>ioctls related to
+ audio are here*/
+#else
+#include "i386/isa/matcd/audio.c" /*<15>ioctls related to
+ audio are here*/
+#endif
+#endif /*FULLDRIVER*/
+
+
+static matcd_devsw_installed = 0;
+
+static void
+matcd_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! matcd_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR,0);
+ cdevsw_add(&dev,&matcd_cdevsw,NULL);
+ dev = makedev(BDEV_MAJOR,0);
+ bdevsw_add(&dev,&matcd_bdevsw,NULL);
+ matcd_devsw_installed = 1;
+ }
+}
+
+SYSINIT(matcddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,matcd_drvinit,NULL)
+
+
+/*End of matcd.c*/
+
diff --git a/sys/pc98/pc98/matcd/matcddrv.h b/sys/pc98/pc98/matcd/matcddrv.h
new file mode 100644
index 0000000..8802149
--- /dev/null
+++ b/sys/pc98/pc98/matcd/matcddrv.h
@@ -0,0 +1,198 @@
+/*matcd.h---------------------------------------------------------------------
+
+ Matsushita(Panasonic) / Creative CD-ROM Driver (matcd)
+ Authored by Frank Durda IV
+
+ Copyright 1994, 1995 Frank Durda IV. All rights reserved.
+ "FDIV" is a trademark of Frank Durda IV.
+
+
+ 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 positioned at the very beginning of this file without
+ modification, all copyright strings, all related programming
+ codes that display the copyright strings, this list of
+ conditions and the following disclaimer.
+ 2. Redistributions in binary form must contain all copyright strings
+ and related programming code that display the copyright strings.
+ 3. 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.
+ 4. All advertising materials mentioning features or use of this
+ software must display the following acknowledgement:
+ "The Matsushita/Panasonic CD-ROM driver was developed
+ by Frank Durda IV for use with "FreeBSD" and similar
+ operating systems."
+ "Similar operating systems" includes mainly non-profit oriented
+ systems for research and education, including but not restricted
+ to "NetBSD", "386BSD", and "Mach" (by CMU). The wording of the
+ acknowledgement (in electronic form or printed text) may not be
+ changed without permission from the author.
+ 5. Absolutely no warranty of function, fitness or purpose is made
+ by the author Frank Durda IV.
+ 6. Neither the name of the author nor the name "FreeBSD" may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ (The author can be reached at bsdmail@nemesis.lonestar.org)
+ 7. The product containing this software must meet all of these
+ conditions even if it is unsupported, not a complete system
+ and/or does not contain compiled code.
+ 8. These conditions will be in force for the full life of the
+ copyright.
+ 9. If all the above conditions are met, modifications to other
+ parts of this file may be freely made, although any person
+ or persons making changes do not receive the right to add their
+ name or names to the copyright strings and notices in this
+ software. Persons making changes are encouraged to insert edit
+ history in matcd.c and to put your name and details of the
+ change there.
+ 10. You must have prior written permission from the author to
+ deviate from these terms.
+
+ Vendors who produce product(s) containing this code are encouraged
+ (but not required) to provide copies of the finished product(s) to
+ the author and to correspond with the author about development
+ activity relating to this code. Donations of development hardware
+ and/or software are also welcome. (This is one of the faster ways
+ to get a driver developed for a device.)
+
+ THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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 DEVELOPER(S) 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.
+
+
+-----No changes are allowed above this line------------------------------------
+
+See matcd.c for Edit History information.
+
+
+ Matsushita CR562/CR563 Commands
+ This is not a complete list - just the ones this version uses
+*/
+
+#define NOP 0x05 /*No action - just return status*/
+#define DOOROPEN 0x06 /*Open tray*/
+#define DOORCLOSE 0x07 /*Close tray*/
+#define ABORT 0x08 /*Abort command*/
+#define MODESELECT 0x09 /*Set drive parameters*/
+#define LOCK 0x0c /*Prevent/Allow medium removal*/
+#define PAUSE 0x0d /*Pause/Resume playback*/
+#define PLAYBLOCKS 0x0e /*Play audio - block to block*/
+#define PLAYTRKS 0x0f /*Play audio - tracks & index*/
+#define READ 0x10 /*Read data*/
+#define READERROR 0x82 /*Read Error*/
+#define READID 0x83 /*Read Drive Type & Firmware Info*/
+#define MODESENSE 0x84 /*<12>Report drive settings*/
+#define READSUBQ 0x87 /*<14>Read Q channel information*/
+#define READDINFO 0x8b /*<13>Read TOC tracks & drive size*/
+#define READTOC 0x8c /*<13>Read entry from TOC*/
+
+#define BLOCKPARAM 0x00 /*Used with MODESELECT command*/
+#define SPEEDPARM 0x03 /*<12>Adjust audio playback speed*/
+#define AUDIOPARM 0x05 /*<12>Set/read audio levels & routing*/
+#define RESUME 0x80 /*Used with PAUSE command*/
+
+#define MAXCMDSIZ 12 /*Max command size with NULL*/
+
+/* Possible data transfers for MODESELECT + BLOCKPARAM */
+
+#define MODE_DATA 0x00 /*2048, 2340*/
+#define MODE_DA 0x82 /*2352*/
+#define MODE_USER 0x01 /*2048, 2052, 2336, 2340, 2352*/
+#define MODE_UNKNOWN 0xff /*Uninitialized state*/
+
+/*<12>The following mode is not implemented in the driver at this time*/
+
+#define MODE_XA 0x81 /*2048, 2060, 2324, 2336, 2340, 2352*/
+
+#define DEFVOL 0xff /*<12>Default drive volume level, 100%
+ volume. Based on drive action.*/
+#define OUTLEFT 0x01 /*Output on Left*/
+#define OUTRIGHT 0x02 /*Output on Right*/
+
+/* Matsushita CR562/CR563 Status bits*/
+
+#define MATCD_ST_DOOROPEN 0x80 /*Door is open right now*/
+#define MATCD_ST_DSKIN 0x40 /*Disc in drive*/
+#define MATCD_ST_SPIN 0x20 /*Disc is spinning*/
+#define MATCD_ST_ERROR 0x10 /*Error on command*/
+#define MATCD_ST_AUDIOBSY 0x08 /*Drive is playing audio*/
+#define MATCD_ST_LOCK 0x04 /*<14>Drive is locked*/
+#define MATCD_ST_X2 0x02 /*<14>Media is at double-speed*/
+#define MATCD_ST_READY 0x01 /*<14>Drive is ready*/
+
+#define MATCDAUDIOBSY MATCD_ST_AUDIOBSY
+#define MATCDDSKCHNG MATCD_ST_DSKCHNG
+#define MATCDDSKIN MATCD_ST_DSKIN
+#define MATCDDOOROPEN MATCD_ST_DOOROPEN
+
+
+/* Error codes returned from READERROR command.*/
+
+#define NO_ERROR 0x00
+#define RECV_RETRY 0x01
+#define RECV_ECC 0x02
+#define NOT_READY 0x03
+#define TOC_ERROR 0x04
+#define UNRECV_ERROR 0x05
+#define SEEK_ERROR 0x06
+#define TRACK_ERROR 0x07
+#define RAM_ERROR 0x08
+#define DIAG_ERROR 0x09
+#define FOCUS_ERROR 0x0a
+#define CLV_ERROR 0x0b
+#define DATA_ERROR 0x0c
+#define ADDRESS_ERROR 0x0d
+#define CDB_ERROR 0x0e
+#define END_ADDRESS 0x0f
+#define MODE_ERROR 0x10
+#define MEDIA_CHANGED 0x11
+#define HARD_RESET 0x12
+#define ROM_ERROR 0x13
+#define CMD_ERROR 0x14
+#define DISC_OUT 0x15
+#define HARD_ERROR 0x16
+#define ILLEGAL_REQ 0x17
+
+
+/* Human-readable error messages - what a concept!*/
+
+static unsigned char * matcderrors[]={"No error", /* 00 */
+ "Soft read error after retry", /* 01 */
+ "Soft read error after error-correction", /* 02 */
+ "Not ready", /* 03 */
+ "Unable to read TOC", /* 04 */
+ "Hard read error of data track",/* 05 */
+ "Seek did not complete", /* 06 */
+ "Tracking servo failure", /* 07 */
+ "Drive RAM failure", /* 08 */
+ "Drive self-test failed", /* 09 */
+ "Focusing servo failure", /* 0a */
+ "Spindle servo failure", /* 0b */
+ "Data path failure", /* 0c */
+ "Illegal logical block address",/* 0d */
+ "Illegal field in CDB", /* 0e */
+ "End of user encountered on this track", /* 0f */
+ "Illegal data mode for this track", /* 10 */
+ "Media changed", /* 11 */
+ "Power-on or drive reset occurred", /* 12 */
+ "Drive ROM failure", /* 13 */
+ "Illegal drive command received from host",/* 14 */
+ "Disc removed during operation",/* 15 */
+ "Drive Hardware error", /* 16 */
+ "Illegal request from host"}; /* 17 */
+
+/*End of matcd.h*/
+
+
diff --git a/sys/pc98/pc98/matcd/options.h b/sys/pc98/pc98/matcd/options.h
new file mode 100644
index 0000000..62d2e2b
--- /dev/null
+++ b/sys/pc98/pc98/matcd/options.h
@@ -0,0 +1,283 @@
+/*options.h--------------------------------------------------------------------
+
+ Matsushita(Panasonic) / Creative CD-ROM Driver (matcd)
+ Authored by Frank Durda IV
+
+ Copyright 1994, 1995 Frank Durda IV. All rights reserved.
+ "FDIV" is a trademark of Frank Durda IV.
+
+
+ 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 positioned at the very beginning of this file without
+ modification, all copyright strings, all related programming
+ codes that display the copyright strings, this list of
+ conditions and the following disclaimer.
+ 2. Redistributions in binary form must contain all copyright strings
+ and related programming code that display the copyright strings.
+ 3. 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.
+ 4. All advertising materials mentioning features or use of this
+ software must display the following acknowledgement:
+ "The Matsushita/Panasonic CD-ROM driver was developed
+ by Frank Durda IV for use with "FreeBSD" and similar
+ operating systems."
+ "Similar operating systems" includes mainly non-profit oriented
+ systems for research and education, including but not restricted
+ to "NetBSD", "386BSD", and "Mach" (by CMU). The wording of the
+ acknowledgement (in electronic form or printed text) may not be
+ changed without permission from the author.
+ 5. Absolutely no warranty of function, fitness or purpose is made
+ by the author Frank Durda IV.
+ 6. Neither the name of the author nor the name "FreeBSD" may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ (The author can be reached at bsdmail@nemesis.lonestar.org)
+ 7. The product containing this software must meet all of these
+ conditions even if it is unsupported, not a complete system
+ and/or does not contain compiled code.
+ 8. These conditions will be in force for the full life of the
+ copyright.
+ 9. If all the above conditions are met, modifications to other
+ parts of this file may be freely made, although any person
+ or persons making changes do not receive the right to add their
+ name or names to the copyright strings and notices in this
+ software. Persons making changes are encouraged to insert edit
+ history in matcd.c and to put your name and details of the
+ change there.
+ 10. You must have prior written permission from the author to
+ deviate from these terms.
+
+ Vendors who produce product(s) containing this code are encouraged
+ (but not required) to provide copies of the finished product(s) to
+ the author and to correspond with the author about development
+ activity relating to this code. Donations of development hardware
+ and/or software are also welcome. (This is one of the faster ways
+ to get a driver developed for a device.)
+
+ THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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 DEVELOPER(S) 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.
+
+
+-----No changes are allowed above this line------------------------------------
+-----------------------------------------------------------------------------
+ Conditional compilation flags - change to suit your system
+---------------------------------------------------------------------------*/
+
+/* AUTOHUNT Adds extra code that allows the driver to search
+ for interface cards rather than having to hard-code
+ the locations in the kernel conf file.
+ Leaving AUTOHUNT enabled is the recommended setting.
+*/
+
+#define AUTOHUNT
+
+
+/* NUMCTRLRS Configures support for between one and four
+ host interfaces, for up to 16 drives.
+ The number of entries in the kernel config
+ file is used by default, but this may be changed
+ to a specific value if desired.
+
+ Leaving NUMCTRLRS based on NMATCD is the
+ recommended setting.
+*/
+
+#if NMATCD >= 4
+#define NUMCTRLRS 4 /*Limit driver to four host interfaces*/
+#else /*NMATCD*/
+#define NUMCTRLRS NMATCD
+#endif /*NMATCD*/
+
+
+/* FULLDRIVER If not set, the audio, non-data functions and
+ some error recovery functions are eliminated from
+ the compiled driver. The resulting driver will be
+ smaller and may help a kernel fit on a boot floppy.
+ Leaving FULLDRIVER enabled is the recommended setting.
+*/
+
+#ifndef BOOTMFS
+#define FULLDRIVER
+#endif /*BOOTMFS*/
+
+
+/* RESETONBOOT causes the driver to reset the drive(s) to be
+ reset during probing. This causes any audio
+ playback to be aborted and the drives will close
+ their trays if they are open.
+ Leaving RESETONBOOT enabled is the recommended setting.
+*/
+
+#define RESETONBOOT
+
+
+/*<15> LOCKDRIVE If enabled, when a drive is opened using a
+<15> minor number greater than 127, the drive door is
+<15> locked. The drive door remains locked until all
+<23> partitions on the drive are closed. The EJECT,
+<23> ALLOW and PREVENT ioctls are refused when this locking
+<23> mechanism is active.
+<15> The additional code size is small so enabling
+<15> LOCKDRIVE is the recommended setting.
+*/
+
+#define LOCKDRIVE
+
+
+/*<14> KRYTEN This enables a bug that someone might consider
+<14> to be a feature. If KRYTEN is enabled and you are
+<14> playing audio and you issue the resume-play ioctl,
+<14> the audio will stutter, playing the same quarter
+<14> of a second or so of audio several times before
+<14> resuming normally. Resuming from a pause acts
+<14> normally regardless of the setting of this flag.
+<14> Leaving KRYTEN disabled is the recommended setting.
+<14>*/
+
+/*#define KRYTEN*/
+
+
+/*---------------------------------------------------------------------------
+ This structure contains the hints for where we should look for the
+ host adapter. If you want to change where we search or reduce the
+ places we search to avoid confusing some other device, either
+ specify explicit addresses in the kernel config file (preferred)
+ or change this array.
+
+ If the kernel config file has multiple ? entries, the probe routines
+ will use this table multiple times and will eliminate each failed
+ entry that probe tries.
+
+ WARNING: The number of controller entries for this driver in config
+ must be less than or equal to the number of hints if hints are used.
+
+ If you add entries to the table, add them immediately before
+ the -1 end-of-table marker. The values already present are
+ the ones used by Creative Labs boards and those of a few
+ other vendors.
+
+ Each additional entry increases the boot time by four seconds,
+ and can increase the chance of accessing some other device.
+ Therefore, the list should be kept to a minimum. Once the
+ devices have been correctly located, the kernel should be
+ configured so that it looks only at the correct location from
+ that point on.
+
+ Be sure to search devices located below 0x3ff BEFORE scanning
+ higher locations. Some boards don't decode all I/O address lines,
+ so 0x230 and 0x630 appear identical.
+---------------------------------------------------------------------------*/
+
+#ifdef AUTOHUNT
+static int port_hints[]={
+#ifdef PC98
+ 0x30d2,
+ 0x30d0,
+ 0x30d4,
+ 0x30d6,
+ 0x30d8,
+ 0x30da,
+ 0x30dc,
+ 0x30de,
+#else
+ 0x230, /*SB Pro & SB16*/
+ 0x240, /*SB Pro & SB16*/
+ 0x250, /*Creative omniCD standalone boards*/
+ 0x260, /*Creative omniCD standalone boards*/
+ 0x340, /*Laser Mate*/
+ 0x360, /*Laser Mate*/
+ 0x630, /*IBM*/
+#if 0
+/* These locations are alternate settings for LaserMate and IBM
+ boards, but they usually conflict with network and SCSI cards.
+ I recommend against probing these randomly.
+*/
+ 0x310, /*Laser Mate*/
+ 0x320, /*Laser Mate*/
+ 0x330, /*Laser Mate*/
+ 0x350, /*Laser Mate*/
+ 0x370, /*Laser Mate*/
+ 0x650, /*IBM*/
+ 0x670, /*IBM*/
+ 0x690, /*IBM*/
+#endif /*0*/
+#endif
+ -1}; /*use. Table MUST end with -1*/
+#endif /*AUTOHUNT*/
+
+
+/*---------------------------------------------------------------------------
+ Debugging flags - Turn these on only if you are looking at a
+ problem.
+---------------------------------------------------------------------------*/
+
+/* DEBUGOPEN If enabled, debug messages for open and close
+ operations.
+*/
+
+/*#define DEBUGOPEN*/
+
+
+/* DEBUGIO If enabled, reports on calls to strategy, start
+ and other I/O related functions.
+*/
+
+/*#define DEBUGIO*/
+
+
+/* DEBUGQUEUE If enabled, shows activity on disk request queues.
+ Warning - This debug is VERY VERY NOISY and will
+ loop endlessly if queues are not null terminated
+ as they should be.
+*/
+
+/*#define DEBUGQUEUE*/
+
+
+/* DEBUGCMD If enabled, shows the actual commands being issued
+ to the CD-ROM drives.
+*/
+
+/*#define DEBUGCMD*/
+
+
+/* DEBUGSLEEP If enabled, reports on timeouts, wakeups, dropped
+ threads, etc.
+*/
+
+/*#define DEBUGSLEEP*/
+
+
+/* DEBUGIOCTL If enabled, reports on the various ioctl-related
+ calls and operations. You might have to enable
+ DEBUGCMD as well to get enough debugging information.
+*/
+
+/*#define DEBUGIOCTL*/
+
+
+/* DEBUGPROBE If enabled, reports on the process of locating
+ adapters and drives. The debugging in matcdprobe()
+ and matcdattach() routines is enabled with this
+ flag.
+*/
+
+/*#define DEBUGPROBE*/
+
+
+/*End of options.h*/
+
diff --git a/sys/pc98/pc98/mk30line b/sys/pc98/pc98/mk30line
new file mode 100644
index 0000000..18ebfd6
--- /dev/null
+++ b/sys/pc98/pc98/mk30line
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+#
+# Copyright (c) KATO Takenori, 1994-1995. 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.
+#
+
+# Default
+($HS, $VS, $HBP, $HFP, $VBP, $VFP) = (7, 8, 7, 9, 25, 7);
+
+# set module name
+print("Enter module file name: ");
+$module = <STDIN>;
+open(MODULE, "<$module") || die "Can't open $module.\n";
+
+# read module file
+while (<MODULE>) {
+ if (/\t\tdb\t255/) {
+ ($HS, $VS, $HBP, $HFP, $VBP, $VFP) =
+ /^\t\tdb\t255, (\d+)\+1,(\d+), (\d+)\+1,(\d+)\+1, (\d+),(\d+).*/;
+ }
+}
+close(MODULE);
+
+print("Enter number of lines: ");
+$ROW = <STDIN>;
+
+# open /sys/pc98/pc98/module.h
+open(MODULE_H, ">/sys/pc98/pc98/module.h") || die "Can't open module.h\n";
+
+# write
+printf(MODULE_H "#define\tLINE30_ROW\t%d\n", $ROW);
+printf(MODULE_H "#define\t_HS\t%d + 1\n", $HS);
+printf(MODULE_H "#define\t_VS\t%d\n", $VS);
+printf(MODULE_H "#define\t_HFP\t%d + 1\n", $HFP);
+printf(MODULE_H "#define\t_HBP\t%d + 1\n", $HBP);
+printf(MODULE_H "#define\t_VFP\t%d\n", $VFP);
+printf(MODULE_H "#define\t_VBP\t%d\n", $VBP);
+close(MODULE_H);
+
diff --git a/sys/pc98/pc98/module.h b/sys/pc98/pc98/module.h
new file mode 100644
index 0000000..c3dfbdb
--- /dev/null
+++ b/sys/pc98/pc98/module.h
@@ -0,0 +1,7 @@
+#define LINE30_ROW 30
+#define _HS 0 + 1
+#define _VS 1
+#define _HFP 3 + 1
+#define _HBP 4 + 1
+#define _VFP 2
+#define _VBP 25
diff --git a/sys/pc98/pc98/mse.c b/sys/pc98/pc98/mse.c
new file mode 100644
index 0000000..79c456f
--- /dev/null
+++ b/sys/pc98/pc98/mse.c
@@ -0,0 +1,821 @@
+/*
+ * Copyright 1992 by the University of Guelph
+ *
+ * Permission to use, copy and modify this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting
+ * documentation.
+ * University of Guelph makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * $Id: mse.c,v 1.27 1996/06/08 09:37:51 bde Exp $
+ */
+/*
+ * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and
+ * the X386 port, courtesy of
+ * Rick Macklem, rick@snowhite.cis.uoguelph.ca
+ * Caveats: The driver currently uses spltty(), but doesn't use any
+ * generic tty code. It could use splmse() (that only masks off the
+ * bus mouse interrupt, but that would require hacking in i386/isa/icu.s.
+ * (This may be worth the effort, since the Logitech generates 30/60
+ * interrupts/sec continuously while it is open.)
+ * NB: The ATI has NOT been tested yet!
+ */
+
+/*
+ * Modification history:
+ * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com)
+ * improved probe based on input from Logitech.
+ *
+ * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
+ * fixes to make it work with Microsoft InPort busmouse
+ *
+ * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
+ * added patches for new "select" interface
+ *
+ * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
+ * changed position of some spl()'s in mseread
+ *
+ * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu)
+ * limit maximum negative x/y value to -127 to work around XFree problem
+ * that causes spurious button pushes.
+ */
+
+#include "mse.h"
+#if NMSE > 0
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/devconf.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /*DEVFS*/
+
+#include <machine/clock.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/icu.h>
+#else
+#include <i386/isa/isa_device.h>
+#endif
+
+
+#ifdef PC98
+static int mseprobe(struct pc98_device *);
+static int mseattach(struct pc98_device *);
+#else
+static int mseprobe(struct isa_device *);
+static int mseattach(struct isa_device *);
+#endif
+
+#ifdef PC98
+struct pc98_driver msedriver = {
+#else
+struct isa_driver msedriver = {
+#endif
+ mseprobe, mseattach, "mse"
+};
+
+
+static d_open_t mseopen;
+static d_close_t mseclose;
+static d_read_t mseread;
+static d_select_t mseselect;
+
+#define CDEV_MAJOR 27
+static struct cdevsw mse_cdevsw =
+ { mseopen, mseclose, mseread, nowrite, /*27*/
+ noioc, nostop, nullreset, nodevtotty,/* mse */
+ mseselect, nommap, NULL, "mse", NULL, -1 };
+
+
+/*
+ * Software control structure for mouse. The sc_enablemouse(),
+ * sc_disablemouse() and sc_getmouse() routines must be called spl'd().
+ */
+#define PROTOBYTES 5
+static struct mse_softc {
+ int sc_flags;
+ int sc_mousetype;
+ struct selinfo sc_selp;
+ u_int sc_port;
+ void (*sc_enablemouse) __P((u_int port));
+ void (*sc_disablemouse) __P((u_int port));
+ void (*sc_getmouse) __P((u_int port, int *dx, int *dy, int *but));
+ int sc_deltax;
+ int sc_deltay;
+ int sc_obuttons;
+ int sc_buttons;
+ int sc_bytesread;
+ u_char sc_bytes[PROTOBYTES];
+#ifdef DEVFS
+ void *devfs_token;
+ void *n_devfs_token;
+#endif
+} mse_sc[NMSE];
+
+/* Flags */
+#define MSESC_OPEN 0x1
+#define MSESC_WANT 0x2
+
+/* and Mouse Types */
+#ifdef PC98
+#define MSE_98BUSMOUSE 0x1
+#else
+#define MSE_LOGITECH 0x1
+#define MSE_ATIINPORT 0x2
+#define MSE_LOGI_SIG 0xA5
+#endif
+
+#ifdef PC98
+#define NORMAL_MSPORT 0x7fd9
+#define PORT_A 0
+#define PORT_B 2
+#define PORT_C 4
+
+#else /* IBM_PC */
+
+#define MSE_PORTA 0
+#define MSE_PORTB 1
+#define MSE_PORTC 2
+#define MSE_PORTD 3
+#endif
+
+#define MSE_UNIT(dev) (minor(dev) >> 1)
+#define MSE_NBLOCKIO(dev) (minor(dev) & 0x1)
+
+#ifdef PC98
+/*
+ * PC-9801 Bus mouse definitions
+ */
+
+#define MODE 6
+#define HC 6
+#define INT 6
+
+#define XL 0x00
+#define XH 0x20
+#define YL 0x40
+#define YH 0x60
+
+#define INT_ENABLE 0x8
+#define INT_DISABLE 0x9
+#define HC_NO_CLEAR 0xe
+#define HC_CLEAR 0xf
+
+#define NORMAL_MSIRQ IRQ13 /* INT6 */
+
+static int msport;
+static int msirq;
+
+static int mse_probe98m __P((struct pc98_device *idp));
+static void mse_disable98m __P((u_int port));
+static void mse_get98m __P((u_int port, int *dx, int *dy, int *but));
+static void mse_enable98m __P((u_int port));
+#else
+/*
+ * Logitech bus mouse definitions
+ */
+#define MSE_SETUP 0x91 /* What does this mean? */
+ /* The definition for the control port */
+ /* is as follows: */
+
+ /* D7 = Mode set flag (1 = active) */
+ /* D6,D5 = Mode selection (port A) */
+ /* 00 = Mode 0 = Basic I/O */
+ /* 01 = Mode 1 = Strobed I/O */
+ /* 10 = Mode 2 = Bi-dir bus */
+ /* D4 = Port A direction (1 = input)*/
+ /* D3 = Port C (upper 4 bits) */
+ /* direction. (1 = input) */
+ /* D2 = Mode selection (port B & C) */
+ /* 0 = Mode 0 = Basic I/O */
+ /* 1 = Mode 1 = Strobed I/O */
+ /* D1 = Port B direction (1 = input)*/
+ /* D0 = Port C (lower 4 bits) */
+ /* direction. (1 = input) */
+
+ /* So 91 means Basic I/O on all 3 ports,*/
+ /* Port A is an input port, B is an */
+ /* output port, C is split with upper */
+ /* 4 bits being an output port and lower*/
+ /* 4 bits an input port, and enable the */
+ /* sucker. */
+ /* Courtesy Intel 8255 databook. Lars */
+#define MSE_HOLD 0x80
+#define MSE_RXLOW 0x00
+#define MSE_RXHIGH 0x20
+#define MSE_RYLOW 0x40
+#define MSE_RYHIGH 0x60
+#define MSE_DISINTR 0x10
+#define MSE_INTREN 0x00
+
+static int mse_probelogi __P((struct isa_device *idp));
+static void mse_disablelogi __P((u_int port));
+static void mse_getlogi __P((u_int port, int *dx, int *dy, int *but));
+static void mse_enablelogi __P((u_int port));
+
+/*
+ * ATI Inport mouse definitions
+ */
+#define MSE_INPORT_RESET 0x80
+#define MSE_INPORT_STATUS 0x00
+#define MSE_INPORT_DX 0x01
+#define MSE_INPORT_DY 0x02
+#define MSE_INPORT_MODE 0x07
+#define MSE_INPORT_HOLD 0x20
+#define MSE_INPORT_INTREN 0x09
+
+static int mse_probeati __P((struct isa_device *idp));
+static void mse_enableati __P((u_int port));
+static void mse_disableati __P((u_int port));
+static void mse_getati __P((u_int port, int *dx, int *dy, int *but));
+#endif
+
+#define MSEPRI (PZERO + 3)
+
+/*
+ * Table of mouse types.
+ * Keep the Logitech last, since I haven't figured out how to probe it
+ * properly yet. (Someday I'll have the documentation.)
+ */
+static struct mse_types {
+ int m_type; /* Type of bus mouse */
+#ifdef PC98
+ int (*m_probe) __P((struct pc98_device *idp));
+#else
+ int (*m_probe) __P((struct isa_device *idp));
+#endif
+ /* Probe routine to test for it */
+ void (*m_enable) __P((u_int port));
+ /* Start routine */
+ void (*m_disable) __P((u_int port));
+ /* Disable interrupts routine */
+ void (*m_get) __P((u_int port, int *dx, int *dy, int *but));
+ /* and get mouse status */
+} mse_types[] = {
+#ifdef PC98
+ { MSE_98BUSMOUSE, mse_probe98m, mse_enable98m, mse_disable98m, mse_get98m },
+#else
+ { MSE_ATIINPORT, mse_probeati, mse_enableati, mse_disableati, mse_getati },
+ { MSE_LOGITECH, mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi },
+#endif
+ { 0, },
+};
+
+static struct kern_devconf kdc_mse[NMSE] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ "mse", 0, { MDDT_PC98, 0, "tty" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "mse", 0, { MDDT_ISA, 0, "tty" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "ATI or Logitech bus mouse adapter",
+ DC_CLS_MISC /* class */
+} };
+
+static inline void
+#ifdef PC98
+mse_registerdev(struct pc98_device *id)
+#else
+mse_registerdev(struct isa_device *id)
+#endif
+{
+ if(id->id_unit)
+ kdc_mse[id->id_unit] = kdc_mse[0];
+ kdc_mse[id->id_unit].kdc_unit = id->id_unit;
+#ifdef PC98
+ kdc_mse[id->id_unit].kdc_pc98 = id;
+#else
+ kdc_mse[id->id_unit].kdc_isa = id;
+#endif
+ dev_attach(&kdc_mse[id->id_unit]);
+}
+
+int
+mseprobe(idp)
+#ifdef PC98
+ register struct pc98_device *idp;
+#else
+ register struct isa_device *idp;
+#endif
+{
+ register struct mse_softc *sc = &mse_sc[idp->id_unit];
+ register int i;
+
+ mse_registerdev(idp);
+ /*
+ * Check for each mouse type in the table.
+ */
+ i = 0;
+ while (mse_types[i].m_type) {
+ if ((*mse_types[i].m_probe)(idp)) {
+ sc->sc_mousetype = mse_types[i].m_type;
+ sc->sc_enablemouse = mse_types[i].m_enable;
+ sc->sc_disablemouse = mse_types[i].m_disable;
+ sc->sc_getmouse = mse_types[i].m_get;
+ return (1);
+ }
+ i++;
+ }
+ return (0);
+}
+
+int
+mseattach(idp)
+#ifdef PC98
+ struct pc98_device *idp;
+#else
+ struct isa_device *idp;
+#endif
+{
+ int unit = idp->id_unit;
+ struct mse_softc *sc = &mse_sc[unit];
+
+#ifdef PC98
+ if (msport != idp->id_iobase) {
+ idp->id_iobase = msport;
+ printf(" [ioport is changed to #0x%x]", msport);
+ }
+
+ if (msirq != idp->id_irq) {
+ idp->id_irq = msirq;
+ printf(" [irq is changed to IR%d]", ffs(msirq)-1);
+ }
+#endif
+
+ sc->sc_port = idp->id_iobase;
+ kdc_mse[unit].kdc_state = DC_IDLE;
+#ifdef DEVFS
+ sc->devfs_token =
+ devfs_add_devswf(&mse_cdevsw, unit << 1, DV_CHR, 0, 0,
+ 0600, "mse%d", unit);
+ sc->n_devfs_token =
+ devfs_add_devswf(&mse_cdevsw, (unit<<1)+1, DV_CHR,0, 0,
+ 0600, "nmse%d", unit);
+#endif
+ return (1);
+}
+
+/*
+ * Exclusive open the mouse, initialize it and enable interrupts.
+ */
+static int
+mseopen(dev, flags, fmt, p)
+ dev_t dev;
+ int flags;
+ int fmt;
+ struct proc *p;
+{
+ register struct mse_softc *sc;
+ int s;
+
+ if (MSE_UNIT(dev) >= NMSE)
+ return (ENXIO);
+ sc = &mse_sc[MSE_UNIT(dev)];
+ if (sc->sc_flags & MSESC_OPEN)
+ return (EBUSY);
+ sc->sc_flags |= MSESC_OPEN;
+ kdc_mse[MSE_UNIT(dev)].kdc_state = DC_BUSY;
+ sc->sc_obuttons = sc->sc_buttons = 0x7;
+ sc->sc_deltax = sc->sc_deltay = 0;
+ sc->sc_bytesread = PROTOBYTES;
+
+ /*
+ * Initialize mouse interface and enable interrupts.
+ */
+ s = spltty();
+ (*sc->sc_enablemouse)(sc->sc_port);
+ splx(s);
+ return (0);
+}
+
+/*
+ * mseclose: just turn off mouse innterrupts.
+ */
+static int
+mseclose(dev, flags, fmt, p)
+ dev_t dev;
+ int flags;
+ int fmt;
+ struct proc *p;
+{
+ struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
+ int s;
+
+ s = spltty();
+ (*sc->sc_disablemouse)(sc->sc_port);
+ sc->sc_flags &= ~MSESC_OPEN;
+ kdc_mse[MSE_UNIT(dev)].kdc_state = DC_IDLE;
+ splx(s);
+ return(0);
+}
+
+/*
+ * mseread: return mouse info using the MSC serial protocol, but without
+ * using bytes 4 and 5.
+ * (Yes this is cheesy, but it makes the X386 server happy, so...)
+ */
+static int
+mseread(dev, uio, ioflag)
+ dev_t dev;
+ struct uio *uio;
+ int ioflag;
+{
+ register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
+ int xfer, s, error;
+
+ /*
+ * If there are no protocol bytes to be read, set up a new protocol
+ * packet.
+ */
+ s = spltty(); /* XXX Should be its own spl, but where is imlXX() */
+ if (sc->sc_bytesread >= PROTOBYTES) {
+ while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
+ (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
+ if (MSE_NBLOCKIO(dev)) {
+ splx(s);
+ return (0);
+ }
+ sc->sc_flags |= MSESC_WANT;
+ if (error = tsleep((caddr_t)sc, MSEPRI | PCATCH,
+ "mseread", 0)) {
+ splx(s);
+ return (error);
+ }
+ }
+
+ /*
+ * Generate protocol bytes.
+ * For some reason X386 expects 5 bytes but never uses
+ * the fourth or fifth?
+ */
+ sc->sc_bytes[0] = 0x80 | (sc->sc_buttons & ~0xf8);
+ if (sc->sc_deltax > 127)
+ sc->sc_deltax = 127;
+ if (sc->sc_deltax < -127)
+ sc->sc_deltax = -127;
+ sc->sc_deltay = -sc->sc_deltay; /* Otherwise mousey goes wrong way */
+ if (sc->sc_deltay > 127)
+ sc->sc_deltay = 127;
+ if (sc->sc_deltay < -127)
+ sc->sc_deltay = -127;
+ sc->sc_bytes[1] = sc->sc_deltax;
+ sc->sc_bytes[2] = sc->sc_deltay;
+ sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
+ sc->sc_obuttons = sc->sc_buttons;
+ sc->sc_deltax = sc->sc_deltay = 0;
+ sc->sc_bytesread = 0;
+ }
+ splx(s);
+ xfer = min(uio->uio_resid, PROTOBYTES - sc->sc_bytesread);
+ if (error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio))
+ return (error);
+ sc->sc_bytesread += xfer;
+ return(0);
+}
+
+/*
+ * mseselect: check for mouse input to be processed.
+ */
+static int
+mseselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ register struct mse_softc *sc = &mse_sc[MSE_UNIT(dev)];
+ int s;
+
+ s = spltty();
+ if (sc->sc_bytesread != PROTOBYTES || sc->sc_deltax != 0 ||
+ sc->sc_deltay != 0 || (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
+ splx(s);
+ return (1);
+ }
+
+ /*
+ * Since this is an exclusive open device, any previous proc.
+ * pointer is trash now, so we can just assign it.
+ */
+ selrecord(p, &sc->sc_selp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
+ */
+void
+mseintr(unit)
+ int unit;
+{
+ register struct mse_softc *sc = &mse_sc[unit];
+
+#ifdef DEBUG
+ static int mse_intrcnt = 0;
+ if((mse_intrcnt++ % 10000) == 0)
+ printf("mseintr\n");
+#endif /* DEBUG */
+ if ((sc->sc_flags & MSESC_OPEN) == 0)
+ return;
+
+ (*sc->sc_getmouse)(sc->sc_port, &sc->sc_deltax, &sc->sc_deltay, &sc->sc_buttons);
+
+ /*
+ * If mouse state has changed, wake up anyone wanting to know.
+ */
+ if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
+ (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
+ if (sc->sc_flags & MSESC_WANT) {
+ sc->sc_flags &= ~MSESC_WANT;
+ wakeup((caddr_t)sc);
+ }
+ selwakeup(&sc->sc_selp);
+ }
+}
+
+#ifndef PC98
+/*
+ * Routines for the Logitech mouse.
+ */
+/*
+ * Test for a Logitech bus mouse and return 1 if it is.
+ * (until I know how to use the signature port properly, just disable
+ * interrupts and return 1)
+ */
+static int
+mse_probelogi(idp)
+ register struct isa_device *idp;
+{
+
+ int sig;
+
+ outb(idp->id_iobase + MSE_PORTD, MSE_SETUP);
+ /* set the signature port */
+ outb(idp->id_iobase + MSE_PORTB, MSE_LOGI_SIG);
+
+ DELAY(30000); /* 30 ms delay */
+ sig = inb(idp->id_iobase + MSE_PORTB) & 0xFF;
+ if (sig == MSE_LOGI_SIG) {
+ outb(idp->id_iobase + MSE_PORTC, MSE_DISINTR);
+ return(1);
+ } else {
+ if (bootverbose)
+ printf("mse%d: wrong signature %x\n",idp->id_unit,sig);
+ return(0);
+ }
+}
+
+/*
+ * Initialize Logitech mouse and enable interrupts.
+ */
+static void
+mse_enablelogi(port)
+ register u_int port;
+{
+ int dx, dy, but;
+
+ outb(port + MSE_PORTD, MSE_SETUP);
+ mse_getlogi(port, &dx, &dy, &but);
+}
+
+/*
+ * Disable interrupts for Logitech mouse.
+ */
+static void
+mse_disablelogi(port)
+ register u_int port;
+{
+
+ outb(port + MSE_PORTC, MSE_DISINTR);
+}
+
+/*
+ * Get the current dx, dy and button up/down state.
+ */
+static void
+mse_getlogi(port, dx, dy, but)
+ register u_int port;
+ int *dx;
+ int *dy;
+ int *but;
+{
+ register char x, y;
+
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RXLOW);
+ x = inb(port + MSE_PORTA);
+ *but = (x >> 5) & 0x7;
+ x &= 0xf;
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RXHIGH);
+ x |= (inb(port + MSE_PORTA) << 4);
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RYLOW);
+ y = (inb(port + MSE_PORTA) & 0xf);
+ outb(port + MSE_PORTC, MSE_HOLD | MSE_RYHIGH);
+ y |= (inb(port + MSE_PORTA) << 4);
+ *dx += x;
+ *dy += y;
+ outb(port + MSE_PORTC, MSE_INTREN);
+}
+
+/*
+ * Routines for the ATI Inport bus mouse.
+ */
+/*
+ * Test for a ATI Inport bus mouse and return 1 if it is.
+ * (do not enable interrupts)
+ */
+static int
+mse_probeati(idp)
+ register struct isa_device *idp;
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (inb(idp->id_iobase + MSE_PORTC) == 0xde)
+ return (1);
+ return (0);
+}
+
+/*
+ * Initialize ATI Inport mouse and enable interrupts.
+ */
+static void
+mse_enableati(port)
+ register u_int port;
+{
+
+ outb(port + MSE_PORTA, MSE_INPORT_RESET);
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, MSE_INPORT_INTREN);
+}
+
+/*
+ * Disable interrupts for ATI Inport mouse.
+ */
+static void
+mse_disableati(port)
+ register u_int port;
+{
+
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, 0);
+}
+
+/*
+ * Get current dx, dy and up/down button state.
+ */
+static void
+mse_getati(port, dx, dy, but)
+ register u_int port;
+ int *dx;
+ int *dy;
+ int *but;
+{
+ register char byte;
+
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, MSE_INPORT_HOLD);
+ outb(port + MSE_PORTA, MSE_INPORT_STATUS);
+ *but = ~(inb(port + MSE_PORTB) & 0x7);
+ outb(port + MSE_PORTA, MSE_INPORT_DX);
+ byte = inb(port + MSE_PORTB);
+ *dx += byte;
+ outb(port + MSE_PORTA, MSE_INPORT_DY);
+ byte = inb(port + MSE_PORTB);
+ *dy += byte;
+ outb(port + MSE_PORTA, MSE_INPORT_MODE);
+ outb(port + MSE_PORTB, MSE_INPORT_INTREN);
+}
+#endif
+
+#ifdef PC98
+
+/*
+ * Routines for the PC98 bus mouse.
+ */
+
+/*
+ * Test for a PC98 bus mouse and return 1 if it is.
+ * (do not enable interrupts)
+ */
+static int
+mse_probe98m(idp)
+ register struct pc98_device *idp;
+{
+
+ msport = NORMAL_MSPORT;
+ msirq = NORMAL_MSIRQ;
+
+ /* mode set */
+ outb(msport + MODE, 0x93);
+ /* initialize */
+ outb(msport + INT, INT_DISABLE); /* INT disable */
+ outb(msport + HC, HC_NO_CLEAR); /* HC = 0 */
+#if 0
+ if (inb(msport + PORT_C) & 0x80 != 0) {
+ return (0);
+ }
+#endif
+ outb(msport + HC, HC_CLEAR); /* HC = 1 */
+#if 0
+ if (inb(msport + PORT_C) & 0x80 == 0) {
+ return (0);
+ }
+#endif
+ return (1);
+}
+
+/*
+ * Initialize PC98 bus mouse and enable interrupts.
+ */
+static void
+mse_enable98m(port)
+ register u_int port;
+{
+ outb(port + INT, INT_ENABLE);/* INT enable */
+ outb(port + HC, HC_NO_CLEAR); /* HC = 0 */
+ outb(port + HC, HC_CLEAR); /* HC = 1 */
+}
+
+/*
+ * Disable interrupts for PC98 Bus mouse.
+ */
+static void
+mse_disable98m(port)
+ register u_int port;
+{
+ outb(port + INT, INT_DISABLE);/* INT disable */
+ outb(port + HC, HC_NO_CLEAR); /* HC = 0 */
+ outb(port + HC, HC_CLEAR); /* HC = 1 */
+}
+
+/*
+ * Get current dx, dy and up/down button state.
+ */
+static void
+mse_get98m(port, dx, dy, but)
+ register u_int port;
+ int *dx;
+ int *dy;
+ int *but;
+{
+ register char x, y;
+
+ outb(port + INT, INT_DISABLE); /* INT disable */
+
+ outb(port + HC, HC_CLEAR); /* HC = 1 */
+
+ outb(port + PORT_C, 0x90 | XL);
+ x = inb(port + PORT_A) & 0x0f; /* X low */
+ outb(port + PORT_C, 0x90 | XH);
+ x |= ((inb(port + PORT_A) & 0x0f) << 4); /* X high */
+
+ outb(port + PORT_C, 0x90 | YL);
+ y = (inb(port + PORT_A) & 0x0f); /* Y low */
+ outb(port + PORT_C, 0x90 | YH);
+ y |= ((inb(port + PORT_A) & 0x0f) << 4);
+
+ *but = (inb(port + PORT_A) >> 5) & 7;
+
+ *dx += x;
+ *dy += y;
+
+ outb(port + HC, HC_NO_CLEAR); /* HC = 0 */
+
+ outb(port + INT, INT_ENABLE); /* INT enable */
+}
+#endif
+
+static mse_devsw_installed = 0;
+
+static void mse_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! mse_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev,&mse_cdevsw, NULL);
+ mse_devsw_installed = 1;
+ }
+}
+
+SYSINIT(msedev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,mse_drvinit,NULL)
+
+
+#endif /* NMSE */
diff --git a/sys/pc98/pc98/npx.c b/sys/pc98/pc98/npx.c
new file mode 100644
index 0000000..822db67
--- /dev/null
+++ b/sys/pc98/pc98/npx.c
@@ -0,0 +1,679 @@
+/*-
+ * Copyright (c) 1990 William Jolitz.
+ * 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: @(#)npx.c 7.2 (Berkeley) 5/12/91
+ * $Id: npx.c,v 1.29 1996/01/06 23:10:52 peter Exp $
+ */
+
+#include "npx.h"
+#if NNPX > 0
+
+#include "opt_math_emulate.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/devconf.h>
+#include <sys/ioctl.h>
+#include <sys/syslog.h>
+#include <sys/signalvar.h>
+
+#include <machine/cpu.h>
+#include <machine/pcb.h>
+#include <machine/trap.h>
+#include <machine/clock.h>
+#include <machine/specialreg.h>
+
+#ifdef PC98
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/pc98.h>
+#else
+#include <i386/isa/icu.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/isa.h>
+#endif
+
+/*
+ * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
+ */
+
+#ifdef __GNUC__
+
+#define fldcw(addr) __asm("fldcw %0" : : "m" (*(addr)))
+#define fnclex() __asm("fnclex")
+#define fninit() __asm("fninit")
+#define fnop() __asm("fnop")
+#define fnsave(addr) __asm("fnsave %0" : "=m" (*(addr)))
+#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*(addr)))
+#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*(addr)))
+#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fnop")
+#define frstor(addr) __asm("frstor %0" : : "m" (*(addr)))
+#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
+ : : "n" (CR0_TS) : "ax")
+#define stop_emulating() __asm("clts")
+
+#else /* not __GNUC__ */
+
+void fldcw __P((caddr_t addr));
+void fnclex __P((void));
+void fninit __P((void));
+void fnop __P((void));
+void fnsave __P((caddr_t addr));
+void fnstcw __P((caddr_t addr));
+void fnstsw __P((caddr_t addr));
+void fp_divide_by_0 __P((void));
+void frstor __P((caddr_t addr));
+void start_emulating __P((void));
+void stop_emulating __P((void));
+
+#endif /* __GNUC__ */
+
+typedef u_char bool_t;
+
+#ifdef PC98
+static int npxattach __P((struct pc98_device *dvp));
+static int npxprobe __P((struct pc98_device *dvp));
+static int npxprobe1 __P((struct pc98_device *dvp));
+#else
+static int npxattach __P((struct isa_device *dvp));
+static int npxprobe __P((struct isa_device *dvp));
+static int npxprobe1 __P((struct isa_device *dvp));
+#endif
+
+#ifdef PC98
+struct pc98_driver npxdriver = {
+#else
+struct isa_driver npxdriver = {
+#endif
+ npxprobe, npxattach, "npx",
+};
+
+int hw_float; /* XXX currently just alias for npx_exists */
+
+SYSCTL_INT(_hw,HW_FLOATINGPT, floatingpoint,
+ CTLFLAG_RD, &hw_float, 0,
+ "Floatingpoint instructions executed in hardware");
+
+static u_int npx0_imask = SWI_CLOCK_MASK;
+struct proc *npxproc;
+
+static bool_t npx_ex16;
+static bool_t npx_exists;
+static struct gate_descriptor npx_idt_probeintr;
+static int npx_intrno;
+static volatile u_int npx_intrs_while_probing;
+static bool_t npx_irq13;
+static volatile u_int npx_traps_while_probing;
+
+/*
+ * Special interrupt handlers. Someday intr0-intr15 will be used to count
+ * interrupts. We'll still need a special exception 16 handler. The busy
+ * latch stuff in probeintr() can be moved to npxprobe().
+ */
+inthand_t probeintr;
+asm
+("
+ .text
+_probeintr:
+ ss
+ incl _npx_intrs_while_probing
+ pushl %eax
+ movb $0x20,%al # EOI (asm in strings loses cpp features)
+#ifdef PC98
+ outb %al,$0x08 # IO_ICU2
+ outb %al,$0x0 # IO_ICU1
+#else
+ outb %al,$0xa0 # IO_ICU2
+ outb %al,$0x20 # IO_ICU1
+#endif
+ movb $0,%al
+#ifdef PC98
+ outb %al,$0xf8 # clear BUSY# latch
+#else
+ outb %al,$0xf0 # clear BUSY# latch
+#endif
+ popl %eax
+ iret
+");
+
+inthand_t probetrap;
+asm
+("
+ .text
+_probetrap:
+ ss
+ incl _npx_traps_while_probing
+ fnclex
+ iret
+");
+
+static struct kern_devconf kdc_npx[NNPX] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ "npx", 0, { MDDT_PC98, 0 },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "npx", 0, { MDDT_ISA, 0 },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+ "Floating-point unit",
+ DC_CLS_MISC /* class */
+} };
+
+static inline void
+#ifdef PC98
+npx_registerdev(struct pc98_device *id)
+#else
+npx_registerdev(struct isa_device *id)
+#endif
+{
+ int unit;
+
+ unit = id->id_unit;
+ if (unit != 0)
+ kdc_npx[unit] = kdc_npx[0];
+ kdc_npx[unit].kdc_unit = unit;
+#ifdef PC98
+ kdc_npx[unit].kdc_pc98 = id;
+#else
+ kdc_npx[unit].kdc_isa = id;
+#endif
+ dev_attach(&kdc_npx[unit]);
+}
+
+/*
+ * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
+ * whether the device exists or not (XXX should be elsewhere). Set flags
+ * to tell npxattach() what to do. Modify device struct if npx doesn't
+ * need to use interrupts. Return 1 if device exists.
+ */
+static int
+npxprobe(dvp)
+ struct pc98_device *dvp;
+{
+ int result;
+ u_long save_eflags;
+ u_char save_icu1_mask;
+ u_char save_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+ struct gate_descriptor save_idt_npxtrap;
+ /*
+ * This routine is now just a wrapper for npxprobe1(), to install
+ * special npx interrupt and trap handlers, to enable npx interrupts
+ * and to disable other interrupts. Someday isa_configure() will
+ * install suitable handlers and run with interrupts enabled so we
+ * won't need to do so much here.
+ */
+ npx_registerdev(dvp);
+ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
+ save_eflags = read_eflags();
+ disable_intr();
+#ifdef PC98
+ save_icu1_mask = inb(IO_ICU1 + 2);
+ save_icu2_mask = inb(IO_ICU2 + 2);
+#else
+ save_icu1_mask = inb(IO_ICU1 + 1);
+ save_icu2_mask = inb(IO_ICU2 + 1);
+#endif
+ save_idt_npxintr = idt[npx_intrno];
+ save_idt_npxtrap = idt[16];
+#ifdef PC98
+ outb(IO_ICU1 + 2, ~(IRQ_SLAVE | dvp->id_irq));
+ outb(IO_ICU2 + 2, ~(dvp->id_irq >> 8));
+#else
+ outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
+ outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
+#endif
+ setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ npx_idt_probeintr = idt[npx_intrno];
+ enable_intr();
+ result = npxprobe1(dvp);
+ disable_intr();
+#ifdef PC98
+ outb(IO_ICU1 + 2, save_icu1_mask);
+ outb(IO_ICU2 + 2, save_icu2_mask);
+#else
+ outb(IO_ICU1 + 1, save_icu1_mask);
+ outb(IO_ICU2 + 1, save_icu2_mask);
+#endif
+ idt[npx_intrno] = save_idt_npxintr;
+ idt[16] = save_idt_npxtrap;
+ write_eflags(save_eflags);
+ return (result);
+}
+
+static int
+npxprobe1(dvp)
+ struct pc98_device *dvp;
+{
+ u_short control;
+ u_short status;
+
+ /*
+ * Partially reset the coprocessor, if any. Some BIOS's don't reset
+ * it after a warm boot.
+ */
+#ifdef PC98
+ outb(IO_NPX,0);
+#else
+ outb(0xf1, 0); /* full reset on some systems, NOP on others */
+ outb(0xf0, 0); /* clear BUSY# latch */
+#endif
+ /*
+ * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
+ * instructions. We must set the CR0_MP bit and use the CR0_TS
+ * bit to control the trap, because setting the CR0_EM bit does
+ * not cause WAIT instructions to trap. It's important to trap
+ * WAIT instructions - otherwise the "wait" variants of no-wait
+ * control instructions would degenerate to the "no-wait" variants
+ * after FP context switches but work correctly otherwise. It's
+ * particularly important to trap WAITs when there is no NPX -
+ * otherwise the "wait" variants would always degenerate.
+ *
+ * Try setting CR0_NE to get correct error reporting on 486DX's.
+ * Setting it should fail or do nothing on lesser processors.
+ */
+ load_cr0(rcr0() | CR0_MP | CR0_NE);
+ /*
+ * But don't trap while we're probing.
+ */
+ stop_emulating();
+ /*
+ * Finish resetting the coprocessor, if any. If there is an error
+ * pending, then we may get a bogus IRQ13, but probeintr() will handle
+ * it OK. Bogus halts have never been observed, but we enabled
+ * IRQ13 and cleared the BUSY# latch early to handle them anyway.
+ */
+ fninit();
+ /*
+ * Don't use fwait here because it might hang.
+ * Don't use fnop here because it usually hangs if there is no FPU.
+ */
+ DELAY(1000); /* wait for any IRQ13 */
+#ifdef DIAGNOSTIC
+ if (npx_intrs_while_probing != 0)
+ printf("fninit caused %u bogus npx interrupt(s)\n",
+ npx_intrs_while_probing);
+ if (npx_traps_while_probing != 0)
+ printf("fninit caused %u bogus npx trap(s)\n",
+ npx_traps_while_probing);
+#endif
+ /*
+ * Check for a status of mostly zero.
+ */
+ status = 0x5a5a;
+ fnstsw(&status);
+ if ((status & 0xb8ff) == 0) {
+ /*
+ * Good, now check for a proper control word.
+ */
+ control = 0x5a5a;
+ fnstcw(&control);
+ if ((control & 0x1f3f) == 0x033f) {
+ hw_float = npx_exists = 1;
+ /*
+ * We have an npx, now divide by 0 to see if exception
+ * 16 works.
+ */
+ control &= ~(1 << 2); /* enable divide by 0 trap */
+ fldcw(&control);
+ npx_traps_while_probing = npx_intrs_while_probing = 0;
+ fp_divide_by_0();
+ if (npx_traps_while_probing != 0) {
+ /*
+ * Good, exception 16 works.
+ */
+ npx_ex16 = 1;
+ dvp->id_irq = 0; /* zap the interrupt */
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+ }
+ if (npx_intrs_while_probing != 0) {
+ /*
+ * Bad, we are stuck with IRQ13.
+ */
+ npx_irq13 = 1;
+ /*
+ * npxattach would be too late to set npx0_imask.
+ */
+ npx0_imask |= dvp->id_irq;
+ return (IO_NPXSIZE);
+ }
+ /*
+ * Worse, even IRQ13 is broken. Use emulator.
+ */
+ }
+ }
+ /*
+ * Probe failed, but we want to get to npxattach to initialize the
+ * emulator and say that it has been installed. XXX handle devices
+ * that aren't really devices better.
+ */
+ dvp->id_irq = 0;
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+}
+
+/*
+ * Attach routine - announce which it is, and wire into system
+ */
+int
+npxattach(dvp)
+ struct pc98_device *dvp;
+{
+ if (npx_ex16)
+ printf("npx%d: Exception 16 interface\n", dvp->id_unit);
+ else if (npx_irq13)
+ ; /* higher level has printed "irq 13" */
+#if defined(MATH_EMULATE) || defined(GPL_MATH_EMULATE)
+ else if (npx_exists) {
+ printf("npx%d: error reporting broken; using 387 emulator\n",
+ dvp->id_unit);
+ npx_exists = 0;
+ } else
+ printf("npx%d: 387 emulator\n",dvp->id_unit);
+#else
+ else
+ printf("npx%d: no 387 emulator in kernel!\n", dvp->id_unit);
+#endif
+ npxinit(__INITIAL_NPXCW__);
+ if (npx_exists) {
+ kdc_npx[dvp->id_unit].kdc_state = DC_BUSY;
+ }
+ return (1); /* XXX unused */
+}
+
+/*
+ * Initialize floating point unit.
+ */
+void
+npxinit(control)
+ u_short control;
+{
+ struct save87 dummy;
+
+ if (!npx_exists)
+ return;
+ /*
+ * fninit has the same h/w bugs as fnsave. Use the detoxified
+ * fnsave to throw away any junk in the fpu. npxsave() initializes
+ * the fpu and sets npxproc = NULL as important side effects.
+ */
+ npxsave(&dummy);
+ stop_emulating();
+ fldcw(&control);
+ if (curpcb != NULL)
+ fnsave(&curpcb->pcb_savefpu);
+ start_emulating();
+}
+
+/*
+ * Free coprocessor (if we have it).
+ */
+void
+npxexit(p)
+ struct proc *p;
+{
+
+ if (p == npxproc)
+ npxsave(&curpcb->pcb_savefpu);
+ if (npx_exists) {
+ u_int masked_exceptions;
+
+ masked_exceptions = curpcb->pcb_savefpu.sv_env.en_cw
+ & curpcb->pcb_savefpu.sv_env.en_sw & 0x7f;
+ /*
+ * Overflow, divde by 0, and invalid operand would have
+ * caused a trap in 1.1.5.
+ */
+ if (masked_exceptions & 0x0d)
+ log(LOG_ERR,
+ "pid %d (%s) exited with masked floating point exceptions 0x%02x\n",
+ p->p_pid, p->p_comm, masked_exceptions);
+ }
+}
+
+/*
+ * Preserve the FP status word, clear FP exceptions, then generate a SIGFPE.
+ *
+ * Clearing exceptions is necessary mainly to avoid IRQ13 bugs. We now
+ * depend on longjmp() restoring a usable state. Restoring the state
+ * or examining it might fail if we didn't clear exceptions.
+ *
+ * XXX there is no standard way to tell SIGFPE handlers about the error
+ * state. The old interface:
+ *
+ * void handler(int sig, int code, struct sigcontext *scp);
+ *
+ * is broken because it is non-ANSI and because the FP state is not in
+ * struct sigcontext.
+ *
+ * XXX the FP state is not preserved across signal handlers. So signal
+ * handlers cannot afford to do FP unless they preserve the state or
+ * longjmp() out. Both preserving the state and longjmp()ing may be
+ * destroyed by IRQ13 bugs. Clearing FP exceptions is not an acceptable
+ * solution for signals other than SIGFPE.
+ */
+void
+npxintr(unit)
+ int unit;
+{
+ int code;
+ struct intrframe *frame;
+
+ if (npxproc == NULL || !npx_exists) {
+ printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n",
+ npxproc, curproc, npx_exists);
+ panic("npxintr from nowhere");
+ }
+ if (npxproc != curproc) {
+ printf("npxintr: npxproc = %p, curproc = %p, npx_exists = %d\n",
+ npxproc, curproc, npx_exists);
+ panic("npxintr from non-current process");
+ }
+
+#ifdef PC98
+ outb(IO_NPX, 0);
+#else
+ outb(0xf0, 0);
+#endif
+ fnstsw(&curpcb->pcb_savefpu.sv_ex_sw);
+ fnclex();
+ fnop();
+
+ /*
+ * Pass exception to process.
+ */
+ frame = (struct intrframe *)&unit; /* XXX */
+ if (ISPL(frame->if_cs) == SEL_UPL) {
+ /*
+ * Interrupt is essentially a trap, so we can afford to call
+ * the SIGFPE handler (if any) as soon as the interrupt
+ * returns.
+ *
+ * XXX little or nothing is gained from this, and plenty is
+ * lost - the interrupt frame has to contain the trap frame
+ * (this is otherwise only necessary for the rescheduling trap
+ * in doreti, and the frame for that could easily be set up
+ * just before it is used).
+ */
+ curproc->p_md.md_regs = &frame->if_es;
+#ifdef notyet
+ /*
+ * Encode the appropriate code for detailed information on
+ * this exception.
+ */
+ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
+#else
+ code = 0; /* XXX */
+#endif
+ trapsignal(curproc, SIGFPE, code);
+ } else {
+ /*
+ * Nested interrupt. These losers occur when:
+ * o an IRQ13 is bogusly generated at a bogus time, e.g.:
+ * o immediately after an fnsave or frstor of an
+ * error state.
+ * o a couple of 386 instructions after
+ * "fstpl _memvar" causes a stack overflow.
+ * These are especially nasty when combined with a
+ * trace trap.
+ * o an IRQ13 occurs at the same time as another higher-
+ * priority interrupt.
+ *
+ * Treat them like a true async interrupt.
+ */
+ psignal(curproc, SIGFPE);
+ }
+}
+
+/*
+ * Implement device not available (DNA) exception
+ *
+ * It would be better to switch FP context here (if curproc != npxproc)
+ * and not necessarily for every context switch, but it is too hard to
+ * access foreign pcb's.
+ */
+int
+npxdna()
+{
+ if (!npx_exists)
+ return (0);
+ if (npxproc != NULL) {
+ printf("npxdna: npxproc = %p, curproc = %p\n",
+ npxproc, curproc);
+ panic("npxdna");
+ }
+ stop_emulating();
+ /*
+ * Record new context early in case frstor causes an IRQ13.
+ */
+ npxproc = curproc;
+ curpcb->pcb_savefpu.sv_ex_sw = 0;
+ /*
+ * The following frstor may cause an IRQ13 when the state being
+ * restored has a pending error. The error will appear to have been
+ * triggered by the current (npx) user instruction even when that
+ * instruction is a no-wait instruction that should not trigger an
+ * error (e.g., fnclex). On at least one 486 system all of the
+ * no-wait instructions are broken the same as frstor, so our
+ * treatment does not amplify the breakage. On at least one
+ * 386/Cyrix 387 system, fnclex works correctly while frstor and
+ * fnsave are broken, so our treatment breaks fnclex if it is the
+ * first FPU instruction after a context switch.
+ */
+ frstor(&curpcb->pcb_savefpu);
+
+ return (1);
+}
+
+/*
+ * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
+ * pending, then fnsave generates a bogus IRQ13 on some systems. Force
+ * any IRQ13 to be handled immediately, and then ignore it. This routine is
+ * often called at splhigh so it must not use many system services. In
+ * particular, it's much easier to install a special handler than to
+ * guarantee that it's safe to use npxintr() and its supporting code.
+ */
+void
+npxsave(addr)
+ struct save87 *addr;
+{
+ u_char icu1_mask;
+ u_char icu2_mask;
+ u_char old_icu1_mask;
+ u_char old_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+
+ disable_intr();
+#ifdef PC98
+ old_icu1_mask = inb(IO_ICU1 + 2);
+ old_icu2_mask = inb(IO_ICU2 + 2);
+#else
+ old_icu1_mask = inb(IO_ICU1 + 1);
+ old_icu2_mask = inb(IO_ICU2 + 1);
+#endif
+ save_idt_npxintr = idt[npx_intrno];
+#ifdef PC98
+ outb(IO_ICU1 + 2, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
+ outb(IO_ICU2 + 2, old_icu2_mask & ~(npx0_imask >> 8));
+#else
+ outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
+ outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8));
+#endif
+ idt[npx_intrno] = npx_idt_probeintr;
+ enable_intr();
+ stop_emulating();
+ fnsave(addr);
+ fnop();
+ start_emulating();
+ npxproc = NULL;
+ disable_intr();
+#ifdef PC98
+ icu1_mask = inb(IO_ICU1 + 2); /* masks may have changed */
+ icu2_mask = inb(IO_ICU2 + 2);
+ outb(IO_ICU1 + 2,
+ (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask));
+ outb(IO_ICU2 + 2,
+ (icu2_mask & ~(npx0_imask >> 8))
+ | (old_icu2_mask & (npx0_imask >> 8)));
+#else
+ icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
+ icu2_mask = inb(IO_ICU2 + 1);
+ outb(IO_ICU1 + 1,
+ (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask));
+ outb(IO_ICU2 + 1,
+ (icu2_mask & ~(npx0_imask >> 8))
+ | (old_icu2_mask & (npx0_imask >> 8)));
+#endif
+ idt[npx_intrno] = save_idt_npxintr;
+ enable_intr(); /* back to usual state */
+}
+
+#endif /* NNPX > 0 */
diff --git a/sys/pc98/pc98/pc98.c b/sys/pc98/pc98/pc98.c
new file mode 100644
index 0000000..43d7e5b
--- /dev/null
+++ b/sys/pc98/pc98/pc98.c
@@ -0,0 +1,1228 @@
+/*-
+ * Copyright (c) 1991 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.c 7.2 (Berkeley) 5/13/91
+ * $Id: isa.c,v 1.70 1996/05/02 10:43:09 phk Exp $
+ */
+
+/*
+ * code to manage AT bus
+ *
+ * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com):
+ * Fixed uninitialized variable problem and added code to deal
+ * with DMA page boundaries in pc98_dmarangecheck(). Fixed word
+ * mode DMA count compution and reorganized DMA setup code in
+ * isa_dmastart()
+ */
+
+/*
+ * modified for PC9801 by A.Kojima F.Ukai M.Ishii
+ * Kyoto University Microcomputer Club (KMC)
+ * $Id: pc98.c,v 1.3 1994/03/17 23:24:40 kakefuda Exp $
+ */
+
+#include "opt_auto_eoi.h"
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/buf.h>
+#include <sys/syslog.h>
+#include <sys/malloc.h>
+#include <machine/segments.h>
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#ifdef PC98
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/ic/i8237.h>
+#else
+#include <i386/isa/isa_device.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/ic/i8237.h>
+#endif
+#include <sys/devconf.h>
+#include "vector.h"
+
+#ifdef PC98
+unsigned char hireso;
+#endif
+
+/*
+** Register definitions for DMA controller 1 (channels 0..3):
+*/
+#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */
+#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */
+#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */
+#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */
+
+/*
+** Register definitions for DMA controller 2 (channels 4..7):
+*/
+#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */
+#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */
+#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */
+#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */
+
+u_long *intr_countp[ICU_LEN];
+inthand2_t *intr_handler[ICU_LEN];
+u_int intr_mask[ICU_LEN];
+u_int* intr_mptr[ICU_LEN];
+int intr_unit[ICU_LEN];
+
+extern struct kern_devconf kdc_cpu0;
+
+#ifdef PC98
+struct kern_devconf kdc_nec0 = {
+ 0, 0, 0, /* filled in by dev_attach */
+ "nec", 0, { MDDT_BUS, 0 },
+ 0, 0, 0, BUS_EXTERNALLEN,
+ &kdc_cpu0, /* parent is the CPU */
+ 0, /* no parentdata */
+ DC_BUSY, /* busses are always busy */
+ "PC-9801 C-bus",
+ DC_CLS_BUS /* class */
+};
+#else
+struct kern_devconf kdc_isa0 = {
+ 0, 0, 0, /* filled in by dev_attach */
+ "isa", 0, { MDDT_BUS, 0 },
+ 0, 0, 0, BUS_EXTERNALLEN,
+ &kdc_cpu0, /* parent is the CPU */
+ 0, /* no parentdata */
+ DC_BUSY, /* busses are always busy */
+ "ISA bus",
+ DC_CLS_BUS /* class */
+};
+#endif
+
+static inthand_t *fastintr[ICU_LEN] = {
+ &IDTVEC(fastintr0), &IDTVEC(fastintr1),
+ &IDTVEC(fastintr2), &IDTVEC(fastintr3),
+ &IDTVEC(fastintr4), &IDTVEC(fastintr5),
+ &IDTVEC(fastintr6), &IDTVEC(fastintr7),
+ &IDTVEC(fastintr8), &IDTVEC(fastintr9),
+ &IDTVEC(fastintr10), &IDTVEC(fastintr11),
+ &IDTVEC(fastintr12), &IDTVEC(fastintr13),
+ &IDTVEC(fastintr14), &IDTVEC(fastintr15)
+};
+
+static inthand_t *slowintr[ICU_LEN] = {
+ &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
+ &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
+ &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
+ &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15)
+};
+
+#ifdef PC98
+static void config_pc98dev __P((struct pc98_device *isdp, u_int *mp));
+static void config_pc98dev_c __P((struct pc98_device *isdp, u_int *mp,
+ int reconfig));
+static void conflict __P((struct pc98_device *dvp, struct pc98_device *tmpdvp,
+ int item, char const *whatnot, char const *reason,
+ char const *format));
+static int haveseen __P((struct pc98_device *dvp, struct pc98_device *tmpdvp,
+ u_int checkbits));
+static int pc98_dmarangecheck __P((caddr_t va, u_int length, int chan));
+static inthand2_t pc98_strayintr;
+static void register_imask __P((struct pc98_device *dvp, u_int mask));
+#else
+static void config_isadev __P((struct isa_device *isdp, u_int *mp));
+static void config_isadev_c __P((struct isa_device *isdp, u_int *mp,
+ int reconfig));
+static void conflict __P((struct isa_device *dvp, struct isa_device *tmpdvp,
+ int item, char const *whatnot, char const *reason,
+ char const *format));
+static int haveseen __P((struct isa_device *dvp, struct isa_device *tmpdvp,
+ u_int checkbits));
+static int isa_dmarangecheck __P((caddr_t va, u_int length, int chan));
+static inthand2_t isa_strayintr;
+static void register_imask __P((struct isa_device *dvp, u_int mask));
+#endif
+
+/*
+ * print a conflict message
+ */
+static void
+conflict(dvp, tmpdvp, item, whatnot, reason, format)
+#ifdef PC98
+ struct pc98_device *dvp;
+ struct pc98_device *tmpdvp;
+#else
+ struct isa_device *dvp;
+ struct isa_device *tmpdvp;
+#endif
+ int item;
+ char const *whatnot;
+ char const *reason;
+ char const *format;
+{
+ printf("%s%d not %sed due to %s conflict with %s%d at ",
+ dvp->id_driver->name, dvp->id_unit, whatnot, reason,
+ tmpdvp->id_driver->name, tmpdvp->id_unit);
+ printf(format, item);
+ printf("\n");
+}
+
+/*
+ * Check to see if things are already in use, like IRQ's, I/O addresses
+ * and Memory addresses.
+ */
+static int
+haveseen(dvp, tmpdvp, checkbits)
+ struct pc98_device *dvp;
+ struct pc98_device *tmpdvp;
+ u_int checkbits;
+{
+ /*
+ * Only check against devices that have already been found and are not
+ * unilaterally allowed to conflict anyway.
+ */
+ if (tmpdvp->id_alive && !dvp->id_conflicts) {
+ char const *whatnot;
+
+ whatnot = checkbits & CC_ATTACH ? "attach" : "prob";
+ /*
+ * Check for I/O address conflict. We can only check the
+ * starting address of the device against the range of the
+ * device that has already been probed since we do not
+ * know how many I/O addresses this device uses.
+ */
+ if (checkbits & CC_IOADDR && tmpdvp->id_alive != -1) {
+ if ((dvp->id_iobase >= tmpdvp->id_iobase) &&
+ (dvp->id_iobase <=
+ (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) {
+ conflict(dvp, tmpdvp, dvp->id_iobase, whatnot,
+ "I/O address", "0x%x");
+ return 1;
+ }
+ }
+ /*
+ * Check for Memory address conflict. We can check for
+ * range overlap, but it will not catch all cases since the
+ * driver may adjust the msize paramater during probe, for
+ * now we just check that the starting address does not
+ * fall within any allocated region.
+ * XXX could add a second check after the probe for overlap,
+ * since at that time we would know the full range.
+ * XXX KERNBASE is a hack, we should have vaddr in the table!
+ */
+ if (checkbits & CC_MEMADDR && tmpdvp->id_maddr) {
+ if ((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) &&
+ (KERNBASE + dvp->id_maddr <=
+ (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) {
+ conflict(dvp, tmpdvp, (int)dvp->id_maddr,
+ whatnot, "maddr", "0x%x");
+ return 1;
+ }
+ }
+ /*
+ * Check for IRQ conflicts.
+ */
+ if (checkbits & CC_IRQ && tmpdvp->id_irq) {
+ if (tmpdvp->id_irq == dvp->id_irq) {
+ conflict(dvp, tmpdvp, ffs(dvp->id_irq) - 1,
+ whatnot, "irq", "%d");
+ return 1;
+ }
+ }
+ /*
+ * Check for DRQ conflicts.
+ */
+ if (checkbits & CC_DRQ && tmpdvp->id_drq != -1) {
+ if (tmpdvp->id_drq == dvp->id_drq) {
+ conflict(dvp, tmpdvp, dvp->id_drq, whatnot,
+ "drq", "%d");
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Search through all the pc98_devtab_* tables looking for anything that
+ * conflicts with the current device.
+ */
+int
+haveseen_pc98dev(dvp, checkbits)
+ struct pc98_device *dvp;
+ u_int checkbits;
+{
+ struct pc98_device *tmpdvp;
+ int status = 0;
+
+ for (tmpdvp = pc98_devtab_tty; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp, checkbits);
+ if (status)
+ return status;
+ }
+ for (tmpdvp = pc98_devtab_bio; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp, checkbits);
+ if (status)
+ return status;
+ }
+ for (tmpdvp = pc98_devtab_net; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp, checkbits);
+ if (status)
+ return status;
+ }
+ for (tmpdvp = pc98_devtab_null; tmpdvp->id_driver; tmpdvp++) {
+ status |= haveseen(dvp, tmpdvp, checkbits);
+ if (status)
+ return status;
+ }
+ return(status);
+}
+
+/*
+ * Configure all PC98 devices
+ */
+void
+pc98_configure() {
+ struct pc98_device *dvp;
+
+ dev_attach(&kdc_nec0);
+
+ splhigh();
+ printf("Probing for devices on the PC98 bus:\n");
+ /* First probe all the sensitive probes */
+ for (dvp = pc98_devtab_tty; dvp->id_driver; dvp++)
+ if (dvp->id_driver->sensitive_hw)
+ config_pc98dev(dvp, &tty_imask);
+ for (dvp = pc98_devtab_bio; dvp->id_driver; dvp++)
+ if (dvp->id_driver->sensitive_hw)
+ config_pc98dev(dvp, &bio_imask);
+ for (dvp = pc98_devtab_net; dvp->id_driver; dvp++)
+ if (dvp->id_driver->sensitive_hw)
+ config_pc98dev(dvp, &net_imask);
+ for (dvp = pc98_devtab_null; dvp->id_driver; dvp++)
+ if (dvp->id_driver->sensitive_hw)
+ config_pc98dev(dvp, (u_int *)NULL);
+
+ /* Then all the bad ones */
+ for (dvp = pc98_devtab_tty; dvp->id_driver; dvp++)
+ if (!dvp->id_driver->sensitive_hw)
+ config_pc98dev(dvp, &tty_imask);
+ for (dvp = pc98_devtab_bio; dvp->id_driver; dvp++)
+ if (!dvp->id_driver->sensitive_hw)
+ config_pc98dev(dvp, &bio_imask);
+ for (dvp = pc98_devtab_net; dvp->id_driver; dvp++)
+ if (!dvp->id_driver->sensitive_hw)
+ config_pc98dev(dvp, &net_imask);
+ for (dvp = pc98_devtab_null; dvp->id_driver; dvp++)
+ if (!dvp->id_driver->sensitive_hw)
+ config_pc98dev(dvp, (u_int *)NULL);
+
+ bio_imask |= SWI_CLOCK_MASK;
+ net_imask |= SWI_NET_MASK;
+ tty_imask |= SWI_TTY_MASK;
+
+/*
+ * XXX we should really add the tty device to net_imask when the line is
+ * switched to SLIPDISC, and then remove it when it is switched away from
+ * SLIPDISC. No need to block out ALL ttys during a splimp when only one
+ * of them is running slip.
+ *
+ * XXX actually, blocking all ttys during a splimp doesn't matter so much
+ * with sio because the serial interrupt layer doesn't use tty_imask. Only
+ * non-serial ttys suffer. It's more stupid that ALL 'net's are blocked
+ * during spltty.
+ */
+#include "sl.h"
+#if NSL > 0
+ net_imask |= tty_imask;
+ tty_imask = net_imask;
+#endif
+
+ /* bio_imask |= tty_imask ; can some tty devices use buffers? */
+
+ if (bootverbose)
+ printf("imasks: bio %x, tty %x, net %x\n",
+ bio_imask, tty_imask, net_imask);
+
+ /*
+ * Finish initializing intr_mask[]. Note that the partly
+ * constructed masks aren't actually used since we're at splhigh.
+ * For fully dynamic initialization, register_intr() and
+ * unregister_intr() will have to adjust the masks for _all_
+ * interrupts and for tty_imask, etc.
+ */
+ for (dvp = pc98_devtab_tty; dvp->id_driver; dvp++)
+ register_imask(dvp, tty_imask);
+ for (dvp = pc98_devtab_bio; dvp->id_driver; dvp++)
+ register_imask(dvp, bio_imask);
+ for (dvp = pc98_devtab_net; dvp->id_driver; dvp++)
+ register_imask(dvp, net_imask);
+ for (dvp = pc98_devtab_null; dvp->id_driver; dvp++)
+ register_imask(dvp, SWI_CLOCK_MASK);
+ spl0();
+}
+
+/*
+ * Configure an PC98 device.
+ */
+
+
+static void
+config_pc98dev(isdp, mp)
+ struct pc98_device *isdp;
+ u_int *mp;
+{
+ config_pc98dev_c(isdp, mp, 0);
+}
+
+void
+reconfig_pc98dev(isdp, mp)
+ struct pc98_device *isdp;
+ u_int *mp;
+{
+ config_pc98dev_c(isdp, mp, 1);
+}
+
+static void
+config_pc98dev_c(isdp, mp, reconfig)
+ struct pc98_device *isdp;
+ u_int *mp;
+ int reconfig;
+{
+ u_int checkbits;
+ int id_alive;
+ int last_alive;
+ struct pc98_driver *dp = isdp->id_driver;
+
+ if (!isdp->id_enabled) {
+ printf("%s%d: disabled, not probed.\n",
+ dp->name, isdp->id_unit);
+ return;
+ }
+ checkbits = CC_DRQ | CC_IOADDR | CC_MEMADDR;
+ if (!reconfig && haveseen_pc98dev(isdp, checkbits))
+ return;
+ if (!reconfig && isdp->id_maddr) {
+ isdp->id_maddr -= 0xa0000; /* XXX should be a define */
+ isdp->id_maddr += atdevbase;
+ }
+ if (reconfig) {
+ last_alive = isdp->id_alive;
+ isdp->id_reconfig = 1;
+ }
+ else {
+ last_alive = 0;
+ isdp->id_reconfig = 0;
+ }
+ id_alive = (*dp->probe)(isdp);
+ if (id_alive) {
+ /*
+ * Only print the I/O address range if id_alive != -1
+ * Right now this is a temporary fix just for the new
+ * NPX code so that if it finds a 486 that can use trap
+ * 16 it will not report I/O addresses.
+ * Rod Grimes 04/26/94
+ */
+ if (!isdp->id_reconfig) {
+ printf("%s%d", dp->name, isdp->id_unit);
+ if (id_alive != -1) {
+ printf(" at 0x%x", isdp->id_iobase);
+ if (isdp->id_iobase + id_alive - 1 !=
+ isdp->id_iobase) {
+ printf("-0x%x",
+ isdp->id_iobase + id_alive - 1);
+ }
+ }
+ if (isdp->id_irq)
+ printf(" irq %d", ffs(isdp->id_irq) - 1);
+ if (isdp->id_drq != -1)
+ printf(" drq %d", isdp->id_drq);
+ if (isdp->id_maddr)
+ printf(" maddr 0x%lx", kvtop(isdp->id_maddr));
+ if (isdp->id_msize)
+ printf(" msize %d", isdp->id_msize);
+ if (isdp->id_flags)
+ printf(" flags 0x%x", isdp->id_flags);
+#ifdef PC98
+ if (isdp->id_iobase) {
+ printf(" on pc98");
+ }
+#else
+ if (isdp->id_iobase && !(isdp->id_iobase & 0xf300)) {
+ printf(" on motherboard");
+ } else if (isdp->id_iobase >= 0x1000 &&
+ !(isdp->id_iobase & 0x300)) {
+ printf (" on eisa slot %d",
+ isdp->id_iobase >> 12);
+ } else {
+ printf (" on isa");
+ }
+#endif
+ printf("\n");
+ /*
+ * Check for conflicts again. The driver may have
+ * changed *dvp. We should weaken the early check
+ * since the driver may have been able to change
+ * *dvp to avoid conflicts if given a chance. We
+ * already skip the early check for IRQs and force
+ * a check for IRQs in the next group of checks.
+ */
+ checkbits |= CC_IRQ;
+ if (haveseen_pc98dev(isdp, checkbits))
+ return;
+ isdp->id_alive = id_alive;
+ }
+ (*dp->attach)(isdp);
+ if (isdp->id_irq) {
+ if (mp)
+ INTRMASK(*mp, isdp->id_irq);
+ register_intr(ffs(isdp->id_irq) - 1, isdp->id_id,
+ isdp->id_ri_flags, isdp->id_intr,
+ mp, isdp->id_unit);
+ INTREN(isdp->id_irq);
+ }
+ } else {
+ if (isdp->id_reconfig) {
+ (*dp->attach)(isdp); /* reconfiguration attach */
+ }
+ if (!last_alive) {
+ if (!isdp->id_reconfig) {
+ printf("%s%d not found",
+ dp->name, isdp->id_unit);
+ if (isdp->id_iobase) {
+ printf(" at 0x%x", isdp->id_iobase);
+ }
+ printf("\n");
+ }
+ }
+ else {
+ /* This code has not been tested.... */
+ if (isdp->id_irq) {
+ INTRDIS(isdp->id_irq);
+ unregister_intr(ffs(isdp->id_irq) - 1,
+ isdp->id_intr);
+ if (mp)
+ INTRUNMASK(*mp, isdp->id_irq);
+ }
+ }
+ }
+}
+
+/*
+ * Provide PC98-specific device information to user programs using the
+ * hw.devconf interface.
+ */
+int
+pc98_externalize(struct pc98_device *id, struct sysctl_req *req)
+{
+ return (SYSCTL_OUT(req, id, sizeof *id));
+}
+
+/*
+ * This is used to forcibly reconfigure an PC98 device. It currently just
+ * returns an error 'cos you can't do that yet. It is here to demonstrate
+ * what the `internalize' routine is supposed to do.
+ */
+int
+pc98_internalize(struct pc98_device *id, struct sysctl_req *req)
+{
+ struct pc98_device myid;
+ int rv;
+
+ rv = SYSCTL_IN(req, &myid, sizeof *id);
+ if(rv)
+ return rv;
+
+ rv = EOPNOTSUPP;
+ /* code would go here to validate the configuration request */
+ /* code would go here to actually perform the reconfiguration */
+ return rv;
+}
+
+int
+pc98_generic_externalize(struct kern_devconf *kdc, struct sysctl_req *req)
+{
+ return pc98_externalize(kdc->kdc_pc98, req);
+}
+
+/*
+ * Fill in default interrupt table (in case of spuruious interrupt
+ * during configuration of kernel, setup interrupt control unit
+ */
+void
+pc98_defaultirq()
+{
+ int i;
+
+ /* icu vectors */
+ for (i = 0; i < ICU_LEN; i++)
+ unregister_intr(i, (inthand2_t *)NULL);
+
+ /* initialize 8259's */
+#ifdef PC98
+ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU1+2, NRSVIDT); /* starting at this vector index */
+ outb(IO_ICU1+2, 1<<7); /* slave on line 7 */
+#ifdef AUTO_EOI_1
+ outb(IO_ICU1+2, 0x1f); /* (master) auto EOI, 8086 mode */
+#else
+ outb(IO_ICU1+2, 0x1d); /* (master) 8086 mode */
+#endif
+ outb(IO_ICU1+2, 0x7f); /* leave interrupts masked */
+ outb(IO_ICU1, 0x0a); /* default to IRR on read */
+
+ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU2+2, NRSVIDT+8); /* staring at this vector index */
+ outb(IO_ICU2+2,7); /* my slave id is 7 */
+ outb(IO_ICU2+2,9); /* 8086 mode */
+ outb(IO_ICU2+2, 0xff); /* leave interrupts masked */
+ outb(IO_ICU2, 0x0a); /* default to IRR on read */
+#else
+ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */
+ outb(IO_ICU1+1, 1<<2); /* slave on line 2 */
+#ifdef AUTO_EOI_1
+ outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */
+#else
+ outb(IO_ICU1+1, 1); /* 8086 mode */
+#endif
+ outb(IO_ICU1+1, 0xff); /* leave interrupts masked */
+ outb(IO_ICU1, 0x0a); /* default to IRR on read */
+ outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */
+
+ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
+ outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */
+ outb(IO_ICU2+1,2); /* my slave id is 2 */
+#ifdef AUTO_EOI_2
+ outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */
+#else
+ outb(IO_ICU2+1,1); /* 8086 mode */
+#endif
+ outb(IO_ICU2+1, 0xff); /* leave interrupts masked */
+ outb(IO_ICU2, 0x0a); /* default to IRR on read */
+#endif
+}
+
+#ifdef PC98
+caddr_t dma_bouncebuf[4];
+static u_int dma_bouncebufsize[4];
+#else
+caddr_t dma_bouncebuf[8];
+static u_int dma_bouncebufsize[8];
+#endif
+static u_int8_t dma_bounced = 0;
+static u_int8_t dma_busy = 0; /* Used in isa_dmastart() */
+static u_int8_t dma_inuse = 0; /* User for acquire/release */
+
+#ifdef PC98
+#define VALID_DMA_MASK (3)
+#else
+#define VALID_DMA_MASK (7)
+#endif
+
+/* high byte of address is stored in this port for i-th dma channel */
+#ifdef PC98
+short dmapageport[4] =
+ { 0x27, 0x21, 0x23, 0x25 };
+#else /* IBM-PC */
+static short dmapageport[8] =
+ { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
+#endif
+
+/*
+ * Setup a DMA channel's bounce buffer.
+ */
+void
+pc98_dmainit(chan, bouncebufsize)
+ int chan;
+ u_int bouncebufsize;
+{
+ void *buf;
+
+#ifdef DIAGNOSTIC
+ if (chan & ~VALID_DMA_MASK)
+ panic("pc98_dmainit: channel out of range");
+
+ if (dma_bouncebuf[chan] != NULL)
+ panic("pc98_dmainit: impossible request");
+#endif
+
+ dma_bouncebufsize[chan] = bouncebufsize;
+
+ /* Try malloc() first. It works better if it works. */
+ buf = malloc(bouncebufsize, M_DEVBUF, M_NOWAIT);
+ if (buf != NULL) {
+ if (pc98_dmarangecheck(buf, bouncebufsize, chan) == 0) {
+ dma_bouncebuf[chan] = buf;
+ return;
+ }
+ free(buf, M_DEVBUF);
+ }
+ buf = contigmalloc(bouncebufsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful,
+ 1ul, chan & 4 ? 0x20000ul : 0x10000ul);
+ if (buf == NULL)
+ printf("pc98_dmainit(%d, %d) failed\n", chan, bouncebufsize);
+ else
+ dma_bouncebuf[chan] = buf;
+}
+
+/*
+ * Register a DMA channel's usage. Usually called from a device driver
+ * in open() or during it's initialization.
+ */
+int
+pc98_dma_acquire(chan)
+ int chan;
+{
+#ifdef DIAGNOSTIC
+ if (chan & ~VALID_DMA_MASK)
+ panic("pc98_dma_acquire: channel out of range");
+#endif
+
+ if (dma_inuse & (1 << chan)) {
+ printf("pc98_dma_acquire: channel %d already in use\n", chan);
+ return (EBUSY);
+ }
+ dma_inuse |= (1 << chan);
+
+ return (0);
+}
+
+/*
+ * Unregister a DMA channel's usage. Usually called from a device driver
+ * during close() or during it's shutdown.
+ */
+void
+pc98_dma_release(chan)
+ int chan;
+{
+#ifdef DIAGNOSTIC
+ if (chan & ~VALID_DMA_MASK)
+ panic("pc98_dma_release: channel out of range");
+
+ if (dma_inuse & (1 << chan) == 0)
+ printf("pc98_dma_release: channel %d not in use\n", chan);
+#endif
+
+ if (dma_busy & (1 << chan)) {
+ dma_busy &= ~(1 << chan);
+ /*
+ * XXX We should also do "dma_bounced &= (1 << chan);"
+ * because we are acting on behalf of isa_dmadone() which
+ * was not called to end the last DMA operation. This does
+ * not matter now, but it may in the future.
+ */
+ }
+
+ dma_inuse &= ~(1 << chan);
+}
+
+#ifndef PC98
+/*
+ * pc98_dmacascade(): program 8237 DMA controller channel to accept
+ * external dma control by a board.
+ */
+void pc98_dmacascade(chan)
+ int chan;
+{
+#ifdef DIAGNOSTIC
+ if (chan & ~VALID_DMA_MASK)
+ panic("pc98_dmacascade: channel out of range");
+#endif
+
+ /* set dma channel mode, and set dma channel mode */
+ if ((chan & 4) == 0) {
+ outb(DMA1_MODE, DMA37MD_CASCADE | chan);
+ outb(DMA1_SMSK, chan);
+ } else {
+ outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
+ outb(DMA2_SMSK, chan & 3);
+ }
+}
+#endif
+
+/*
+ * pc98_dmastart(): program 8237 DMA controller channel, avoid page alignment
+ * problems by using a bounce buffer.
+ */
+int dma_init_flag = 0;
+
+void pc98_dmastart(int flags, caddr_t addr, u_int nbytes, int chan)
+{
+ vm_offset_t phys;
+ int modeport, waport, mskport;
+ caddr_t newaddr;
+ int s;
+
+#ifdef DIAGNOSTIC
+ if (chan & ~VALID_DMA_MASK)
+ panic("pc98_dmastart: channel out of range");
+
+ if ((chan < 4 && nbytes > (1<<16))
+ || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1)))
+ panic("pc98_dmastart: impossible request");
+
+ if (dma_inuse & (1 << chan) == 0)
+ printf("pc98_dmastart: channel %d not acquired\n", chan);
+#endif
+
+ if (dma_busy & (1 << chan))
+ printf("pc98_dmastart: channel %d busy\n", chan);
+
+ dma_busy |= (1 << chan);
+
+ if (pc98_dmarangecheck(addr, nbytes, chan)) {
+ if (dma_bouncebuf[chan] == NULL
+ || dma_bouncebufsize[chan] < nbytes)
+ panic("pc98_dmastart: bad bounce buffer");
+ dma_bounced |= (1 << chan);
+ newaddr = dma_bouncebuf[chan];
+
+ /* copy bounce buffer on write */
+ if (!(flags & B_READ))
+ bcopy(addr, newaddr, nbytes);
+ addr = newaddr;
+ }
+ /* translate to physical */
+ phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
+
+ s = splbio(); /* mask on */
+
+#ifdef CYRIX_5X86
+ asm("wbinvd"); /* wbinvd (WB cache flush) */
+#endif
+
+ if (!dma_init_flag) {
+ dma_init_flag = 1;
+ outb(0x439, (inb(0x439) & 0xfb)); /* DMA Accsess Control over 1MB */
+ outb(0x29, (0x0c | 0)); /* Bank Mode Reg. 16M mode */
+ outb(0x29, (0x0c | 1)); /* Bank Mode Reg. 16M mode */
+ outb(0x29, (0x0c | 2)); /* Bank Mode Reg. 16M mode */
+ outb(0x29, (0x0c | 3)); /* Bank Mode Reg. 16M mode */
+ outb(0x11, 0x50); /* PC98 must be 0x40 */
+ }
+
+ /* mask channel */
+ mskport = IO_DMA + 0x14; /* 0x15 */
+ outb(mskport, chan & 3 | 0x04);
+
+ /* set dma channel mode, and reset address ff */
+
+ modeport = IO_DMA + 0x16; /* 0x17 */
+ /* If B_RAW flag is set, then use autoinitialise mode */
+ if (flags & B_RAW) {
+ if (flags & B_READ)
+ outb(modeport, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3));
+ else
+ outb(modeport, DMA37MD_AUTO|DMA37MD_READ|(chan&3));
+ }
+ else
+ if (flags & B_READ)
+ outb(modeport, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
+ else
+ outb(modeport, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
+ outb(modeport + 1*2, 0); /* 0x19 (clear byte pointer) */
+
+ /* send start address */
+ waport = IO_DMA + (chan<<2); /* 0x1, 0x5, 0x9, 0xd */
+ outb(waport, phys);
+ outb(waport, phys>>8);
+ outb(dmapageport[chan], phys>>16);
+
+ /* send count */
+ outb(waport + 2, --nbytes); /* 0x3, 0x7, 0xb, 0xf */
+ outb(waport + 2, nbytes>>8);
+
+ /* unmask channel */
+ mskport = IO_DMA + 0x14; /* 0x15 */
+ outb(mskport, chan & 3);
+
+ splx(s); /* mask off */
+}
+
+void pc98_dmadone(int flags, caddr_t addr, int nbytes, int chan)
+{
+#if defined(CYRIX_486DLC) || defined(IBM_486SLC)
+ if (flags & B_READ) {
+ /* cache flush only after reading 92/12/9 by A.Kojima */
+ asm(" .byte 0x0f,0x08"); /* invd (cache flush) */
+ }
+#endif
+
+#ifdef DIAGNOSTIC
+ if (chan & ~VALID_DMA_MASK)
+ panic("pc98_dmadone: channel out of range");
+
+ if (dma_inuse & (1 << chan) == 0)
+ printf("pc98_dmadone: channel %d not acquired\n", chan);
+#endif
+
+#if 0
+ /*
+ * XXX This should be checked, but drivers like ad1848 only call
+ * isa_dmastart() once because they use Auto DMA mode. If we
+ * leave this in, drivers that do this will print this continuously.
+ */
+ if (dma_busy & (1 << chan) == 0)
+ printf("pc98_dmadone: channel %d not busy\n", chan);
+#endif
+
+ if (dma_bounced & (1 << chan)) {
+ /* copy bounce buffer on read */
+ if (flags & B_READ)
+ bcopy(dma_bouncebuf[chan], addr, nbytes);
+
+ dma_bounced &= ~(1 << chan);
+ }
+ dma_busy &= ~(1 << chan);
+}
+
+/*
+ * Check for problems with the address range of a DMA transfer
+ * (non-contiguous physical pages, outside of bus address space,
+ * crossing DMA page boundaries).
+ * Return true if special handling needed.
+ */
+
+int
+pc98_dmarangecheck(caddr_t va, u_int length, int chan) {
+ vm_offset_t phys, priorpage = 0, endva;
+ u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1);
+
+ endva = (vm_offset_t)round_page(va + length);
+ for (; va < (caddr_t) endva ; va += PAGE_SIZE) {
+ phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va));
+#ifdef EPSON_BOUNCEDMA
+#define PC98RAM_END 0xf00000
+#else
+#define PC98RAM_END RAM_END
+#endif
+ if (phys == 0)
+ panic("pc98_dmacheck: no physical page present");
+ if (phys >= PC98RAM_END)
+ return (1);
+ if (priorpage) {
+ if (priorpage + PAGE_SIZE != phys)
+ return (1);
+ /* check if crossing a DMA page boundary */
+ if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk)
+ return (1);
+ }
+ priorpage = phys;
+ }
+ return (0);
+}
+
+#ifdef PC98
+#define NMI_PARITY 0x04
+#define NMI_EPARITY 0x02
+#else
+#define NMI_PARITY (1 << 7)
+#define NMI_IOCHAN (1 << 6)
+#define ENMI_WATCHDOG (1 << 7)
+#define ENMI_BUSTIMER (1 << 6)
+#define ENMI_IOSTATUS (1 << 5)
+#endif
+
+/*
+ * Handle a NMI, possibly a machine check.
+ * return true to panic system, false to ignore.
+ */
+int
+pc98_nmi(cd)
+ int cd;
+{
+#ifdef PC98
+ int port = inb(0x33);
+ if (epson_machine_id == 0x20)
+ epson_outb(0xc16, epson_inb(0xc16) | 0x1);
+ if (port & NMI_PARITY) {
+ panic("BASE RAM parity error, likely hardware failure.");
+ } else if (port & NMI_EPARITY) {
+ panic("EXTENDED RAM parity error, likely hardware failure.");
+ } else {
+ printf("\nNMI Resume ??\n");
+ return(0);
+ }
+#else /* IBM-PC */
+ int isa_port = inb(0x61);
+ int eisa_port = inb(0x461);
+ if(isa_port & NMI_PARITY) {
+ panic("RAM parity error, likely hardware failure.");
+ } else if(isa_port & NMI_IOCHAN) {
+ panic("I/O channel check, likely hardware failure.");
+ } else if(eisa_port & ENMI_WATCHDOG) {
+ panic("EISA watchdog timer expired, likely hardware failure.");
+ } else if(eisa_port & ENMI_BUSTIMER) {
+ panic("EISA bus timeout, likely hardware failure.");
+ } else if(eisa_port & ENMI_IOSTATUS) {
+ panic("EISA I/O port status error.");
+ } else {
+ printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port);
+ return(0);
+ }
+#endif
+}
+
+/*
+ * Caught a stray interrupt, notify
+ */
+static void
+pc98_strayintr(d)
+ int d;
+{
+
+ /* DON'T BOTHER FOR NOW! */
+ /* for some reason, we get bursts of intr #7, even if not enabled! */
+ /*
+ * Well the reason you got bursts of intr #7 is because someone
+ * raised an interrupt line and dropped it before the 8259 could
+ * prioritize it. This is documented in the intel data book. This
+ * means you have BAD hardware! I have changed this so that only
+ * the first 5 get logged, then it quits logging them, and puts
+ * out a special message. rgrimes 3/25/1993
+ */
+ /*
+ * XXX TODO print a different message for #7 if it is for a
+ * glitch. Glitches can be distinguished from real #7's by
+ * testing that the in-service bit is _not_ set. The test
+ * must be done before sending an EOI so it can't be done if
+ * we are using AUTO_EOI_1.
+ */
+ if (intrcnt[NR_DEVICES + d] <= 5)
+ log(LOG_ERR, "stray irq %d\n", d);
+ if (intrcnt[NR_DEVICES + d] == 5)
+ log(LOG_CRIT,
+ "too many stray irq %d's; not logging any more\n", d);
+}
+
+/*
+ * Find the highest priority enabled display device. Since we can't
+ * distinguish display devices from ttys, depend on display devices
+ * being sensitive and before sensitive non-display devices (if any)
+ * in isa_devtab_tty.
+ *
+ * XXX we should add capability flags IAMDISPLAY and ISUPPORTCONSOLES.
+ */
+#ifdef PC98
+struct pc98_device *
+#else
+struct isa_device *
+#endif
+find_display()
+{
+#ifdef PC98
+ struct pc98_device *dvp;
+
+ for (dvp = pc98_devtab_tty; dvp->id_driver != NULL; dvp++)
+#else
+ struct isa_device *dvp;
+
+ for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++)
+#endif
+ if (dvp->id_driver->sensitive_hw && dvp->id_enabled)
+ return (dvp);
+ return (NULL);
+}
+
+/*
+ * find an PC98 device in a given pc98_devtab_* table, given
+ * the table to search, the expected id_driver entry, and the unit number.
+ *
+ * this function is defined in pc98_device.h, and this location is debatable;
+ * i put it there because it's useless w/o, and directly operates on
+ * the other stuff in that file.
+ *
+ */
+
+struct pc98_device *find_pc98dev(table, driverp, unit)
+ struct pc98_device *table;
+ struct pc98_driver *driverp;
+ int unit;
+{
+ if (driverp == NULL) /* sanity check */
+ return NULL;
+
+ while ((table->id_driver != driverp) || (table->id_unit != unit)) {
+ if (table->id_driver == 0)
+ return NULL;
+
+ table++;
+ }
+
+ return table;
+}
+
+/*
+ * Return nonzero if a (masked) irq is pending for a given device.
+ */
+int
+pc98_irq_pending(dvp)
+ struct pc98_device *dvp;
+{
+ unsigned id_irq;
+
+ id_irq = dvp->id_irq;
+ if (id_irq & 0xff)
+ return (inb(IO_ICU1) & id_irq);
+ return (inb(IO_ICU2) & (id_irq >> 8));
+}
+
+int
+update_intr_masks(void)
+{
+ int intr, n=0;
+ u_int mask,*maskptr;
+
+ for (intr=0; intr < ICU_LEN; intr ++) {
+#ifdef PC98
+ if (intr==7) continue;
+#else
+ if (intr==2) continue;
+#endif
+ maskptr = intr_mptr[intr];
+ if (!maskptr) continue;
+ *maskptr |= 1 << intr;
+ mask = *maskptr;
+ if (mask != intr_mask[intr]) {
+#if 0
+ printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n",
+ intr, intr_mask[intr], mask, maskptr);
+#endif
+ intr_mask[intr]=mask;
+ n++;
+ }
+
+ }
+ return (n);
+}
+
+int
+register_intr(intr, device_id, flags, handler, maskptr, unit)
+ int intr;
+ int device_id;
+ u_int flags;
+ inthand2_t *handler;
+ u_int *maskptr;
+ int unit;
+{
+ char *cp;
+ u_long ef;
+ int id;
+ u_int mask = (maskptr ? *maskptr : 0);
+
+#ifdef PC98
+ if ((u_int)intr >= ICU_LEN || intr == 7
+#else
+ if ((u_int)intr >= ICU_LEN || intr == 2
+#endif
+ || (u_int)device_id >= NR_DEVICES)
+ return (EINVAL);
+ if (intr_handler[intr] != pc98_strayintr)
+ return (EBUSY);
+ ef = read_eflags();
+ disable_intr();
+ intr_countp[intr] = &intrcnt[device_id];
+ intr_handler[intr] = handler;
+ intr_mptr[intr] = maskptr;
+ intr_mask[intr] = mask | (1 << intr);
+ intr_unit[intr] = unit;
+ setidt(ICU_OFFSET + intr,
+ flags & RI_FAST ? fastintr[intr] : slowintr[intr],
+ SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+ write_eflags(ef);
+ for (cp = intrnames, id = 0; id <= device_id; id++)
+ while (*cp++ != '\0')
+ ;
+ if (cp > eintrnames)
+ return (0);
+ if (intr < 10) {
+ cp[-3] = intr + '0';
+ cp[-2] = ' ';
+ } else {
+ cp[-3] = '1';
+ cp[-2] = intr - 10 + '0';
+ }
+ return (0);
+}
+
+static void
+register_imask(dvp, mask)
+ struct pc98_device *dvp;
+ u_int mask;
+{
+ if (dvp->id_alive && dvp->id_irq) {
+ int intr;
+
+ intr = ffs(dvp->id_irq) - 1;
+ intr_mask[intr] = mask | (1 <<intr);
+ }
+ (void) update_intr_masks();
+}
+
+int
+unregister_intr(intr, handler)
+ int intr;
+ inthand2_t *handler;
+{
+ u_long ef;
+
+ if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr])
+ return (EINVAL);
+ ef = read_eflags();
+ disable_intr();
+ intr_countp[intr] = &intrcnt[NR_DEVICES + intr];
+ intr_handler[intr] = pc98_strayintr;
+ intr_mptr[intr] = NULL;
+ intr_mask[intr] = HWI_MASK | SWI_MASK;
+ intr_unit[intr] = intr;
+ setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL,
+ GSEL(GCODE_SEL, SEL_KPL));
+ write_eflags(ef);
+ return (0);
+}
+
+#ifdef DDB
+unsigned int ddb_inb(unsigned int addr)
+{
+ return inb(addr);
+}
+
+void ddb_outb(unsigned int addr, unsigned char dt)
+{
+ outb(addr, dt);
+}
+#endif
diff --git a/sys/pc98/pc98/pc98.h b/sys/pc98/pc98/pc98.h
new file mode 100644
index 0000000..806c840
--- /dev/null
+++ b/sys/pc98/pc98/pc98.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/pc98/pc98_device.h b/sys/pc98/pc98/pc98_device.h
new file mode 100644
index 0000000..3285e92
--- /dev/null
+++ b/sys/pc98/pc98/pc98_device.h
@@ -0,0 +1,248 @@
+/*-
+ * 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: @(#)isa_device.h 7.1 (Berkeley) 5/9/91
+ * $Id: isa_device.h,v 1.29 1996/04/08 19:38:57 smpatel Exp $
+ */
+
+#ifndef _PC98_PC98_PC98_DEVICE_H_
+#define _PC98_PC98_PC98_DEVICE_H_
+
+/*
+ * PC98 Bus Autoconfiguration
+ */
+/*
+ * modified for PC9801 by A.Kojima F.Ukai M.Ishii
+ * Kyoto University Microcomputer Club (KMC)
+ */
+
+#define IDTVEC(name) __CONCAT(X,name)
+
+/*
+ * Type of the first (asm) part of an interrupt handler.
+ */
+typedef void inthand_t __P((u_int cs, u_int ef, u_int esp, u_int ss));
+
+/*
+ * Usual type of the second (C) part of an interrupt handler. Some bogus
+ * ones need the arg to be the interrupt frame (and not a copy of it, which
+ * is all that is possible in C).
+ */
+typedef void inthand2_t __P((int unit));
+
+/*
+ * Bits to specify the type and amount of conflict checking.
+ */
+#define CC_ATTACH (1 << 0)
+#define CC_DRQ (1 << 1)
+#define CC_IOADDR (1 << 2)
+#define CC_IRQ (1 << 3)
+#define CC_MEMADDR (1 << 4)
+
+/*
+ * Per device structure.
+ *
+ * XXX Note: id_conflicts should either become an array of things we're
+ * specifically allowed to conflict with or be subsumed into some
+ * more powerful mechanism for detecting and dealing with multiple types
+ * of non-fatal conflict. -jkh XXX
+ */
+struct pc98_device {
+ int id_id; /* device id */
+ struct pc98_driver *id_driver;
+ int id_iobase; /* base i/o address */
+ u_short id_irq; /* interrupt request */
+ short id_drq; /* DMA request */
+ caddr_t id_maddr; /* physical i/o memory address on bus (if any)*/
+ int id_msize; /* size of i/o memory */
+ inthand2_t *id_intr; /* interrupt interface routine */
+ int id_unit; /* unit number */
+ int id_flags; /* flags */
+ int id_scsiid; /* scsi id if needed */
+ int id_alive; /* device is present */
+#define RI_FAST 1 /* fast interrupt handler */
+ u_int id_ri_flags; /* flags for register_intr() */
+ int id_reconfig; /* hot eject device support (such as PCMCIA) */
+ int id_enabled; /* is device enabled */
+ int id_conflicts; /* we're allowed to conflict with things */
+ struct pc98_device *id_next; /* used in isa_devlist in userconfig() */
+};
+
+/*
+ * Per-driver structure.
+ *
+ * Each device driver defines entries for a set of routines
+ * as well as an array of types which are acceptable to it.
+ * These are used at boot time by the configuration program.
+ */
+struct pc98_driver {
+ int (*probe) __P((struct pc98_device *idp));
+ /* test whether device is present */
+ int (*attach) __P((struct pc98_device *idp));
+ /* setup driver for a device */
+ char *name; /* device name */
+ int sensitive_hw; /* true if other probes confuse us */
+};
+
+#define PC98_EXTERNALLEN (sizeof(struct pc98_device))
+
+#ifdef KERNEL
+
+extern char eintrnames[]; /* end of intrnames[] */
+extern u_long intrcnt[]; /* counts for for each device and stray */
+extern char intrnames[]; /* string table containing device names */
+extern u_long *intr_countp[]; /* pointers into intrcnt[] */
+extern inthand2_t *intr_handler[]; /* C entry points of intr handlers */
+extern u_int intr_mask[]; /* sets of intrs masked during handling of 1 */
+extern int intr_unit[]; /* cookies to pass to intr handlers */
+
+extern struct pc98_device pc98_biotab_fdc[];
+extern struct pc98_device pc98_biotab_wdc[];
+extern struct pc98_device pc98_devtab_bio[];
+extern struct pc98_device pc98_devtab_net[];
+extern struct pc98_device pc98_devtab_null[];
+extern struct pc98_device pc98_devtab_tty[];
+extern struct kern_devconf kdc_nec0;
+
+struct kern_devconf;
+struct sysctl_req;
+
+inthand_t
+ IDTVEC(fastintr0), IDTVEC(fastintr1),
+ IDTVEC(fastintr2), IDTVEC(fastintr3),
+ IDTVEC(fastintr4), IDTVEC(fastintr5),
+ IDTVEC(fastintr6), IDTVEC(fastintr7),
+ IDTVEC(fastintr8), IDTVEC(fastintr9),
+ IDTVEC(fastintr10), IDTVEC(fastintr11),
+ IDTVEC(fastintr12), IDTVEC(fastintr13),
+ IDTVEC(fastintr14), IDTVEC(fastintr15);
+inthand_t
+ IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3),
+ IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7),
+ IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11),
+ IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15);
+
+struct pc98_device *
+ find_display __P((void));
+struct pc98_device *
+ find_pc98dev __P((struct pc98_device *table, struct pc98_driver *driverp,
+ int unit));
+int haveseen_pc98dev __P((struct pc98_device *dvp, u_int checkbits));
+void pc98_configure __P((void));
+void pc98_defaultirq __P((void));
+void pc98_dmacascade __P((int chan));
+void pc98_dmadone __P((int flags, caddr_t addr, int nbytes, int chan));
+void pc98_dmainit __P((int chan, u_int bouncebufsize));
+void pc98_dmastart __P((int flags, caddr_t addr, u_int nbytes, int chan));
+int pc98_dma_acquire __P((int chan));
+void pc98_dma_release __P((int chan));
+int pc98_externalize __P((struct pc98_device *id, struct sysctl_req *req));
+int pc98_generic_externalize __P((struct kern_devconf *kdc,
+ struct sysctl_req *req));
+int pc98_internalize __P((struct pc98_device *id, struct sysctl_req *req));
+int pc98_irq_pending __P((struct pc98_device *dvp));
+int pc98_nmi __P((int cd));
+void reconfig_pc98dev __P((struct pc98_device *isdp, u_int *mp));
+int register_intr __P((int intr, int device_id, u_int flags,
+ inthand2_t *handler, u_int *maskptr, int unit));
+int unregister_intr __P((int intr, inthand2_t *handler));
+int update_intr_masks __P((void));
+
+#endif /* KERNEL */
+
+#ifdef PC98
+#if 1
+#define PC98_VECTOR_SIZE (0x400)
+#define PC98_SYSTEM_PARAMETER_SIZE (0x230)
+
+#define PC98_SAVE_AREA(highreso_flag) (0xa1000)
+#define PC98_SAVE_AREA_ADDRESS (0x10)
+
+#define OFS_BOOT_boothowto 0x210
+#define OFS_BOOT_bootdev 0x214
+#define OFS_BOOT_cyloffset 0x218
+#define OFS_WD_BIOS_SECSIZE(i) (0x200+(i)*6)
+#define OFS_WD_BIOS_NCYL(i) (0x202+(i)*6)
+#define OFS_WD_BIOS_HEAD(i) (0x205+(i)*6)
+#define OFS_WD_BIOS_SEC(i) (0x204+(i)*6)
+#define OFS_pc98_machine_type 0x220
+#define OFS_epson_machine_id 0x224
+#define OFS_epson_bios_id 0x225
+#define OFS_epson_system_type 0x226
+
+#define M_NEC_PC98 0x0001
+#define M_EPSON_PC98 0x0002
+#define M_NOT_H98 0x0010
+#define M_H98 0x0020
+#define M_NOTE 0x0040
+#define M_NORMAL 0x1000
+#define M_HIGHRESO 0x2000
+#define M_8M 0x8000
+
+# ifdef KERNEL
+
+extern unsigned char pc98_system_parameter[]; /* in locore.c */
+
+#define PC98_SYSTEM_PARAMETER(x) pc98_system_parameter[(x)-0x400]
+#define BOOT_boothowto (*(unsigned long*)(&pc98_system_parameter[OFS_BOOT_boothowto]))
+#define BOOT_bootdev (*(unsigned long*)(&pc98_system_parameter[OFS_BOOT_bootdev]))
+#define BOOT_cyloffset (*(unsigned long*)(&pc98_system_parameter[OFS_BOOT_cyloffset]))
+#define WD_BIOS_SECSIZE(i) (*(unsigned short*)(&pc98_system_parameter[OFS_WD_BIOS_SECSIZE(i)]))
+#define WD_BIOS_NCYL(i) (*(unsigned short*)(&pc98_system_parameter[OFS_WD_BIOS_NCYL(i)]))
+#define WD_BIOS_HEAD(i) (pc98_system_parameter[OFS_WD_BIOS_HEAD(i)])
+#define WD_BIOS_SEC(i) (pc98_system_parameter[OFS_WD_BIOS_SEC(i)])
+#define pc98_machine_type (*(unsigned long*)&pc98_system_parameter[OFS_pc98_machine_type])
+#define epson_machine_id (pc98_system_parameter[OFS_epson_machine_id])
+#define epson_bios_id (pc98_system_parameter[OFS_epson_bios_id])
+#define epson_system_type (pc98_system_parameter[OFS_epson_system_type])
+
+# define PC98_TYPE_CHECK(x) ((pc98_machine_type & (x)) == (x))
+
+# endif /* KERNEL */
+
+extern u_char hireso;
+
+#else
+ /* OLD:386bsd-0.1-pc98-a&b */
+extern unsigned char pc98_system_parameter[0x214]; /* in pc98.c */
+#define WD_BIOS_NCYL(i) (*(unsigned short*)(&pc98_system_parameter[0x206+(i)*6]))
+#define WD_BIOS_HEAD(i) (pc98_system_parameter[0x209+(i)*6])
+#define WD_BIOS_SEC(i) (pc98_system_parameter[0x208+(i)*6])
+#define PC98_SYSTEM_PARAMETER(x) pc98_system_parameter[(x)-0x400]
+#define pc98_machine_type (pc98_system_parameter[0x210])
+#define NEC_PC98 1
+#define EPSON_PC98 2
+#define epson_machine_id (pc98_system_parameter[0x211])
+#endif
+#endif
+
+#endif /* !_PC98_PC98_PC98_DEVICE_H_ */
diff --git a/sys/pc98/pc98/pcaudio.c b/sys/pc98/pc98/pcaudio.c
new file mode 100644
index 0000000..caa0cd4
--- /dev/null
+++ b/sys/pc98/pc98/pcaudio.c
@@ -0,0 +1,589 @@
+/*-
+ * Copyright (c) 1994 Sen Schmidt
+ * 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
+ * in this position and unchanged.
+ * 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 withough 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.
+ *
+ * $Id: pcaudio.c,v 1.27 1996/03/28 14:28:47 scrappy Exp $
+ */
+
+#include "pca.h"
+#if NPCA > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/devconf.h>
+#include <sys/kernel.h>
+
+#include <machine/clock.h>
+#include <machine/pcaudioio.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/timerreg.h>
+
+#include <pc98/pc98/sound/ulaw.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/timerreg.h>
+
+#include <i386/isa/sound/ulaw.h>
+#endif
+
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /* DEVFS */
+
+#define BUF_SIZE 8192
+#define SAMPLE_RATE 8000
+#define INTERRUPT_RATE 16000
+
+static struct pca_status {
+ char open; /* device open */
+ char queries; /* did others try opening */
+ unsigned char *buf[2]; /* double buffering */
+ unsigned char *buffer; /* current buffer ptr */
+ unsigned in_use[2]; /* buffers fill */
+ unsigned index; /* index in current buffer */
+ unsigned counter; /* sample counter */
+ unsigned scale; /* sample counter scale */
+ unsigned sample_rate; /* sample rate */
+ unsigned processed; /* samples processed */
+ unsigned volume; /* volume for pc-speaker */
+ char encoding; /* Ulaw, Alaw or linear */
+ char current; /* current buffer */
+ unsigned char oldval; /* old timer port value */
+ char timer_on; /* is playback running */
+ struct selinfo wsel; /* select status */
+} pca_status;
+
+static char buffer1[BUF_SIZE];
+static char buffer2[BUF_SIZE];
+static char volume_table[256];
+
+#ifdef DEVFS
+static void *pca_devfs_token;
+static void *pcac_devfs_token;
+#endif
+
+static int pca_sleep = 0;
+static int pca_initialized = 0;
+
+void pcaintr(struct clockframe *frame);
+#ifdef PC98
+static int pcaprobe(struct pc98_device *dvp);
+static int pcaattach(struct pc98_device *dvp);
+#else
+static int pcaprobe(struct isa_device *dvp);
+static int pcaattach(struct isa_device *dvp);
+#endif
+
+#ifdef PC98
+struct pc98_driver pcadriver = {
+#else
+struct isa_driver pcadriver = {
+#endif
+ pcaprobe, pcaattach, "pca",
+};
+
+static d_open_t pcaopen;
+static d_close_t pcaclose;
+static d_write_t pcawrite;
+static d_ioctl_t pcaioctl;
+static d_select_t pcaselect;
+
+#define CDEV_MAJOR 24
+static struct cdevsw pca_cdevsw =
+ { pcaopen, pcaclose, noread, pcawrite, /*24*/
+ pcaioctl, nostop, nullreset, nodevtotty,/* pcaudio */
+ pcaselect, nommap, NULL, "pca", NULL, -1 };
+
+static void pca_continue __P((void));
+static void pca_init __P((void));
+static void pca_pause __P((void));
+
+static inline void conv(const void *table, void *buff, unsigned long n)
+{
+ __asm__("1:\tmovb (%2), %3\n"
+ "\txlatb\n"
+ "\tmovb %3, (%2)\n"
+ "\tinc %2\n"
+ "\tdec %1\n"
+ "\tjnz 1b\n"
+ :
+ :"b" ((long)table), "c" (n), "D" ((long)buff), "a" ((char)n)
+ :"bx","cx","di","ax");
+}
+
+
+static void
+pca_volume(int volume)
+{
+ int i, j;
+
+ for (i=0; i<256; i++) {
+ j = ((i-128)*volume)/100;
+ if (j<-128)
+ j = -128;
+ if (j>127)
+ j = 127;
+ volume_table[i] = (((255-(j + 128))/4)+1);
+ }
+}
+
+
+static void
+pca_init()
+{
+ pca_status.open = 0;
+ pca_status.queries = 0;
+ pca_status.timer_on = 0;
+ pca_status.buf[0] = (unsigned char *)&buffer1[0];
+ pca_status.buf[1] = (unsigned char *)&buffer2[0];
+ pca_status.buffer = pca_status.buf[0];
+ pca_status.in_use[0] = pca_status.in_use[1] = 0;
+ pca_status.current = 0;
+ pca_status.sample_rate = SAMPLE_RATE;
+ pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
+ pca_status.encoding = AUDIO_ENCODING_ULAW;
+ pca_status.volume = 100;
+
+ pca_volume(pca_status.volume);
+}
+
+
+static int
+pca_start(void)
+{
+ /* use the first buffer */
+ pca_status.current = 0;
+ pca_status.index = 0;
+ pca_status.counter = 0;
+ pca_status.buffer = pca_status.buf[pca_status.current];
+#ifdef PC98
+ pca_status.oldval = inb(IO_PPI) & ~0x08;
+#else
+ pca_status.oldval = inb(IO_PPI) | 0x03;
+#endif
+ /* acquire the timers */
+#ifdef PC98
+ if (acquire_timer1(TIMER_LSB|TIMER_ONESHOT)) {
+#else
+ if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT)) {
+#endif
+ return -1;
+ }
+ if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
+#ifdef PC98
+ release_timer1();
+#else
+ release_timer2();
+#endif
+ return -1;
+ }
+ pca_status.timer_on = 1;
+ return 0;
+}
+
+
+static void
+pca_stop(void)
+{
+ /* release the timers */
+ release_timer0();
+#ifdef PC98
+ release_timer1();
+#else
+ release_timer2();
+#endif
+ /* reset the buffer */
+ pca_status.in_use[0] = pca_status.in_use[1] = 0;
+ pca_status.index = 0;
+ pca_status.counter = 0;
+ pca_status.current = 0;
+ pca_status.buffer = pca_status.buf[pca_status.current];
+ pca_status.timer_on = 0;
+}
+
+
+static void
+pca_pause()
+{
+ release_timer0();
+#ifdef PC98
+ release_timer1();
+#else
+ release_timer2();
+#endif
+ pca_status.timer_on = 0;
+}
+
+
+static void
+pca_continue()
+{
+#ifdef PC98
+ pca_status.oldval = inb(IO_PPI) & ~0x08;
+ acquire_timer1(TIMER_LSB|TIMER_ONESHOT);
+#else
+ pca_status.oldval = inb(IO_PPI) | 0x03;
+ acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
+#endif
+ acquire_timer0(INTERRUPT_RATE, pcaintr);
+ pca_status.timer_on = 1;
+}
+
+
+static int
+pca_wait(void)
+{
+ int error;
+
+ while (pca_status.in_use[0] || pca_status.in_use[1]) {
+ pca_sleep = 1;
+ error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
+ pca_sleep = 0;
+ if (error != 0 && error != ERESTART) {
+ pca_stop();
+ return error;
+ }
+ }
+ return 0;
+}
+
+
+static int
+#ifdef PC98
+pcaprobe(struct pc98_device *dvp)
+#else
+pcaprobe(struct isa_device *dvp)
+#endif
+{
+ return(-1);
+}
+
+
+static struct kern_devconf kdc_pca[NPCA] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ "pca", 0, { MDDT_PC98, 0, "tty" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "pca", 0, { MDDT_ISA, 0, "tty" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNKNOWN, /* not supported */
+ "PC speaker audio driver"
+} };
+
+
+static inline void
+#ifdef PC98
+pca_registerdev(struct pc98_device *id)
+#else
+pca_registerdev(struct isa_device *id)
+#endif
+{
+ if(id->id_unit)
+ kdc_pca[id->id_unit] = kdc_pca[0];
+ kdc_pca[id->id_unit].kdc_unit = id->id_unit;
+#ifdef PC98
+ kdc_pca[id->id_unit].kdc_pc98 = id;
+#else
+ kdc_pca[id->id_unit].kdc_isa = id;
+#endif
+ dev_attach(&kdc_pca[id->id_unit]);
+}
+
+
+static int
+#ifdef PC98
+pcaattach(struct pc98_device *dvp)
+#else
+pcaattach(struct isa_device *dvp)
+#endif
+{
+ printf("pca%d: PC speaker audio driver\n", dvp->id_unit);
+ pca_init();
+ pca_registerdev(dvp);
+#ifdef DEVFS
+ pca_devfs_token =
+ devfs_add_devswf(&pca_cdevsw, 0, DV_CHR, 0, 0, 0600, "pcaudio");
+ pcac_devfs_token =
+ devfs_add_devswf(&pca_cdevsw, 128, DV_CHR, 0, 0, 0600,
+ "pcaudioctl");
+#endif /*DEVFS*/
+
+ return 1;
+}
+
+
+static int
+pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ /* audioctl device can always be opened */
+ if (minor(dev) == 128)
+ return 0;
+ if (minor(dev) > 0)
+ return ENXIO;
+
+ if (!pca_initialized) {
+ pca_init();
+ pca_initialized = 1;
+ }
+
+ /* audio device can only be open by one process */
+ if (pca_status.open) {
+ pca_status.queries = 1;
+ return EBUSY;
+ }
+ pca_status.buffer = pca_status.buf[0];
+ pca_status.in_use[0] = pca_status.in_use[1] = 0;
+ pca_status.timer_on = 0;
+ pca_status.open = 1;
+ pca_status.processed = 0;
+ return 0;
+}
+
+
+static int
+pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ /* audioctl device can always be closed */
+ if (minor(dev) == 128)
+ return 0;
+ if (minor(dev) > 0)
+ return ENXIO;
+ /* audio device close drains all output and restores timers */
+ pca_wait();
+ pca_stop();
+ pca_status.open = 0;
+ return 0;
+}
+
+
+static int
+pcawrite(dev_t dev, struct uio *uio, int flag)
+{
+ int count, error, which;
+
+ /* only audio device can be written */
+ if (minor(dev) > 0)
+ return ENXIO;
+
+ while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
+ if (pca_status.in_use[0] && pca_status.in_use[1]) {
+ pca_sleep = 1;
+ error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
+ pca_sleep = 0;
+ if (error != 0 && error != ERESTART) {
+ pca_stop();
+ return error;
+ }
+ }
+ which = pca_status.in_use[0] ? 1 : 0;
+ if (count && !pca_status.in_use[which]) {
+ uiomove(pca_status.buf[which], count, uio);
+ pca_status.processed += count;
+ switch (pca_status.encoding) {
+ case AUDIO_ENCODING_ULAW:
+ conv(ulaw_dsp, pca_status.buf[which], count);
+ break;
+
+ case AUDIO_ENCODING_ALAW:
+ break;
+
+ case AUDIO_ENCODING_RAW:
+ break;
+ }
+ pca_status.in_use[which] = count;
+ if (!pca_status.timer_on)
+ if (pca_start())
+ return EBUSY;
+ }
+ }
+ return 0;
+}
+
+
+static int
+pcaioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ audio_info_t *auptr;
+
+ switch(cmd) {
+
+ case AUDIO_GETINFO:
+ auptr = (audio_info_t *)data;
+ auptr->play.sample_rate = pca_status.sample_rate;
+ auptr->play.channels = 1;
+ auptr->play.precision = 8;
+ auptr->play.encoding = pca_status.encoding;
+
+ auptr->play.gain = pca_status.volume;
+ auptr->play.port = 0;
+
+ auptr->play.samples = pca_status.processed;
+ auptr->play.eof = 0;
+ auptr->play.pause = !pca_status.timer_on;
+ auptr->play.error = 0;
+ auptr->play.waiting = pca_status.queries;
+
+ auptr->play.open = pca_status.open;
+ auptr->play.active = pca_status.timer_on;
+ return 0;
+
+ case AUDIO_SETINFO:
+ auptr = (audio_info_t *)data;
+ if (auptr->play.sample_rate != (unsigned int)~0) {
+ pca_status.sample_rate = auptr->play.sample_rate;
+ pca_status.scale =
+ (pca_status.sample_rate << 8) / INTERRUPT_RATE;
+ }
+ if (auptr->play.encoding != (unsigned int)~0) {
+ pca_status.encoding = auptr->play.encoding;
+ }
+ if (auptr->play.gain != (unsigned int)~0) {
+ pca_status.volume = auptr->play.gain;
+ pca_volume(pca_status.volume);
+ }
+ if (auptr->play.pause != (unsigned char)~0) {
+ if (auptr->play.pause)
+ pca_pause();
+ else
+ pca_continue();
+ }
+
+ return 0;
+
+ case AUDIO_DRAIN:
+ return pca_wait();
+
+ case AUDIO_FLUSH:
+ pca_stop();
+ return 0;
+
+ }
+ return ENXIO;
+}
+
+
+void
+pcaintr(struct clockframe *frame)
+{
+ if (pca_status.index < pca_status.in_use[pca_status.current]) {
+ disable_intr();
+#ifdef PC98
+ __asm__("outb %0,$0x35\n"
+ "orb $0x08,%0\n"
+ "outb %0,$0x35"
+#else
+ __asm__("outb %0,$0x61\n"
+ "andb $0xFE,%0\n"
+ "outb %0,$0x61"
+#endif
+ : : "a" ((char)pca_status.oldval) );
+ __asm__("xlatb\n"
+#ifdef PC98
+ "outb %0,$0x3fdb"
+#else
+ "outb %0,$0x42"
+#endif
+ : : "a" ((char)pca_status.buffer[pca_status.index]),
+ "b" ((long)volume_table) );
+ enable_intr();
+ pca_status.counter += pca_status.scale;
+ pca_status.index = (pca_status.counter >> 8);
+ }
+ if (pca_status.index >= pca_status.in_use[pca_status.current]) {
+ pca_status.index = pca_status.counter = 0;
+ pca_status.in_use[pca_status.current] = 0;
+ pca_status.current ^= 1;
+ pca_status.buffer = pca_status.buf[pca_status.current];
+ if (pca_sleep) {
+ wakeup(&pca_sleep);
+ pca_sleep = 0;
+ }
+ if (pca_status.wsel.si_pid) {
+ selwakeup((struct selinfo *)&pca_status.wsel.si_pid);
+ pca_status.wsel.si_pid = 0;
+ pca_status.wsel.si_flags = 0;
+ }
+ }
+}
+
+
+int
+pcaselect(dev_t dev, int rw, struct proc *p)
+{
+ int s = spltty();
+ struct proc *p1;
+
+ switch (rw) {
+
+ case FWRITE:
+ if (!pca_status.in_use[0] || !pca_status.in_use[1]) {
+ splx(s);
+ return(1);
+ }
+ if (pca_status.wsel.si_pid && (p1=pfind(pca_status.wsel.si_pid))
+ && p1->p_wchan == (caddr_t)&selwait)
+ pca_status.wsel.si_flags = SI_COLL;
+ else
+ pca_status.wsel.si_pid = p->p_pid;
+ splx(s);
+ return 0;
+ default:
+ splx(s);
+ return(0);
+ }
+}
+
+static pca_devsw_installed = 0;
+
+static void pca_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! pca_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev,&pca_cdevsw, NULL);
+ pca_devsw_installed = 1;
+ }
+}
+
+SYSINIT(pcadev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pca_drvinit,NULL)
+
+
+#endif
diff --git a/sys/pc98/pc98/pcibus.c b/sys/pc98/pc98/pcibus.c
new file mode 100644
index 0000000..a5c4395
--- /dev/null
+++ b/sys/pc98/pc98/pcibus.c
@@ -0,0 +1,533 @@
+/**************************************************************************
+**
+** $Id: pcibus.c,v 1.24 1996/04/30 21:37:21 se Exp $
+**
+** pci bus subroutines for i386 architecture.
+**
+** FreeBSD
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier. 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. 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.
+**
+***************************************************************************
+*/
+
+#include "vector.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#ifdef PC98
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#else
+#include <i386/isa/icu.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#endif
+
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+#include <pci/pcibus.h>
+
+/*-----------------------------------------------------------------
+**
+** The following functions are provided by the pci bios.
+** They are used only by the pci configuration.
+**
+** pcibus_setup():
+** Probes for a pci system.
+** Sets pci_maxdevice and pci_mechanism.
+**
+** pcibus_tag():
+** Creates a handle for pci configuration space access.
+** This handle is given to the read/write functions.
+**
+** pcibus_ftag():
+** Creates a modified handle.
+**
+** pcibus_read():
+** Read a long word from the pci configuration space.
+** Requires a tag (from pcitag) and the register
+** number (should be a long word alligned one).
+**
+** pcibus_write():
+** Writes a long word to the pci configuration space.
+** Requires a tag (from pcitag), the register number
+** (should be a long word alligned one), and a value.
+**
+** pcibus_regirq():
+** Register an interupt handler for a pci device.
+** Requires a tag (from pcitag), the register number
+** (should be a long word alligned one), and a value.
+**
+**-----------------------------------------------------------------
+*/
+
+static int
+pcibus_check (void);
+
+static void
+pcibus_setup (void);
+
+static pcici_t
+pcibus_tag (u_char bus, u_char device, u_char func);
+
+static pcici_t
+pcibus_ftag (pcici_t tag, u_char func);
+
+static u_long
+pcibus_read (pcici_t tag, u_long reg);
+
+static void
+pcibus_write (pcici_t tag, u_long reg, u_long data);
+
+static int
+pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr);
+
+static int
+pcibus_ihandler_detach (int irq, inthand2_t *func);
+
+static int
+pcibus_imask_include (int irq, unsigned* maskptr);
+
+static int
+pcibus_imask_exclude (int irq, unsigned* maskptr);
+
+static struct pcibus i386pci = {
+ "pci",
+ pcibus_setup,
+ pcibus_tag,
+ pcibus_ftag,
+ pcibus_read,
+ pcibus_write,
+ ICU_LEN,
+ pcibus_ihandler_attach,
+ pcibus_ihandler_detach,
+ pcibus_imask_include,
+ pcibus_imask_exclude,
+};
+
+/*
+** Announce structure to generic driver
+*/
+
+DATA_SET (pcibus_set, i386pci);
+
+/*--------------------------------------------------------------------
+**
+** Determine configuration mode
+**
+**--------------------------------------------------------------------
+*/
+
+
+#define CONF1_ADDR_PORT 0x0cf8
+#define CONF1_DATA_PORT 0x0cfc
+
+#define CONF1_ENABLE 0x80000000ul
+#define CONF1_ENABLE_CHK 0x80000000ul
+#define CONF1_ENABLE_MSK 0x00ff0700ul
+#define CONF1_ENABLE_CHK1 0xff000001ul
+#define CONF1_ENABLE_MSK1 0x80000001ul
+#define CONF1_ENABLE_RES1 0x80000000ul
+
+#define CONF2_ENABLE_PORT 0x0cf8
+#ifdef PC98
+#define CONF2_FORWARD_PORT 0x0cf9
+#else
+#define CONF2_FORWARD_PORT 0x0cfa
+#endif
+
+#define CONF2_ENABLE_CHK 0x0e
+#define CONF2_ENABLE_RES 0x0e
+
+static int
+pcibus_check (void)
+{
+ u_char device;
+
+ if (bootverbose) printf ("pcibus_check:\tdevice ");
+
+ for (device = 0; device < pci_maxdevice; device++) {
+ unsigned long id;
+ if (bootverbose)
+ printf ("%d ", device);
+ id = pcibus_read (pcibus_tag (0,device,0), 0);
+ if (id && id != 0xfffffffful) {
+ if (bootverbose) printf ("is there (id=%08lx)\n", id);
+ return 1;
+ }
+ }
+ if (bootverbose)
+ printf ("-- nothing found\n");
+ return 0;
+}
+
+static void
+pcibus_setup (void)
+{
+ unsigned long mode1res,oldval1;
+ unsigned char mode2res,oldval2;
+
+ oldval1 = inl (CONF1_ADDR_PORT);
+
+ if (bootverbose) {
+ printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1);
+ }
+
+ /*---------------------------------------
+ ** Assume configuration mechanism 1 for now ...
+ **---------------------------------------
+ */
+
+ if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
+
+ pci_mechanism = 1;
+ pci_maxdevice = 32;
+
+ outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
+ outb (CONF1_ADDR_PORT +3, 0);
+ mode1res = inl (CONF1_ADDR_PORT);
+ outl (CONF1_ADDR_PORT, oldval1);
+
+ if (bootverbose)
+ printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n",
+ mode1res, CONF1_ENABLE_CHK);
+
+ if (mode1res) {
+ if (pcibus_check())
+ return;
+ };
+
+ outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
+ mode1res = inl(CONF1_ADDR_PORT);
+ outl (CONF1_ADDR_PORT, oldval1);
+
+ if (bootverbose)
+ printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n",
+ mode1res, CONF1_ENABLE_CHK1);
+
+ if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
+ if (pcibus_check())
+ return;
+ };
+ }
+
+ /*---------------------------------------
+ ** Try configuration mechanism 2 ...
+ **---------------------------------------
+ */
+
+ oldval2 = inb (CONF2_ENABLE_PORT);
+
+ if (bootverbose) {
+ printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2);
+ }
+
+ if ((oldval2 & 0xf0) == 0) {
+
+ pci_mechanism = 2;
+ pci_maxdevice = 16;
+
+ outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
+ mode2res = inb(CONF2_ENABLE_PORT);
+ outb (CONF2_ENABLE_PORT, oldval2);
+
+ if (bootverbose)
+ printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n",
+ mode2res, CONF2_ENABLE_CHK);
+
+ if (mode2res == CONF2_ENABLE_RES) {
+ if (bootverbose)
+ printf ("pcibus_setup(2a):\tnow trying mechanism 2\n");
+
+ if (pcibus_check())
+ return;
+ }
+ }
+
+ /*---------------------------------------
+ ** No PCI bus host bridge found
+ **---------------------------------------
+ */
+
+ pci_mechanism = 0;
+ pci_maxdevice = 0;
+}
+
+/*--------------------------------------------------------------------
+**
+** Build a pcitag from bus, device and function number
+**
+**--------------------------------------------------------------------
+*/
+
+static pcici_t
+pcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
+{
+ pcici_t tag;
+
+ tag.cfg1 = 0;
+ if (func >= 8) return tag;
+
+ switch (pci_mechanism) {
+
+ case 1:
+ if (device < 32) {
+ tag.cfg1 = CONF1_ENABLE
+ | (((u_long) bus ) << 16ul)
+ | (((u_long) device) << 11ul)
+ | (((u_long) func ) << 8ul);
+ }
+ break;
+ case 2:
+ if (device < 16) {
+ tag.cfg2.port = 0xc000 | (device << 8ul);
+ tag.cfg2.enable = 0xf0 | (func << 1ul);
+ tag.cfg2.forward = bus;
+ }
+ break;
+ };
+ return tag;
+}
+
+static pcici_t
+pcibus_ftag (pcici_t tag, u_char func)
+{
+ switch (pci_mechanism) {
+
+ case 1:
+ tag.cfg1 &= ~0x700ul;
+ tag.cfg1 |= (((u_long) func) << 8ul);
+ break;
+ case 2:
+ tag.cfg2.enable = 0xf0 | (func << 1ul);
+ break;
+ };
+ return tag;
+}
+
+/*--------------------------------------------------------------------
+**
+** Read register from configuration space.
+**
+**--------------------------------------------------------------------
+*/
+
+static u_long
+pcibus_read (pcici_t tag, u_long reg)
+{
+ u_long addr, data = 0;
+
+ if (!tag.cfg1) return (0xfffffffful);
+
+ switch (pci_mechanism) {
+
+ case 1:
+ addr = tag.cfg1 | (reg & 0xfc);
+#ifdef PCI_DEBUG
+ printf ("pci_conf_read(1): addr=%x ", addr);
+#endif
+ outl (CONF1_ADDR_PORT, addr);
+ data = inl (CONF1_DATA_PORT);
+ outl (CONF1_ADDR_PORT, 0 );
+ break;
+
+ case 2:
+ addr = tag.cfg2.port | (reg & 0xfc);
+#ifdef PCI_DEBUG
+ printf ("pci_conf_read(2): addr=%x ", addr);
+#endif
+ outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
+ outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
+
+ data = inl ((u_short) addr);
+
+ outb (CONF2_ENABLE_PORT, 0);
+ outb (CONF2_FORWARD_PORT, 0);
+ break;
+ };
+
+#ifdef PCI_DEBUG
+ printf ("data=%x\n", data);
+#endif
+
+ return (data);
+}
+
+/*--------------------------------------------------------------------
+**
+** Write register into configuration space.
+**
+**--------------------------------------------------------------------
+*/
+
+static void
+pcibus_write (pcici_t tag, u_long reg, u_long data)
+{
+ u_long addr;
+
+ if (!tag.cfg1) return;
+
+ switch (pci_mechanism) {
+
+ case 1:
+ addr = tag.cfg1 | (reg & 0xfc);
+#ifdef PCI_DEBUG
+ printf ("pci_conf_write(1): addr=%x data=%x\n",
+ addr, data);
+#endif
+ outl (CONF1_ADDR_PORT, addr);
+ outl (CONF1_DATA_PORT, data);
+ outl (CONF1_ADDR_PORT, 0 );
+ break;
+
+ case 2:
+ addr = tag.cfg2.port | (reg & 0xfc);
+#ifdef PCI_DEBUG
+ printf ("pci_conf_write(2): addr=%x data=%x\n",
+ addr, data);
+#endif
+ outb (CONF2_ENABLE_PORT, tag.cfg2.enable);
+ outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
+
+ outl ((u_short) addr, data);
+
+ outb (CONF2_ENABLE_PORT, 0);
+ outb (CONF2_FORWARD_PORT, 0);
+ break;
+ };
+}
+
+/*-----------------------------------------------------------------------
+**
+** Register an interupt handler for a pci device.
+**
+**-----------------------------------------------------------------------
+*/
+
+static int
+pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr)
+{
+ char buf[16];
+ char *cp;
+ int free_id, id, result;
+
+ sprintf(buf, "pci irq%d", irq);
+ for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) {
+ if (strcmp(cp, buf) == 0)
+ break;
+ if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0)
+ free_id = id;
+ while (*cp++ != '\0')
+ ;
+ }
+ if (id == NR_DEVICES) {
+ id = free_id;
+ if (id == 0) {
+ /*
+ * All pci irq counters are in use, perhaps because
+ * config is old so there aren't any. Abuse the
+ * clk0 counter.
+ */
+ printf (
+ "pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n",
+ irq);
+ }
+ }
+ result = register_intr(
+ irq, /* isa irq */
+ id, /* device id */
+ 0, /* flags? */
+ func, /* handler */
+ maskptr, /* mask pointer */
+ arg); /* handler arg */
+
+ if (result) {
+ printf ("@@@ pcibus_ihandler_attach: result=%d\n", result);
+ return (result);
+ };
+ update_intr_masks();
+
+ INTREN ((1ul<<irq));
+ return (0);
+}
+
+static int
+pcibus_ihandler_detach (int irq, inthand2_t *func)
+{
+ int result;
+
+ INTRDIS ((1ul<<irq));
+
+ result = unregister_intr (irq, func);
+
+ if (result)
+ printf ("@@@ pcibus_ihandler_detach: result=%d\n", result);
+
+ update_intr_masks();
+
+ return (result);
+}
+
+static int
+pcibus_imask_include (int irq, unsigned* maskptr)
+{
+ unsigned mask;
+
+ if (!maskptr) return (0);
+
+ mask = 1ul << irq;
+
+ if (*maskptr & mask)
+ return (-1);
+
+ INTRMASK (*maskptr, mask);
+ update_intr_masks();
+
+ return (0);
+}
+
+static int
+pcibus_imask_exclude (int irq, unsigned* maskptr)
+{
+ unsigned mask;
+
+ if (!maskptr) return (0);
+
+ mask = 1ul << irq;
+
+ if (! (*maskptr & mask))
+ return (-1);
+
+ *maskptr &= ~mask;
+ update_intr_masks();
+
+ return (0);
+}
diff --git a/sys/pc98/pc98/pcic.h b/sys/pc98/pc98/pcic.h
new file mode 100644
index 0000000..bc5ae5e
--- /dev/null
+++ b/sys/pc98/pc98/pcic.h
@@ -0,0 +1,181 @@
+/*-
+ * TODO:
+ * [1] integrate into current if_ed.c
+ * [2] parse tuples to find out where to map the shared memory buffer,
+ * and what to write into the configuration register
+ * [3] move pcic-specific code into a separate module.
+ *
+ * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet,
+ * if_ze.c
+ *
+ * Based on the Device driver for National Semiconductor DS8390 ethernet
+ * adapters by David Greenman. Modifications for PCMCIA by Keith Moore.
+ * Adapted for FreeBSD 1.1.5 by Jordan Hubbard.
+ *
+ * Currently supports only the IBM Credit Card Adapter for Ethernet, but
+ * could probably work with other PCMCIA cards also, if it were modified
+ * to get the locations of the PCMCIA configuration option register (COR)
+ * by parsing the configuration tuples, rather than by hard-coding in
+ * the value expected by IBM's card.
+ *
+ * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver:
+ *
+ * [1] _Local Area Network Credit Card Adapters Technical Reference_,
+ * IBM Corp., SC30-3585-00, part # 33G9243.
+ * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan.
+ * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel
+ * Order Number 290423-002
+ * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network
+ * Interface Controller for Twisted Pair data sheet.
+ *
+ *
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ */
+
+#ifndef __PCIC_H__
+#define __PCIC_H__
+
+/*****************************************************************************
+ * pcmcia controller chip (PCIC) support *
+ * (eventually, move this to a separate file) *
+ *****************************************************************************/
+#include "ic/i82365.h"
+
+/*
+ * Each PCIC chip (82365SL or clone) can handle two card slots, and there
+ * can be up to four PCICs in a system. (On some machines, not all of the
+ * address lines are decoded, so a card may appear to be in more than one
+ * slot.)
+ */
+#define MAXSLOT 8
+
+/*
+ * To access a register on the PCIC for a particular slot, you
+ * first write the correct OFFSET value for that slot in the
+ * INDEX register for the PCIC controller. You then read or write
+ * the value from or to the DATA register for that controller.
+ *
+ * The first pair of chips shares I/O addresses for DATA and INDEX,
+ * as does the second pair. (To the programmer, it looks like each
+ * pair is a single chip.) The i/o port addresses are hard-wired
+ * into the PCIC; so the following addresses should be valid for
+ * any machine that uses this chip.
+ */
+
+#define PCIC_INDEX_0 0x3E0 /* index reg, chips 0 and 1 */
+#define PCIC_DATA_0 0x3E1 /* data register, chips 0 and 1 */
+#define PCIC_INDEX_1 0x3E2 /* index reg, chips 1 and 2 */
+#define PCIC_DATA_1 0x3E3 /* data register, chips 1 and 2 */
+
+/*
+ * Given a slot number, calculate the INDEX and DATA registers
+ * to talk to that slot. OFFSET is added to the register number
+ * to address the registers for a particular slot.
+ */
+#define INDEX(slot) ((slot) < 4 ? PCIC_INDEX_0 : PCIC_INDEX_1)
+#define DATA(slot) ((slot) < 4 ? PCIC_DATA_0 : PCIC_DATA_1)
+#define OFFSET(slot) ((slot) % 4 * 0x40)
+
+/*
+ * There are 5 sets (windows) of memory mapping registers on the PCIC chip
+ * for each slot, numbered 0..4.
+ *
+ * They start at 10/50 hex within the chip's register space (not system
+ * I/O space), and are eight addresses apart. These are actually pairs of
+ * 8-bit-wide registers (low byte first, then high byte) since the
+ * address fields are actually 12 bits long. The upper bits are used
+ * for other things like 8/16-bit select and wait states.
+ *
+ * Memory mapping registers include start/stop addresses to define the
+ * region to be mapped (in terms of system memory addresses), and
+ * an offset register to allow for translation from system space
+ * to card space. The lower 12 bits aren't included in these, so memory is
+ * mapped in 4K chunks.
+ */
+#define MEM_START_ADDR(window) (((window) * 0x08) + 0x10)
+#define MEM_STOP_ADDR(window) (((window) * 0x08) + 0x12)
+#define MEM_OFFSET(window) (((window) * 0x08) + 0x14)
+/*
+ * this bit gets set in the address window enable register (PCIC_ADDRWINE)
+ * to enable a particular address window.
+ */
+#define MEM_ENABLE_BIT(window) ((1) << (window))
+
+/*
+ * There are two i/o port addressing windows. I/O ports cannot be
+ * relocated within system i/o space (unless the card doesn't decode
+ * all of the address bits); unlike card memory, there is no address
+ * translation offset.
+ */
+#define IO_START_ADDR(window) ((window) ? PCIC_IO1_STL : PCIC_IO0_STL)
+#define IO_STOP_ADDR(window) ((window) ? PCIC_IO1_SPL : PCIC_IO0_SPL)
+#define IO_ENABLE_BIT(window) ((window) ? PCIC_IO1_EN : PCIC_IO0_EN)
+#define IO_CS16_BIT(window) ((window) ? PCIC_IO1_CS16 : PCIC_IO0_CS16)
+
+/*
+ * types of mapped memory
+ */
+enum memtype { COMMON, ATTRIBUTE };
+
+/*
+ * read a byte from a pcic register for a particular slot
+ */
+static inline unsigned char
+pcic_getb (int slot, int reg)
+{
+ outb (INDEX(slot), OFFSET (slot) + reg);
+ return inb (DATA (slot));
+}
+
+/*
+ * write a byte to a pcic register for a particular slot
+ */
+static inline void
+pcic_putb (int slot, int reg, unsigned char val)
+{
+ outb (INDEX(slot), OFFSET (slot) + reg);
+ outb (DATA (slot), val);
+}
+
+/*
+ * read a word from a pcic register for a particular slot
+ */
+static inline unsigned short
+pcic_getw (int slot, int reg)
+{
+ return pcic_getb (slot, reg) | (pcic_getb (slot, reg+1) << 8);
+}
+
+/*
+ * write a word to a pcic register at a particular slot
+ */
+static inline void
+pcic_putw (int slot, int reg, unsigned short val)
+{
+ pcic_putb (slot, reg, val & 0xff);
+ pcic_putb (slot, reg + 1, (val >> 8) & 0xff);
+}
+
+
+void pcic_print_regs (int slot);
+void pcic_map_memory (int slot, int window, unsigned long sys_addr,
+ unsigned long card_addr, unsigned long length,
+ enum memtype type, int width);
+void pcic_unmap_memory (int slot, int window);
+void pcic_map_io (int slot, int window, unsigned short base,
+ unsigned short length, unsigned short width);
+#ifdef TEST
+void pcic_unmap_io (int slot, int window);
+#endif /* TEST */
+void pcic_map_irq (int slot, int irq);
+void pcic_power_on (int slot);
+void pcic_power_off (int slot);
+void pcic_reset (int slot);
+
+
+#endif /* __PCIC_H__ */
diff --git a/sys/pc98/pc98/pcicx.c b/sys/pc98/pc98/pcicx.c
new file mode 100644
index 0000000..fbed452
--- /dev/null
+++ b/sys/pc98/pc98/pcicx.c
@@ -0,0 +1,241 @@
+/*-
+ * TODO:
+ * [1] integrate into current if_ed.c
+ * [2] parse tuples to find out where to map the shared memory buffer,
+ * and what to write into the configuration register
+ * [3] move pcic-specific code into a separate module.
+ *
+ * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet,
+ * if_ze.c
+ *
+ * Based on the Device driver for National Semiconductor DS8390 ethernet
+ * adapters by David Greenman. Modifications for PCMCIA by Keith Moore.
+ * Adapted for FreeBSD 1.1.5 by Jordan Hubbard.
+ *
+ * Currently supports only the IBM Credit Card Adapter for Ethernet, but
+ * could probably work with other PCMCIA cards also, if it were modified
+ * to get the locations of the PCMCIA configuration option register (COR)
+ * by parsing the configuration tuples, rather than by hard-coding in
+ * the value expected by IBM's card.
+ *
+ * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver:
+ *
+ * [1] _Local Area Network Credit Card Adapters Technical Reference_,
+ * IBM Corp., SC30-3585-00, part # 33G9243.
+ * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan.
+ * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel
+ * Order Number 290423-002
+ * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network
+ * Interface Controller for Twisted Pair data sheet.
+ *
+ *
+ * Copyright (C) 1993, David Greenman. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ */
+#include <sys/param.h>
+#if defined(__FreeBSD__)
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <machine/clock.h>
+#endif
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/pcic.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/pcic.h>
+#endif
+
+/*
+ * map a portion of the card's memory space into system memory
+ * space.
+ *
+ * slot = # of the slot the card is plugged into
+ * window = which pcic memory map registers to use (0..4)
+ * sys_addr = base system PHYSICAL memory address where we want it. must
+ * be on an appropriate boundary (lower 12 bits are zero).
+ * card_addr = the base address of the card's memory to correspond
+ * to sys_addr
+ * length = length of the segment to map (may be rounded up as necessary)
+ * type = which card memory space to map (attribute or shared)
+ * width = 1 for byte-wide mapping; 2 for word (16-bit) mapping.
+ */
+
+void
+pcic_map_memory (int slot, int window, unsigned long sys_addr,
+ unsigned long card_addr, unsigned long length,
+ enum memtype type, int width)
+{
+ unsigned short offset;
+ unsigned short mem_start_addr;
+ unsigned short mem_stop_addr;
+
+ sys_addr >>= 12;
+ card_addr >>= 12;
+ length >>= 12;
+ /*
+ * compute an offset for the chip such that
+ * (sys_addr + offset) = card_addr
+ * but the arithmetic is done modulo 2^14
+ */
+ offset = (card_addr - sys_addr) & 0x3FFF;
+ /*
+ * now OR in the bit for "attribute memory" if necessary
+ */
+ if (type == ATTRIBUTE) {
+ offset |= (PCIC_REG << 8);
+ /* REG == "region active" pin on card */
+ }
+ /*
+ * okay, set up the chip memory mapping registers, and turn
+ * on the enable bit for this window.
+ * if we are doing 16-bit wide accesses (width == 2),
+ * turn on the appropriate bit.
+ *
+ * XXX for now, we set all of the wait state bits to zero.
+ * Not really sure how they should be set.
+ */
+ mem_start_addr = sys_addr & 0xFFF;
+ if (width == 2)
+ mem_start_addr |= (PCIC_DATA16 << 8);
+ mem_stop_addr = (sys_addr + length) & 0xFFF;
+
+ pcic_putw (slot, MEM_START_ADDR(window), mem_start_addr);
+ pcic_putw (slot, MEM_STOP_ADDR(window), mem_stop_addr);
+ pcic_putw (slot, MEM_OFFSET(window), offset);
+ /*
+ * Assert the bit (PCIC_MEMCS16) that says to decode all of
+ * the address lines.
+ */
+ pcic_putb (slot, PCIC_ADDRWINE,
+ pcic_getb (slot, PCIC_ADDRWINE) |
+ MEM_ENABLE_BIT(window) | PCIC_MEMCS16);
+}
+
+void
+pcic_unmap_memory (int slot, int window)
+{
+ /*
+ * seems like we need to turn off the enable bit first, after which
+ * we can clear the registers out just to be sure.
+ */
+ pcic_putb (slot, PCIC_ADDRWINE,
+ pcic_getb (slot, PCIC_ADDRWINE) & ~MEM_ENABLE_BIT(window));
+ pcic_putw (slot, MEM_START_ADDR(window), 0);
+ pcic_putw (slot, MEM_STOP_ADDR(window), 0);
+ pcic_putw (slot, MEM_OFFSET(window), 0);
+}
+
+/*
+ * map a range of addresses into system i/o space
+ * (no translation of i/o addresses is possible)
+ *
+ * 'width' is:
+ * + 0 to tell the PCIC to generate the ISA IOCS16* signal from
+ * the PCMCIA IOIS16* signal.
+ * + 1 to select 8-bit width
+ * + 2 to select 16-bit width
+ */
+
+void
+pcic_map_io (int slot, int window, unsigned short base, unsigned short length,
+ unsigned short width)
+{
+ unsigned char x;
+
+ pcic_putw (slot, IO_START_ADDR(window), base);
+ pcic_putw (slot, IO_STOP_ADDR(window), base+length-1);
+ /*
+ * select the bits that determine whether
+ * an i/o operation is 8 or 16 bits wide
+ */
+ x = pcic_getb (slot, PCIC_IOCTL);
+ switch (width) {
+ case 0: /* PCMCIA card decides */
+ if (window)
+ x = (x & 0xf0) | PCIC_IO1_CS16;
+ else
+ x = (x & 0x0f) | PCIC_IO0_CS16;
+ break;
+ case 1: /* 8 bits wide */
+ break;
+ case 2: /* 16 bits wide */
+ if (window)
+ x = (x & 0xf0) | PCIC_IO1_16BIT;
+ else
+ x = (x & 0x0f) | PCIC_IO0_16BIT;
+ break;
+ }
+ pcic_putb (slot, PCIC_IOCTL, x);
+ pcic_putb (slot, PCIC_ADDRWINE,
+ pcic_getb (slot, PCIC_ADDRWINE) | IO_ENABLE_BIT(window));
+}
+
+#ifdef TEST
+void
+pcic_unmap_io (int slot, int window)
+{
+ pcic_putb (slot, PCIC_ADDRWINE,
+ pcic_getb (slot, PCIC_ADDRWINE) & ~IO_ENABLE_BIT(window));
+ pcic_putw (slot, IO_START_ADDR(window), 0);
+ pcic_putw (slot, IO_STOP_ADDR(window), 0);
+}
+#endif /* TEST */
+
+/*
+ * tell the PCIC which irq we want to use. only the following are legal:
+ * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15
+ *
+ * NB: 'irq' is an interrupt NUMBER, not a MASK as in struct isa_device.
+ */
+
+void
+pcic_map_irq (int slot, int irq)
+{
+ if (irq < 3 || irq == 6 || irq == 8 || irq == 13 || irq > 15) {
+ printf ("zp: pcic_map_irq (slot %d): illegal irq %d\n", slot, irq);
+ return;
+ }
+ pcic_putb (slot, PCIC_INT_GEN,
+ pcic_getb (slot, PCIC_INT_GEN) | (irq & 0x0F));
+}
+
+void
+pcic_power_on (int slot)
+{
+ pcic_putb (slot, PCIC_STATUS,
+ pcic_getb (slot, PCIC_STATUS) | PCIC_POW);
+ DELAY (100000);
+ pcic_putb (slot, PCIC_POWER,
+ pcic_getb (slot, PCIC_POWER) | PCIC_DISRST | PCIC_PCPWRE);
+ DELAY (100000);
+ pcic_putb (slot, PCIC_POWER,
+ pcic_getb (slot, PCIC_POWER) | PCIC_OUTENA);
+}
+
+void
+pcic_power_off (int slot)
+{
+ pcic_putb (slot, PCIC_POWER,
+ pcic_getb (slot, PCIC_POWER) & ~(PCIC_OUTENA|PCIC_PCPWRE));
+}
+
+void
+pcic_reset (int slot)
+{
+ /* assert RESET (by clearing a bit!), wait a bit, and de-assert it */
+ pcic_putb (slot, PCIC_INT_GEN,
+ pcic_getb (slot, PCIC_INT_GEN) & ~PCIC_CARDRESET);
+ DELAY (100000);
+ pcic_putb (slot, PCIC_INT_GEN,
+ pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDRESET);
+}
+
diff --git a/sys/pc98/pc98/prof_machdep.c b/sys/pc98/pc98/prof_machdep.c
new file mode 100644
index 0000000..50e7c3b
--- /dev/null
+++ b/sys/pc98/pc98/prof_machdep.c
@@ -0,0 +1,163 @@
+/*
+ * NEED A COPYRIGHT NOPTICE HERE
+ *
+ * $Id: prof_machdep.c,v 1.2 1996/04/08 16:41:06 wollman Exp $
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <machine/clock.h>
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/timerreg.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/timerreg.h>
+#endif
+
+#ifdef GUPROF
+extern u_int cputime __P((void));
+#endif
+
+#ifdef __GNUC__
+asm("
+GM_STATE = 0
+GMON_PROF_OFF = 3
+
+ .text
+ .align 4,0x90
+ .globl __mcount
+__mcount:
+ #
+ # Check that we are profiling. Do it early for speed.
+ #
+ cmpl $GMON_PROF_OFF,__gmonparam+GM_STATE
+ je Lmcount_exit
+ #
+ # __mcount is the same as mcount except the caller hasn't changed
+ # the stack except to call here, so the caller's raddr is above
+ # our raddr.
+ #
+ movl 4(%esp),%edx
+ jmp Lgot_frompc
+
+ .align 4,0x90
+ .globl mcount
+mcount:
+ cmpl $GMON_PROF_OFF,__gmonparam+GM_STATE
+ je Lmcount_exit
+ #
+ # The caller's stack frame has already been built, so %ebp is
+ # the caller's frame pointer. The caller's raddr is in the
+ # caller's frame following the caller's caller's frame pointer.
+ #
+ movl 4(%ebp),%edx
+Lgot_frompc:
+ #
+ # Our raddr is the caller's pc.
+ #
+ movl (%esp),%eax
+
+ pushf
+ pushl %eax
+ pushl %edx
+ cli
+ call _mcount
+ addl $8,%esp
+ popf
+Lmcount_exit:
+ ret
+");
+#else /* !__GNUC__ */
+#error
+#endif /* __GNUC__ */
+
+#ifdef GUPROF
+/*
+ * mexitcount saves the return register(s), loads selfpc and calls
+ * mexitcount(selfpc) to do the work. Someday it should be in a machine
+ * dependent file together with cputime(), __mcount and mcount. cputime()
+ * can't just be put in machdep.c because it has to be compiled without -pg.
+ */
+#ifdef __GNUC__
+asm("
+ .text
+#
+# Dummy label to be seen when gprof -u hides mexitcount.
+#
+ .align 4,0x90
+ .globl __mexitcount
+__mexitcount:
+ nop
+
+GMON_PROF_HIRES = 4
+
+ .align 4,0x90
+ .globl mexitcount
+mexitcount:
+ cmpl $GMON_PROF_HIRES,__gmonparam+GM_STATE
+ jne Lmexitcount_exit
+ pushl %edx
+ pushl %eax
+ movl 8(%esp),%eax
+ pushf
+ pushl %eax
+ cli
+ call _mexitcount
+ addl $4,%esp
+ popf
+ popl %eax
+ popl %edx
+Lmexitcount_exit:
+ ret
+");
+#else /* !__GNUC__ */
+#error
+#endif /* __GNUC__ */
+
+/*
+ * Return the time elapsed since the last call. The units are machine-
+ * dependent.
+ */
+u_int
+cputime()
+{
+ u_int count;
+ u_int delta;
+ u_char low;
+ static u_int prev_count;
+
+ /*
+ * Read the current value of the 8254 timer counter 0.
+ */
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
+ low = inb(TIMER_CNTR0);
+ count = low | (inb(TIMER_CNTR0) << 8);
+
+ /*
+ * The timer counts down from TIMER_CNTR0_MAX to 0 and then resets.
+ * While profiling is enabled, this routine is called at least twice
+ * per timer reset (for mcounting and mexitcounting hardclock()),
+ * so at most one reset has occurred since the last call, and one
+ * has occurred iff the current count is larger than the previous
+ * count. This allows counter underflow to be detected faster
+ * than in microtime().
+ */
+ delta = prev_count - count;
+ prev_count = count;
+ if ((int) delta <= 0)
+ return (delta + timer0_max_count);
+ return (delta);
+}
+#else /* not GUPROF */
+#ifdef __GNUC__
+asm("
+ .text
+ .align 4,0x90
+ .globl mexitcount
+mexitcount:
+ ret
+");
+#else /* !__GNUC__ */
+#error
+#endif /* __GNUC__ */
+#endif /* GUPROF */
diff --git a/sys/pc98/pc98/random_machdep.c b/sys/pc98/pc98/random_machdep.c
new file mode 100644
index 0000000..61b99b5
--- /dev/null
+++ b/sys/pc98/pc98/random_machdep.c
@@ -0,0 +1,488 @@
+/*
+ * random_machdep.c -- A strong random number generator
+ *
+ * $Id: random_machdep.c,v 1.7 1996/06/08 08:18:00 bde Exp $
+ *
+ * Version 0.95, last modified 18-Oct-95
+ *
+ * Copyright Theodore Ts'o, 1994, 1995. 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``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.
+ */
+
+/*
+ * modified for PC-9801 by KATO T. of Nagoya University
+ */
+
+#define MAX_BLKDEV 4
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <machine/clock.h>
+#include <machine/random.h>
+
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/timerreg.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/timerreg.h>
+#endif
+
+/*
+ * The pool is stirred with a primitive polynomial of degree 128
+ * over GF(2), namely x^128 + x^99 + x^59 + x^31 + x^9 + x^7 + 1.
+ * For a pool of size 64, try x^64+x^62+x^38+x^10+x^6+x+1.
+ */
+#define POOLWORDS 128 /* Power of 2 - note that this is 32-bit words */
+#define POOLBITS (POOLWORDS*32)
+
+#if POOLWORDS == 128
+#define TAP1 99 /* The polynomial taps */
+#define TAP2 59
+#define TAP3 31
+#define TAP4 9
+#define TAP5 7
+#elif POOLWORDS == 64
+#define TAP1 62 /* The polynomial taps */
+#define TAP2 38
+#define TAP3 10
+#define TAP4 6
+#define TAP5 1
+#else
+#error No primitive polynomial available for chosen POOLWORDS
+#endif
+
+#define WRITEBUFFER 512 /* size in bytes */
+
+/* There is actually only one of these, globally. */
+struct random_bucket {
+ u_int add_ptr;
+ u_int entropy_count;
+ int input_rotate;
+ u_int32_t *pool;
+};
+
+/* There is one of these per entropy source */
+struct timer_rand_state {
+ u_long last_time;
+ int last_delta;
+ int nbits;
+};
+
+static struct random_bucket random_state;
+static u_int32_t random_pool[POOLWORDS];
+static struct timer_rand_state keyboard_timer_state;
+static struct timer_rand_state extract_timer_state;
+static struct timer_rand_state irq_timer_state[ICU_LEN];
+static struct timer_rand_state blkdev_timer_state[MAX_BLKDEV];
+static struct wait_queue *random_wait;
+
+inthand2_t *sec_intr_handler[ICU_LEN];
+int sec_intr_unit[ICU_LEN];
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+void
+rand_initialize(void)
+{
+ random_state.add_ptr = 0;
+ random_state.entropy_count = 0;
+ random_state.pool = random_pool;
+ random_wait = NULL;
+}
+
+/*
+ * This function adds an int into the entropy "pool". It does not
+ * update the entropy estimate. The caller must do this if appropriate.
+ *
+ * The pool is stirred with a primitive polynomial of degree 128
+ * over GF(2), namely x^128 + x^99 + x^59 + x^31 + x^9 + x^7 + 1.
+ * For a pool of size 64, try x^64+x^62+x^38+x^10+x^6+x+1.
+ *
+ * We rotate the input word by a changing number of bits, to help
+ * assure that all bits in the entropy get toggled. Otherwise, if we
+ * consistently feed the entropy pool small numbers (like ticks and
+ * scancodes, for example), the upper bits of the entropy pool don't
+ * get affected. --- TYT, 10/11/95
+ */
+static inline void
+add_entropy_word(struct random_bucket *r, const u_int32_t input)
+{
+ u_int i;
+ u_int32_t w;
+
+ w = (input << r->input_rotate) | (input >> (32 - r->input_rotate));
+ i = r->add_ptr = (r->add_ptr - 1) & (POOLWORDS-1);
+ if (i)
+ r->input_rotate = (r->input_rotate + 7) & 31;
+ else
+ /*
+ * At the beginning of the pool, add an extra 7 bits
+ * rotation, so that successive passes spread the
+ * input bits across the pool evenly.
+ */
+ r->input_rotate = (r->input_rotate + 14) & 31;
+
+ /* XOR in the various taps */
+ w ^= r->pool[(i+TAP1)&(POOLWORDS-1)];
+ w ^= r->pool[(i+TAP2)&(POOLWORDS-1)];
+ w ^= r->pool[(i+TAP3)&(POOLWORDS-1)];
+ w ^= r->pool[(i+TAP4)&(POOLWORDS-1)];
+ w ^= r->pool[(i+TAP5)&(POOLWORDS-1)];
+ w ^= r->pool[i];
+ /* Rotate w left 1 bit (stolen from SHA) and store */
+ r->pool[i] = (w << 1) | (w >> 31);
+}
+
+/*
+ * This function adds entropy to the entropy "pool" by using timing
+ * delays. It uses the timer_rand_state structure to make an estimate
+ * of how any bits of entropy this call has added to the pool.
+ *
+ * The number "num" is also added to the pool - it should somehow describe
+ * the type of event which just happened. This is currently 0-255 for
+ * keyboard scan codes, and 256 upwards for interrupts.
+ * On the i386, this is assumed to be at most 16 bits, and the high bits
+ * are used for a high-resolution timer.
+ */
+static void
+add_timer_randomness(struct random_bucket *r, struct timer_rand_state *state,
+ u_int num)
+{
+ int delta, delta2;
+ u_int nbits;
+ u_int32_t time;
+
+#if defined(I586_CPU) || defined(I686_CPU)
+ if (i586_ctr_rate != 0) {
+ num ^= (u_int32_t) rdtsc() << 16;
+ r->entropy_count += 2;
+ } else {
+#endif
+ disable_intr();
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
+ num ^= inb(TIMER_CNTR0) << 16;
+ num ^= inb(TIMER_CNTR0) << 24;
+ enable_intr();
+ r->entropy_count += 2;
+#if defined(I586_CPU) || defined(I686_CPU)
+ }
+#endif
+
+ time = ticks;
+
+ add_entropy_word(r, (u_int32_t) num);
+ add_entropy_word(r, time);
+
+ /*
+ * Calculate number of bits of randomness we probably
+ * added. We take into account the first and second order
+ * deltas in order to make our estimate.
+ */
+ delta = time - state->last_time;
+ state->last_time = time;
+
+ delta2 = delta - state->last_delta;
+ state->last_delta = delta;
+
+ if (delta < 0) delta = -delta;
+ if (delta2 < 0) delta2 = -delta2;
+ delta = MIN(delta, delta2) >> 1;
+ for (nbits = 0; delta; nbits++)
+ delta >>= 1;
+
+ r->entropy_count += nbits;
+
+ /* Prevent overflow */
+ if (r->entropy_count > POOLBITS)
+ r->entropy_count = POOLBITS;
+}
+
+void
+add_keyboard_randomness(u_char scancode)
+{
+ add_timer_randomness(&random_state, &keyboard_timer_state, scancode);
+}
+
+void
+add_interrupt_randomness(int irq)
+{
+ (sec_intr_handler[irq])(sec_intr_unit[irq]);
+ add_timer_randomness(&random_state, &irq_timer_state[irq], irq);
+}
+
+#ifdef notused
+void
+add_blkdev_randomness(int major)
+{
+ if (major >= MAX_BLKDEV)
+ return;
+
+ add_timer_randomness(&random_state, &blkdev_timer_state[major],
+ 0x200+major);
+}
+#endif /* notused */
+
+/*
+ * MD5 transform algorithm, taken from code written by Colin Plumb,
+ * and put into the public domain
+ *
+ * QUESTION: Replace this with SHA, which as generally received better
+ * reviews from the cryptographic community?
+ */
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(u_int32_t buf[4],
+ u_int32_t const in[16])
+{
+ u_int32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#undef F1
+#undef F2
+#undef F3
+#undef F4
+#undef MD5STEP
+
+
+#if POOLWORDS % 16
+#error extract_entropy() assumes that POOLWORDS is a multiple of 16 words.
+#endif
+/*
+ * This function extracts randomness from the "entropy pool", and
+ * returns it in a buffer. This function computes how many remaining
+ * bits of entropy are left in the pool, but it does not restrict the
+ * number of bytes that are actually obtained.
+ */
+static inline int
+extract_entropy(struct random_bucket *r, char *buf, int nbytes)
+{
+ int ret, i;
+ u_int32_t tmp[4];
+
+ add_timer_randomness(r, &extract_timer_state, nbytes);
+
+ /* Redundant, but just in case... */
+ if (r->entropy_count > POOLBITS)
+ r->entropy_count = POOLBITS;
+ /* Why is this here? Left in from Ted Ts'o. Perhaps to limit time. */
+ if (nbytes > 32768)
+ nbytes = 32768;
+
+ ret = nbytes;
+ if (r->entropy_count / 8 >= nbytes)
+ r->entropy_count -= nbytes*8;
+ else
+ r->entropy_count = 0;
+
+ while (nbytes) {
+ /* Hash the pool to get the output */
+ tmp[0] = 0x67452301;
+ tmp[1] = 0xefcdab89;
+ tmp[2] = 0x98badcfe;
+ tmp[3] = 0x10325476;
+ for (i = 0; i < POOLWORDS; i += 16)
+ MD5Transform(tmp, r->pool+i);
+ /* Modify pool so next hash will produce different results */
+ add_entropy_word(r, tmp[0]);
+ add_entropy_word(r, tmp[1]);
+ add_entropy_word(r, tmp[2]);
+ add_entropy_word(r, tmp[3]);
+ /*
+ * Run the MD5 Transform one more time, since we want
+ * to add at least minimal obscuring of the inputs to
+ * add_entropy_word(). --- TYT
+ */
+ MD5Transform(tmp, r->pool);
+
+ /* Copy data to destination buffer */
+ i = MIN(nbytes, 16);
+ bcopy(tmp, buf, i);
+ nbytes -= i;
+ buf += i;
+ }
+
+ /* Wipe data from memory */
+ bzero(tmp, sizeof(tmp));
+
+ return ret;
+}
+
+#ifdef notused /* XXX NOT the exported kernel interface */
+/*
+ * This function is the exported kernel interface. It returns some
+ * number of good random numbers, suitable for seeding TCP sequence
+ * numbers, etc.
+ */
+void
+get_random_bytes(void *buf, u_int nbytes)
+{
+ extract_entropy(&random_state, (char *) buf, nbytes);
+}
+#endif /* notused */
+
+u_int
+read_random(char *buf, u_int nbytes)
+{
+ if ((nbytes * 8) > random_state.entropy_count)
+ nbytes = random_state.entropy_count / 8;
+
+ return extract_entropy(&random_state, buf, nbytes);
+}
+
+u_int
+read_random_unlimited(char *buf, u_int nbytes)
+{
+ return extract_entropy(&random_state, buf, nbytes);
+}
+
+#ifdef notused
+u_int
+write_random(const char *buf, u_int nbytes)
+{
+ u_int i;
+ u_int32_t word, *p;
+
+ for (i = nbytes, p = (u_int32_t *)buf;
+ i >= sizeof(u_int32_t);
+ i-= sizeof(u_int32_t), p++)
+ add_entropy_word(&random_state, *p);
+ if (i) {
+ word = 0;
+ bcopy(p, &word, i);
+ add_entropy_word(&random_state, word);
+ }
+ return nbytes;
+}
+#endif /* notused */
diff --git a/sys/pc98/pc98/sbic55.c b/sys/pc98/pc98/sbic55.c
new file mode 100644
index 0000000..9bfd4b4
--- /dev/null
+++ b/sys/pc98/pc98/sbic55.c
@@ -0,0 +1,956 @@
+/*
+ * Julian SCSI driver for PC-9801 based on aha1542.c
+ *
+ * Copyright (c) by Yoshio Kimura
+ * 05/14/1994
+ */
+
+#include <sys/types.h>
+#include "sbic.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+
+#include <machine/clock.h>
+#include <machine/cpu.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/ic/i8237.h>
+#include <pc98/pc98/scsireg.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <sys/devconf.h>
+
+#include <sys/kernel.h>
+
+/************************** board definitions *******************************/
+
+/*
+ * I/O Port Interface
+ */
+
+#define SBIC_BASE sbic->sbic_base
+#define SBIC_AUX_REG (SBIC_BASE + 0) /* auxiliary status(R) */
+#define SBIC_ADR_REG (SBIC_BASE + 0) /* address(W) */
+#define SBIC_CTL_REG (SBIC_BASE + 2) /* control(R/W) */
+#define SBIC_STA_REG (SBIC_BASE + 4) /* status(R/W) */
+
+/*
+ * Register Access Interface
+ */
+
+#define SBIC_asr(val) (val) = inb(SBIC_AUX_REG)
+#define GET_SBIC_REG(regno, val) { \
+ outb(SBIC_ADR_REG, (regno)); \
+ (val) = inb(SBIC_CTL_REG); \
+ }
+#define SET_SBIC_REG(regno, val) { \
+ outb(SBIC_ADR_REG, (regno)); \
+ outb(SBIC_CTL_REG, (val)); \
+ }
+#define SET_SCSI_CMD(cmd, cmdlen) { \
+ int n; \
+ u_char *cmds = (u_char *)(cmd); \
+ SET_SBIC_REG(SBIC_cdbsize, (cmdlen)); \
+ for (n = 0; n < (cmdlen); n++) { \
+ SET_SBIC_REG(SBIC_cdb1 + n, cmds[n]); \
+ } \
+ }
+#define SET_XFER_LEN(val) { \
+ SET_SBIC_REG(SBIC_count_hi, ((val) & 0xff0000) >> 16); \
+ SET_SBIC_REG(SBIC_count_med, ((val) & 0x00ff00) >> 8); \
+ SET_SBIC_REG(SBIC_count_lo, (val) & 0x0000ff); \
+ }
+#define SBIC_ENABLE_INT() { \
+ int tmp; \
+ GET_SBIC_REG(SBIC_bank, tmp); \
+ SET_SBIC_REG(SBIC_bank, tmp | 0x04); \
+ }
+#define SBIC_DISABLE_INT() { \
+ int tmp; \
+ GET_SBIC_REG(SBIC_bank, tmp); \
+ SET_SBIC_REG(SBIC_bank, tmp & 0xfb); \
+ }
+#define SBIC_DMA_ENABLE() outb(SBIC_STA_REG, 1)
+
+#define INT3 0
+#define INT5 1
+#define INT6 2
+#define INT9 3
+#define INT12 4
+#define INT13 5
+
+#define SBIC_NSEG 17
+#define SBIC_ID 7
+#define MAXSIMUL 8
+#define SBIC_RESET_TIMEOUT 2000 /* time to wait for reset */
+
+extern int dma_init_flag;
+extern short dmapageport[];
+
+
+struct sbic_ccb {
+ u_char target;
+ u_char lun;
+ u_char status;
+ u_char message;
+ int datalen;
+ int sense_addr;
+ int sense_len;
+ u_char scsi_cmdlen;
+ struct scsi_generic scsi_cmd;
+ struct scsi_xfer *xfer;
+ struct scat_gather {
+ int seg_addr;
+ int seg_len;
+ } scat_gath[SBIC_NSEG];
+ int seg;
+ int use_seg;
+ int xs_flags;
+ int flags;
+#define CCB_FREE 0x00
+#define CCB_ACTIVE 0x01
+#define CCB_ABORT 0x40
+#define CCB_SENSE 0x80
+#define CCB_BOUNCE 0x100
+};
+
+struct sbic_config {
+ u_char chan;
+ u_char intr;
+ u_char scsi_dev:3;
+ u_char :5;
+};
+/*********************************** end of board definitions***************/
+
+#define KVTOPHYS(x) vtophys(x)
+#define SBIC_DMA_PAGES SBIC_NSEG
+
+#define PAGESIZ 4096
+#define ALLWAYS_BOUNCE
+
+#ifdef ALLWAYS_BOUNCE
+static vm_offset_t sbic_bounce;
+#endif
+
+#ifdef SBICDEBUG
+static int sbic_debug = 1;
+#endif
+
+struct sbic_data {
+ short sbic_base; /* base port for each board */
+ struct sbic_ccb sbic_ccb[MAXSIMUL];
+ int top; /* ccb queue top */
+ int bottom; /* ccb queue end */
+ int active; /* number of active ccb */
+ int free; /* number of free ccb */
+ int unit;
+ int sbic_int; /* our irq level */
+ int sbic_dma; /* our DMA req channel */
+ int sbic_scsi_dev; /* our scsi bus address */
+ struct scsi_link sc_link; /* prototype for subdevs */
+};
+
+struct sbic_data *sbicdata[NSBIC];;
+
+static struct sbic_ccb *sbic_get_ccb(struct sbic_data *, int);
+static int sbicprobe(struct pc98_device *);
+static void sbic_done(struct sbic_data *, struct sbic_ccb *);
+static int sbicattach(struct pc98_device *);
+static int32_t sbic_scsi_cmd(struct scsi_xfer *xs);
+static u_int32_t sbic_adapter_info(int);
+static void sbicminphys(struct buf *);
+static void sbic_free_ccb(struct sbic_data *, struct sbic_ccb *, int);
+static int sbic_init(int);
+static int xfer_addr_check(int, struct sbic_ccb *);
+static int sbic_poll(struct sbic_data *, struct sbic_ccb *);
+static void start_scsi(struct sbic_data *, struct sbic_ccb *);
+static void dataphase(struct sbic_data *, struct sbic_ccb *);
+static void sbic_request_sense(struct sbic_data *, struct sbic_ccb *);
+static void sbic_dmastart(int, int, unsigned, unsigned);
+static void sbic_dmadone(int, int, unsigned, unsigned);
+
+static struct scsi_adapter sbic_switch = {
+ sbic_scsi_cmd,
+ sbicminphys,
+ 0,
+ 0,
+ sbic_adapter_info,
+ "sbic",
+ { 0, 0 }
+};
+
+/* the below structure is so we have a default dev struct for out link struct */
+static struct scsi_device sbic_dev = {
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "sbic",
+ 0,
+ { 0, 0 }
+};
+
+struct pc98_driver sbicdriver = {
+ sbicprobe,
+ sbicattach,
+ "sbic"
+};
+
+static struct kern_devconf kdc_sbic[NSBIC] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+ "sbic", 0, { MDDT_PC98, 0, "bio" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* always start out here in probe */
+ "55 compatible SCSI board host adapter",
+ DC_CLS_MISC /* host adapters aren't special */
+} };
+
+static inline void
+sbic_registerdev(struct pc98_device *id)
+{
+ if(id->id_unit)
+ kdc_sbic[id->id_unit] = kdc_sbic[0];
+ kdc_sbic[id->id_unit].kdc_unit = id->id_unit;
+ kdc_sbic[id->id_unit].kdc_parentdata = id;
+ dev_attach(&kdc_sbic[id->id_unit]);
+}
+
+
+static int sbicunit = 0;
+
+
+/*
+ * Check if the device can be found at the port given
+ * and if so, set it up ready for further work
+ * as an argument, takes the pc98_device structure from
+ * autoconf.c
+ */
+static int
+sbicprobe(struct pc98_device *dev)
+{
+ int unit = sbicunit;
+ struct sbic_data *sbic;
+
+ /*
+ * find unit and check we have that many defined
+ */
+ if (unit >= NSBIC) {
+ printf("sbic: unit number (%d) to high\n", unit);
+ return(0);
+ }
+ dev->id_unit = unit;
+
+ /*
+ * a quick safety check so we can be sleazy later
+ */
+ if (sizeof(struct sbic_data) > PAGESIZ) {
+ printf("sbic struct > pagesize\n");
+ return(0);
+ }
+ /*
+ * Allocate a storage area for us
+ */
+ if (sbicdata[unit]) {
+ printf("sbic%d: memory already allocated\n", unit);
+ return(0);
+ }
+ sbic = malloc(sizeof(struct sbic_data), M_TEMP, M_NOWAIT);
+ if (!sbic) {
+ printf("sbic%d: cannot malloc!\n", unit);
+ return(0);
+ }
+ bzero(sbic, sizeof(struct sbic_data));
+ sbicdata[unit] = sbic;
+ sbic->sbic_base = dev->id_iobase;
+
+#ifdef ALLWAYS_BOUNCE
+ /*
+ * allocate bounce buffer for sense data
+ */
+#ifdef EPSON_BOUNCEDMA
+#define PC98_RAMEND 0xf00000
+#else
+#define PC98_RAMEND RAM_END
+#endif
+
+ /* try malloc() first. */
+ sbic_bounce = (vm_offset_t)malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT);
+ if (sbic_bounce != NULL) {
+ if (vtophys(sbic_bounce) >= PC98_RAMEND) {
+ free(buf, M_DEVBUF);
+ sbic_bounce = (vm_offset_t)contigmalloc(PAGE_SIZE, M_DEVBUF,
+ M_NOWAIT,
+ 0ul, PC98_RAMEND, 1ul,
+ 0x10000ul);
+ }
+ }
+ if (sbic_bounce == NULL)
+ panic("Can't allocate bounce buffer.");
+#endif
+
+#ifndef DEV_LKM
+ sbic_registerdev(dev);
+#endif
+
+ /*
+ * Try initialize a unit at this location
+ * sets up dma, loads sbic_init[unit]
+ */
+ if (sbic_init(unit) != 0) {
+ sbicdata[unit] = NULL;
+ free(sbic, M_TEMP);
+ return(0);
+ }
+ /*
+ * If it's there, put in it's interrupt vectors
+ */
+ dev->id_irq = (1 << sbic->sbic_int);
+ dev->id_drq = sbic->sbic_dma;
+ sbicunit++;
+ return(5);
+}
+
+/*
+ * Attach all the sub-devices we can find.
+ */
+static int
+sbicattach(struct pc98_device *dev)
+{
+ int unit = dev->id_unit;
+ struct sbic_data *sbic = sbicdata[unit];
+ struct scsibus_data *scbus;
+
+ /*
+ * fill in the prototype scsi_link
+ */
+ sbic->sc_link.adapter_unit = unit;
+ sbic->sc_link.adapter_targ = sbic->sbic_scsi_dev;
+ sbic->sc_link.adapter_softc = sbic;
+ sbic->sc_link.adapter = &sbic_switch;
+ sbic->sc_link.device = &sbic_dev;
+ sbic->sc_link.flags = SDEV_BOUNCE;
+
+ /*
+ * Prepare the scsibus_data area for the upperlevel
+ * scsi code.
+ */
+ scbus = scsi_alloc_bus();
+ if(!scbus)
+ return 0;
+ scbus->adapter_link = &sbic->sc_link;
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ kdc_sbic[unit].kdc_state = DC_BUSY; /* host adapters are always busy */
+ scsi_attachdevs(scbus);
+
+ return 1;
+}
+
+/*
+ * Return some information to the caller about
+ * the adapter and it's capabilities
+ */
+static u_int32_t
+sbic_adapter_info(int unit)
+{
+ return(1); /* 1 outstanding request at a time per device */
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+void
+sbicintr(int unit)
+{
+ struct sbic_ccb *ccb;
+ struct sbic_data *sbic = sbicdata[unit];
+ u_char asr, host_stat, cmd_phase;
+ u_char *cmd;
+ int i, seg, phys;
+
+#ifdef SBICDEBUG
+ printf("sbicintr");
+#endif /* SBICDEBUG */
+
+ SBIC_asr(asr);
+ /* drop spurious interrupts */
+ if (!(asr & SBIC_ASR_INT))
+ return;
+ GET_SBIC_REG(SBIC_csr, host_stat);
+ GET_SBIC_REG(SBIC_cmd_phase, cmd_phase);
+
+ ccb = &sbic->sbic_ccb[sbic->top];
+ seg = ccb->seg;
+
+ switch(host_stat) {
+ case SBIC_CSR_XFERRED | SBIC_MCI_DATA_OUT: /* data phase start */
+ case SBIC_CSR_MIS_1 | SBIC_MCI_DATA_OUT: /* data phase continue */
+ case SBIC_CSR_XFERRED | SBIC_MCI_DATA_IN:
+ case SBIC_CSR_MIS_1 | SBIC_MCI_DATA_IN:
+ dataphase(sbic, ccb);
+ return;
+ case SBIC_CSR_MIS_1 | SBIC_MCI_STATUS: /* status phase start */
+ case SBIC_CSR_XFERRED | SBIC_MCI_STATUS: /* status phase start */
+ SET_XFER_LEN(0);
+ SET_SBIC_REG(SBIC_cmd_phase, 0x46);
+ SET_SBIC_REG(SBIC_cmd, SBIC_CMD_SEL_XFER);
+ return;
+ case SBIC_CSR_S_XFERRED:
+ phys = KVTOPHYS(ccb->sense_addr);
+ if (ccb->flags & CCB_SENSE) {
+ sbic_dmadone(B_READ, phys,
+ ccb->sense_len, sbic->sbic_dma);
+ } else {
+ if ((ccb->datalen) && (ccb->xs_flags & SCSI_DATA_IN)) {
+ sbic_dmadone(B_READ,
+ ccb->scat_gath[seg - 1].seg_addr,
+ ccb->scat_gath[seg - 1].seg_len,
+ sbic->sbic_dma);
+ }
+ if ((ccb->datalen) && (ccb->xs_flags & SCSI_DATA_OUT)) {
+ sbic_dmadone(B_WRITE,
+ ccb->scat_gath[seg - 1].seg_addr,
+ ccb->scat_gath[seg - 1].seg_len,
+ sbic->sbic_dma);
+ }
+ }
+ GET_SBIC_REG(SBIC_tlun, ccb->status);
+ ccb->status &= 0x1f;
+ switch(ccb->status) {
+ case SCSI_CHECK:
+ ccb->flags |= CCB_SENSE;
+ ccb->xfer->error = XS_SENSE;
+ sbic_request_sense(sbic, ccb);
+ return;
+ case SCSI_BUSY:
+ ccb->xfer->error = XS_BUSY;
+ break;
+ default:
+ break;
+ }
+ sbic_done(sbic, ccb);
+ break;
+
+ case SBIC_CSR_SEL_TIMEO: /* selection timeout */
+ ccb->xfer->error = XS_TIMEOUT;
+ sbic_done(sbic, ccb);
+ break;
+
+ default:
+ printf("sbic%d:%d:%d -- ", unit, ccb->target, ccb->lun);
+ printf("host: %x interrupt occured\n", host_stat);
+ panic("Unsupported interrupts. check intr code\n");
+ break;
+ }
+ return;
+}
+
+/*
+ * A ccb is put onto the free list.
+ */
+static void
+sbic_free_ccb(struct sbic_data *sbic, struct sbic_ccb *ccb, int flags)
+{
+ unsigned opri = 0;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ if (ccb = &sbic->sbic_ccb[sbic->top])
+ sbic->top = ++sbic->top % MAXSIMUL;
+ else
+ sbic->bottom = (--sbic->bottom + MAXSIMUL) % MAXSIMUL;
+ sbic->active--;
+ sbic->free++;
+ ccb->flags = CCB_FREE;
+ if (sbic->free)
+ wakeup((caddr_t)&sbic->free);
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+/*
+ * Get a free ccb
+ */
+static struct sbic_ccb *
+sbic_get_ccb(struct sbic_data *sbic, int flags)
+{
+ unsigned opri = 0;
+ struct sbic_ccb *rc;
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+ /*
+ * If we can and have to, sleep waiting for one to come free
+ */
+ while((!sbic->free) && (!(flags & SCSI_NOSLEEP)))
+ tsleep((caddr_t)&sbic->free, PRIBIO, "sbicccb", 0);
+ if (sbic->free) {
+ rc = &sbic->sbic_ccb[sbic->bottom];
+ sbic->free--;
+ sbic->active++;
+ sbic->bottom = ++sbic->bottom % MAXSIMUL;
+ rc->flags = CCB_ACTIVE;
+ } else
+ rc = (struct sbic_ccb *)0;
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+ return(rc);
+}
+
+/*
+ * We have a ccb which has been processed by the
+ * adapter, now we lock to see how the operation
+ * went. Wake up the owner if waiting
+ */
+static void
+sbic_done(struct sbic_data *sbic, struct sbic_ccb *ccb)
+{
+ struct scsi_xfer *xs = ccb->xfer;
+ unsigned opri;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("sbic_done\n"));
+ if (!(xs->flags & INUSE)) {
+ printf("sbic%d: exiting but not in use!\n", sbic->unit);
+ Debugger("sbic55");
+ }
+ if ((!xs->error) || (xs->flags & SCSI_ERR_OK))
+ xs->resid = 0;
+
+ xs->flags |= ITSDONE;
+#ifdef ALLWAYS_BOUNCE
+ bcopy((char *)ccb->sense_addr, (char *)&xs->sense, ccb->sense_len);
+#else
+ if(ccb->flags & CCB_BOUNCE)
+ bcopy((char *)ccb->sense_addr,
+ (char *)&xs->sense, ccb->sense_len);
+#endif
+ sbic_free_ccb(sbic, ccb, xs->flags);
+ if(sbic->active) {
+ opri = splbio();
+ SBIC_ENABLE_INT();
+ start_scsi(sbic, &sbic->sbic_ccb[sbic->top]);
+ splx(opri);
+ }
+ scsi_done(xs);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+static int
+sbic_init(int unit)
+{
+ struct sbic_data *sbic = sbicdata[unit];
+ struct sbic_config conf;
+ int i, asr, csr, tmp;
+
+ SBIC_asr(asr); /* dummy read */
+ GET_SBIC_REG(SBIC_csr, csr);
+ SBIC_DISABLE_INT();
+
+ SET_SBIC_REG(SBIC_myid, SBIC_ID_FS_16_20 | SBIC_ID);
+ SET_SBIC_REG(SBIC_cmd, SBIC_CMD_RESET);
+ for (i = SBIC_RESET_TIMEOUT; i; i--) {
+ SBIC_asr(asr);
+ if ((asr != 0xff) && (asr & SBIC_ASR_INT)) {
+ GET_SBIC_REG(SBIC_csr, csr);
+ if (csr == SBIC_CSR_RESET)
+ break;
+ }
+ DELAY(1); /* calibrated in msec */
+ }
+ if (i == 0) {
+#ifdef SBICDEBUG
+ if (sbic_debug)
+ printf("sbic_init: No answer from sbic board\n");
+#endif
+ return(ENXIO);
+ }
+
+ conf.chan = (inb(SBIC_STA_REG) & 0x03);
+ GET_SBIC_REG(SBIC_int, tmp);
+ conf.intr = (tmp >> 3) & 0x07;
+ conf.scsi_dev = tmp & 0x07;
+ sbic->sbic_dma = conf.chan;
+ sbic->sbic_scsi_dev = conf.scsi_dev;
+ switch(conf.intr) {
+ case INT3:
+ sbic->sbic_int = 3;
+ break;
+ case INT5:
+ sbic->sbic_int = 5;
+ break;
+ case INT6:
+ sbic->sbic_int = 6;
+ break;
+ case INT9:
+ sbic->sbic_int = 9;
+ break;
+ case INT12:
+ sbic->sbic_int = 12;
+ break;
+ case INT13:
+ sbic->sbic_int = 13;
+ break;
+ default:
+ printf("illegal int jumpter setting\n");
+ return(EIO);
+ }
+
+ SET_SBIC_REG(SBIC_rselid, 0);
+#if 1
+ SET_SBIC_REG(SBIC_timeo, 0x80);
+#else
+ SET_SBIC_REG(SBIC_timeo, 0xa0);
+#endif
+ SET_SBIC_REG(SBIC_control, SBIC_CTL_EDI);
+ SET_SBIC_REG(SBIC_syn, 0);
+ SBIC_ENABLE_INT();
+ /*
+ * ccb queue initialize
+ */
+ for (i = 0; i < MAXSIMUL; i++) {
+ sbic->sbic_ccb[i].flags = CCB_FREE;
+ }
+ sbic->top = sbic->bottom = sbic->active = 0;
+ sbic->free = MAXSIMUL;
+ /*
+ * Note that wa are going and return (to probe)
+ */
+ return(0);
+}
+
+static void
+sbicminphys(struct buf *bp)
+{
+/* sbic seems to explode with 17 segs (64k may require 17 segs) */
+ if (bp->b_bcount > ((SBIC_NSEG - 1) *PAGESIZ))
+ bp->b_bcount = ((SBIC_NSEG - 1) * PAGESIZ);
+}
+
+/*
+ * start a scsi operation given the command and
+ * the data address. Also needs the unit, target
+ * and lu
+ */
+static int32_t
+sbic_scsi_cmd(struct scsi_xfer *xs)
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ int unit = sc_link->adapter_unit;
+ struct sbic_data *sbic = sc_link->adapter_softc;
+ struct sbic_ccb *ccb;
+ int s, i, retval, flags;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB2, ("sbic_scsi_cmd\n"));
+ flags = xs->flags;
+ if (!(ccb = sbic_get_ccb(sbic, flags))) {
+ xs->error = XS_DRIVER_STUFFUP;
+ return(TRY_AGAIN_LATER);
+ }
+ ccb->xfer = xs;
+ ccb->target = xs->sc_link->target;
+ ccb->lun = xs->sc_link->lun;
+ ccb->xs_flags = xs->flags;
+ ccb->scsi_cmdlen = xs->cmdlen;
+ ccb->datalen = xs->datalen;
+ ccb->sense_len = sizeof(xs->sense);
+#ifdef ALLWAYS_BOUNCE
+ ccb->sense_addr = (int)(sbic_bounce);
+#else
+ ccb->sense_addr = (int)&xs->sense;
+#endif
+ ccb->seg = 0;
+
+ if (retval = xfer_addr_check(unit, ccb)) {
+ xs->error = XS_DRIVER_STUFFUP;
+ sbic_free_ccb(sbic, ccb, flags);
+ return(retval);
+ }
+ if (!(flags & SCSI_RESET)) {
+ bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmdlen);
+ ccb->scsi_cmd.bytes[0] |= ((ccb->lun << 5) & 0xe0);
+ }
+ if (!(flags & SCSI_NOMASK)) {
+ if (sbic->active == 1) {
+ s = splbio();
+ SBIC_ENABLE_INT();
+ start_scsi(sbic, ccb);
+ splx(s);
+ }
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("sent\n"));
+ return(SUCCESSFULLY_QUEUED);
+ }
+ SBIC_DISABLE_INT();
+ start_scsi(sbic, ccb);
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd sent, waiting\n"));
+ return(sbic_poll(sbic, ccb));
+}
+
+static int
+sbic_poll(struct sbic_data *sbic, struct sbic_ccb *ccb)
+{
+ int count, asr, done = 0;
+
+ count = ccb->xfer->timeout * 1000;
+ while(count) {
+ SBIC_asr(asr);
+ if ((asr != 0xff) && (asr & SBIC_ASR_INT)) {
+ sbicintr(sbic->unit);
+ }
+ if (ccb->xfer->flags & ITSDONE) {
+ break;
+ }
+ DELAY(1);
+ count--;
+ }
+ if (!count) panic("sbic scsi timeout!!");
+ if (ccb->xfer->error) return(HAD_ERROR);
+ return(COMPLETE);
+}
+
+static int
+xfer_addr_check(int unit, struct sbic_ccb *ccb)
+{
+ struct iovec *iovp;
+ struct scat_gather *sg;
+ struct scsi_xfer *xs;
+ int thiskv, thisphys, nextphys;
+ int bytes_this_page, bytes_this_seg;
+ int datalen, flags;
+
+ xs = ccb->xfer;
+ flags = ccb->xs_flags;
+ if ((xs->datalen) && (!(flags & SCSI_RESET))) {
+ sg = ccb->scat_gath;
+ ccb->use_seg = 0;
+ if (flags & SCSI_DATA_UIO) {
+ iovp = ((struct uio *)xs->data)->uio_iov;
+ datalen = ((struct uio *)xs->data)->uio_iovcnt;
+ while((datalen) && (ccb->use_seg < SBIC_NSEG)) {
+ sg->seg_addr = KVTOPHYS((int)iovp->iov_base);
+ sg->seg_len = iovp->iov_len;
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("UIO(0x%x@0x%x)"
+ ,iovp->iov_len
+ ,iovp->iov_base));
+ sg++;
+ iovp++;
+ ccb->use_seg++;
+ datalen++;
+ }
+ } else {
+ /*
+ * Set up scatter gather block
+ */
+ SC_DEBUG(xs->sc_link, SDEV_DB4,
+ ("%d @0x%x:- ", xs->datalen, xs->data));
+ datalen = xs->datalen;
+ thiskv = (int)xs->data;
+ thisphys = KVTOPHYS(thiskv);
+ while((datalen) && (ccb->use_seg < SBIC_NSEG)) {
+ bytes_this_seg = 0;
+ sg->seg_addr = thisphys;
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("0x%x", thisphys));
+ nextphys = thisphys;
+ while((datalen) && (thisphys == nextphys)) {
+ if (thisphys > 0xFFFFFF) {
+ printf("sbic%d: DMA beyond"
+ " end of PC98\n", unit);
+ xs->error = XS_DRIVER_STUFFUP;
+ return(HAD_ERROR);
+ }
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ bytes_this_page = min(bytes_this_page,
+ datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if (datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ ("(0x%x)", bytes_this_seg));
+ sg->seg_len = bytes_this_seg;
+ sg++;
+ ccb->use_seg++;
+ }
+ }
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
+ if (datalen) {
+ printf("sbic%d: sbic_scsi_cmd, more than %d DMA segs\n",
+ unit, SBIC_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ return(HAD_ERROR);
+ }
+ } else {
+ ccb->scat_gath[0].seg_len = 0;
+ }
+ return(0);
+}
+
+static void
+start_scsi(struct sbic_data *sbic, struct sbic_ccb *ccb)
+{
+ if (ccb->xs_flags & SCSI_RESET) {
+ SET_SBIC_REG(SBIC_cmd, SBIC_CMD_RESET);
+ return;
+ }
+
+ if ((ccb->datalen) && (ccb->xs_flags & SCSI_DATA_IN))
+ sbic_dmastart(B_READ, ccb->scat_gath[0].seg_addr,
+ ccb->scat_gath[0].seg_len, sbic->sbic_dma);
+ if ((ccb->datalen) && (ccb->xs_flags & SCSI_DATA_OUT))
+ sbic_dmastart(B_WRITE, ccb->scat_gath[0].seg_addr,
+ ccb->scat_gath[0].seg_len, sbic->sbic_dma);
+ SET_SCSI_CMD(&ccb->scsi_cmd, ccb->scsi_cmdlen);
+ SET_XFER_LEN(ccb->scat_gath[0].seg_len);
+ SET_SBIC_REG(SBIC_selid, ccb->target);
+ SET_SBIC_REG(SBIC_tlun, ccb->lun);
+ SBIC_DMA_ENABLE();
+ SET_SBIC_REG(SBIC_control, SBIC_CTL_DMA | SBIC_CTL_EDI);
+ SET_SBIC_REG(SBIC_cmd, SBIC_CMD_SEL_XFER);
+ ccb->seg++;
+ return;
+}
+
+static void
+dataphase(struct sbic_data *sbic, struct sbic_ccb *ccb)
+{
+ int seg = ccb->seg, tmp;
+
+ if ((ccb->datalen) && (ccb->xs_flags & SCSI_DATA_IN)) {
+ sbic_dmadone(B_READ, ccb->scat_gath[seg - 1].seg_addr,
+ ccb->scat_gath[seg - 1].seg_len, sbic->sbic_dma);
+ sbic_dmastart(B_READ, ccb->scat_gath[seg].seg_addr,
+ ccb->scat_gath[seg].seg_len, sbic->sbic_dma);
+ }
+ if ((ccb->datalen) && (ccb->xs_flags & SCSI_DATA_OUT)) {
+ sbic_dmadone(B_WRITE, ccb->scat_gath[seg - 1].seg_addr,
+ ccb->scat_gath[seg - 1].seg_len, sbic->sbic_dma);
+ sbic_dmastart(B_WRITE, ccb->scat_gath[seg].seg_addr,
+ ccb->scat_gath[seg].seg_len, sbic->sbic_dma);
+ }
+ SBIC_DMA_ENABLE();
+ SET_SBIC_REG(SBIC_control, SBIC_CTL_DMA | SBIC_CTL_EDI);
+ SET_XFER_LEN(ccb->scat_gath[seg].seg_len);
+ SET_SBIC_REG(SBIC_cmd, SBIC_CMD_XFER_INFO);
+ ccb->seg++;
+ return;
+}
+
+static void
+sbic_request_sense(struct sbic_data *sbic, struct sbic_ccb *ccb)
+{
+ int phys, len;
+ unsigned chan;
+ u_char cmd[6] = { 3, 0, 0, 0, 0, 0 };
+
+ cmd[1] |= ((ccb->lun << 5) & 0xe0);
+
+ chan = sbic->sbic_dma;
+ len = ccb->sense_len;
+#ifndef ALLWAYS_BOUNCE
+ if(pc98_dmarangecheck((caddr_t)ccb->sense_addr,len)) {
+ ccb->sense_addr = (int)(sbic_bounce);
+ ccb->flags |= CCB_BOUNCE;
+ }
+#endif
+ phys = KVTOPHYS(ccb->sense_addr);
+ sbic_dmastart(B_READ, phys, len, chan);
+
+ SET_SCSI_CMD(cmd, 6);
+ SET_XFER_LEN(ccb->sense_len);
+ SET_SBIC_REG(SBIC_selid, ccb->target);
+ SET_SBIC_REG(SBIC_tlun, ccb->lun);
+ SBIC_DMA_ENABLE();
+ SET_SBIC_REG(SBIC_control, SBIC_CTL_DMA | SBIC_CTL_EDI);
+ SET_SBIC_REG(SBIC_cmd, SBIC_CMD_SEL_XFER);
+ return;
+}
+
+
+static void
+sbic_dmastart(int flags, int phys, unsigned nbytes, unsigned chan)
+{
+ int modeport, waport, mskport;
+ caddr_t newaddr;
+ int s;
+
+ if (chan > 3 || nbytes > (1<<16))
+ panic("sbic_dmastart: impossible request");
+
+ s = splbio(); /* mask on */
+
+#ifdef CYRIX_5X86
+ asm("wbinvd"); /* wbinvd (WB cache flush) */
+#endif
+ if (!dma_init_flag) {
+ dma_init_flag = 1;
+ outb(0x439, (inb(0x439) & 0xfb)); /* DMA Accsess Control over 1MB */
+ outb(0x29, (0x0c | 0)); /* Bank Mode Reg. 16M mode */
+ outb(0x29, (0x0c | 1)); /* Bank Mode Reg. 16M mode */
+ outb(0x29, (0x0c | 2)); /* Bank Mode Reg. 16M mode */
+ outb(0x29, (0x0c | 3)); /* Bank Mode Reg. 16M mode */
+ outb(0x11, 0x50); /* PC98 must be 0x40 */
+ }
+
+ /* mask channel */
+ mskport = IO_DMA + 0x14; /* 0x15 */
+ outb(mskport, chan & 3 | 0x04);
+
+ /* set dma channel mode, and reset address ff */
+ modeport = IO_DMA + 0x16; /* 0x17 */
+ if (flags & B_READ) {
+ outb(modeport, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
+ } else {
+ outb(modeport, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
+ }
+ outb(modeport + 1*2, 0); /* 0x19 (clear byte pointer) */
+
+ /* send start address */
+ waport = IO_DMA + (chan<<2); /* 0x1, 0x5, 0x9, 0xd */
+ outb(waport, phys);
+ outb(waport, phys>>8);
+ outb(dmapageport[chan], phys>>16);
+
+ /* send count */
+ outb(waport + 2, --nbytes); /* 0x3, 0x7, 0xb, 0xf */
+ outb(waport + 2, nbytes>>8);
+
+ /* unmask channel */
+ mskport = IO_DMA + 0x14; /* 0x15 */
+ outb(mskport, chan & 3);
+
+ splx(s); /* mask off */
+}
+
+static void
+sbic_dmadone(int flags, int addr, unsigned nbytes, unsigned chan)
+{
+#if defined(CYRIX_486DLC) || defined(IBM_486SLC)
+ if (flags & B_READ) {
+ /* cache flush only after reading 92/12/9 by A.Kojima */
+ asm(" .byte 0x0f,0x08"); /* invd (cache flush) */
+ }
+#endif
+}
diff --git a/sys/pc98/pc98/sbic55.c.new b/sys/pc98/pc98/sbic55.c.new
new file mode 100644
index 0000000..a139449
--- /dev/null
+++ b/sys/pc98/pc98/sbic55.c.new
@@ -0,0 +1,1783 @@
+/* $NetBSD: sbic.c,v 1.10 1995/02/12 19:19:20 chopps Exp $ */
+
+/*
+ * Copyright (c) 1994 Christian E. Hopps
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * 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.
+ *
+ * @(#)scsi.c 7.5 (Berkeley) 5/4/91
+ */
+/*
+ * Ported to PC-9801 by Yoshio Kimura, 1994
+ * last update 09/25/1994
+ */
+
+/*
+ * AMIGA AMD 33C93 scsi adaptor driver
+ */
+
+#include "sbic.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/dkstat.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+
+#include <sys/devconf.h>
+#include <machine/clock.h>
+#include <machine/cpu.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/ic/i8237.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#include <pc98/pc98/sbicreg.h>
+#include <pc98/pc98/sbicvar.h>
+
+extern int dma_init_flag;
+extern short dmapageport[];
+
+/*
+ * SCSI delays
+ * In u-seconds, primarily for state changes on the SPC.
+ */
+#define SBIC_CMD_WAIT 50000 /* wait per step of 'immediate' cmds */
+#define SBIC_DATA_WAIT 50000 /* wait per data in/out step */
+#define SBIC_INIT_WAIT 50000 /* wait per step (both) during init */
+#define SBIC_RESET_TIMEOUT 2000
+
+#define b_cylin b_resid
+#define SBIC_WAIT(sbic, until, timeo) sbicwait(sbic, until, timeo, __LINE__)
+#define SBIC_ENABLE_INT(iobase) { \
+ u_char tmp; \
+ GET_SBIC_mem_bank(iobase, tmp); \
+ SET_SBIC_mem_bank(iobase, tmp | 0x04);\
+ }
+#define SBIC_DISABLE_INT(iobase) { \
+ u_char tmp; \
+ GET_SBIC_mem_bank(iobase, tmp); \
+ SET_SBIC_mem_bank(iobase, tmp & 0xfb); \
+ }
+
+#define INT0 0
+#define INT1 1
+#define INT2 2
+#define INT3 3
+#define INT5 5
+#define INT6 6
+
+static struct sbic_softc *sbicdata[NSBIC];
+
+#define KVTOPHYS(x) vtophys(x)
+
+static int sbicprobe __P((struct pc98_device *));
+static int sbicattach __P((struct pc98_device *));
+
+static u_int32_t sbic_adapter_info __P((int));
+static int sbic_find __P((struct sbic_softc *));
+static int32_t sbic_scsi_cmd __P((struct scsi_xfer *));
+static int sbicicmd __P((struct sbic_softc *,
+ int, int, void *, int, void *, int,u_char));
+static int sbicgo __P((struct sbic_softc *, struct scsi_xfer *));
+static int sbicdmaok __P((struct sbic_softc *, struct scsi_xfer *));
+static int sbicgetsense __P((struct sbic_softc *, struct scsi_xfer *));
+static int sbicwait __P((struct sbic_softc *, char, int , int));
+static int sbicselectbus __P((struct sbic_softc *, u_char, u_char, u_char));
+static int sbicxfstart __P((struct sbic_softc *, int, u_char, int));
+static int sbicxfout __P((struct sbic_softc *, int, void *, int));
+static int sbicfromscsiperiod __P((struct sbic_softc *, int));
+static int sbictoscsiperiod __P((struct sbic_softc *, int));
+static void sbicminphys __P((struct buf *));
+static void sbicxfin __P((struct sbic_softc *, int, void *));
+static void sbicxfdone __P((struct sbic_softc *, int));
+static void sbicabort __P((struct sbic_softc *, char *));
+static void sbicerror __P((struct sbic_softc *, u_char));
+static void sbicreset __P((struct sbic_softc *));
+static void sbicsetdelay __P((int));
+static void sbic_scsidone __P((struct sbic_softc *, int));
+static void sbic_donextcmd __P((struct sbic_softc *));
+static void sbic_dmastart __P((struct sbic_softc *));
+static void sbic_dmastop __P((struct sbic_softc *));
+
+/*
+ * Synch xfer parameters, and timing conversions
+ */
+static int sbic_min_period = SBIC_SYN_MIN_PERIOD; /* in cycles = f(ICLK,FSn) */
+static int sbic_max_offset = SBIC_SYN_MAX_OFFSET; /* pure number */
+
+static int sbic_cmd_wait = SBIC_CMD_WAIT;
+static int sbic_data_wait = SBIC_DATA_WAIT;
+static int sbic_init_wait = SBIC_INIT_WAIT;
+
+/*
+ * was broken before.. now if you want this you get it for all drives
+ * on sbic controllers.
+ */
+#ifdef SCSI_SYNC
+static int sbic_inhibit_sync = 0;
+#else
+static int sbic_inhibit_sync = 1;
+#endif
+
+static int sbic_force_async_flag = 0;
+
+#ifdef SC98BUSMASTER
+static int sc98busmaster = 0;
+#endif
+
+#ifdef DEBUG
+#define QPRINTF(a) if (sbic_debug > 1) printf a
+int sbic_debug = 0;
+int sync_debug = 0;
+int sbic_dma_debug = 0;
+#else
+#define QPRINTF(a)
+#endif
+
+static
+struct scsi_adapter sbic_switch = {
+ sbic_scsi_cmd,
+ sbicminphys,
+ 0,
+ 0,
+ sbic_adapter_info,
+ "sbic"
+ , { 0, 0 }
+};
+
+/* the below structure is so we have a default dev struct for out link struct */
+static
+struct scsi_device sbic_dev = {
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "sbic",
+ 0
+ , { 0, 0 }
+};
+
+struct pc98_driver sbicdriver = {
+ sbicprobe,
+ sbicattach,
+ "sbic"
+};
+
+static int sbicunit = 0;
+
+static struct kern_devconf kdc_sbic[NSBIC] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+ "sbic", 0, { MDDT_PC98, 0, "bio" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* always start out here in probe */
+ "WD33C93A based SCSI host adapter",
+ DC_CLS_MISC /* host adapters aren't special */
+} };
+
+static inline void
+sbic_registerdev(struct pc98_device *id)
+{
+ if(id->id_unit)
+ kdc_sbic[id->id_unit] = kdc_sbic[0];
+ kdc_sbic[id->id_unit].kdc_unit = id->id_unit;
+ kdc_sbic[id->id_unit].kdc_parentdata = id;
+ dev_attach(&kdc_sbic[id->id_unit]);
+}
+
+/*
+ * Check if the device can be found at the port given
+ * and if so, set ip up ready for further work
+ * as an argument, takes the pc98_device structure from
+ * autoconf.c
+ */
+static int
+sbicprobe(dev)
+ struct pc98_device *dev;
+{
+ int unit = sbicunit;
+ struct sbic_softc *sbic;
+
+#define SBICFLAGS
+# ifdef SBICFLAGS
+ sbic_force_async_flag = dev->id_flags;
+# else
+ sbic_force_async_flag = 0;
+# endif
+ if (unit >= NSBIC) {
+ printf("sbic%d: unit number too high\n", unit);
+ return 0;
+ }
+ dev->id_unit = unit;
+ /*
+ * Allocate a storage area for us
+ */
+ if (sbicdata[unit]) {
+ printf("sbic%d: memory already allocated\n", unit);
+ return 0;
+ }
+ sbic = malloc(sizeof(struct sbic_softc), M_TEMP, M_NOWAIT);
+ if (!sbic) {
+ printf("sbic%d: cannot malloc!\n", unit);
+ return 0;
+ }
+ bzero(sbic, sizeof(struct sbic_softc));
+ sbicdata[unit] = sbic;
+ sbic->sc_base = dev->id_iobase;
+
+ if (sbic_find(sbic) != 0) {
+ sbicdata[unit] = NULL;
+ free(sbic, M_TEMP);
+ return 0;
+ }
+#ifndef DEV_LKM
+ sbic_registerdev(dev);
+#endif /* DEV_LKM */
+
+ dev->id_irq = sbic->sc_int;
+ dev->id_drq = sbic->sc_dma;
+ sbicunit++;
+ return 5;
+}
+
+
+/*
+ * Attach all the sub-devices we can find
+ */
+static int
+sbicattach(dev)
+ struct pc98_device *dev;
+{
+ int unit = dev->id_unit;
+ struct sbic_softc *sbic = sbicdata[unit];
+ struct scsibus_data *scbus;
+#if defined(SC98BUSMASTER) || defined(FORCE_BUSMASTER)
+ int a;
+ int i;
+#endif
+
+#ifdef FORCE_BUSMASTER
+ sbic_read_reg(sbic->sc_base, 0x37, a);
+ /* set busmaster flag */
+ sbic_write_reg(sbic->sc_base, 0x37, a | 0x08);
+ printf("sbic%d: set busmaster flag\n", unit);
+#if defined(HA55BS_ID) && defined(SCSI_SYNC)
+ sbic_read_reg(sbic->sc_base, 0x60 + HA55BS_ID, a);
+ for (i = 0; i < 7; i++) {
+ if (sbic_force_async_flag & 1 << i) {
+ sbic_write_reg(sbic->sc_base, 0x60 + i, a);
+ printf("sbic%d: set transfer rate (ID %d)\n",
+ unit, i);
+ }
+ }
+ sbic_force_async_flag = 0;
+#endif
+#endif
+
+#ifdef SC98BUSMASTER
+ sbic_read_reg(sbic->sc_base, 0x37, a);
+ if (a & 0x08) {
+ printf("sbic%d: busmaster enable\n", unit);
+ sc98busmaster = 1;
+ }
+#endif
+ /*
+ * fill in the prototype scsi_link.
+ */
+ sbic->sc_link.adapter_unit = unit;
+ sbic->sc_link.adapter_softc = sbic;
+ sbic->sc_link.adapter_targ = sbic->sc_scsi_dev;
+ sbic->sc_link.adapter = &sbic_switch;
+ sbic->sc_link.device = &sbic_dev;
+ sbic->sc_link.flags = SDEV_BOUNCE;
+ TAILQ_INIT(&sbic->sc_xslist);
+
+ /*
+ * Prepare the scsibus_data area for the upperlevel
+ * scsi code.
+ */
+ scbus = scsi_alloc_bus();
+ if(!scbus)
+ return 0;
+ scbus->adapter_link = &sbic->sc_link;
+
+ /*
+ * ask the adapter what subunits are present
+ */
+ kdc_sbic[unit].kdc_state = DC_BUSY; /* host adapters are always busy */
+ scsi_attachdevs(scbus);
+
+ return 1;
+}
+
+/*
+ * Find the board and find its irq/drq
+ */
+static int
+sbic_find(sbic)
+ struct sbic_softc *sbic;
+{
+ int intr, my_id, asr, csr;
+ volatile int i, sts;
+
+ GET_SBIC_asr(sbic->sc_base, asr); /* dummy read */
+ GET_SBIC_csr(sbic->sc_base, csr);
+ SBIC_DISABLE_INT(sbic->sc_base);
+
+ GET_SBIC_myid(sbic->sc_base, my_id);
+ switch(my_id & 0xc0) {
+ case SBIC_ID_FS_8_10:
+ sbic->sc_clkfreq = 100;
+ break;
+ case SBIC_ID_FS_12_15:
+ sbic->sc_clkfreq = 150;
+ break;
+ case SBIC_ID_FS_16_20:
+ sbic->sc_clkfreq = 200;
+ break;
+ }
+ sbic->sc_scsi_dev = my_id & SBIC_ID_MASK;
+ my_id &= 0xc7;
+ my_id |= SBIC_ID_EAF;
+ /*
+ * reset board, If it doesn't respond, assume
+ * that it's not there.. good for the probe
+ */
+ SET_SBIC_myid(sbic->sc_base, my_id);
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_RESET);
+ DELAY(10);
+ SBIC_WAIT(sbic, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(sbic->sc_base, csr);
+ if (csr != SBIC_CSR_RESET_AM)
+ return(ENXIO);
+
+ sbic->sc_dma = inb(sbic->sc_base + 4) & 0x03;
+ outb(sbic->sc_base, SBIC_reset_int);
+ intr = (inb(sbic->sc_base + 2) >> 3) & 0x07;
+ switch(intr) {
+ case INT0:
+ sbic->sc_int = IRQ3;
+ break;
+ case INT1:
+ sbic->sc_int = IRQ5;
+ break;
+ case INT2:
+ sbic->sc_int = IRQ6;
+ break;
+ case INT3:
+ sbic->sc_int = IRQ9;
+ break;
+ case INT5:
+ sbic->sc_int = IRQ12;
+ break;
+ case INT6:
+ sbic->sc_int = IRQ13;
+ break;
+ default:
+ printf("illegal int jumper setting\n");
+ return(EIO);
+ }
+ SET_SBIC_control(sbic->sc_base, SBIC_CTL_EDI | SBIC_CTL_IDI
+ | SBIC_MACHINE_DMA_MODE);
+ SET_SBIC_rselid(sbic->sc_base, 0);
+ SET_SBIC_syn(sbic->sc_base, 0); /* asynch for now */
+
+ sbic->sc_flags = SBICF_ALIVE;
+ return 0;
+}
+
+/*
+ * default minphys routine for sbic based controllers
+ */
+static void
+sbicminphys(bp)
+ struct buf *bp;
+{
+ if (bp->b_bcount > ((SBIC_NSEG - 1) << PAGE_SHIFT))
+ bp->b_bcount = ((SBIC_NSEG - 1) << PAGE_SHIFT);
+}
+
+/*
+ * must be used
+ */
+static u_int32_t
+sbic_adapter_info(unit)
+ int unit;
+{
+ /*
+ * one request at a time please
+ */
+ return(1);
+}
+
+/*
+ * used by specific sbic controller
+ *
+ * it appears that the higher level code does nothing with LUN's
+ * so I will too. I could plug it in, however so could they
+ * in scsi_scsi_cmd().
+ */
+static int32_t
+sbic_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct sbic_pending *pendp;
+ struct sbic_softc *sbic;
+ struct scsi_link *sc_link;
+ int flags, s;
+ int unit;
+
+ sc_link = xs->sc_link;
+ unit = sc_link->adapter_unit;
+ sbic = sc_link->adapter_softc;
+ flags = xs->flags;
+
+ if (flags & SCSI_DATA_UIO)
+ panic("sbic: scsi data uio requested");
+
+ if (sbic->sc_xs && flags & SCSI_NOMASK)
+ panic("sbic_scsi_cmd: busy");
+
+ s = splbio();
+ pendp = &sbic->sc_xsstore[sc_link->target][sc_link->lun];
+ if (pendp->xs) {
+ splx(s);
+ return(TRY_AGAIN_LATER);
+ }
+
+ if (sbic->sc_xs) {
+ pendp->xs = xs;
+ TAILQ_INSERT_TAIL(&sbic->sc_xslist, pendp, link);
+ splx(s);
+ return(SUCCESSFULLY_QUEUED);
+ }
+ pendp->xs = NULL;
+ sbic->sc_xs = xs;
+ splx(s);
+
+ /*
+ * nothing is pending do it now.
+ */
+ sbic_donextcmd(sbic);
+
+ if (flags & SCSI_NOMASK)
+ return(COMPLETE);
+ return(SUCCESSFULLY_QUEUED);
+}
+
+/*
+ * entered with sbic->sc_xs pointing to the next xfer to perform
+ */
+static void
+sbic_donextcmd(sbic)
+ struct sbic_softc *sbic;
+{
+ struct scsi_xfer *xs;
+ struct scsi_link *sc_link;
+ int flags, phase, stat;
+
+ xs = sbic->sc_xs;
+ sc_link = xs->sc_link;
+ flags = xs->flags;
+
+ if (flags & SCSI_DATA_IN)
+ phase = DATA_IN_PHASE;
+ else if (flags & SCSI_DATA_OUT)
+ phase = DATA_OUT_PHASE;
+ else
+ phase = STATUS_PHASE;
+
+ if (flags & SCSI_RESET)
+ sbicreset(sbic);
+
+ sbic->sc_stat[0] = -1;
+ xs->cmd->bytes[0] |= sc_link->lun << 5;
+ if (phase == STATUS_PHASE || flags & SCSI_NOMASK ||
+ sbicdmaok(sbic, xs) == 0)
+ stat = sbicicmd(sbic, sc_link->target, sc_link->lun, xs->cmd,
+ xs->cmdlen, xs->data, xs->datalen, phase);
+ else if (sbicgo(sbic, xs) == 0)
+ return;
+ else
+ stat = sbic->sc_stat[0];
+
+ sbic_scsidone(sbic, stat);
+}
+
+static void
+sbic_scsidone(sbic, stat)
+ struct sbic_softc *sbic;
+ int stat;
+{
+ struct sbic_pending *pendp;
+ struct scsi_xfer *xs;
+ int s, donext;
+
+ xs = sbic->sc_xs;
+#ifdef DIAGNOSTIC
+ if (xs == NULL)
+ panic("sbic_scsidone");
+#endif
+ /*
+ * is this right?
+ */
+ xs->status = stat;
+
+ if (stat == 0)
+ xs->resid = 0;
+ else {
+ switch(stat) {
+ case SCSI_CHECK:
+ if (stat = sbicgetsense(sbic, xs))
+ goto bad_sense;
+ xs->error = XS_SENSE;
+ break;
+ case SCSI_BUSY:
+ xs->error = XS_BUSY;
+ break;
+ bad_sense:
+ default:
+ xs->error = XS_DRIVER_STUFFUP;
+ QPRINTF(("sbic_scsicmd() bad %x\n", stat));
+ break;
+ }
+ }
+ xs->flags |= ITSDONE;
+
+ /*
+ * grab next command before scsi_done()
+ * this way no single device can hog scsi resources.
+ */
+ s = splbio();
+ pendp = sbic->sc_xslist.tqh_first;
+ if (pendp == NULL) {
+ donext = 0;
+ sbic->sc_xs = NULL;
+ } else {
+ donext = 1;
+ TAILQ_REMOVE(&sbic->sc_xslist, pendp, link);
+ sbic->sc_xs = pendp->xs;
+ pendp->xs = NULL;
+ }
+ splx(s);
+ scsi_done(xs);
+
+ if (donext)
+ sbic_donextcmd(sbic);
+}
+
+static int
+sbicgetsense(sbic, xs)
+ struct sbic_softc *sbic;
+ struct scsi_xfer *xs;
+{
+ struct scsi_sense rqs;
+ struct scsi_link *sc_link;
+ int stat;
+
+ sc_link = xs->sc_link;
+
+ rqs.op_code = REQUEST_SENSE;
+ rqs.byte2 = sc_link->lun << 5;
+#ifdef not_yet
+ rqs.length = xs->req_sense_length ? xs->req_sense_length :
+ sizeof(xs->sense);
+#else
+ rqs.length = sizeof(xs->sense);
+#endif
+
+ rqs.unused[0] = rqs.unused[1] = rqs.control = 0;
+
+ return(sbicicmd(sbic, sc_link->target, sc_link->lun, &rqs,
+ sizeof(rqs), &xs->sense, rqs.length, DATA_IN_PHASE));
+}
+
+static int
+sbicdmaok(sbic, xs)
+ struct sbic_softc *sbic;
+ struct scsi_xfer *xs;
+{
+ int va, phys, priorpage = 0, endva, length;
+
+ va = (int)xs->data;
+ length = xs->datalen;
+ endva = round_page(va + length);
+
+ for (; va < endva; va += PAGE_SIZE) {
+ phys = trunc_page(pmap_extract(kernel_pmap, va));
+ if (phys == 0)
+ panic("sbicdmaok: no physical page present");
+ if (phys >= (1 << 24))
+ return 0;
+ priorpage = phys;
+ }
+ return 1;
+}
+
+static int
+sbicwait(sbic, until, timeo, line)
+ struct sbic_softc *sbic;
+ char until;
+ int timeo;
+ int line;
+{
+ u_char val;
+ int csr;
+
+ if (timeo == 0)
+ timeo = 1000000; /* some large value.. */
+
+ GET_SBIC_asr(sbic->sc_base,val);
+ while ((val & until) == 0) {
+ if (timeo-- == 0) {
+ GET_SBIC_csr(sbic->sc_base, csr);
+ printf("sbicwait TIMEO @%d with asr=x%x csr=x%x\n",
+ line, val, csr);
+ break;
+ }
+ DELAY(1);
+ GET_SBIC_asr(sbic->sc_base,val);
+ }
+ return(val);
+}
+
+static void
+sbicabort(sbic, where)
+ struct sbic_softc *sbic;
+ char *where;
+{
+ u_char csr, asr;
+
+ GET_SBIC_csr(sbic->sc_base, csr);
+ GET_SBIC_asr(sbic->sc_base, asr);
+
+ printf ("sbic%d: abort %s: csr = 0x%02x, asr = 0x%02x\n",
+ sbic->sc_link.adapter_unit, where, csr, asr);
+
+ if (sbic->sc_flags & SBICF_SELECTED) {
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_ABORT);
+ WAIT_CIP(sbic->sc_base);
+
+ GET_SBIC_asr(sbic->sc_base, asr);
+ if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) {
+ /* ok, get more drastic.. */
+
+ SET_SBIC_cmd (sbic->sc_base, SBIC_CMD_RESET);
+ DELAY(25);
+ SBIC_WAIT(sbic, SBIC_ASR_INT, 0);
+ /* clears interrupt also */
+ GET_SBIC_csr (sbic->sc_base, csr);
+
+ sbic->sc_flags &= ~SBICF_SELECTED;
+ return;
+ }
+
+ do {
+ SBIC_WAIT (sbic, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (sbic->sc_base, csr);
+ } while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
+ && (csr != SBIC_CSR_CMD_INVALID));
+
+ /* lets just hope it worked.. */
+ sbic->sc_flags &= ~SBICF_SELECTED;
+ }
+}
+
+/*
+ * XXX Set/reset long delays.
+ *
+ * if delay == 0, reset default delays
+ * if delay < 0, set both delays to default long initialization values
+ * if delay > 0, set both delays to this value
+ *
+ * Used when a devices is expected to respond slowly (e.g. during
+ * initialization).
+ */
+static void
+sbicsetdelay(del)
+ int del;
+{
+ static int saved_cmd_wait, saved_data_wait;
+
+ if (del) {
+ saved_cmd_wait = sbic_cmd_wait;
+ saved_data_wait = sbic_data_wait;
+ if (del > 0)
+ sbic_cmd_wait = sbic_data_wait = del;
+ else
+ sbic_cmd_wait = sbic_data_wait = sbic_init_wait;
+ } else {
+ sbic_cmd_wait = saved_cmd_wait;
+ sbic_data_wait = saved_data_wait;
+ }
+}
+
+void
+sbicreset(sbic)
+ struct sbic_softc *sbic;
+{
+ u_int i, s;
+ u_char my_id, csr;
+
+ if (sbic->sc_flags & SBICF_ALIVE)
+ sbicabort(sbic, "reset");
+
+ s = splbio();
+ /* preserve our ID for now */
+ GET_SBIC_myid (sbic->sc_base, my_id);
+ my_id &= SBIC_ID_MASK;
+
+ if (sbic->sc_clkfreq < 110)
+ my_id |= SBIC_ID_FS_8_10;
+ else if (sbic->sc_clkfreq < 160)
+ my_id |= SBIC_ID_FS_12_15;
+ else if (sbic->sc_clkfreq < 210)
+ my_id |= SBIC_ID_FS_16_20;
+
+ my_id |= SBIC_ID_EAF /*| SBIC_ID_EHP*/ ;
+
+ SET_SBIC_myid(sbic->sc_base, my_id);
+
+ /*
+ * Disable interrupts (in dmainit) then reset the chip
+ */
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_RESET);
+ DELAY(25);
+ SBIC_WAIT(sbic, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(sbic->sc_base, csr); /* clears interrupt also */
+
+ /*
+ * Set up various chip parameters
+ */
+ SET_SBIC_control(sbic->sc_base, SBIC_CTL_EDI | SBIC_CTL_IDI
+ | SBIC_MACHINE_DMA_MODE);
+ /*
+ * don't allow (re)selection (SBIC_RID_ES)
+ * until we can handle target mode!!
+ */
+ SET_SBIC_rselid(sbic->sc_base, 0);
+ SET_SBIC_syn(sbic->sc_base, 0); /* asynch for now */
+
+ /*
+ * anything else was zeroed by reset
+ */
+ splx(s);
+
+ sbic->sc_flags |= SBICF_ALIVE;
+ sbic->sc_flags &= ~SBICF_SELECTED;
+}
+
+static void
+sbicerror(sbic, csr)
+ struct sbic_softc *sbic;
+ u_char csr;
+{
+ struct scsi_xfer *xs;
+
+ xs = sbic->sc_xs;
+
+#ifdef DIAGNOSTIC
+ if (xs == NULL)
+ panic("sbicerror");
+#endif
+ if (xs->flags & SCSI_SILENT)
+ return;
+
+ printf("sbic%d: ", sbic->sc_link.adapter_unit);
+ printf("csr == 0x%02x\n", csr); /* XXX */
+}
+
+/*
+ * select the bus, return when selected or error.
+ */
+static int
+sbicselectbus(sbic, target, lun, our_addr)
+ struct sbic_softc *sbic;
+ u_char target, lun, our_addr;
+{
+ u_char asr, csr, id;
+
+ QPRINTF(("sbicselectbus %d\n", target));
+
+ /*
+ * if we're already selected, return (XXXX panic maybe?)
+ */
+ if (sbic->sc_flags & SBICF_SELECTED)
+ return(1);
+
+ /*
+ * issue select
+ */
+ SBIC_TC_PUT(sbic->sc_base, 0);
+ SET_SBIC_selid(sbic->sc_base, target);
+ SET_SBIC_timeo(sbic->sc_base, SBIC_TIMEOUT(250,sbic->sc_clkfreq));
+
+ /*
+ * set sync or async
+ */
+ if (sbic->sc_sync[target].state == SYNC_DONE)
+ SET_SBIC_syn(sbic->sc_base, SBIC_SYN (sbic->sc_sync[target].offset,
+ sbic->sc_sync[target].period));
+ else
+ SET_SBIC_syn(sbic->sc_base, SBIC_SYN (0, sbic_min_period));
+
+ SET_SBIC_cmd(sbic->sc_base, sbic_inhibit_sync ? SBIC_CMD_SEL : SBIC_CMD_SEL_ATN);
+
+ /*
+ * wait for select (merged from seperate function may need
+ * cleanup)
+ */
+ WAIT_CIP(sbic->sc_base);
+ do {
+ SBIC_WAIT(sbic, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (sbic->sc_base, csr);
+ QPRINTF(("%02x ", csr));
+ } while (csr != (SBIC_CSR_MIS_2|MESG_OUT_PHASE)
+ && csr != (SBIC_CSR_MIS_2|CMD_PHASE) && csr != SBIC_CSR_SEL_TIMEO);
+
+ if (csr == (SBIC_CSR_MIS_2|CMD_PHASE))
+ sbic->sc_flags |= SBICF_SELECTED; /* device ignored ATN */
+ else if (csr == (SBIC_CSR_MIS_2|MESG_OUT_PHASE)) {
+ /*
+ * Send identify message
+ * (SCSI-2 requires an identify msg (?))
+ */
+ GET_SBIC_selid(sbic->sc_base, id);
+
+ /*
+ * handle drives that don't want to be asked
+ * whether to go sync at all.
+ */
+ if ((sbic_force_async_flag & (1 << id)) || (sbic_inhibit_sync && sbic->sc_sync[id].state == SYNC_START)) {
+#ifdef DEBUG
+ if (sync_debug)
+ printf("Forcing target %d asynchronous.\n", id);
+#endif
+ sbic->sc_sync[id].offset = 0;
+ sbic->sc_sync[id].period = sbic_min_period;
+ sbic->sc_sync[id].state = SYNC_DONE;
+ }
+
+
+ if (sbic->sc_sync[id].state != SYNC_START)
+ SEND_BYTE (sbic, MSG_IDENTIFY | lun);
+ else {
+ /*
+ * try to initiate a sync transfer.
+ * So compose the sync message we're going
+ * to send to the target
+ */
+
+#ifdef DEBUG
+ if (sync_debug)
+ printf("Sending sync request to target %d ... ",
+ id);
+#endif
+ /*
+ * setup scsi message sync message request
+ */
+ sbic->sc_msg[0] = MSG_IDENTIFY | lun;
+ sbic->sc_msg[1] = MSG_EXT_MESSAGE;
+ sbic->sc_msg[2] = 3;
+ sbic->sc_msg[3] = MSG_SYNC_REQ;
+ sbic->sc_msg[4] = sbictoscsiperiod(sbic,
+ sbic_min_period);
+ sbic->sc_msg[5] = sbic_max_offset;
+
+ if (sbicxfstart(sbic, 6, MESG_OUT_PHASE, sbic_cmd_wait))
+ sbicxfout(sbic, 6, sbic->sc_msg, MESG_OUT_PHASE);
+
+ sbic->sc_sync[id].state = SYNC_SENT;
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("sent\n");
+#endif
+ }
+
+ SBIC_WAIT (sbic, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (sbic->sc_base, csr);
+ QPRINTF(("[%02x]", csr));
+#ifdef DEBUG
+ if (sync_debug && sbic->sc_sync[id].state == SYNC_SENT)
+ printf("csr-result of last msgout: 0x%x\n", csr);
+#endif
+
+ if (csr != SBIC_CSR_SEL_TIMEO)
+ sbic->sc_flags |= SBICF_SELECTED;
+ }
+
+ QPRINTF(("\n"));
+
+ return(csr == SBIC_CSR_SEL_TIMEO);
+}
+
+static int
+sbicxfstart(sbic, len, phase, wait)
+ struct sbic_softc *sbic;
+ int len, wait;
+ u_char phase;
+{
+ u_char id;
+
+ if (phase == DATA_IN_PHASE || phase == MESG_IN_PHASE) {
+ GET_SBIC_selid (sbic->sc_base, id);
+ id |= SBIC_SID_FROM_SCSI;
+ SET_SBIC_selid (sbic->sc_base, id);
+ SBIC_TC_PUT (sbic->sc_base, (unsigned)len);
+ } else if (phase == DATA_OUT_PHASE || phase == MESG_OUT_PHASE
+ || phase == CMD_PHASE)
+ SBIC_TC_PUT (sbic->sc_base, (unsigned)len);
+ else
+ SBIC_TC_PUT (sbic->sc_base, 0);
+ QPRINTF(("sbicxfstart %d, %d, %d\n", len, phase, wait));
+
+ return(1);
+}
+
+static int
+sbicxfout(sbic, len, bp, phase)
+ struct sbic_softc *sbic;
+ int len;
+ void *bp;
+ int phase;
+{
+ u_char orig_csr, csr, asr, *buf;
+ int wait;
+
+ buf = bp;
+ wait = sbic_data_wait;
+
+ QPRINTF(("sbicxfout {%d} %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x\n", len, buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9]));
+
+ GET_SBIC_csr (sbic->sc_base, orig_csr);
+
+ /*
+ * sigh.. WD-PROTO strikes again.. sending the command in one go
+ * causes the chip to lock up if talking to certain (misbehaving?)
+ * targets. Anyway, this procedure should work for all targets, but
+ * it's slightly slower due to the overhead
+ */
+ WAIT_CIP (sbic->sc_base);
+ SET_SBIC_cmd (sbic->sc_base, SBIC_CMD_XFER_INFO);
+ for (;len > 0; len--) {
+ GET_SBIC_asr (sbic->sc_base, asr);
+ while ((asr & SBIC_ASR_DBR) == 0) {
+ if ((asr & SBIC_ASR_INT) || --wait < 0) {
+#ifdef DEBUG
+ if (sbic_debug)
+ printf("sbicxfout fail: l%d i%x w%d\n",
+ len, asr, wait);
+#endif
+ return (len);
+ }
+ DELAY(1);
+ GET_SBIC_asr (sbic->sc_base, asr);
+ }
+
+ SET_SBIC_data (sbic->sc_base, *buf);
+ buf++;
+ }
+
+ QPRINTF(("sbicxfout done\n"));
+ /*
+ * this leaves with one csr to be read
+ */
+ return(0);
+}
+
+static void
+sbicxfin(sbic, len, bp)
+ struct sbic_softc *sbic;
+ int len;
+ void *bp;
+{
+ int wait;
+ u_char *obp, *buf;
+ u_char orig_csr, csr, asr;
+
+ wait = sbic_data_wait;
+ obp = bp;
+ buf = bp;
+
+ GET_SBIC_csr (sbic->sc_base, orig_csr);
+
+ QPRINTF(("sbicxfin %d, csr=%02x\n", len, orig_csr));
+
+ WAIT_CIP (sbic->sc_base);
+ SET_SBIC_cmd (sbic->sc_base, SBIC_CMD_XFER_INFO);
+ for (;len > 0; len--) {
+ GET_SBIC_asr (sbic->sc_base, asr);
+ while ((asr & SBIC_ASR_DBR) == 0) {
+ if ((asr & SBIC_ASR_INT) || --wait < 0) {
+#ifdef DEBUG
+ if (sbic_debug)
+ printf("sbicxfin fail: l%d i%x w%d\n",
+ len, asr, wait);
+#endif
+ return;
+ }
+
+ DELAY(1);
+ GET_SBIC_asr (sbic->sc_base, asr);
+ }
+
+ GET_SBIC_data (sbic->sc_base, *buf);
+ buf++;
+ }
+
+ QPRINTF(("sbicxfin {%d} %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2],
+ obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9]));
+
+ /* this leaves with one csr to be read */
+}
+
+
+/*
+ * SCSI 'immediate' command: issue a command to some SCSI device
+ * and get back an 'immediate' response (i.e., do programmed xfer
+ * to get the response data). 'cbuf' is a buffer containing a scsi
+ * command of length clen bytes. 'buf' is a buffer of length 'len'
+ * bytes for data. The transfer direction is determined by the device
+ * (i.e., by the scsi bus data xfer phase). If 'len' is zero, the
+ * command must supply no data. 'xferphase' is the bus phase the
+ * caller expects to happen after the command is issued. It should
+ * be one of DATA_IN_PHASE, DATA_OUT_PHASE or STATUS_PHASE.
+ */
+static int
+sbicicmd(sbic, target, lun, cbuf, clen, buf, len, xferphase)
+ struct sbic_softc *sbic;
+ int target, lun;
+ void *cbuf, *buf;
+ int clen, len;
+ u_char xferphase;
+{
+ u_char phase, csr, asr;
+ int wait;
+
+ /*
+ * set the sbic into non-DMA mode
+ */
+ SET_SBIC_control(sbic->sc_base, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+retry_selection:
+ /*
+ * select the SCSI bus (it's an error if bus isn't free)
+ */
+ if (sbicselectbus(sbic, target, lun, sbic->sc_scsi_dev))
+ return(-1);
+ /*
+ * Wait for a phase change (or error) then let the device sequence
+ * us through the various SCSI phases.
+ */
+ sbic->sc_stat[0] = 0xff;
+ sbic->sc_msg[0] = 0xff;
+ phase = CMD_PHASE;
+
+new_phase:
+ wait = sbic_cmd_wait;
+
+ GET_SBIC_csr (sbic->sc_base, csr);
+ QPRINTF((">CSR:%02x<", csr));
+
+ /*
+ * requesting some new phase
+ */
+ if ((csr != 0xff) && (csr & 0xf0) && (csr & 0x08))
+ phase = csr & PHASE;
+ else if ((csr == SBIC_CSR_DISC) || (csr == SBIC_CSR_DISC_1)
+ || (csr == SBIC_CSR_S_XFERRED)) {
+ sbic->sc_flags &= ~SBICF_SELECTED;
+ GET_SBIC_cmd_phase (sbic->sc_base, phase);
+ if (phase == 0x60)
+ GET_SBIC_tlun (sbic->sc_base, sbic->sc_stat[0]);
+ else
+ return(-1);
+ goto out;
+ } else {
+ sbicerror(sbic, csr);
+ goto abort;
+ }
+
+ switch (phase) {
+ case CMD_PHASE:
+ if (sbicxfstart (sbic, clen, phase, wait))
+ if (sbicxfout (sbic, clen, cbuf, phase))
+ goto abort;
+ phase = xferphase;
+ break;
+ case DATA_IN_PHASE:
+ if (len <= 0)
+ goto abort;
+ wait = sbic_data_wait;
+ if (sbicxfstart(sbic, len, phase, wait))
+ sbicxfin(sbic, len, buf);
+ phase = STATUS_PHASE;
+ break;
+ case MESG_IN_PHASE:
+ if (sbicxfstart(sbic, sizeof(sbic->sc_msg), phase, wait) == 0)
+ break;
+ sbic->sc_msg[0] = 0xff;
+ sbicxfin(sbic, sizeof(sbic->sc_msg), sbic->sc_msg);
+ /*
+ * get the command completion interrupt, or we
+ * can't send a new command (LCI)
+ */
+ SBIC_WAIT(sbic, SBIC_ASR_INT, wait);
+ GET_SBIC_csr(sbic->sc_base, csr);
+#ifdef DEBUG
+ if (sync_debug)
+ printf("msgin done csr 0x%x\n", csr);
+#endif
+ /*
+ * test whether this is a reply to our sync
+ * request
+ */
+ if (sbic->sc_msg[0] == MSG_EXT_MESSAGE && sbic->sc_msg[1] == 3
+ && sbic->sc_msg[2] == MSG_SYNC_REQ) {
+
+ sbic->sc_sync[target].period = sbicfromscsiperiod(sbic,
+ sbic->sc_msg[3]);
+ sbic->sc_sync[target].offset = sbic->sc_msg[4];
+ sbic->sc_sync[target].state = SYNC_DONE;
+ SET_SBIC_syn(sbic->sc_base, SBIC_SYN(sbic->sc_sync[target].offset,
+ sbic->sc_sync[target].period));
+ /* ACK the message */
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_CLR_ACK);
+ WAIT_CIP(sbic->sc_base);
+ phase = CMD_PHASE; /* or whatever */
+ printf("sbic%d: target %d now synchronous,"
+ " period=%dns, offset=%d.\n",
+ sbic->sc_link.adapter_unit, target, sbic->sc_msg[3] * 4,
+ sbic->sc_msg[4]);
+ } else if (sbic->sc_msg[0] == MSG_REJECT
+ && sbic->sc_sync[target].state == SYNC_SENT) {
+#ifdef DEBUG
+ if (sync_debug)
+ printf("target %d rejected sync, going async\n",
+ target);
+#endif
+ sbic->sc_sync[target].period = sbic_min_period;
+ sbic->sc_sync[target].offset = 0;
+ sbic->sc_sync[target].state = SYNC_DONE;
+ SET_SBIC_syn(sbic->sc_base, SBIC_SYN(sbic->sc_sync[target].offset,
+ sbic->sc_sync[target].period));
+ /* ACK the message */
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_CLR_ACK);
+ WAIT_CIP(sbic->sc_base);
+ phase = CMD_PHASE; /* or whatever */
+ } else if (sbic->sc_msg[0] == MSG_REJECT) {
+ /*
+ * we'll never REJECt a REJECT message..
+ */
+ /* ACK the message */
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_CLR_ACK);
+ WAIT_CIP(sbic->sc_base);
+ phase = CMD_PHASE; /* or whatever */
+ } else if (sbic->sc_msg[0] == MSG_CMD_COMPLETE
+ || sbic->sc_msg[0] == 0xff) {
+ /* !! KLUDGE ALERT !! quite a few drives don't seem to
+ * really like the current way of sending the
+ * sync-handshake together with the ident-message, and
+ * they react by sending command-complete and
+ * disconnecting right after returning the valid sync
+ * handshake. So, all I can do is reselect the drive,
+ * and hope it won't disconnect again. I don't think
+ * this is valid behavior, but I can't help fixing a
+ * problem that apparently exists.
+ *
+ * Note: we should not get here on `normal' command
+ * completion, as that condition is handled by the
+ * high-level sel&xfer resume command used to walk
+ * thru status/cc-phase.
+ */
+
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("GOT CMD-COMPLETE! %d acting weird.."
+ " waiting for disconnect...\n", target);
+#endif
+ /* ACK the message */
+ SET_SBIC_cmd (sbic->sc_base, SBIC_CMD_CLR_ACK);
+ WAIT_CIP(sbic->sc_base);
+
+ /* wait for disconnect */
+ while (csr != SBIC_CSR_DISC &&
+ csr != SBIC_CSR_DISC_1) {
+ DELAY(1);
+ GET_SBIC_csr(sbic->sc_base, csr);
+ }
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("ok.\nRetrying selection.\n");
+#endif
+ sbic->sc_flags &= ~SBICF_SELECTED;
+ goto retry_selection;
+ } else {
+#ifdef DEBUG
+ if (sbic_debug || sync_debug)
+ printf ("Rejecting message 0x%02x\n",
+ sbic->sc_msg[0]);
+#endif
+ /* prepare to reject the message, NACK */
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_SET_ATN);
+ WAIT_CIP(sbic->sc_base);
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_CLR_ACK);
+ WAIT_CIP(sbic->sc_base);
+ phase = MESG_OUT_PHASE;
+ }
+ break;
+
+ case MESG_OUT_PHASE:
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("sending REJECT msg to last msg.\n");
+#endif
+ /*
+ * should only get here on reject,
+ * since it's always US that
+ * initiate a sync transfer
+ */
+ SEND_BYTE(sbic, MSG_REJECT);
+ phase = STATUS_PHASE;
+ break;
+ case DATA_OUT_PHASE:
+ if (len <= 0)
+ goto abort;
+ wait = sbic_data_wait;
+ if (sbicxfstart(sbic, len, phase, wait))
+ if (sbicxfout (sbic, len, buf, phase))
+ goto abort;
+ phase = STATUS_PHASE;
+ break;
+ case STATUS_PHASE:
+ /*
+ * the sbic does the status/cmd-complete reading ok,
+ * so do this with its hi-level commands.
+ */
+ SBIC_TC_PUT(sbic->sc_base, 0);
+ SET_SBIC_cmd_phase(sbic->sc_base, 0x46);
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_SEL_ATN_XFER);
+ phase = BUS_FREE_PHASE;
+ break;
+ case BUS_FREE_PHASE:
+ goto out;
+ default:
+ printf("sbic%d: unexpected phase %d in icmd from %d\n",
+ sbic->sc_link.adapter_unit, phase, target);
+ goto abort;
+ }
+
+ /*
+ * make sure the last command was taken,
+ * ie. we're not hunting after an ignored command..
+ */
+ GET_SBIC_asr(sbic->sc_base, asr);
+ if (asr & SBIC_ASR_LCI)
+ goto abort;
+
+ /* tapes may take a loooong time.. */
+ while (asr & SBIC_ASR_BSY) {
+ DELAY(1);
+ GET_SBIC_asr(sbic->sc_base, asr);
+ }
+
+ /*
+ * wait for last command to complete
+ */
+ SBIC_WAIT (sbic, SBIC_ASR_INT, wait);
+
+ /*
+ * do it again
+ */
+ goto new_phase;
+abort:
+ sbicabort(sbic, "icmd");
+out:
+ QPRINTF(("=STS:%02x=", sbic->sc_stat[0]));
+ return(sbic->sc_stat[0]);
+}
+
+/*
+ * Finish SCSI xfer command: After the completion interrupt from
+ * a read/write operation, sequence through the final phases in
+ * programmed i/o. This routine is a lot like sbicicmd except we
+ * skip (and don't allow) the select, cmd out and data in/out phases.
+ */
+static void
+sbicxfdone(sbic, target)
+ struct sbic_softc *sbic;
+ int target;
+{
+ u_char phase, csr;
+ int s;
+
+ QPRINTF(("{"));
+ s = splbio();
+
+ /*
+ * have the sbic complete on its own
+ */
+ SBIC_TC_PUT(sbic->sc_base, 0);
+ SET_SBIC_cmd_phase(sbic->sc_base, 0x46);
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_SEL_ATN_XFER);
+
+ do {
+ SBIC_WAIT (sbic, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (sbic->sc_base, csr);
+ QPRINTF(("%02x:", csr));
+ } while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
+ && (csr != SBIC_CSR_S_XFERRED));
+
+ sbic->sc_flags &= ~SBICF_SELECTED;
+
+ GET_SBIC_cmd_phase (sbic->sc_base, phase);
+ QPRINTF(("}%02x", phase));
+ if (phase == 0x60)
+ GET_SBIC_tlun(sbic->sc_base, sbic->sc_stat[0]);
+ else
+ sbicerror(sbic, csr);
+
+ QPRINTF(("=STS:%02x=\n", sbic->sc_stat[0]));
+ splx(s);
+}
+
+static int
+sbicgo(sbic, xs)
+ struct sbic_softc *sbic;
+ struct scsi_xfer *xs;
+{
+ int i, target, len, wait;
+ int seg, thiskv, thisphys, nextphys, datalen;
+ int bytes_this_seg, bytes_this_page;
+ u_char phase, csr, asr, cmd;
+ struct dma_chain *sg;
+
+ target = xs->sc_link->target;
+ datalen = xs->datalen;
+ thiskv = (int)xs->data;
+
+ /*
+ * set the sbic into DMA mode
+ */
+ SET_SBIC_control(sbic->sc_base, SBIC_CTL_EDI | SBIC_CTL_IDI |
+ SBIC_MACHINE_DMA_MODE);
+
+ /*
+ * select the SCSI bus (it's an error if bus isn't free)
+ */
+ if (sbicselectbus(sbic, target, xs->sc_link->lun,
+ sbic->sc_scsi_dev)) {
+ sbic_dmastop(sbic);
+ SBIC_DISABLE_INT(sbic->sc_base);
+ return(-1);
+ }
+
+ /*
+ * Wait for a phase change (or error) then let the device
+ * sequence us through command phase (we may have to take
+ * a msg in/out before doing the command). If the disk has
+ * to do a seek, it may be a long time until we get a change
+ * to data phase so, in the absense of an explicit phase
+ * change, we assume data phase will be coming up and tell
+ * the SPC to start a transfer whenever it does. We'll get
+ * a service required interrupt later if this assumption is
+ * wrong. Otherwise we'll get a service required int when
+ * the transfer changes to status phase.
+ */
+ phase = CMD_PHASE;
+
+new_phase:
+ wait = sbic_cmd_wait;
+ switch (phase) {
+ case CMD_PHASE:
+ if (sbicxfstart(sbic, xs->cmdlen, phase, wait))
+ if (sbicxfout(sbic, xs->cmdlen, xs->cmd, phase))
+ goto abort;
+ break;
+ case MESG_IN_PHASE:
+ if (sbicxfstart(sbic, sizeof(sbic->sc_msg), phase, wait) == 0)
+ break;
+
+ sbicxfin(sbic, sizeof(sbic->sc_msg), sbic->sc_msg);
+ /*
+ * prepare to reject any mesgin,
+ * no matter what it might be..
+ */
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_SET_ATN);
+ WAIT_CIP(sbic->sc_base);
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_CLR_ACK);
+ phase = MESG_OUT_PHASE;
+ break;
+ case MESG_OUT_PHASE:
+ SEND_BYTE(sbic, MSG_REJECT);
+ phase = STATUS_PHASE;
+ break;
+ case DATA_IN_PHASE:
+ case DATA_OUT_PHASE:
+ goto out;
+ /*
+ * status phase can happen, if the issued read/write command
+ * is illegal (for example, reading after EOT on tape) and the
+ * device doesn't even go to data in/out phase. So handle this
+ * here normally, instead of going thru abort-handling.
+ */
+ case STATUS_PHASE:
+ sbic_dmastop(sbic);
+ SBIC_DISABLE_INT(sbic->sc_base);
+ sbicxfdone(sbic, target);
+ sbic->sc_flags &= ~(SBICF_INDMA | SBICF_BBUF);
+ sbic_scsidone(sbic, sbic->sc_stat[0]);
+ return(0);
+ default:
+ printf("sbic%d: unexpected phase %d in go from %d\n", phase,
+ sbic->sc_link.adapter_unit, target);
+ goto abort;
+ }
+
+ /*
+ * make sure the last command was taken,
+ * ie. we're not hunting after an ignored command..
+ */
+ GET_SBIC_asr(sbic->sc_base, asr);
+ if (asr & SBIC_ASR_LCI)
+ goto abort;
+
+ /*
+ * tapes may take a loooong time..
+ */
+ while (asr & SBIC_ASR_BSY) {
+ DELAY(1);
+ GET_SBIC_asr(sbic->sc_base, asr);
+ }
+
+ if (wait <= 0)
+ goto abort;
+
+ /*
+ * wait for last command to complete
+ */
+ SBIC_WAIT(sbic, SBIC_ASR_INT, wait);
+
+ GET_SBIC_csr(sbic->sc_base, csr);
+ QPRINTF((">CSR:%02x<", csr));
+
+ /*
+ * requesting some new phase
+ */
+ if ((csr != 0xff) && (csr & 0xf0) && (csr & 0x08))
+ phase = csr & PHASE;
+ else {
+ sbicerror(sbic, csr);
+ goto abort;
+ }
+ /*
+ * start again with for new phase
+ */
+ goto new_phase;
+out:
+ /*
+ * Build the DMA chain
+ */
+ thisphys = KVTOPHYS(thiskv);
+ seg = 0;
+ sg = sbic->sc_chain;
+ while((datalen) && (seg < SBIC_NSEG)) {
+ bytes_this_seg = 0;
+ sg->dc_addr = thisphys;
+ nextphys = thisphys;
+ while((datalen) && (thisphys == nextphys)) {
+ nextphys = (thisphys & ~PAGE_MASK) + PAGE_SIZE;
+ bytes_this_page = nextphys - thisphys;
+ bytes_this_page = min(bytes_this_page, datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+ thiskv = (thiskv & ~PAGE_MASK) + PAGE_SIZE;
+ if (datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ sg->dc_count = bytes_this_seg;
+ sg++;
+ seg++;
+ }
+ sbic->sc_cur = sbic->sc_chain;
+ sbic->sc_last = --sg;
+
+#ifdef DEBUG
+ if (sbic_dma_debug) {
+ for (sg = sbic->sc_chain; sg <= sbic->sc_last; sg++)
+ printf("\n %d: %d@%x", sg-sbic->sc_chain,
+ sg->dc_count, sg->dc_addr);
+ printf("Total: %d ", xs->datalen);
+ }
+#endif
+
+ sbic_dmastart(sbic);
+ SBIC_ENABLE_INT(sbic->sc_base);
+ SBIC_TC_PUT(sbic->sc_base, (unsigned)sbic->sc_cur->dc_count);
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_XFER_INFO);
+
+ return(0);
+
+abort:
+ sbicabort(sbic, "go");
+ sbic_dmastop(sbic);
+ SBIC_DISABLE_INT(sbic->sc_base);
+ return(-1);
+}
+
+
+void
+sbicintr(unit)
+ int unit;
+{
+ struct sbic_softc *sbic = sbicdata[unit];
+ u_char asr, csr;
+ int i;
+
+ /*
+ * pending interrupt?
+ */
+ GET_SBIC_asr (sbic->sc_base, asr);
+ if ((asr & SBIC_ASR_INT) == 0)
+ return;
+ GET_SBIC_csr(sbic->sc_base, csr);
+ QPRINTF(("[0x%x]", csr));
+
+ if (csr == (SBIC_CSR_XFERRED|STATUS_PHASE)
+ || csr == (SBIC_CSR_MIS|STATUS_PHASE)
+ || csr == (SBIC_CSR_MIS_1|STATUS_PHASE)
+ || csr == (SBIC_CSR_MIS_2|STATUS_PHASE)) {
+ /*
+ * this should be the normal i/o completion case.
+ * get the status & cmd complete msg then let the
+ * device driver look at what happened.
+ */
+ sbic_dmastop(sbic);
+ SBIC_DISABLE_INT(sbic->sc_base);
+ sbicxfdone(sbic, sbic->sc_xs->sc_link->target);
+ /*
+ * check for overlapping cache line, flush if so
+ */
+ sbic->sc_flags &= ~(SBICF_INDMA | SBICF_BBUF | SBICF_DCFLUSH);
+ sbic_scsidone(sbic, sbic->sc_stat[0]);
+ } else if (csr == (SBIC_CSR_XFERRED|DATA_OUT_PHASE)
+ || csr == (SBIC_CSR_XFERRED|DATA_IN_PHASE)
+ || csr == (SBIC_CSR_MIS|DATA_OUT_PHASE)
+ || csr == (SBIC_CSR_MIS|DATA_IN_PHASE)
+ || csr == (SBIC_CSR_MIS_1|DATA_OUT_PHASE)
+ || csr == (SBIC_CSR_MIS_1|DATA_IN_PHASE)
+ || csr == (SBIC_CSR_MIS_2|DATA_OUT_PHASE)
+ || csr == (SBIC_CSR_MIS_2|DATA_IN_PHASE)) {
+ /*
+ * do scatter-gather dma
+ * hacking the controller chip, ouch..
+ */
+ /*
+ * set next dma addr and dec count
+ */
+ sbic_dmastop(sbic);
+ ++sbic->sc_cur; /* advance to next segment */
+ sbic_dmastart(sbic);
+ SBIC_TC_PUT(sbic->sc_base, (unsigned)sbic->sc_cur->dc_count);
+ SET_SBIC_cmd(sbic->sc_base, SBIC_CMD_XFER_INFO);
+ } else {
+ /*
+ * Something unexpected happened -- deal with it.
+ */
+ sbic_dmastop(sbic);
+ sbicerror(sbic, csr);
+ sbicabort(sbic, "intr");
+ if (sbic->sc_flags & SBICF_INDMA) {
+ /*
+ * check for overlapping cache line, flush if so
+ */
+ sbic->sc_flags &=
+ ~(SBICF_INDMA | SBICF_BBUF | SBICF_DCFLUSH);
+ SBIC_DISABLE_INT(sbic->sc_base);
+ sbic_scsidone(sbic, -1);
+ }
+ }
+ return;
+}
+
+static int
+sbictoscsiperiod(sbic, a)
+ struct sbic_softc *sbic;
+ int a;
+{
+ unsigned int fs;
+
+ /*
+ * cycle = DIV / (2*CLK)
+ * DIV = FS+2
+ * best we can do is 200ns at 20Mhz, 2 cycles
+ */
+
+ GET_SBIC_myid(sbic->sc_base,fs);
+ fs = (fs >>6) + 2; /* DIV */
+ fs = (fs * 10000) / (sbic->sc_clkfreq<<1); /* Cycle, in ns */
+ if (a < 2) a = 8; /* map to Cycles */
+ return ((fs*a)>>2); /* in 4 ns units */
+}
+
+static int
+sbicfromscsiperiod(sbic, p)
+ struct sbic_softc *sbic;
+ int p;
+{
+ register unsigned int fs, ret;
+
+ /* Just the inverse of the above */
+
+ GET_SBIC_myid(sbic->sc_base,fs);
+ fs = (fs >>6) + 2; /* DIV */
+ fs = (fs * 10000) / (sbic->sc_clkfreq<<1); /* Cycle, in ns */
+
+ ret = p << 2; /* in ns units */
+ ret = ret / fs; /* in Cycles */
+ if (ret < sbic_min_period)
+ return(sbic_min_period);
+
+ /* verify rounding */
+ if (sbictoscsiperiod(sbic, ret) < p)
+ ret++;
+ return (ret >= 8) ? 0 : ret;
+}
+
+#define DMA_SMSK (IO_DMA + 0x14)
+#define DMA_MODE (IO_DMA + 0x16)
+#define DMA_FFC (IO_DMA + 0x18)
+
+#define DMA37SM_CLEAR 0x00
+#define DMA37SM_SET 0x04
+
+#define DMA_CHN(c) (IO_DMA + ((c) << 2))
+
+static void
+sbic_dmastart(sbic)
+ struct sbic_softc *sbic;
+{
+ int waport;
+
+#ifdef CYRIX_5X86
+ /* Cyrix 5x86 */
+ asm("wbinvd"); /* wbinvd (WB cache flush) */
+#endif
+
+#ifdef SC98BUSMASTER
+ if (sc98busmaster) {
+ sbic_write_reg(sbic->sc_base, 0x73, 0x32);
+ sbic_write_reg(sbic->sc_base, 0x74, 0x23);
+ }
+#endif
+
+ if (!dma_init_flag) {
+ dma_init_flag = 1;
+ outb(0x439, (inb(0x439) & 0xfb)); /* DMA Accsess Control over 1MB */
+ outb(0x29, (0x0c | 0)); /* Bank Mode Reg. 16M mode */
+ outb(0x29, (0x0c | 1)); /* Bank Mode Reg. 16M mode */
+ outb(0x29, (0x0c | 2)); /* Bank Mode Reg. 16M mode */
+ outb(0x29, (0x0c | 3)); /* Bank Mode Reg. 16M mode */
+ outb(0x11, 0x50); /* PC98 must be 0x40 */
+ }
+
+ /* mask channel */
+ outb(DMA_SMSK, sbic->sc_dma | DMA37SM_SET);
+
+ /* set dma channel mode, and reset address ff */
+ if (sbic->sc_xs->flags & SCSI_DATA_IN)
+ outb(DMA_MODE, sbic->sc_dma | DMA37MD_SINGLE | DMA37MD_WRITE);
+ else
+ outb(DMA_MODE, sbic->sc_dma | DMA37MD_SINGLE | DMA37MD_READ);
+ outb(DMA_FFC, 0);
+
+ /* send start address */
+ waport = DMA_CHN(sbic->sc_dma);
+ outb(waport, sbic->sc_cur->dc_addr);
+ outb(waport, sbic->sc_cur->dc_addr>>8);
+ outb(dmapageport[sbic->sc_dma], sbic->sc_cur->dc_addr>>16);
+
+ /* send count */
+ outb(waport + 2, sbic->sc_cur->dc_count - 1);
+ outb(waport + 2, (sbic->sc_cur->dc_count-1)>>8);
+
+ /* unmask channel */
+ outb(DMA_SMSK, sbic->sc_dma | DMA37SM_CLEAR);
+
+ /* SCSI DMA enable */
+ outb(sbic->sc_base + 4, 1);
+}
+
+static void
+sbic_dmastop(sbic)
+ struct sbic_softc *sbic;
+{
+#if defined(CYRIX_486DLC) || defined(IBM_486SLC)
+ if (sbic->sc_xs->flags & SCSI_DATA_IN) {
+ /* cache flush only after reading 92/12/9 by A.Kojima */
+ asm(" .byte 0x0f,0x08"); /* invd (cache flush) */
+ }
+#endif
+ /* mask channel */
+ outb(DMA_SMSK, DMA37SM_SET | sbic->sc_dma);
+
+ /* SCSI DMA disable */
+ outb(sbic->sc_base + 4, 2);
+
+#ifdef SC98BUSMASTER
+ if (sc98busmaster) {
+ sbic_write_reg(sbic->sc_base, 0x73, 0x43);
+ sbic_write_reg(sbic->sc_base, 0x74, 0x34);
+ }
+#endif
+}
diff --git a/sys/pc98/pc98/sbicreg.h b/sys/pc98/pc98/sbicreg.h
new file mode 100644
index 0000000..e71a535
--- /dev/null
+++ b/sys/pc98/pc98/sbicreg.h
@@ -0,0 +1,429 @@
+/* $NetBSD: sbicreg.h,v 1.2 1994/10/26 02:04:40 cgd Exp $ */
+
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * 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.
+ *
+ * @(#)scsireg.h 7.3 (Berkeley) 2/5/91
+ */
+/*
+ * Ported to PC-9801 by Yoshio Kimura, 1994
+ * last update 09/24/1994
+ */
+
+/*
+ * AMD AM33C93A SCSI interface hardware description.
+ *
+ * Using parts of the Mach scsi driver for the 33C93
+ */
+
+#define SBIC_myid 0
+#define SBIC_cdbsize 0
+#define SBIC_control 1
+#define SBIC_timeo 2
+#define SBIC_cdb1 3
+#define SBIC_tsecs 3
+#define SBIC_cdb2 4
+#define SBIC_theads 4
+#define SBIC_cdb3 5
+#define SBIC_tcyl_hi 5
+#define SBIC_cdb4 6
+#define SBIC_tcyl_lo 6
+#define SBIC_cdb5 7
+#define SBIC_addr_hi 7
+#define SBIC_cdb6 8
+#define SBIC_addr_2 8
+#define SBIC_cdb7 9
+#define SBIC_addr_3 9
+#define SBIC_cdb8 10
+#define SBIC_addr_lo 10
+#define SBIC_cdb9 11
+#define SBIC_secno 11
+#define SBIC_cdb10 12
+#define SBIC_headno 12
+#define SBIC_cdb11 13
+#define SBIC_cylno_hi 13
+#define SBIC_cdb12 14
+#define SBIC_cylno_lo 14
+#define SBIC_tlun 15
+#define SBIC_cmd_phase 16
+#define SBIC_syn 17
+#define SBIC_count_hi 18
+#define SBIC_count_med 19
+#define SBIC_count_lo 20
+#define SBIC_selid 21
+#define SBIC_rselid 22
+#define SBIC_csr 23
+#define SBIC_cmd 24
+#define SBIC_data 25
+#define SBIC_mem_bank 48
+#define SBIC_mem_win 49
+#define SBIC_reserved1 50
+#define SBIC_reset_int 51
+#define SBIC_reserved2 52
+#define SBIC_reserved3 53
+/* sbic_asr is addressed directly */
+
+/*
+ * Register defines
+ */
+
+/*
+ * Auxiliary Status Register
+ */
+
+#define SBIC_ASR_INT 0x80 /* Interrupt pending */
+#define SBIC_ASR_LCI 0x40 /* Last command ignored */
+#define SBIC_ASR_BSY 0x20 /* Busy, only cmd/data/asr readable */
+#define SBIC_ASR_CIP 0x10 /* Busy, cmd unavail also */
+#define SBIC_ASR_xxx 0x0c
+#define SBIC_ASR_PE 0x02 /* Parity error (even) */
+#define SBIC_ASR_DBR 0x01 /* Data Buffer Ready */
+
+/*
+ * My ID register, and/or CDB Size
+ */
+
+#define SBIC_ID_FS_8_10 0x00 /* Input clock is 8-10 Mhz */
+ /* 11 Mhz is invalid */
+#define SBIC_ID_FS_12_15 0x40 /* Input clock is 12-15 Mhz */
+#define SBIC_ID_FS_16_20 0x80 /* Input clock is 16-20 Mhz */
+#define SBIC_ID_EHP 0x10 /* Enable host parity */
+#define SBIC_ID_EAF 0x08 /* Enable Advanced Features */
+#define SBIC_ID_MASK 0x07
+#define SBIC_ID_CBDSIZE_MASK 0x0f /* if unk SCSI cmd group */
+
+/*
+ * Control register
+ */
+
+#define SBIC_CTL_DMA 0x80 /* Single byte dma */
+#define SBIC_CTL_DBA_DMA 0x40 /* direct buffer acces (bus master)*/
+#define SBIC_CTL_BURST_DMA 0x20 /* continuous mode (8237) */
+#define SBIC_CTL_NO_DMA 0x00 /* Programmed I/O */
+#define SBIC_CTL_HHP 0x10 /* Halt on host parity error */
+#define SBIC_CTL_EDI 0x08 /* Ending disconnect interrupt */
+#define SBIC_CTL_IDI 0x04 /* Intermediate disconnect interrupt*/
+#define SBIC_CTL_HA 0x02 /* Halt on ATN */
+#define SBIC_CTL_HSP 0x01 /* Halt on SCSI parity error */
+
+/*
+ * Timeout period register
+ * [val in msecs, input clk in 0.1 Mhz]
+ */
+
+#define SBIC_TIMEOUT(val,clk) ((((val) * (clk)) / 800) + 1)
+
+/*
+ * CDBn registers, note that
+ * cdb11 is used for status byte in target mode (send-status-and-cc)
+ * cdb12 sez if linked command complete, and w/flag if so
+ */
+
+/*
+ * Target LUN register
+ * [holds target status when select-and-xfer]
+ */
+
+#define SBIC_TLUN_VALID 0x80 /* did we receive an Identify msg */
+#define SBIC_TLUN_DOK 0x40 /* Disconnect OK */
+#define SBIC_TLUN_xxx 0x38
+#define SBIC_TLUN_MASK 0x07
+
+/*
+ * Command Phase register
+ */
+
+#define SBIC_CPH_MASK 0x7f /* values/restarts are cmd specific */
+#define SBIC_CPH(p) ((p) & SBIC_CPH_MASK)
+
+/*
+ * FIFO register
+ */
+
+#define SBIC_FIFO_DEEP 12
+
+/*
+ * maximum possible size in TC registers. Since this is 24 bit, it's easy
+ */
+#define SBIC_TC_MAX ((1 << 24) - 1)
+
+/*
+ * Synchronous xfer register
+ */
+
+#define SBIC_SYN_OFF_MASK 0x0f
+#define SBIC_SYN_MAX_OFFSET SBIC_FIFO_DEEP
+#define SBIC_SYN_PER_MASK 0x70
+#define SBIC_SYN_MIN_PERIOD 2 /* upto 8, encoded as 0 */
+
+#define SBIC_SYN(o,p) \
+ (((o) & SBIC_SYN_OFF_MASK) | (((p) << 4) & SBIC_SYN_PER_MASK))
+
+/*
+ * Transfer count register
+ * optimal access macros depend on addressing
+ */
+
+/*
+ * Destination ID (selid) register
+ */
+
+#define SBIC_SID_SCC 0x80 /* Select command chaining (tgt) */
+#define SBIC_SID_DPD 0x40 /* Data phase direction (inittor) */
+#define SBIC_SID_FROM_SCSI 0x40
+#define SBIC_SID_TO_SCSI 0x00
+#define SBIC_SID_xxx 0x38
+#define SBIC_SID_IDMASK 0x07
+
+/*
+ * Source ID (rselid) register
+ */
+
+#define SBIC_RID_ER 0x80 /* Enable reselection */
+#define SBIC_RID_ES 0x40 /* Enable selection */
+#define SBIC_RID_DSP 0x20 /* Disable select parity */
+#define SBIC_RID_SIV 0x08 /* Source ID valid */
+#define SBIC_RID_MASK 0x07
+
+/*
+ * Status register
+ */
+
+#define SBIC_CSR_CAUSE 0xf0
+#define SBIC_CSR_RESET 0x00 /* chip was reset */
+#define SBIC_CSR_CMD_DONE 0x10 /* cmd completed */
+#define SBIC_CSR_CMD_STOPPED 0x20 /* interrupted or abrted*/
+#define SBIC_CSR_CMD_ERR 0x40 /* end with error */
+#define SBIC_CSR_BUS_SERVICE 0x80 /* REQ pending on the bus */
+
+
+#define SBIC_CSR_QUALIFIER 0x0f
+/* Reset State Interrupts */
+#define SBIC_CSR_RESET 0x00 /* reset w/advanced features*/
+#define SBIC_CSR_RESET_AM 0x01 /* reset w/advanced features*/
+/* Successful Completion Interrupts */
+#define SBIC_CSR_TARGET 0x10 /* reselect complete */
+#define SBIC_CSR_INITIATOR 0x11 /* select complete */
+#define SBIC_CSR_WO_ATN 0x13 /* tgt mode completion */
+#define SBIC_CSR_W_ATN 0x14 /* ditto */
+#define SBIC_CSR_XLATED 0x15 /* translate address cmd */
+#define SBIC_CSR_S_XFERRED 0x16 /* initiator mode completion*/
+#define SBIC_CSR_XFERRED 0x18 /* phase in low bits */
+/* Paused or Aborted Interrupts */
+#define SBIC_CSR_MSGIN_W_ACK 0x20 /* (I) msgin, ACK asserted*/
+#define SBIC_CSR_SDP 0x21 /* (I) SDP msg received */
+#define SBIC_CSR_SEL_ABRT 0x22 /* sel/resel aborted */
+#define SBIC_CSR_XFR_PAUSED 0x23 /* (T) no ATN */
+#define SBIC_CSR_XFR_PAUSED_ATN 0x24 /* (T) ATN is asserted */
+#define SBIC_CSR_RSLT_AM 0x27 /* (I) lost selection (AM) */
+#define SBIC_CSR_MIS 0x28 /* (I) xfer aborted, ph mis */
+/* Terminated Interrupts */
+#define SBIC_CSR_CMD_INVALID 0x40
+#define SBIC_CSR_DISC 0x41 /* (I) tgt disconnected */
+#define SBIC_CSR_SEL_TIMEO 0x42
+#define SBIC_CSR_PE 0x43 /* parity error */
+#define SBIC_CSR_PE_ATN 0x44 /* ditto, ATN is asserted */
+#define SBIC_CSR_XLATE_TOOBIG 0x45
+#define SBIC_CSR_RSLT_NOAM 0x46 /* (I) lost sel, no AM mode */
+#define SBIC_CSR_BAD_STATUS 0x47 /* status byte was nok */
+#define SBIC_CSR_MIS_1 0x48 /* ph mis, see low bits */
+/* Service Required Interrupts */
+#define SBIC_CSR_RSLT_NI 0x80 /* reselected, no ify msg */
+#define SBIC_CSR_RSLT_IFY 0x81 /* ditto, AM mode, got ify */
+#define SBIC_CSR_SLT 0x82 /* selected, no ATN */
+#define SBIC_CSR_SLT_ATN 0x83 /* selected with ATN */
+#define SBIC_CSR_ATN 0x84 /* (T) ATN asserted */
+#define SBIC_CSR_DISC_1 0x85 /* (I) bus is free */
+#define SBIC_CSR_UNK_GROUP 0x87 /* strange CDB1 */
+#define SBIC_CSR_MIS_2 0x88 /* (I) ph mis, see low bits */
+
+#define SBIC_PHASE(csr) SCSI_PHASE(csr)
+
+/*
+ * Command register (command codes)
+ */
+
+#define SBIC_CMD_SBT 0x80 /* Single byte xfer qualifier */
+#define SBIC_CMD_MASK 0x7f
+
+ /* Miscellaneous */
+#define SBIC_CMD_RESET 0x00 /* (DTI) lev I */
+#define SBIC_CMD_ABORT 0x01 /* (DTI) lev I */
+#define SBIC_CMD_DISC 0x04 /* ( TI) lev I */
+#define SBIC_CMD_SSCC 0x0d /* ( TI) lev I */
+#define SBIC_CMD_SET_IDI 0x0f /* (DTI) lev I */
+#define SBIC_CMD_XLATE 0x18 /* (DT ) lev II */
+
+ /* Initiator state */
+#define SBIC_CMD_SET_ATN 0x02 /* ( I) lev I */
+#define SBIC_CMD_CLR_ACK 0x03 /* ( I) lev I */
+#define SBIC_CMD_XFER_PAD 0x19 /* ( I) lev II */
+#define SBIC_CMD_XFER_INFO 0x20 /* ( I) lev II */
+
+ /* Target state */
+#define SBIC_CMD_SND_DISC 0x0e /* ( T ) lev II */
+#define SBIC_CMD_RCV_CMD 0x10 /* ( T ) lev II */
+#define SBIC_CMD_RCV_DATA 0x11 /* ( T ) lev II */
+#define SBIC_CMD_RCV_MSG_OUT 0x12 /* ( T ) lev II */
+#define SBIC_CMD_RCV 0x13 /* ( T ) lev II */
+#define SBIC_CMD_SND_STATUS 0x14 /* ( T ) lev II */
+#define SBIC_CMD_SND_DATA 0x15 /* ( T ) lev II */
+#define SBIC_CMD_SND_MSG_IN 0x16 /* ( T ) lev II */
+#define SBIC_CMD_SND 0x17 /* ( T ) lev II */
+
+ /* Disconnected state */
+#define SBIC_CMD_RESELECT 0x05 /* (D ) lev II */
+#define SBIC_CMD_SEL_ATN 0x06 /* (D ) lev II */
+#define SBIC_CMD_SEL 0x07 /* (D ) lev II */
+#define SBIC_CMD_SEL_ATN_XFER 0x08 /* (D I) lev II */
+#define SBIC_CMD_SEL_XFER 0x09 /* (D I) lev II */
+#define SBIC_CMD_RESELECT_RECV 0x0a /* (DT ) lev II */
+#define SBIC_CMD_RESELECT_SEND 0x0b /* (DT ) lev II */
+#define SBIC_CMD_WAIT_SEL_RECV 0x0c /* (DT ) lev II */
+
+/* approximate, but we won't do SBT on selects */
+#define sbic_isa_select(cmd) (((cmd) > 0x5) && ((cmd) < 0xa))
+
+#define SBIC_MACHINE_DMA_MODE SBIC_CTL_DMA
+
+#define sbic_read_reg(iobase,regno,val) do { \
+ outb(iobase, (regno)); \
+ (val) = inb((iobase) + 2); \
+ } while (0)
+
+#define sbic_write_reg(iobase,regno,val) do { \
+ outb(iobase, (regno)); \
+ outb((iobase) + 2, (val)); \
+ } while (0)
+
+#define SET_SBIC_myid(iobase,val) sbic_write_reg(iobase,SBIC_myid,val)
+#define GET_SBIC_myid(iobase,val) sbic_read_reg(iobase,SBIC_myid,val)
+#define SET_SBIC_cdbsize(iobase,val) sbic_write_reg(iobase,SBIC_cdbsize,val)
+#define GET_SBIC_cdbsize(iobase,val) sbic_read_reg(iobase,SBIC_cdbsize,val)
+#define SET_SBIC_control(iobase,val) sbic_write_reg(iobase,SBIC_control,val)
+#define GET_SBIC_control(iobase,val) sbic_read_reg(iobase,SBIC_control,val)
+#define SET_SBIC_timeo(iobase,val) sbic_write_reg(iobase,SBIC_timeo,val)
+#define GET_SBIC_timeo(iobase,val) sbic_read_reg(iobase,SBIC_timeo,val)
+#define SET_SBIC_cdb1(iobase,val) sbic_write_reg(iobase,SBIC_cdb1,val)
+#define GET_SBIC_cdb1(iobase,val) sbic_read_reg(iobase,SBIC_cdb1,val)
+#define SET_SBIC_cdb2(iobase,val) sbic_write_reg(iobase,SBIC_cdb2,val)
+#define GET_SBIC_cdb2(iobase,val) sbic_read_reg(iobase,SBIC_cdb2,val)
+#define SET_SBIC_cdb3(iobase,val) sbic_write_reg(iobase,SBIC_cdb3,val)
+#define GET_SBIC_cdb3(iobase,val) sbic_read_reg(iobase,SBIC_cdb3,val)
+#define SET_SBIC_cdb4(iobase,val) sbic_write_reg(iobase,SBIC_cdb4,val)
+#define GET_SBIC_cdb4(iobase,val) sbic_read_reg(iobase,SBIC_cdb4,val)
+#define SET_SBIC_cdb5(iobase,val) sbic_write_reg(iobase,SBIC_cdb5,val)
+#define GET_SBIC_cdb5(iobase,val) sbic_read_reg(iobase,SBIC_cdb5,val)
+#define SET_SBIC_cdb6(iobase,val) sbic_write_reg(iobase,SBIC_cdb6,val)
+#define GET_SBIC_cdb6(iobase,val) sbic_read_reg(iobase,SBIC_cdb6,val)
+#define SET_SBIC_cdb7(iobase,val) sbic_write_reg(iobase,SBIC_cdb7,val)
+#define GET_SBIC_cdb7(iobase,val) sbic_read_reg(iobase,SBIC_cdb7,val)
+#define SET_SBIC_cdb8(iobase,val) sbic_write_reg(iobase,SBIC_cdb8,val)
+#define GET_SBIC_cdb8(iobase,val) sbic_read_reg(iobase,SBIC_cdb8,val)
+#define SET_SBIC_cdb9(iobase,val) sbic_write_reg(iobase,SBIC_cdb9,val)
+#define GET_SBIC_cdb9(iobase,val) sbic_read_reg(iobase,SBIC_cdb9,val)
+#define SET_SBIC_cdb10(iobase,val) sbic_write_reg(iobase,SBIC_cdb10,val)
+#define GET_SBIC_cdb10(iobase,val) sbic_read_reg(iobase,SBIC_cdb10,val)
+#define SET_SBIC_cdb11(iobase,val) sbic_write_reg(iobase,SBIC_cdb11,val)
+#define GET_SBIC_cdb11(iobase,val) sbic_read_reg(iobase,SBIC_cdb11,val)
+#define SET_SBIC_cdb12(iobase,val) sbic_write_reg(iobase,SBIC_cdb12,val)
+#define GET_SBIC_cdb12(iobase,val) sbic_read_reg(iobase,SBIC_cdb12,val)
+#define SET_SBIC_tlun(iobase,val) sbic_write_reg(iobase,SBIC_tlun,val)
+#define GET_SBIC_tlun(iobase,val) sbic_read_reg(iobase,SBIC_tlun,val)
+#define SET_SBIC_cmd_phase(iobase,val) sbic_write_reg(iobase,SBIC_cmd_phase,val)
+#define GET_SBIC_cmd_phase(iobase,val) sbic_read_reg(iobase,SBIC_cmd_phase,val)
+#define SET_SBIC_syn(iobase,val) sbic_write_reg(iobase,SBIC_syn,val)
+#define GET_SBIC_syn(iobase,val) sbic_read_reg(iobase,SBIC_syn,val)
+#define SET_SBIC_count_hi(iobase,val) sbic_write_reg(iobase,SBIC_count_hi,val)
+#define GET_SBIC_count_hi(iobase,val) sbic_read_reg(iobase,SBIC_count_hi,val)
+#define SET_SBIC_count_med(iobase,val) sbic_write_reg(iobase,SBIC_count_med,val)
+#define GET_SBIC_count_med(iobase,val) sbic_read_reg(iobase,SBIC_count_med,val)
+#define SET_SBIC_count_lo(iobase,val) sbic_write_reg(iobase,SBIC_count_lo,val)
+#define GET_SBIC_count_lo(iobase,val) sbic_read_reg(iobase,SBIC_count_lo,val)
+#define SET_SBIC_selid(iobase,val) sbic_write_reg(iobase,SBIC_selid,val)
+#define GET_SBIC_selid(iobase,val) sbic_read_reg(iobase,SBIC_selid,val)
+#define SET_SBIC_rselid(iobase,val) sbic_write_reg(iobase,SBIC_rselid,val)
+#define GET_SBIC_rselid(iobase,val) sbic_read_reg(iobase,SBIC_rselid,val)
+#define SET_SBIC_csr(iobase,val) sbic_write_reg(iobase,SBIC_csr,val)
+#define GET_SBIC_csr(iobase,val) sbic_read_reg(iobase,SBIC_csr,val)
+#define SET_SBIC_cmd(iobase,val) sbic_write_reg(iobase,SBIC_cmd,val)
+#define GET_SBIC_cmd(iobase,val) sbic_read_reg(iobase,SBIC_cmd,val)
+#define SET_SBIC_data(iobase,val) sbic_write_reg(iobase,SBIC_data,val)
+#define GET_SBIC_data(iobase,val) sbic_read_reg(iobase,SBIC_data,val)
+#define SET_SBIC_mem_bank(iobase,val) sbic_write_reg(iobase,SBIC_mem_bank,val)
+#define GET_SBIC_mem_bank(iobase,val) sbic_read_reg(iobase,SBIC_mem_bank,val)
+#define GET_SBIC_mem_win(iobase,val) sbic_read_reg(iobase,SBIC_mem_win,val)
+#define GET_SBIC_reset_int(iobase,val) sbic_read_reg(iobase,SBIC_reset_int,val)
+
+#define SBIC_TC_PUT(iobase,val) do { \
+ sbic_write_reg(iobase,SBIC_count_hi,((val)>>16)); \
+ outb((iobase) + 2, ((val)>>8)); \
+ outb((iobase) + 2, (val)); \
+} while (0)
+#define SBIC_TC_GET(iobase,val) do { \
+ sbic_read_reg(iobase,SBIC_count_hi,(val)); \
+ (val) = ((val)<<8) | inb((iobase) + 2); \
+ (val) = ((val)<<8) | inb((iobase) + 2); \
+} while (0)
+
+#define SBIC_LOAD_COMMAND(iobase,cmd,cmdsize) do { \
+ int n=(cmdsize)-1; \
+ char *ptr = (char*)(cmd); \
+ sbic_write_reg(iobase,SBIC_cdb1,*ptr++); \
+ while (n-- > 0) outb((iobase) + 2, *ptr++); \
+} while (0)
+
+#define GET_SBIC_asr(iobase,val) (val) = inb(iobase)
+
+#define WAIT_CIP(iobase) do { \
+ while (inb(iobase) & SBIC_ASR_CIP) \
+ ; \
+} while (0)
+
+/* transmit a byte in programmed I/O mode */
+#define SEND_BYTE(iobase,ch) do { \
+ WAIT_CIP((iobase)->sc_base); \
+ SET_SBIC_cmd((iobase)->sc_base, SBIC_CMD_SBT | SBIC_CMD_XFER_INFO); \
+ SBIC_WAIT(iobase, SBIC_ASR_DBR, 0); \
+ SET_SBIC_data((iobase)->sc_base, ch); \
+ } while (0)
+
+/* receive a byte in programmed I/O mode */
+#define RECV_BYTE(iobase,ch) do { \
+ WAIT_CIP((iobase)->sc_base); \
+ SET_SBIC_cmd((iobase)->sc_base, SBIC_CMD_SBT | SBIC_CMD_XFER_INFO); \
+ SBIC_WAIT(iobase, SBIC_ASR_DBR, 0); \
+ GET_SBIC_data((iobase)->sc_base, ch); \
+ } while (0)
diff --git a/sys/pc98/pc98/sbicvar.h b/sys/pc98/pc98/sbicvar.h
new file mode 100644
index 0000000..d4de02c
--- /dev/null
+++ b/sys/pc98/pc98/sbicvar.h
@@ -0,0 +1,157 @@
+/* $NetBSD: sbicvar.h,v 1.5 1995/02/12 19:19:21 chopps Exp $ */
+
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * 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.
+ *
+ * @(#)scsivar.h 7.1 (Berkeley) 5/8/90
+ */
+/*
+ * Ported to PC-9801 by Yoshio Kimura, 1994
+ * last update 09/23/1994
+ */
+#ifndef _SBICVAR_H_
+#define _SBICVAR_H_
+
+/*
+ * The largest single request will be MAXPHYS bytes which will require
+ * at most MAXPHYS/PAGE_SIZE+1 chain elements to describe, i.e. if none of
+ * the buffer pages are physically contiguous (MAXPHYS/PAGE_SIZE) and the
+ * buffer is not page aligned (+1).
+ */
+#define SBIC_NSEG 17
+
+struct dma_chain {
+ int dc_count;
+ int dc_addr;
+};
+
+struct sbic_pending {
+ TAILQ_ENTRY(sbic_pending) link;
+ struct scsi_xfer *xs;
+};
+
+struct sbic_softc {
+#ifndef __FreeBSD__
+ struct device sc_dev;
+ struct pc98dev sc_id;
+ struct intrhand sc_ih;
+#endif
+
+#ifdef __FreeBSD__
+ int unit; /* unit number */
+#endif
+ u_short sc_base;
+ u_short sc_int;
+ u_short sc_dma;
+ int sc_scsi_dev;
+ u_long sc_clkfreq;
+
+ struct target_sync {
+ u_char state;
+ u_char period;
+ u_char offset;
+ } sc_sync[8];
+ struct scsi_link sc_link; /* proto for sub devices */
+ TAILQ_HEAD(,sbic_pending) sc_xslist; /* LIFO */
+ struct sbic_pending sc_xsstore[8][8]; /* one for every unit */
+ struct scsi_xfer *sc_xs; /* transfer from high level code */
+ u_char sc_flags;
+ u_char sc_stat[2];
+ u_char sc_msg[7];
+ struct dma_chain sc_chain[SBIC_NSEG];
+ struct dma_chain *sc_cur;
+ struct dma_chain *sc_last;
+};
+
+/* sc_flags */
+#define SBICF_ALIVE 0x01 /* controller initialized */
+#define SBICF_DCFLUSH 0x02 /* need flush for overlap after dma finishes */
+#define SBICF_SELECTED 0x04 /* bus is in selected state. */
+#define SBICF_BADDMA 0x10 /* controller can only DMA to ztwobus space */
+#define SBICF_BBUF 0x20 /* DMA input needs to be copied from bounce */
+#define SBICF_INTR 0x40 /* SBICF interrupt expected */
+#define SBICF_INDMA 0x80 /* not used yet, DMA I/O in progress */
+
+/* sync states */
+#define SYNC_START 0 /* no sync handshake started */
+#define SYNC_SENT 1 /* we sent sync request, no answer yet */
+#define SYNC_DONE 2 /* target accepted our (or inferior) settings,
+ or it rejected the request and we stay async */
+#ifdef DEBUG
+#define DDB_FOLLOW 0x04
+#define DDB_IO 0x08
+#endif
+
+#define PHASE 0x07 /* mask for psns/pctl phase */
+#define DATA_OUT_PHASE 0x00
+#define DATA_IN_PHASE 0x01
+#define CMD_PHASE 0x02
+#define STATUS_PHASE 0x03
+#define BUS_FREE_PHASE 0x04
+#define ARB_SEL_PHASE 0x05 /* Fuji chip combines arbitration with sel. */
+#define MESG_OUT_PHASE 0x06
+#define MESG_IN_PHASE 0x07
+
+#define MSG_CMD_COMPLETE 0x00
+#define MSG_EXT_MESSAGE 0x01
+#define MSG_SAVE_DATA_PTR 0x02
+#define MSG_RESTORE_PTR 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_INIT_DETECT_ERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT 0x07
+#define MSG_NOOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define MSG_BUS_DEVICE_RESET 0x0C
+#define MSG_IDENTIFY 0x80
+#define MSG_IDENTIFY_DR 0xc0 /* (disconnect/reconnect allowed) */
+#define MSG_SYNC_REQ 0x01
+
+
+#define STS_CHECKCOND 0x02 /* Check Condition (ie., read sense) */
+#define STS_CONDMET 0x04 /* Condition Met (ie., search worked) */
+#define STS_BUSY 0x08
+#define STS_INTERMED 0x10 /* Intermediate status sent */
+#define STS_EXT 0x80 /* Extended status valid */
+
+/*
+ * XXXX
+ */
+struct scsi_fmt_cdb {
+ int len; /* cdb length (in bytes) */
+ u_char cdb[28]; /* cdb to use on next read/write */
+};
+
+#endif /* _SBICVAR_H_ */
diff --git a/sys/pc98/pc98/scd.c b/sys/pc98/pc98/scd.c
new file mode 100644
index 0000000..b018e3f
--- /dev/null
+++ b/sys/pc98/pc98/scd.c
@@ -0,0 +1,1601 @@
+/*-
+ * Copyright (c) 1995 Mikael Hybsch
+ * All rights reserved.
+ *
+ * Portions of this file are copied from mcd.c
+ * which has the following copyrights:
+ *
+ * Copyright 1993 by Holger Veit (data part)
+ * Copyright 1993 by Brian Moore (audio part)
+ * Changes Copyright 1993 by Gary Clark II
+ * Changes Copyright (C) 1994 by Andrew A. Chernov
+ *
+ * Rewrote probe routine to work on newer Mitsumi drives.
+ * Additional changes (C) 1994 by Jordan K. Hubbard
+ *
+ * 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
+ * in this position and unchanged.
+ * 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 withough 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.
+ *
+ */
+
+
+/* $Id: scd.c,v 1.21 1996/06/08 09:18:23 bde Exp $ */
+
+/* Please send any comments to micke@dynas.se */
+
+#define SCD_DEBUG 0
+
+#include "scd.h"
+#if NSCD > 0
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+#include <sys/errno.h>
+#include <sys/dkbad.h>
+#include <sys/disklabel.h>
+#include <sys/devconf.h>
+#include <sys/kernel.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /*DEVFS*/
+
+#include <machine/clock.h>
+#include <machine/stdarg.h>
+
+#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>
+#endif
+#include <i386/isa/scdreg.h>
+
+
+#define scd_part(dev) ((minor(dev)) & 7)
+#define scd_unit(dev) (((minor(dev)) & 0x38) >> 3)
+#define scd_phys(dev) (((minor(dev)) & 0x40) >> 6)
+#define RAW_PART 2
+
+/* flags */
+#define SCDOPEN 0x0001 /* device opened */
+#define SCDVALID 0x0002 /* parameters loaded */
+#define SCDINIT 0x0004 /* device is init'd */
+#define SCDPROBING 0x0020 /* probing */
+#define SCDTOC 0x0100 /* already read toc */
+#define SCDMBXBSY 0x0200 /* local mbx is busy */
+#define SCDSPINNING 0x0400 /* drive is spun up */
+
+#define SCD_S_BEGIN 0
+#define SCD_S_BEGIN1 1
+#define SCD_S_WAITSTAT 2
+#define SCD_S_WAITFIFO 3
+#define SCD_S_WAITSPIN 4
+#define SCD_S_WAITREAD 5
+#define SCD_S_WAITPARAM 6
+
+#define RDELAY_WAIT 300
+#define RDELAY_WAITREAD 300
+
+#define SCDBLKSIZE 2048
+
+#ifdef SCD_DEBUG
+ static int scd_debuglevel = SCD_DEBUG;
+# define XDEBUG(level, data) {if (scd_debuglevel >= level) printf data;}
+#else
+# define XDEBUG(level, data)
+#endif
+
+struct scd_mbx {
+ short unit;
+ short port;
+ short retry;
+ short nblk;
+ int sz;
+ u_long skip;
+ struct buf *bp;
+ int p_offset;
+ short count;
+};
+
+static struct scd_data {
+ int iobase;
+ char double_speed;
+ char *name;
+ short flags;
+ int blksize;
+ u_long disksize;
+ struct disklabel dlabel;
+ int openflag;
+ struct {
+ unsigned char adr :4;
+ unsigned char ctl :4; /* xcdplayer needs this */
+ unsigned char start_msf[3];
+ } toc[MAX_TRACKS];
+ short first_track;
+ short last_track;
+ struct ioc_play_msf last_play;
+
+ short audio_status;
+ struct buf_queue_head head; /* head of buf queue */
+ struct scd_mbx mbx;
+#ifdef DEVFS
+ void *ra_devfs_token;
+ void *rc_devfs_token;
+ void *a_devfs_token;
+ void *c_devfs_token;
+#endif
+} scd_data[NSCD];
+
+/* prototypes */
+static void hsg2msf(int hsg, bcd_t *msf);
+static int msf2hsg(bcd_t *msf);
+
+static void process_attention(unsigned unit);
+static inline void write_control(unsigned port, unsigned data);
+static int waitfor_status_bits(int unit, int bits_set, int bits_clear);
+static int send_cmd(u_int unit, u_char cmd, u_int nargs, ...);
+static void init_drive(unsigned unit);
+static int spin_up(unsigned unit);
+static int read_toc(dev_t dev);
+static int get_result(u_int unit, int result_len, u_char *result);
+static void print_error(int unit, int errcode);
+
+static void scd_start(int unit);
+static void scd_doread(int state, struct scd_mbx *mbxin);
+
+static int scd_eject(int unit);
+static int scd_stop(int unit);
+static int scd_pause(int unit);
+static int scd_resume(int unit);
+static int scd_playtracks(int unit, struct ioc_play_track *pt);
+static int scd_playmsf(int unit, struct ioc_play_msf *msf);
+static int scd_play(int unit, struct ioc_play_msf *msf);
+static int scd_subchan(int unit, struct ioc_read_subchannel *sc);
+static int read_subcode(int unit, struct sony_subchannel_position_data *sc);
+
+/* for xcdplayer */
+static int scd_toc_header(int unit, struct ioc_toc_header *th);
+static int scd_toc_entrys(int unit, struct ioc_read_toc_entry *te);
+#define SCD_LASTPLUS1 170 /* don't ask, xcdplayer passes this in */
+
+#ifdef PC98
+static int scd_probe(struct pc98_device *dev);
+static int scd_attach(struct pc98_device *dev);
+struct pc98_driver scddriver = { scd_probe, scd_attach, "scd" };
+#else
+static int scd_probe(struct isa_device *dev);
+static int scd_attach(struct isa_device *dev);
+struct isa_driver scddriver = { scd_probe, scd_attach, "scd" };
+#endif
+
+static d_open_t scdopen;
+static d_close_t scdclose;
+static d_ioctl_t scdioctl;
+static d_strategy_t scdstrategy;
+
+#define CDEV_MAJOR 45
+#define BDEV_MAJOR 16
+extern struct cdevsw scd_cdevsw;
+static struct bdevsw scd_bdevsw =
+ { scdopen, scdclose, scdstrategy, scdioctl, /*16*/
+ nodump, nopsize, 0, "scd", &scd_cdevsw, -1 };
+
+static struct cdevsw scd_cdevsw =
+ { scdopen, scdclose, rawread, nowrite, /*45*/
+ scdioctl, nostop, nullreset, nodevtotty,/* sony cd */
+ seltrue, nommap, scdstrategy, "scd",
+ &scd_bdevsw, -1 };
+
+
+static struct kern_devconf kdc_scd[NSCD] = { {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ "scd", 0, { MDDT_PC98, 0, "bio" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "scd", 0, { MDDT_ISA, 0, "bio" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* status */
+ "Sony CD-ROM drive", /* properly filled later */
+ DC_CLS_RDISK /* class */
+} };
+
+static inline void
+#ifdef PC98
+scd_registerdev(struct pc98_device *id)
+#else
+scd_registerdev(struct isa_device *id)
+#endif
+{
+ if(id->id_unit)
+ kdc_scd[id->id_unit] = kdc_scd[0];
+ kdc_scd[id->id_unit].kdc_unit = id->id_unit;
+#ifdef PC98
+ kdc_scd[id->id_unit].kdc_pc98 = id;
+#else
+ kdc_scd[id->id_unit].kdc_isa = id;
+#endif
+ dev_attach(&kdc_scd[id->id_unit]);
+}
+
+#ifdef PC98
+int scd_attach(struct pc98_device *dev)
+#else
+int scd_attach(struct isa_device *dev)
+#endif
+{
+ int unit = dev->id_unit;
+ struct scd_data *cd = scd_data + unit;
+
+ cd->iobase = dev->id_iobase; /* Already set by probe, but ... */
+
+ kdc_scd[dev->id_unit].kdc_state = DC_IDLE;
+ /* name filled in probe */
+ kdc_scd[dev->id_unit].kdc_description = scd_data[dev->id_unit].name;
+ printf("scd%d: <%s>\n", dev->id_unit, scd_data[dev->id_unit].name);
+
+ init_drive(dev->id_unit);
+
+ cd->flags = SCDINIT;
+ cd->audio_status = CD_AS_AUDIO_INVALID;
+ TAILQ_INIT(&cd->head);
+
+#ifdef DEVFS
+ cd->ra_devfs_token =
+ devfs_add_devswf(&scd_cdevsw, dkmakeminor(unit, 0, 0),
+ DV_CHR, UID_ROOT, GID_OPERATOR, 0640,
+ "rscd%da", unit);
+ cd->rc_devfs_token =
+ devfs_add_devswf(&scd_cdevsw, dkmakeminor(unit, 0, RAW_PART),
+ DV_CHR, UID_ROOT, GID_OPERATOR, 0640,
+ "rscd%dc", unit);
+ cd->a_devfs_token =
+ devfs_add_devswf(&scd_bdevsw, dkmakeminor(unit, 0, 0),
+ DV_BLK, UID_ROOT, GID_OPERATOR, 0640,
+ "scd%da", unit);
+ cd->c_devfs_token =
+ devfs_add_devswf(&scd_bdevsw, dkmakeminor(unit, 0, RAW_PART),
+ DV_BLK, UID_ROOT, GID_OPERATOR, 0640,
+ "scd%dc", unit);
+#endif
+ return 1;
+}
+
+static int
+scdopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ int unit,part,phys;
+ int rc;
+ struct scd_data *cd;
+
+ unit = scd_unit(dev);
+ if (unit >= NSCD)
+ return ENXIO;
+
+ cd = scd_data + unit;
+ part = scd_part(dev);
+ phys = scd_phys(dev);
+
+ /* not initialized*/
+ if (!(cd->flags & SCDINIT))
+ return ENXIO;
+
+ /* invalidated in the meantime? mark all open part's invalid */
+ if (cd->openflag)
+ return ENXIO;
+
+ XDEBUG(1,("scd%d: DEBUG: status = 0x%x\n", unit, inb(cd->iobase+IREG_STATUS)));
+
+ if ((rc = spin_up(unit)) != 0) {
+ print_error(unit, rc);
+ return EIO;
+ }
+ if (!(cd->flags & SCDTOC)) {
+ int loop_count = 3;
+
+ while (loop_count-- > 0 && (rc = read_toc(dev)) != 0) {
+ if (rc == ERR_NOT_SPINNING) {
+ rc = spin_up(unit);
+ if (rc) {
+ print_error(unit, rc);\
+ return EIO;
+ }
+ continue;
+ }
+ printf("scd%d: TOC read error 0x%x\n", unit, rc);
+ return EIO;
+ }
+ }
+
+ cd->openflag = 1;
+ cd->flags |= SCDVALID;
+ kdc_scd[unit].kdc_state = DC_BUSY;
+
+ return 0;
+}
+
+static int
+scdclose(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ int unit,part,phys;
+ struct scd_data *cd;
+
+ unit = scd_unit(dev);
+ if (unit >= NSCD)
+ return ENXIO;
+
+ cd = scd_data + unit;
+ part = scd_part(dev);
+ phys = scd_phys(dev);
+
+ if (!(cd->flags & SCDINIT) || !cd->openflag)
+ return ENXIO;
+
+ if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) {
+ (void)send_cmd(unit, CMD_SPIN_DOWN, 0);
+ cd->flags &= ~SCDSPINNING;
+ }
+
+ kdc_scd[unit].kdc_state = DC_IDLE;
+
+ /* close channel */
+ cd->openflag = 0;
+
+ return 0;
+}
+
+static void
+scdstrategy(struct buf *bp)
+{
+ struct scd_data *cd;
+ int s;
+ int unit = scd_unit(bp->b_dev);
+
+ cd = scd_data + unit;
+
+ XDEBUG(2, ("scd%d: DEBUG: strategy: block=%ld, bcount=%ld\n", unit, bp->b_blkno, bp->b_bcount));
+
+ if (unit >= NSCD || bp->b_blkno < 0 || (bp->b_bcount % SCDBLKSIZE)) {
+ printf("scd%d: strategy failure: blkno = %ld, bcount = %ld\n",
+ unit, bp->b_blkno, bp->b_bcount);
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto bad;
+ }
+
+ /* if device invalidated (e.g. media change, door open), error */
+ if (!(cd->flags & SCDVALID)) {
+ printf("scd%d: media changed\n", unit);
+ bp->b_error = EIO;
+ goto bad;
+ }
+
+ /* read only */
+ if (!(bp->b_flags & B_READ)) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+
+ /* no data to read */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ if (!(cd->flags & SCDTOC)) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /* adjust transfer if necessary */
+ if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0)
+ goto done;
+
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = 0;
+
+ /* queue it */
+ s = splbio();
+ tqdisksort(&cd->head, bp);
+ splx(s);
+
+ /* now check whether we can perform processing */
+ scd_start(unit);
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ return;
+}
+
+static void
+scd_start(int unit)
+{
+ struct scd_data *cd = scd_data + unit;
+ struct buf *bp;
+ struct partition *p;
+ register s = splbio();
+
+ if (cd->flags & SCDMBXBSY) {
+ splx(s);
+ return;
+ }
+
+ bp = TAILQ_FIRST(&cd->head);
+ if (bp != 0) {
+ /* block found to process, dequeue */
+ TAILQ_REMOVE(&cd->head, bp, b_act);
+ cd->flags |= SCDMBXBSY;
+ splx(s);
+ } else {
+ /* nothing to do */
+ splx(s);
+ return;
+ }
+
+ p = cd->dlabel.d_partitions + scd_part(bp->b_dev);
+
+ cd->mbx.unit = unit;
+ cd->mbx.port = cd->iobase;
+ cd->mbx.retry = 3;
+ cd->mbx.bp = bp;
+ cd->mbx.p_offset = p->p_offset;
+ splx(s);
+
+ scd_doread(SCD_S_BEGIN,&(cd->mbx));
+ return;
+}
+
+static int
+scdioctl(dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
+{
+ struct scd_data *cd;
+ int unit,part;
+
+ unit = scd_unit(dev);
+ part = scd_part(dev);
+ cd = scd_data + unit;
+
+ XDEBUG(1, ("scd%d: ioctl: cmd=0x%x\n", unit, cmd));
+
+ if (!(cd->flags & SCDVALID))
+ return EIO;
+
+ switch (cmd) {
+ case DIOCSBAD:
+ return EINVAL;
+ case DIOCGDINFO:
+ *(struct disklabel *)addr = cd->dlabel;
+ return 0;
+ case DIOCGPART:
+ ((struct partinfo *)addr)->disklab = &cd->dlabel;
+ ((struct partinfo *)addr)->part =
+ &cd->dlabel.d_partitions[0];
+ return 0;
+ case CDIOCPLAYTRACKS:
+ return scd_playtracks(unit, (struct ioc_play_track *) addr);
+ case CDIOCPLAYBLOCKS:
+ return EINVAL;
+ case CDIOCPLAYMSF:
+ return scd_playmsf(unit, (struct ioc_play_msf *) addr);
+ case CDIOCREADSUBCHANNEL:
+ return scd_subchan(unit, (struct ioc_read_subchannel *) addr);
+ case CDIOREADTOCHEADER:
+ return scd_toc_header (unit, (struct ioc_toc_header *) addr);
+ case CDIOREADTOCENTRYS:
+ return scd_toc_entrys (unit, (struct ioc_read_toc_entry*) addr);
+ case CDIOCSETPATCH:
+ case CDIOCGETVOL:
+ case CDIOCSETVOL:
+ case CDIOCSETMONO:
+ case CDIOCSETSTERIO:
+ case CDIOCSETMUTE:
+ case CDIOCSETLEFT:
+ case CDIOCSETRIGHT:
+ return EINVAL;
+ case CDIOCRESUME:
+ return scd_resume(unit);
+ case CDIOCPAUSE:
+ return scd_pause(unit);
+ case CDIOCSTART:
+ return EINVAL;
+ case CDIOCSTOP:
+ return scd_stop(unit);
+ case CDIOCEJECT:
+ return scd_eject(unit);
+ case CDIOCALLOW:
+ return 0;
+ case CDIOCSETDEBUG:
+#ifdef SCD_DEBUG
+ scd_debuglevel++;
+#endif
+ return 0;
+ case CDIOCCLRDEBUG:
+#ifdef SCD_DEBUG
+ scd_debuglevel = 0;
+
+#endif
+ return 0;
+ default:
+ printf("scd%d: unsupported ioctl (cmd=0x%x)\n", unit, cmd);
+ return ENOTTY;
+ }
+}
+
+/***************************************************************
+ * lower level of driver starts here
+ **************************************************************/
+
+static int
+scd_playtracks(int unit, struct ioc_play_track *pt)
+{
+ struct scd_data *cd = scd_data + unit;
+ struct ioc_play_msf msf;
+ int a = pt->start_track;
+ int z = pt->end_track;
+ int rc;
+
+ if (!(cd->flags & SCDTOC) && (rc = read_toc(unit)) != 0) {
+ if (rc == -ERR_NOT_SPINNING) {
+ if (spin_up(unit) != 0)
+ return EIO;
+ rc = read_toc(unit);
+ }
+ if (rc != 0) {
+ print_error(unit, rc);
+ return EIO;
+ }
+ }
+
+ XDEBUG(1, ("scd%d: playtracks from %d:%d to %d:%d\n", unit,
+ a, pt->start_index, z, pt->end_index));
+
+ if ( a < cd->first_track
+ || a > cd->last_track
+ || a > z
+ || z > cd->last_track)
+ return EINVAL;
+
+ bcopy(cd->toc[a].start_msf, &msf.start_m, 3);
+ hsg2msf(msf2hsg(cd->toc[z+1].start_msf)-1, &msf.end_m);
+
+ return scd_play(unit, &msf);
+}
+
+/* The start/end msf is expected to be in bin format */
+static int
+scd_playmsf(int unit, struct ioc_play_msf *msfin)
+{
+ struct ioc_play_msf msf;
+
+ msf.start_m = bin2bcd(msfin->start_m);
+ msf.start_s = bin2bcd(msfin->start_s);
+ msf.start_f = bin2bcd(msfin->start_f);
+ msf.end_m = bin2bcd(msfin->end_m);
+ msf.end_s = bin2bcd(msfin->end_s);
+ msf.end_f = bin2bcd(msfin->end_f);
+
+ return scd_play(unit, &msf);
+}
+
+/* The start/end msf is expected to be in bcd format */
+static int
+scd_play(int unit, struct ioc_play_msf *msf)
+{
+ struct scd_data *cd = scd_data + unit;
+ int i, rc;
+
+ XDEBUG(1, ("scd%d: playing: %02x:%02x:%02x -> %02x:%02x:%02x\n", unit,
+ msf->start_m, msf->start_s, msf->start_f,
+ msf->end_m, msf->end_s, msf->end_f));
+
+ for (i = 0; i < 2; i++) {
+ rc = send_cmd(unit, CMD_PLAY_AUDIO, 7,
+ 0x03,
+ msf->start_m, msf->start_s, msf->start_f,
+ msf->end_m, msf->end_s, msf->end_f);
+ if (rc == -ERR_NOT_SPINNING) {
+ cd->flags &= ~SCDSPINNING;
+ if (spin_up(unit) != 0)
+ return EIO;
+ } else if (rc < 0) {
+ print_error(unit, rc);
+ return EIO;
+ } else {
+ break;
+ }
+ }
+ cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
+ bcopy((char *)msf, (char *)&cd->last_play, sizeof(struct ioc_play_msf));
+ return 0;
+}
+
+static int
+scd_stop(int unit)
+{
+ struct scd_data *cd = scd_data + unit;
+
+ (void)send_cmd(unit, CMD_STOP_AUDIO, 0);
+ cd->audio_status = CD_AS_PLAY_COMPLETED;
+ return 0;
+}
+
+static int
+scd_pause(int unit)
+{
+ struct scd_data *cd = scd_data + unit;
+ struct sony_subchannel_position_data subpos;
+
+ if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS)
+ return EINVAL;
+
+ if (read_subcode(unit, &subpos) != 0)
+ return EIO;
+
+ if (send_cmd(unit, CMD_STOP_AUDIO, 0) != 0)
+ return EIO;
+
+ cd->last_play.start_m = subpos.abs_msf[0];
+ cd->last_play.start_s = subpos.abs_msf[1];
+ cd->last_play.start_f = subpos.abs_msf[2];
+ cd->audio_status = CD_AS_PLAY_PAUSED;
+
+ XDEBUG(1, ("scd%d: pause @ %02x:%02x:%02x\n", unit,
+ cd->last_play.start_m,
+ cd->last_play.start_s,
+ cd->last_play.start_f));
+
+ return 0;
+}
+
+static int
+scd_resume(int unit)
+{
+ if (scd_data[unit].audio_status != CD_AS_PLAY_PAUSED)
+ return EINVAL;
+ return scd_play(unit, &scd_data[unit].last_play);
+}
+
+static int
+scd_eject(int unit)
+{
+ struct scd_data *cd = scd_data + unit;
+
+ cd->audio_status = CD_AS_AUDIO_INVALID;
+ cd->flags &= ~(SCDSPINNING|SCDTOC);
+
+ if (send_cmd(unit, CMD_STOP_AUDIO, 0) != 0 ||
+ send_cmd(unit, CMD_SPIN_DOWN, 0) != 0 ||
+ send_cmd(unit, CMD_EJECT, 0) != 0)
+ {
+ return EIO;
+ }
+ return 0;
+}
+
+static int
+scd_subchan(int unit, struct ioc_read_subchannel *sc)
+{
+ struct scd_data *cd = scd_data + unit;
+ struct sony_subchannel_position_data q;
+ struct cd_sub_channel_info data;
+
+ XDEBUG(1, ("scd%d: subchan af=%d, df=%d\n", unit,
+ sc->address_format,
+ sc->data_format));
+
+ if (sc->address_format != CD_MSF_FORMAT)
+ return EINVAL;
+
+ if (sc->data_format != CD_CURRENT_POSITION)
+ return EINVAL;
+
+ if (read_subcode(unit, &q) != 0)
+ return EIO;
+
+ data.header.audio_status = cd->audio_status;
+ data.what.position.data_format = CD_MSF_FORMAT;
+ data.what.position.track_number = bcd2bin(q.track_number);
+ data.what.position.reladdr.msf.unused = 0;
+ data.what.position.reladdr.msf.minute = bcd2bin(q.rel_msf[0]);
+ data.what.position.reladdr.msf.second = bcd2bin(q.rel_msf[1]);
+ data.what.position.reladdr.msf.frame = bcd2bin(q.rel_msf[2]);
+ data.what.position.absaddr.msf.unused = 0;
+ data.what.position.absaddr.msf.minute = bcd2bin(q.abs_msf[0]);
+ data.what.position.absaddr.msf.second = bcd2bin(q.abs_msf[1]);
+ data.what.position.absaddr.msf.frame = bcd2bin(q.abs_msf[2]);
+
+ if (copyout(&data, sc->data, min(sizeof(struct cd_sub_channel_info), sc->data_len))!=0)
+ return EFAULT;
+ return 0;
+}
+
+int
+#ifdef PC98
+scd_probe(struct pc98_device *dev)
+#else
+scd_probe(struct isa_device *dev)
+#endif
+{
+ struct sony_drive_configuration drive_config;
+ int unit = dev->id_unit;
+ int rc;
+ static char namebuf[8+16+8+3];
+ char *s = namebuf;
+ int loop_count = 0;
+
+ scd_data[unit].flags = SCDPROBING;
+ scd_data[unit].iobase = dev->id_iobase;
+
+ bzero(&drive_config, sizeof(drive_config));
+
+ scd_registerdev(dev);
+
+again:
+ /* Reset drive */
+ write_control(dev->id_iobase, CBIT_RESET_DRIVE);
+
+ /* Calm down */
+ DELAY(300000);
+
+ /* Only the ATTENTION bit may be set */
+ if ((inb(dev->id_iobase+IREG_STATUS) & ~1) != 0) {
+ XDEBUG(1, ("scd: too many bits set. probe failed.\n"));
+ return 0;
+ }
+ rc = send_cmd(unit, CMD_GET_DRIVE_CONFIG, 0);
+ if (rc != sizeof(drive_config)) {
+ /* Sometimes if the drive is playing audio I get */
+ /* the bad result 82. Fix by repeating the reset */
+ if (rc > 0 && loop_count++ == 0)
+ goto again;
+ return 0;
+ }
+ if (get_result(unit, rc, (u_char *)&drive_config) != 0)
+ return 0;
+
+ bcopy(drive_config.vendor, namebuf, 8);
+ s = namebuf+8;
+ while (*(s-1) == ' ') /* Strip trailing spaces */
+ s--;
+ *s++ = ' ';
+ bcopy(drive_config.product, s, 16);
+ s += 16;
+ while (*(s-1) == ' ')
+ s--;
+ *s++ = ' ';
+ bcopy(drive_config.revision, s, 8);
+ s += 8;
+ while (*(s-1) == ' ')
+ s--;
+ *s = 0;
+
+ scd_data[unit].name = namebuf;
+
+ if (drive_config.config & 0x10)
+ scd_data[unit].double_speed = 1;
+ else
+ scd_data[unit].double_speed = 0;
+
+ return 4;
+}
+
+static int
+read_subcode(int unit, struct sony_subchannel_position_data *sc)
+{
+ int rc;
+
+ rc = send_cmd(unit, CMD_GET_SUBCHANNEL_DATA, 0);
+ if (rc < 0 || rc < sizeof(*sc))
+ return EIO;
+ if (get_result(unit, rc, (u_char *)sc) != 0)
+ return EIO;
+ return 0;
+}
+
+/* State machine copied from mcd.c */
+
+/* This (and the code in mcd.c) will not work with more than one drive */
+/* because there is only one mbxsave below. Should fix that some day. */
+/* (mbxsave & state should probably be included in the scd_data struct and */
+/* the unit number used as first argument to scd_doread().) /Micke */
+
+/* state machine to process read requests
+ * initialize with SCD_S_BEGIN: reset state machine
+ * SCD_S_WAITSTAT: wait for ready (!busy)
+ * SCD_S_WAITSPIN: wait for drive to spin up (if not spinning)
+ * SCD_S_WAITFIFO: wait for param fifo to get ready, them exec. command.
+ * SCD_S_WAITREAD: wait for data ready, read data
+ * SCD_S_WAITPARAM: wait for command result params, read them, error if bad data read.
+ */
+
+static struct scd_mbx *mbxsave;
+
+static void
+scd_doread(int state, struct scd_mbx *mbxin)
+{
+ struct scd_mbx *mbx = (state!=SCD_S_BEGIN) ? mbxsave : mbxin;
+ int unit = mbx->unit;
+ int port = mbx->port;
+ struct buf *bp = mbx->bp;
+ struct scd_data *cd = scd_data + unit;
+ int reg,i;
+ int blknum;
+ caddr_t addr;
+ static char sdata[3]; /* Must be preserved between calls to this function */
+
+loop:
+ switch (state) {
+ case SCD_S_BEGIN:
+ mbx = mbxsave = mbxin;
+
+ case SCD_S_BEGIN1:
+ /* get status */
+ mbx->count = RDELAY_WAIT;
+
+ process_attention(unit);
+ goto trystat;
+
+ case SCD_S_WAITSTAT:
+ untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITSTAT);
+ if (mbx->count-- <= 0) {
+ printf("scd%d: timeout. drive busy.\n",unit);
+ goto harderr;
+ }
+
+trystat:
+ if (IS_BUSY(port)) {
+ timeout((timeout_func_t)scd_doread,
+ (caddr_t)SCD_S_WAITSTAT,hz/100); /* XXX */
+ return;
+ }
+
+ process_attention(unit);
+
+ /* reject, if audio active */
+ if (cd->audio_status & CD_AS_PLAY_IN_PROGRESS) {
+ printf("scd%d: audio is active\n",unit);
+ goto harderr;
+ }
+
+ mbx->sz = cd->blksize;
+
+ /* for first block */
+ mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
+ mbx->skip = 0;
+
+nextblock:
+ if (!(cd->flags & SCDVALID))
+ goto changed;
+
+ blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE))
+ + mbx->p_offset + mbx->skip/mbx->sz;
+
+ XDEBUG(2, ("scd%d: scd_doread: read blknum=%d\n", unit, blknum));
+
+ /* build parameter block */
+ hsg2msf(blknum, sdata);
+
+ write_control(port, CBIT_RESULT_READY_CLEAR);
+ write_control(port, CBIT_RPARAM_CLEAR);
+ write_control(port, CBIT_DATA_READY_CLEAR);
+
+ if (FSTATUS_BIT(port, FBIT_WPARAM_READY))
+ goto writeparam;
+
+ mbx->count = 100;
+ timeout((timeout_func_t)scd_doread,
+ (caddr_t)SCD_S_WAITFIFO,hz/100); /* XXX */
+ return;
+
+ case SCD_S_WAITSPIN:
+ untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITSPIN);
+ if (mbx->count-- <= 0) {
+ printf("scd%d: timeout waiting for drive to spin up.\n", unit);
+ goto harderr;
+ }
+ if (!STATUS_BIT(port, SBIT_RESULT_READY)) {
+ timeout((timeout_func_t)scd_doread,
+ (caddr_t)SCD_S_WAITSPIN,hz/100); /* XXX */
+ return;
+ }
+ write_control(port, CBIT_RESULT_READY_CLEAR);
+ switch ((i = inb(port+IREG_RESULT)) & 0xf0) {
+ case 0x20:
+ i = inb(port+IREG_RESULT);
+ print_error(unit, i);
+ goto harderr;
+ case 0x00:
+ (void)inb(port+IREG_RESULT);
+ cd->flags |= SCDSPINNING;
+ break;
+ }
+ XDEBUG(1, ("scd%d: DEBUG: spin up complete\n", unit));
+
+ state = SCD_S_BEGIN1;
+ goto loop;
+
+ case SCD_S_WAITFIFO:
+ untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITFIFO);
+ if (mbx->count-- <= 0) {
+ printf("scd%d: timeout. write param not ready.\n",unit);
+ goto harderr;
+ }
+ if (!FSTATUS_BIT(port, FBIT_WPARAM_READY)) {
+ timeout((timeout_func_t)scd_doread,
+ (caddr_t)SCD_S_WAITFIFO,hz/100); /* XXX */
+ return;
+ }
+ XDEBUG(1, ("scd%d: mbx->count (writeparamwait) = %d(%d)\n", unit, mbx->count, 100));
+
+writeparam:
+ /* The reason this test isn't done 'till now is to make sure */
+ /* that it is ok to send the SPIN_UP cmd below. */
+ if (!(cd->flags & SCDSPINNING)) {
+ XDEBUG(1, ("scd%d: spinning up drive ...\n", unit));
+ outb(port+OREG_COMMAND, CMD_SPIN_UP);
+ mbx->count = 300;
+ timeout((timeout_func_t)scd_doread,
+ (caddr_t)SCD_S_WAITSPIN,hz/100); /* XXX */
+ return;
+ }
+
+ reg = port + OREG_WPARAMS;
+ /* send the read command */
+ disable_intr();
+ outb(reg, sdata[0]);
+ outb(reg, sdata[1]);
+ outb(reg, sdata[2]);
+ outb(reg, 0);
+ outb(reg, 0);
+ outb(reg, 1);
+ outb(port+OREG_COMMAND, CMD_READ);
+ enable_intr();
+
+ mbx->count = RDELAY_WAITREAD;
+ for (i = 0; i < 50; i++) {
+ if (STATUS_BIT(port, SBIT_DATA_READY))
+ goto got_data;
+ DELAY(100);
+ }
+
+ timeout((timeout_func_t)scd_doread,
+ (caddr_t)SCD_S_WAITREAD,hz/100); /* XXX */
+ return;
+
+ case SCD_S_WAITREAD:
+ untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITREAD);
+ if (mbx->count-- <= 0) {
+ if (STATUS_BIT(port, SBIT_RESULT_READY))
+ goto got_param;
+ printf("scd%d: timeout while reading data\n",unit);
+ goto readerr;
+ }
+ if (!STATUS_BIT(port, SBIT_DATA_READY)) {
+ process_attention(unit);
+ if (!(cd->flags & SCDVALID))
+ goto changed;
+ timeout((timeout_func_t)scd_doread,
+ (caddr_t)SCD_S_WAITREAD,hz/100); /* XXX */
+ return;
+ }
+ XDEBUG(2, ("scd%d: mbx->count (after RDY_BIT) = %d(%d)\n", unit, mbx->count, RDELAY_WAITREAD));
+
+got_data:
+ /* data is ready */
+ addr = bp->b_un.b_addr + mbx->skip;
+ write_control(port, CBIT_DATA_READY_CLEAR);
+ insb(port+IREG_DATA, addr, mbx->sz);
+
+ mbx->count = 100;
+ for (i = 0; i < 20; i++) {
+ if (STATUS_BIT(port, SBIT_RESULT_READY))
+ goto waitfor_param;
+ DELAY(100);
+ }
+ goto waitfor_param;
+
+ case SCD_S_WAITPARAM:
+ untimeout((timeout_func_t)scd_doread,(caddr_t)SCD_S_WAITPARAM);
+ if (mbx->count-- <= 0) {
+ printf("scd%d: timeout waiting for params\n",unit);
+ goto readerr;
+ }
+
+waitfor_param:
+ if (!STATUS_BIT(port, SBIT_RESULT_READY)) {
+ timeout((timeout_func_t)scd_doread,
+ (caddr_t)SCD_S_WAITPARAM,hz/100); /* XXX */
+ return;
+ }
+#if SCD_DEBUG
+ if (mbx->count < 100 && scd_debuglevel > 0)
+ printf("scd%d: mbx->count (paramwait) = %d(%d)\n", unit, mbx->count, 100);
+#endif
+
+got_param:
+ write_control(port, CBIT_RESULT_READY_CLEAR);
+ switch ((i = inb(port+IREG_RESULT)) & 0xf0) {
+ case 0x50:
+ switch (i) {
+ case ERR_FATAL_READ_ERROR1:
+ case ERR_FATAL_READ_ERROR2:
+ printf("scd%d: unrecoverable read error 0x%x\n", unit, i);
+ goto harderr;
+ }
+ break;
+ case 0x20:
+ i = inb(port+IREG_RESULT);
+ switch (i) {
+ case ERR_NOT_SPINNING:
+ XDEBUG(1, ("scd%d: read error: drive not spinning\n", unit));
+ if (mbx->retry-- > 0) {
+ state = SCD_S_BEGIN1;
+ cd->flags &= ~SCDSPINNING;
+ goto loop;
+ }
+ goto harderr;
+ default:
+ print_error(unit, i);
+ goto readerr;
+ }
+ case 0x00:
+ i = inb(port+IREG_RESULT);
+ break;
+ }
+
+ if (--mbx->nblk > 0) {
+ mbx->skip += mbx->sz;
+ goto nextblock;
+ }
+
+ /* return buffer */
+ bp->b_resid = 0;
+ biodone(bp);
+
+ cd->flags &= ~SCDMBXBSY;
+ scd_start(mbx->unit);
+ return;
+ }
+
+readerr:
+ if (mbx->retry-- > 0) {
+ printf("scd%d: retrying ...\n",unit);
+ state = SCD_S_BEGIN1;
+ goto loop;
+ }
+harderr:
+ /* invalidate the buffer */
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+
+ cd->flags &= ~SCDMBXBSY;
+ scd_start(mbx->unit);
+ return;
+
+changed:
+ printf("scd%d: media changed\n", unit);
+ goto harderr;
+}
+
+static void
+hsg2msf(int hsg, bcd_t *msf)
+{
+ hsg += 150;
+ M_msf(msf) = bin2bcd(hsg / 4500);
+ hsg %= 4500;
+ S_msf(msf) = bin2bcd(hsg / 75);
+ F_msf(msf) = bin2bcd(hsg % 75);
+}
+
+static int
+msf2hsg(bcd_t *msf)
+{
+ return (bcd2bin(M_msf(msf)) * 60 +
+ bcd2bin(S_msf(msf))) * 75 +
+ bcd2bin(F_msf(msf)) - 150;
+}
+
+static void
+process_attention(unsigned unit)
+{
+ unsigned port = scd_data[unit].iobase;
+ unsigned char code;
+ int count = 0;
+
+ while (IS_ATTENTION(port) && count++ < 30) {
+ write_control(port, CBIT_ATTENTION_CLEAR);
+ code = inb(port+IREG_RESULT);
+
+#if SCD_DEBUG
+ if (scd_debuglevel > 0) {
+ if (count == 1)
+ printf("scd%d: DEBUG: ATTENTIONS = 0x%x", unit, code);
+ else
+ printf(",0x%x", code);
+ }
+#endif
+
+ switch (code) {
+ case ATTEN_SPIN_DOWN:
+ scd_data[unit].flags &= ~SCDSPINNING;
+ break;
+
+ case ATTEN_SPIN_UP_DONE:
+ scd_data[unit].flags |= SCDSPINNING;
+ break;
+
+ case ATTEN_AUDIO_DONE:
+ scd_data[unit].audio_status = CD_AS_PLAY_COMPLETED;
+ break;
+
+ case ATTEN_DRIVE_LOADED:
+ scd_data[unit].flags &= ~(SCDTOC|SCDSPINNING|SCDVALID);
+ scd_data[unit].audio_status = CD_AS_AUDIO_INVALID;
+ break;
+
+ case ATTEN_EJECT_PUSHED:
+ scd_data[unit].flags &= ~SCDVALID;
+ break;
+ }
+ DELAY(100);
+ }
+#if SCD_DEBUG
+ if (scd_debuglevel > 0 && count > 0)
+ printf("\n");
+#endif
+}
+
+/* Returns 0 OR sony error code */
+static int
+spin_up(unsigned unit)
+{
+ unsigned char res_reg[12];
+ unsigned int res_size;
+ int rc;
+ int loop_count = 0;
+
+again:
+ rc = send_cmd(unit, CMD_SPIN_UP, NULL, 0, res_reg, &res_size);
+ if (rc != 0) {
+ XDEBUG(2, ("scd%d: CMD_SPIN_UP error 0x%x\n", unit, rc));
+ return rc;
+ }
+
+ if (!(scd_data[unit].flags & SCDTOC)) {
+ rc = send_cmd(unit, CMD_READ_TOC, 0);
+ if (rc == ERR_NOT_SPINNING) {
+ if (loop_count++ < 3)
+ goto again;
+ return rc;
+ }
+ if (rc != 0)
+ return rc;
+ }
+
+ scd_data[unit].flags |= SCDSPINNING;
+
+ return 0;
+}
+
+static struct sony_tracklist *
+get_tl(struct sony_toc *toc, int size)
+{
+ struct sony_tracklist *tl = &toc->tracks[0];
+
+ if (tl->track != 0xb0)
+ return tl;
+ (char *)tl += 9;
+ if (tl->track != 0xb1)
+ return tl;
+ (char *)tl += 9;
+ if (tl->track != 0xb2)
+ return tl;
+ (char *)tl += 9;
+ if (tl->track != 0xb3)
+ return tl;
+ (char *)tl += 9;
+ if (tl->track != 0xb4)
+ return tl;
+ (char *)tl += 9;
+ if (tl->track != 0xc0)
+ return tl;
+ (char *)tl += 9;
+ return tl;
+}
+
+static int
+read_toc(dev_t dev)
+{
+ unsigned unit;
+ struct scd_data *cd;
+ unsigned part = 0; /* For now ... */
+ struct sony_toc toc;
+ struct sony_tracklist *tl;
+ int rc, i, j;
+ u_long first, last;
+
+ unit = scd_unit(dev);
+ cd = scd_data + unit;
+
+ rc = send_cmd(unit, CMD_GET_TOC, 1, part+1);
+ if (rc < 0)
+ return rc;
+ if (rc > sizeof(toc)) {
+ printf("scd%d: program error: toc too large (%d)\n", unit, rc);
+ return EIO;
+ }
+ if (get_result(unit, rc, (u_char *)&toc) != 0)
+ return EIO;
+
+ XDEBUG(1, ("scd%d: toc read. len = %d, sizeof(toc) = %d\n", unit, rc, sizeof(toc)));
+
+ tl = get_tl(&toc, rc);
+ first = msf2hsg(tl->start_msf);
+ last = msf2hsg(toc.lead_out_start_msf);
+ cd->blksize = SCDBLKSIZE;
+ cd->disksize = last*cd->blksize/DEV_BSIZE;
+
+ XDEBUG(1, ("scd%d: firstsector = %ld, lastsector = %ld", unit,
+ first, last));
+
+ cd->first_track = bcd2bin(toc.first_track);
+ cd->last_track = bcd2bin(toc.last_track);
+ if (cd->last_track > (MAX_TRACKS-2))
+ cd->last_track = MAX_TRACKS-2;
+ for (j = 0, i = cd->first_track; i <= cd->last_track; i++, j++) {
+ cd->toc[i].adr = tl[j].adr;
+ cd->toc[i].ctl = tl[j].ctl; /* for xcdplayer */
+ bcopy(tl[j].start_msf, cd->toc[i].start_msf, 3);
+#ifdef SCD_DEBUG
+ if (scd_debuglevel > 0) {
+ if ((j % 3) == 0)
+ printf("\nscd%d: tracks ", unit);
+ printf("[%03d: %2d %2d %2d] ", i,
+ bcd2bin(cd->toc[i].start_msf[0]),
+ bcd2bin(cd->toc[i].start_msf[1]),
+ bcd2bin(cd->toc[i].start_msf[2]));
+ }
+#endif
+ }
+ bcopy(toc.lead_out_start_msf, cd->toc[cd->last_track+1].start_msf, 3);
+#ifdef SCD_DEBUG
+ if (scd_debuglevel > 0) {
+ i = cd->last_track+1;
+ printf("[END: %2d %2d %2d]\n",
+ bcd2bin(cd->toc[i].start_msf[0]),
+ bcd2bin(cd->toc[i].start_msf[1]),
+ bcd2bin(cd->toc[i].start_msf[2]));
+ }
+#endif
+
+ bzero(&cd->dlabel,sizeof(struct disklabel));
+ /* filled with spaces first */
+ strncpy(cd->dlabel.d_typename," ",
+ sizeof(cd->dlabel.d_typename));
+ strncpy(cd->dlabel.d_typename, cd->name,
+ min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1));
+ strncpy(cd->dlabel.d_packname,"unknown ",
+ sizeof(cd->dlabel.d_packname));
+ cd->dlabel.d_secsize = cd->blksize;
+ cd->dlabel.d_nsectors = 100;
+ cd->dlabel.d_ntracks = 1;
+ cd->dlabel.d_ncylinders = (cd->disksize/100)+1;
+ cd->dlabel.d_secpercyl = 100;
+ cd->dlabel.d_secperunit = cd->disksize;
+ cd->dlabel.d_rpm = 300;
+ cd->dlabel.d_interleave = 1;
+ cd->dlabel.d_flags = D_REMOVABLE;
+ cd->dlabel.d_npartitions= 1;
+ cd->dlabel.d_partitions[0].p_offset = 0;
+ cd->dlabel.d_partitions[0].p_size = cd->disksize;
+ cd->dlabel.d_partitions[0].p_fstype = 9;
+ cd->dlabel.d_magic = DISKMAGIC;
+ cd->dlabel.d_magic2 = DISKMAGIC;
+ cd->dlabel.d_checksum = dkcksum(&cd->dlabel);
+
+ cd->flags |= SCDTOC;
+
+ return 0;
+}
+
+static inline void
+write_control(unsigned port, unsigned data)
+{
+ outb(port + OREG_CONTROL, data);
+}
+
+static void
+init_drive(unsigned unit)
+{
+ int rc;
+
+ rc = send_cmd(unit, CMD_SET_DRIVE_PARAM, 2,
+ 0x05, 0x03 | ((scd_data[unit].double_speed) ? 0x04: 0));
+ if (rc != 0)
+ printf("scd%d: Unable to set parameters. Errcode = 0x%x\n", unit, rc);
+}
+
+/* Returns 0 or errno */
+static int
+get_result(u_int unit, int result_len, u_char *result)
+{
+ unsigned int port = scd_data[unit].iobase;
+ unsigned int res_reg = port + IREG_RESULT;
+ int loop_index = 2; /* send_cmd() reads two bytes ... */
+
+ XDEBUG(1, ("scd%d: DEBUG: get_result: bytes=%d\n", unit, result_len));
+
+ while (result_len-- > 0) {
+ if (loop_index++ >= 10) {
+ loop_index = 1;
+ if (waitfor_status_bits(unit, SBIT_RESULT_READY, 0))
+ return EIO;
+ write_control(port, CBIT_RESULT_READY_CLEAR);
+ }
+ if (result)
+ *result++ = inb(res_reg);
+ else
+ (void)inb(res_reg);
+ }
+ return 0;
+}
+
+/* Returns -0x100 for timeout, -(drive error code) OR number of result bytes */
+static int
+send_cmd(u_int unit, u_char cmd, u_int nargs, ...)
+{
+ va_list ap;
+ u_int port = scd_data[unit].iobase;
+ u_int reg;
+ u_char c;
+ int rc;
+ int i;
+
+ if (waitfor_status_bits(unit, 0, SBIT_BUSY)) {
+ printf("scd%d: drive busy\n", unit);
+ return -0x100;
+ }
+
+ XDEBUG(1,("scd%d: DEBUG: send_cmd: cmd=0x%x nargs=%d", unit, cmd, nargs));
+
+ write_control(port, CBIT_RESULT_READY_CLEAR);
+ write_control(port, CBIT_RPARAM_CLEAR);
+
+ for (i = 0; i < 100; i++)
+ if (FSTATUS_BIT(port, FBIT_WPARAM_READY))
+ break;
+ if (!FSTATUS_BIT(port, FBIT_WPARAM_READY)) {
+ XDEBUG(1, ("\nscd%d: wparam timeout\n", unit));
+ return -EIO;
+ }
+
+ va_start(ap, nargs);
+ reg = port + OREG_WPARAMS;
+ for (i = 0; i < nargs; i++) {
+ c = (u_char)va_arg(ap, int);
+ outb(reg, c);
+ XDEBUG(1, (",{0x%x}", c));
+ }
+ va_end(ap);
+ XDEBUG(1, ("\n"));
+
+ outb(port+OREG_COMMAND, cmd);
+
+ rc = waitfor_status_bits(unit, SBIT_RESULT_READY, SBIT_BUSY);
+ if (rc)
+ return -0x100;
+
+ reg = port + IREG_RESULT;
+ write_control(port, CBIT_RESULT_READY_CLEAR);
+ switch ((rc = inb(reg)) & 0xf0) {
+ case 0x20:
+ rc = inb(reg);
+ /* FALL TROUGH */
+ case 0x50:
+ XDEBUG(1, ("scd%d: DEBUG: send_cmd: drive_error=0x%x\n", unit, rc));
+ return -rc;
+ case 0x00:
+ default:
+ rc = inb(reg);
+ XDEBUG(1, ("scd%d: DEBUG: send_cmd: result_len=%d\n", unit, rc));
+ return rc;
+ }
+}
+
+static void
+print_error(int unit, int errcode)
+{
+ switch (errcode) {
+ case -ERR_CD_NOT_LOADED:
+ printf("scd%d: door is open\n", unit);
+ break;
+ case -ERR_NO_CD_INSIDE:
+ printf("scd%d: no cd inside\n", unit);
+ break;
+ default:
+ if (errcode == -0x100 || errcode > 0)
+ printf("scd%d: device timeout\n", unit);
+ else
+ printf("scd%d: unexpected error 0x%x\n", unit, -errcode);
+ break;
+ }
+}
+
+/* Returns 0 or errno value */
+static int
+waitfor_status_bits(int unit, int bits_set, int bits_clear)
+{
+ u_int port = scd_data[unit].iobase;
+ u_int flags = scd_data[unit].flags;
+ u_int reg = port + IREG_STATUS;
+ u_int max_loop;
+ u_char c = 0;
+
+ if (flags & SCDPROBING) {
+ max_loop = 0;
+ while (max_loop++ < 1000) {
+ c = inb(reg);
+ if (c == 0xff)
+ return EIO;
+ if (c & SBIT_ATTENTION) {
+ process_attention(unit);
+ continue;
+ }
+ if ((c & bits_set) == bits_set &&
+ (c & bits_clear) == 0)
+ {
+ break;
+ }
+ DELAY(10000);
+ }
+ } else {
+ max_loop = 100;
+ while (max_loop-- > 0) {
+ c = inb(reg);
+ if (c & SBIT_ATTENTION) {
+ process_attention(unit);
+ continue;
+ }
+ if ((c & bits_set) == bits_set &&
+ (c & bits_clear) == 0)
+ {
+ break;
+ }
+ tsleep(waitfor_status_bits, PZERO - 1, "waitfor", hz/10);
+ }
+ }
+ if ((c & bits_set) == bits_set &&
+ (c & bits_clear) == 0)
+ {
+ return 0;
+ }
+#ifdef SCD_DEBUG
+ if (scd_debuglevel > 0)
+ printf("scd%d: DEBUG: waitfor: TIMEOUT (0x%x,(0x%x,0x%x))\n", unit, c, bits_set, bits_clear);
+ else
+#endif
+ printf("scd%d: timeout.\n", unit);
+ return EIO;
+}
+
+/* these two routines for xcdplayer - "borrowed" from mcd.c */
+static int
+scd_toc_header (int unit, struct ioc_toc_header* th)
+{
+ struct scd_data *cd = scd_data + unit;
+ int rc;
+
+ if (!(cd->flags & SCDTOC) && (rc = read_toc(unit)) != 0) {
+ print_error(unit, rc);
+ return EIO;
+ }
+
+ th->starting_track = cd->first_track;
+ th->ending_track = cd->last_track;
+ th->len = 0; /* not used */
+
+ return 0;
+}
+
+static int
+scd_toc_entrys (int unit, struct ioc_read_toc_entry *te)
+{
+ struct scd_data *cd = scd_data + unit;
+ struct cd_toc_entry toc_entry;
+ int rc, i, len = te->data_len;
+
+ if (!(cd->flags & SCDTOC) && (rc = read_toc(unit)) != 0) {
+ print_error(unit, rc);
+ return EIO;
+ }
+
+ /* find the toc to copy*/
+ i = te->starting_track;
+ if (i == SCD_LASTPLUS1)
+ i = cd->last_track + 1;
+
+ /* verify starting track */
+ if (i < cd->first_track || i > cd->last_track+1)
+ return EINVAL;
+
+ /* valid length ? */
+ if (len < sizeof(struct cd_toc_entry)
+ || (len % sizeof(struct cd_toc_entry)) != 0)
+ return EINVAL;
+
+ /* copy the toc data */
+ toc_entry.control = cd->toc[i].ctl;
+ toc_entry.addr_type = te->address_format;
+ toc_entry.track = i;
+ if (te->address_format == CD_MSF_FORMAT) {
+ toc_entry.addr.msf.unused = 0;
+ toc_entry.addr.msf.minute = bcd2bin(cd->toc[i].start_msf[0]);
+ toc_entry.addr.msf.second = bcd2bin(cd->toc[i].start_msf[1]);
+ toc_entry.addr.msf.frame = bcd2bin(cd->toc[i].start_msf[2]);
+ }
+
+ /* copy the data back */
+ if (copyout(&toc_entry, te->data, sizeof(struct cd_toc_entry)) != 0)
+ return EFAULT;
+
+ return 0;
+}
+
+
+static scd_devsw_installed = 0;
+
+static void scd_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! scd_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev,&scd_cdevsw, NULL);
+ dev = makedev(BDEV_MAJOR, 0);
+ bdevsw_add(&dev,&scd_bdevsw, NULL);
+ scd_devsw_installed = 1;
+ }
+}
+
+SYSINIT(scddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,scd_drvinit,NULL)
+
+
+#endif /* NSCD > 0 */
diff --git a/sys/pc98/pc98/scsireg.h b/sys/pc98/pc98/scsireg.h
new file mode 100644
index 0000000..cefe741
--- /dev/null
+++ b/sys/pc98/pc98/scsireg.h
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * 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.
+ *
+ * @(#)scsireg.h 7.3 (Berkeley) 2/5/91
+ */
+
+/*
+ * AMD AM33C93A SCSI interface hardware description.
+ *
+ * Using parts of the Mach scsi driver for the 33C93
+ */
+
+#define SBIC_myid 0
+#define SBIC_cdbsize 0
+#define SBIC_control 1
+#define SBIC_timeo 2
+#define SBIC_cdb1 3
+#define SBIC_tsecs 3
+#define SBIC_cdb2 4
+#define SBIC_theads 4
+#define SBIC_cdb3 5
+#define SBIC_tcyl_hi 5
+#define SBIC_cdb4 6
+#define SBIC_tcyl_lo 6
+#define SBIC_cdb5 7
+#define SBIC_addr_hi 7
+#define SBIC_cdb6 8
+#define SBIC_addr_2 8
+#define SBIC_cdb7 9
+#define SBIC_addr_3 9
+#define SBIC_cdb8 10
+#define SBIC_addr_lo 10
+#define SBIC_cdb9 11
+#define SBIC_secno 11
+#define SBIC_cdb10 12
+#define SBIC_headno 12
+#define SBIC_cdb11 13
+#define SBIC_cylno_hi 13
+#define SBIC_cdb12 14
+#define SBIC_cylno_lo 14
+#define SBIC_tlun 15
+#define SBIC_cmd_phase 16
+#define SBIC_syn 17
+#define SBIC_count_hi 18
+#define SBIC_count_med 19
+#define SBIC_count_lo 20
+#define SBIC_selid 21
+#define SBIC_rselid 22
+#define SBIC_csr 23
+#define SBIC_cmd 24
+#define SBIC_data 25
+#define SBIC_bank 48
+#define SBIC_window 49
+#define SBIC_int 51
+/* sbic_asr is addressed directly */
+
+/*
+ * Register defines
+ */
+
+/*
+ * Auxiliary Status Register
+ */
+
+#define SBIC_ASR_INT 0x80 /* Interrupt pending */
+#define SBIC_ASR_LCI 0x40 /* Last command ignored */
+#define SBIC_ASR_BSY 0x20 /* Busy, only cmd/data/asr readable */
+#define SBIC_ASR_CIP 0x10 /* Busy, cmd unavail also */
+#define SBIC_ASR_xxx 0x0c
+#define SBIC_ASR_PE 0x02 /* Parity error (even) */
+#define SBIC_ASR_DBR 0x01 /* Data Buffer Ready */
+
+/*
+ * My ID register, and/or CDB Size
+ */
+
+#define SBIC_ID_FS_8_10 0x00 /* Input clock is 8-10 Mhz */
+ /* 11 Mhz is invalid */
+#define SBIC_ID_FS_12_15 0x40 /* Input clock is 12-15 Mhz */
+#define SBIC_ID_FS_16_20 0x80 /* Input clock is 16-20 Mhz */
+#define SBIC_ID_EHP 0x10 /* Enable host parity */
+#define SBIC_ID_EAF 0x08 /* Enable Advanced Features */
+#define SBIC_ID_MASK 0x07
+#define SBIC_ID_CBDSIZE_MASK 0x0f /* if unk SCSI cmd group */
+
+/*
+ * Control register
+ */
+
+#define SBIC_CTL_DMA 0x80 /* Single byte dma */
+#define SBIC_CTL_DBA_DMA 0x40 /* direct buffer acces (bus master)*/
+#define SBIC_CTL_BURST_DMA 0x20 /* continuous mode (8237) */
+#define SBIC_CTL_NO_DMA 0x00 /* Programmed I/O */
+#define SBIC_CTL_HHP 0x10 /* Halt on host parity error */
+#define SBIC_CTL_EDI 0x08 /* Ending disconnect interrupt */
+#define SBIC_CTL_IDI 0x04 /* Intermediate disconnect interrupt*/
+#define SBIC_CTL_HA 0x02 /* Halt on ATN */
+#define SBIC_CTL_HSP 0x01 /* Halt on SCSI parity error */
+
+/*
+ * Timeout period register
+ * [val in msecs, input clk in Mhz]
+ */
+
+#define SBIC_TIMEOUT(val,clk) ((((val)*(clk))/80)+1)
+
+/*
+ * CDBn registers, note that
+ * cdb11 is used for status byte in target mode (send-status-and-cc)
+ * cdb12 sez if linked command complete, and w/flag if so
+ */
+
+/*
+ * Target LUN register
+ * [holds target status when select-and-xfer]
+ */
+
+#define SBIC_TLUN_VALID 0x80 /* did we receive an Identify msg */
+#define SBIC_TLUN_DOK 0x40 /* Disconnect OK */
+#define SBIC_TLUN_xxx 0x38
+#define SBIC_TLUN_MASK 0x07
+
+/*
+ * Command Phase register
+ */
+
+#define SBIC_CPH_MASK 0x7f /* values/restarts are cmd specific */
+#define SBIC_CPH(p) ((p)&SBIC_CPH_MASK)
+
+/*
+ * FIFO register
+ */
+
+#define SBIC_FIFO_DEEP 12
+
+/*
+ * Synchronous xfer register
+ */
+
+#define SBIC_SYN_OFF_MASK 0x0f
+#define SBIC_SYN_MAX_OFFSET (SBIC_FIFO_DEEP-1)
+#define SBIC_SYN_PER_MASK 0x70
+#define SBIC_SYN_MIN_PERIOD 2 /* upto 8, encoded as 0 */
+
+#define SBIC_SYN(o,p) (((o)&SBIC_SYN_OFF_MASK)|(((p)<<4)&SBIC_SYN_PER_MASK))
+
+/*
+ * Transfer count register
+ * optimal access macros depend on addressing
+ */
+
+/*
+ * Destination ID (selid) register
+ */
+
+#define SBIC_SID_SCC 0x80 /* Select command chaining (tgt) */
+#define SBIC_SID_DPD 0x40 /* Data phase direction (inittor) */
+# define SBIC_SID_FROM_SCSI 0x40
+# define SBIC_SID_TO_SCSI 0x00
+#define SBIC_SID_xxx 0x38
+#define SBIC_SID_IDMASK 0x07
+
+/*
+ * Source ID (rselid) register
+ */
+
+#define SBIC_RID_ER 0x80 /* Enable reselection */
+#define SBIC_RID_ES 0x40 /* Enable selection */
+#define SBIC_RID_DSP 0x20 /* Disable select parity */
+#define SBIC_RID_SIV 0x08 /* Source ID valid */
+#define SBIC_RID_MASK 0x07
+
+/*
+ * Status register
+ */
+
+#define SBIC_CSR_CAUSE 0xf0
+# define SBIC_CSR_RESET 0x00 /* chip was reset */
+# define SBIC_CSR_CMD_DONE 0x10 /* cmd completed */
+# define SBIC_CSR_CMD_STOPPED 0x20 /* interrupted or abrted*/
+# define SBIC_CSR_CMD_ERR 0x40 /* end with error */
+# define SBIC_CSR_BUS_SERVICE 0x80 /* REQ pending on the bus */
+
+#define SBIC_CSR_QUALIFIER 0x0f
+
+ /* Reset State Interrupts */
+# define SBIC_CSR_RESET 0x00 /* reset w/advanced features*/
+# define SBIC_CSR_RESET_AM 0x01 /* reset w/advanced features*/
+
+ /* Successful Completion Interrupts */
+# define SBIC_CSR_TARGET 0x10 /* reselect complete */
+# define SBIC_CSR_INITIATOR 0x11 /* select complete */
+# define SBIC_CSR_WO_ATN 0x13 /* tgt mode completion */
+# define SBIC_CSR_W_ATN 0x14 /* ditto */
+# define SBIC_CSR_XLATED 0x15 /* translate address cmd */
+# define SBIC_CSR_S_XFERRED 0x16 /* initiator mode completion*/
+# define SBIC_CSR_XFERRED 0x18 /* phase in low bits */
+
+ /* Paused or Aborted Interrupts */
+# define SBIC_CSR_MSGIN_W_ACK 0x20 /* (I) msgin, ACK asserted*/
+# define SBIC_CSR_SDP 0x21 /* (I) SDP msg received */
+# define SBIC_CSR_SEL_ABRT 0x22 /* sel/resel aborted */
+# define SBIC_CSR_XFR_PAUSED 0x23 /* (T) no ATN */
+# define SBIC_CSR_XFR_PAUSED_ATN 0x24 /* (T) ATN is asserted */
+# define SBIC_CSR_RSLT_AM 0x27 /* (I) lost selection (AM) */
+# define SBIC_CSR_MIS 0x28 /* (I) xfer aborted, ph mis */
+
+ /* Terminated Interrupts */
+# define SBIC_CSR_CMD_INVALID 0x40
+# define SBIC_CSR_DISC 0x41 /* (I) tgt disconnected */
+# define SBIC_CSR_SEL_TIMEO 0x42
+# define SBIC_CSR_PE 0x43 /* parity error */
+# define SBIC_CSR_PE_ATN 0x44 /* ditto, ATN is asserted */
+# define SBIC_CSR_XLATE_TOOBIG 0x45
+# define SBIC_CSR_RSLT_NOAM 0x46 /* (I) lost sel, no AM mode */
+# define SBIC_CSR_BAD_STATUS 0x47 /* status byte was nok */
+# define SBIC_CSR_MIS_1 0x48 /* ph mis, see low bits */
+
+# define SBIC_MCI_DATA_OUT 0x00 /* Data Out phase */
+# define SBIC_MCI_DATA_IN 0x01 /* Data In phase */
+# define SBIC_MCI_CMD 0x02 /* Command phase */
+# define SBIC_MCI_STATUS 0x03 /* Status phase */
+# define SBIC_MCI_INFO_OUT 0x04 /* Unspecified Info Out phase */
+# define SBIC_MCI_INFO_IN 0x05 /* Unspecified Info In phase */
+# define SBIC_MCI_MES_OUT 0x06 /* Message Out phase */
+# define SBIC_MCI_MES_IN 0x07 /* Message In phase */
+
+ /* Service Required Interrupts */
+# define SBIC_CSR_RSLT_NI 0x80 /* reselected, no ify msg */
+# define SBIC_CSR_RSLT_IFY 0x81 /* ditto, AM mode, got ify */
+# define SBIC_CSR_SLT 0x82 /* selected, no ATN */
+# define SBIC_CSR_SLT_ATN 0x83 /* selected with ATN */
+# define SBIC_CSR_ATN 0x84 /* (T) ATN asserted */
+# define SBIC_CSR_DISC_1 0x85 /* (I) bus is free */
+# define SBIC_CSR_UNK_GROUP 0x87 /* strange CDB1 */
+# define SBIC_CSR_MIS_2 0x88 /* (I) ph mis, see low bits */
+
+#define SBIC_PHASE(csr) SCSI_PHASE(csr)
+
+/*
+ * Command register (command codes)
+ */
+
+#define SBIC_CMD_SBT 0x80 /* Single byte xfer qualifier */
+#define SBIC_CMD_MASK 0x7f
+
+ /* Miscellaneous */
+#define SBIC_CMD_RESET 0x00 /* (DTI) lev I */
+#define SBIC_CMD_ABORT 0x01 /* (DTI) lev I */
+#define SBIC_CMD_DISC 0x04 /* ( TI) lev I */
+#define SBIC_CMD_SSCC 0x0d /* ( TI) lev I */
+#define SBIC_CMD_SET_IDI 0x0f /* (DTI) lev I */
+#define SBIC_CMD_XLATE 0x18 /* (DT ) lev II */
+
+ /* Initiator state */
+#define SBIC_CMD_SET_ATN 0x02 /* ( I) lev I */
+#define SBIC_CMD_CLR_ACK 0x03 /* ( I) lev I */
+#define SBIC_CMD_XFER_PAD 0x19 /* ( I) lev II */
+#define SBIC_CMD_XFER_INFO 0x20 /* ( I) lev II */
+
+ /* Target state */
+#define SBIC_CMD_SND_DISC 0x0e /* ( T ) lev II */
+#define SBIC_CMD_RCV_CMD 0x10 /* ( T ) lev II */
+#define SBIC_CMD_RCV_DATA 0x11 /* ( T ) lev II */
+#define SBIC_CMD_RCV_MSG_OUT 0x12 /* ( T ) lev II */
+#define SBIC_CMD_RCV 0x13 /* ( T ) lev II */
+#define SBIC_CMD_SND_STATUS 0x14 /* ( T ) lev II */
+#define SBIC_CMD_SND_DATA 0x15 /* ( T ) lev II */
+#define SBIC_CMD_SND_MSG_IN 0x16 /* ( T ) lev II */
+#define SBIC_CMD_SND 0x17 /* ( T ) lev II */
+
+ /* Disconnected state */
+#define SBIC_CMD_RESELECT 0x05 /* (D ) lev II */
+#define SBIC_CMD_SEL_ATN 0x06 /* (D ) lev II */
+#define SBIC_CMD_SEL 0x07 /* (D ) lev II */
+#define SBIC_CMD_SEL_ATN_XFER 0x08 /* (D I) lev II */
+#define SBIC_CMD_SEL_XFER 0x09 /* (D I) lev II */
+#define SBIC_CMD_RESELECT_RECV 0x0a /* (DT ) lev II */
+#define SBIC_CMD_RESELECT_SEND 0x0b /* (DT ) lev II */
+#define SBIC_CMD_WAIT_SEL_RECV 0x0c /* (DT ) lev II */
diff --git a/sys/pc98/pc98/sio.c b/sys/pc98/pc98/sio.c
new file mode 100644
index 0000000..b16d153
--- /dev/null
+++ b/sys/pc98/pc98/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/pc98/sioreg.h b/sys/pc98/pc98/sioreg.h
new file mode 100644
index 0000000..70602f0
--- /dev/null
+++ b/sys/pc98/pc98/sioreg.h
@@ -0,0 +1,125 @@
+/*-
+ * 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: @(#)comreg.h 7.2 (Berkeley) 5/9/91
+ * $Id: sioreg.h,v 1.4 1995/12/10 13:39:15 phk Exp $
+ */
+
+
+/* 16 bit baud rate divisor (lower byte in dca_data, upper in dca_ier) */
+#if defined(PC98)
+#define COMBRD(x) (7372800 / (16*(x)))
+#else
+#define COMBRD(x) (1843200 / (16*(x)))
+#endif
+
+/* interrupt enable register */
+#define IER_ERXRDY 0x1
+#define IER_ETXRDY 0x2
+#define IER_ERLS 0x4
+#define IER_EMSC 0x8
+
+/* interrupt identification register */
+#define IIR_IMASK 0xf
+#define IIR_RXTOUT 0xc
+#define IIR_RLS 0x6
+#define IIR_RXRDY 0x4
+#define IIR_TXRDY 0x2
+#define IIR_NOPEND 0x1
+#define IIR_MLSC 0x0
+#define IIR_FIFO_MASK 0xc0 /* set if FIFOs are enabled */
+
+/* fifo control register */
+#define FIFO_ENABLE 0x01
+#define FIFO_RCV_RST 0x02
+#define FIFO_XMT_RST 0x04
+#define FIFO_DMA_MODE 0x08
+#define FIFO_RX_LOW 0x00
+#define FIFO_RX_MEDL 0x40
+#define FIFO_RX_MEDH 0x80
+#define FIFO_RX_HIGH 0xc0
+
+/* character format control register */
+#define CFCR_DLAB 0x80
+#define CFCR_SBREAK 0x40
+#define CFCR_PZERO 0x30
+#define CFCR_PONE 0x20
+#define CFCR_PEVEN 0x10
+#define CFCR_PODD 0x00
+#define CFCR_PENAB 0x08
+#define CFCR_STOPB 0x04
+#define CFCR_8BITS 0x03
+#define CFCR_7BITS 0x02
+#define CFCR_6BITS 0x01
+#define CFCR_5BITS 0x00
+
+/* modem control register */
+#define MCR_LOOPBACK 0x10
+#define MCR_IENABLE 0x08
+#define MCR_DRS 0x04
+#define MCR_RTS 0x02
+#define MCR_DTR 0x01
+
+/* line status register */
+#define LSR_RCV_FIFO 0x80
+#define LSR_TSRE 0x40
+#define LSR_TXRDY 0x20
+#define LSR_BI 0x10
+#define LSR_FE 0x08
+#define LSR_PE 0x04
+#define LSR_OE 0x02
+#define LSR_RXRDY 0x01
+#define LSR_RCV_MASK 0x1f
+
+/* modem status register */
+#define MSR_DCD 0x80
+#define MSR_RI 0x40
+#define MSR_DSR 0x20
+#define MSR_CTS 0x10
+#define MSR_DDCD 0x08
+#define MSR_TERI 0x04
+#define MSR_DDSR 0x02
+#define MSR_DCTS 0x01
+
+/*
+ * WARNING: Serial console is assumed to be at COM1 address
+ * and CONUNIT must be 0.
+ *
+ * Well, maybe not...
+ */
+#ifndef CONADDR
+#define CONADDR (0x3F8)
+#endif
+
+#ifndef CONUNIT
+#define CONUNIT (0)
+#endif
diff --git a/sys/pc98/pc98/sound/CHANGELOG b/sys/pc98/pc98/sound/CHANGELOG
new file mode 100644
index 0000000..e21dfaa
--- /dev/null
+++ b/sys/pc98/pc98/sound/CHANGELOG
@@ -0,0 +1,122 @@
+Changelog for version 3.0-950506
+------------------------------------
+
+Since 3.0-94xxxx
+- Too many changes
+
+Since 3.0-940818
+- Fixes for Linux 1.1.4x.
+- Disables Disney Sound System with SG NX Pro 16 (less noise).
+
+Since 2.90-2
+- Fixes to soundcard.h
+- Non blocking mode to /dev/sequencer
+- Experimental detection code for Ensoniq Soundscape.
+
+Since 2.90
+- Minor and major bug fixes
+
+Since pre-3.0-940712
+- GUS MAX support
+- Partially working MSS/WSS support (could work with some cards).
+- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs
+ (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and
+ GUS MAX, but it doesn't work yet.
+Since pre-3.0-940426
+- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc).
+This codec chip is used in various soundcards. This version is developed
+for the 16 bit daughtercard of GUS. It should work with other cards also
+if the following requirements are met:
+ - The I/O, IRQ and DMA settings are jumper selectable or
+ the card is initialized by booting DOS before booting Linux (etc.).
+ - You add the IO, IRQ and DMA settings manually to the local.h.
+ (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that
+ the base address bust be the base address of the codec chip not the
+ card itself. For the GUS16 these are the same but most MSS compatible
+ cards have the codec located at card_base+4.
+- Some minor changes
+
+Since 2.5 (******* MAJOR REWRITE ***********)
+
+This version is based on v2.3. I have tried to maintain two versions
+together so that this one should have the same features than v2.5.
+Something may still be missing. If you notice such things, please let me
+know.
+
+The Readme.v30 contains more details.
+
+- /dev/midi## devices.
+- /dev/sequencer2
+
+Since 2.5-beta2
+- Some fine tuning to the GUS v3.7 mixer code.
+- Fixed speed limits for the plain SB (1.0 to 2.0).
+
+Since 2.5-beta
+- Fixed OPL-3 detection with SB. Caused problems with PAS16.
+- GUS v3.7 mixer support.
+
+Since 2.4
+- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h).
+- Fixed truncated sound on /dev/dsp when the device is closed.
+- Linear volume mode for GUS
+- Pitch bends larger than +/- 2 octaves.
+- MIDI recording for SB and SB Pro. (Untested).
+- Some other fixes.
+- SB16 MIDI and DSP drivers only initialized if SB16 actually installed.
+- Implemented better detection for OPL-3. This should be useful if you
+ have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3.
+- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested).
+
+Since 2.3b
+- Fixed bug which made it impossible to make long recordings to disk.
+ Recording was not restarted after a buffer overflow situation.
+- Limited mixer support for GUS.
+- Numerous improvements to the GUS driver by Andrew Robinson. Including
+ some click removal etc.
+
+Since 2.3
+- Fixed some minor bugs in the SB16 driver.
+
+Since 2.2b
+- Full SB16 DSP support. 8/16 bit, mono/stereo
+- The SCO and FreeBSD versions should be in sync now. There are some
+ problems with SB16 and GUS in the freebsd versions.
+ The DMA buffer allocation of the SCO version has been polished but
+ there could still be some problems. At least it hogs memory.
+ The DMA channel
+ configuration method used in the sco/System is a hack.
+- Support for the MPU emulation of the SB16.
+- Some big arrays are now allocated boot time. This makes the bss segment
+ smaller which makes it possible to use the full driver with
+ NetBSD. These arrays are not allocated if no suitable soundcard is available.
+- Fixed a bug in the compute_and_set_volume in gus_wave.c
+- Fixed the too fast mono playback problem of SB Pro and PAS16.
+
+Since 2.2
+- Stereo recording for SB Pro. Somehow it was missing and nobody
+ had noticed it earlier.
+- Minor polishing.
+- Interpreting of boot time arguments (sound=) for Linux.
+- Breakup of sb_dsp.c. Parts of the code has been moved to
+ sb_mixer.c and sb_midi.c
+
+Since 2.1
+- Preliminary support for SB16.
+ - The SB16 mixer is supported in it's native mode.
+ - Digitized voice capability up to 44.1 kHz/8 bit/mono
+ (16 bit and stereo support coming in the next release).
+- Fixed some bugs in the digitized voice driver for PAS16.
+- Proper initialization of the SB emulation of latest PAS16 models.
+
+- Significantly improved /dev/dsp and /dev/audio support.
+ - Now supports half duplex mode. It's now possible to record and
+ playback without closing and reopening the device.
+ - It's possible to use smaller buffers than earlier. There is a new
+ ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4.
+ This call instructs the driver to use smaller buffers. The default
+ buffer size (0.5 to 1.0 seconds) is divided by n. Should be called
+ immediately after opening the device.
+
+Since 2.0
+Just cosmetic changes.
diff --git a/sys/pc98/pc98/sound/COPYING b/sys/pc98/pc98/sound/COPYING
new file mode 100644
index 0000000..d1509c5
--- /dev/null
+++ b/sys/pc98/pc98/sound/COPYING
@@ -0,0 +1,25 @@
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
diff --git a/sys/pc98/pc98/sound/README b/sys/pc98/pc98/sound/README
new file mode 100644
index 0000000..1c31ac6
--- /dev/null
+++ b/sys/pc98/pc98/sound/README
@@ -0,0 +1,86 @@
+VoxWare v2.90 release notes
+--------------------------
+
+
+ This version includes some hidden features which
+ are described in the file experimental.txt
+ Some of these features are not enabled by default. Look at
+ experimental.txt for more info.
+
+ I just decided to release this version with some
+ incompletely implemented features disabled since
+ there are some new features required by a popular
+ application. In addition there is also support
+ for the GUS MAX and the 16 bit sampling option of GUS.
+
+ The MSS/WSS support works now. At least with SG NX Pro 16.
+
+********* IMPORTANT *****************************************
+Linux 1.0 or later is required to by this driver version.
+
+Don't distribute binaries which use /dev/sequencer and are
+compiled with the soundcard.h of this version. They will
+not work with version 2.x of the driver.
+*************************************************************
+
+
+You will need the snd-util-2.5.tar.gz and snd-data-0.1.tar.Z
+packages to use this driver. They should be in the same
+ftp site or BBS from where you got this driver. For
+example at nic.funet.fi:pub/OS/Linux/*.
+
+If you are looking for the installation instructions, please
+look at linux/Readme.
+
+Compatibility with the earlier versions
+---------------------------------------
+
+This version is backward compatible with the version 2.X. All programs
+compiled with sys/soundcard.h of v2.X should work without problems.
+PROGRAMS COMPILED WITH THE sys/soundcard.h OF THIS VERSION WILL NOT
+WORK WITH v2.X DRIVER. BE CAREFUL WHEN DISTRIBUTING BINARIES COMPILED
+FOR THIS VERSION.
+
+Contributors
+------------
+
+This driver contains code by several contributors. In addition several other
+persons have given useful suggestions. The following is a list of major
+contributors. (I could have forgotten some names.)
+
+ Craig Metz 1/2 of the PAS16 Mixer and PCM support
+ Rob Hooft Volume computation algorithm for the FM synth.
+ Mika Liljeberg uLaw encoding and decoding routines
+ Greg Lee Volume computation algorithm for the GUS and
+ lot's of valuable suggestions.
+ Andy Warner ISC port
+ Jim Lowe FreeBSD port
+ Anders Baekgaard Bughunting and valuable suggestions.
+ Joerg Schubert SB16 DSP support.
+ Andrew Robinson Improvements to the GUS driver
+ Megens SA MIDI recording for SB and SB Pro.
+ Mikael Nordqvist Linear volume support for GUS.
+ Mikael Nordqvist Linear volume support for GUS.
+ Ian Hartas SVR4.2 port
+ Markus Aroharju and
+ Risto Kankkunen Major contributions to the mixer support
+ of GUS v3.7.
+ Hunyue Yau Mixer support for SG NX Pro.
+ Marc Hoffman PSS support.
+
+Regards,
+
+Hannu Savolainen
+hannu@voxware.pp.fi
+
+Snail mail: Hannu Savolainen
+ Hiekkalaiturintie 3 A 8
+ 00980 Helsinki
+ Finland
+FAX: +358 0 341 6272 (answers if I have my machine (mgetty) on).
+
+NOTE! I probably don't answer to Snail mail or FAX messages. Sending answer
+ to each of them is simply too expensive and time consuming. However I
+ try to reply every email message I get (within a week). If you don't
+ get response, please check how your address is written in the message
+ header. I can't answer if I don't have a valid reply address.
diff --git a/sys/pc98/pc98/sound/Readme.aedsp16 b/sys/pc98/pc98/sound/Readme.aedsp16
new file mode 100644
index 0000000..b205a9d
--- /dev/null
+++ b/sys/pc98/pc98/sound/Readme.aedsp16
@@ -0,0 +1,6 @@
+Informations about Audio Excel DSP 16 can be found in the source
+file aedsp16.c
+Please, read the head of the source before using it. It contain useful
+informations.
+
+ Riccardo
diff --git a/sys/pc98/pc98/sound/Readme.modules b/sys/pc98/pc98/sound/Readme.modules
new file mode 100644
index 0000000..315540f
--- /dev/null
+++ b/sys/pc98/pc98/sound/Readme.modules
@@ -0,0 +1,87 @@
+ Linux sound-driver module
+ (c) Peter Trattler
+ License: GPL (Gnu Public License)
+
+
+Idea:
+
+I've modified the sources for the sound driver to allow simply insert and
+remove the sound driver from the kernel by calling (only available for Linux)
+
+ insmod /usr/src/linux/modules/sound.o
+
+and
+
+ rmmod sound
+
+This may be useful if you are doing one of the following things:
+
+1) Debugging the sound driver
+2) Creating a new device within the sound-driver
+3) You do not the sound driver all the time (as it wastes quite a lot of
+memory for its buffers)
+
+
+Compilation:
+
+Go to /usr/src/linux and make the following steps:
+
+a) configure the sound driver: To do that call "make config" and enable the
+sound-driver -- you will be asked different questions about your
+sound-hardware (remember not to use a too big DMA-Buffer size; you
+should use 16kB, if you have 16Bit devices, otherwise you can use 32kB)
+
+b) disable the sound driver in the kernel: call make config again but answer
+'N' to "Sound card support"
+
+c) run "make modules"; the sound-driver sound.o should end up in
+/usr/src/linux/modules
+
+
+If memory is tight:
+
+I've allocated at about 70kB for the sound-drivers internal tables. If this
+is too much, 'insmod sound.o' will generate the following warning
+...
+use 'insmod memsize=xxxx'
+...
+You can only use this command, if you have (I think) at least
+modules-1.1.87 or up. You can also switch debugging on by running the command
+
+insmod sound.o debugmem=1
+
+
+Files I changed:
+
+I've only changed the files soundcard.c(most changes) and some changes within
+the Makefile, sound_config.h and the Makefile in /usr/src/linux/drivers
+
+
+Bugs:
+
+a) As the kmalloc (..., GFP_DMA) caused some unexpected errors (I don't know if
+it is my fault), I created some code, which is (by default) enabled by
+
+#define KMALLOC_DMA_BROKEN 1 (within soundcard.c).
+
+It trys to allocate a large enough region, so that the complete dma-buffer
+can be occupied in this space. If it does not fit within this region it
+doubles the size of it. But this can cause problems, if the sound-buffer is
+too big (as kmalloc can only handle regions at up to circa 100kB).
+
+So take care to use for 8Bit devices a sound-DMA-buffer of 32kB (maximum)
+and for 16Bit devices a maximum of 16kB. Otherwise the allocation scheme
+might fail.
+
+b) Buffers allocated by the different sound devices via calls to kmalloc are
+not freed, if the sound driver is removed again (these buffers tend to be
+quite small -- so it does not harm a lot)
+
+c) If there is not enough (kernel-) memory available, the installation of
+the sound-driver fails. (This happens quite often, if you did not install the
+driver right after booting -- [PS: I've only got 5MB of Ram, so this might
+be the source for this problem])
+
+
+Author:
+ Peter Trattler (peter@sbox.tu-graz.ac.at)
diff --git a/sys/pc98/pc98/sound/Readme.v30 b/sys/pc98/pc98/sound/Readme.v30
new file mode 100644
index 0000000..8884ad8
--- /dev/null
+++ b/sys/pc98/pc98/sound/Readme.v30
@@ -0,0 +1,142 @@
+VoxWare v3.0
+------------
+
+This is a late alpha/early beta of the VoxWare v3.0 to be relased May/June 95.
+
+All features of v2.90-2 should work as earlier. There could be some
+omissions but they are unintentional. I started this version thread
+after v2.3 so all features implemented before it are there.
+
+New features
+============
+
+There are now two new device interfaces. The /dev/midi## is a raw
+tty like interface to MIDI ports. There is a device file for each MIDI
+port on your system. They are named (/dev/midi00 to /dev/midiNN).
+The second addition is the /dev/music which is higher level interface
+than the old /dev/sequencer. It's intended for writing device independent
+applications like sequencers.
+
+/dev/midi##
+-----------
+
+This interface should be usefull for applications like MIDI sysex librarians.
+There are (currently) no timing features so making music could be impossible.
+
+There are as many /dev/midi## devices as there are MIDI ports in the system.
+The /dev/midi00 is connected to the first one, /dev/midi01 to the second etc.
+
+These devices work like tty devices in raw mode. Everything written to them is
+sent out to the MIDI port. There is currently an extra delay of at most
+1/100th of sec but it will be removed later.
+
+The reading algorithm is little bit more complicated. There are two different
+cases:
+
+1) There is at least one byte in the input buffer.
+
+The read returns as many bytes as it can without waiting for more bytes.
+For example when a process reads 100 bytes and there are 10 bytes in the
+buffer, the read returns just 10 bytes.
+
+2) The input buffer is empty when the process calls read.
+
+The read waits for the first byte and then continues as in case 1. By
+default it waits infinitely but there is an ioctl for setting a timeout
+for this. The ioctl(fd, SNDCTL_MIDI_PRETIME, &time) changes the timeout.
+The time is given in 1/10th of seconds (10 means one second).
+
+Other ioctl calls:
+
+ioctl(fd, SNDCTL_MIDI_MPUMODE, &mode) is available for full MPU-401
+compatible devices such as MPU-IPC-T, MQ PC Midi Card or MQX-32.
+It's not available for the so called MPU UART ports of some soundcards
+(PAS16, SB16 etc). By default the MIDI port is in UART mode after open.
+If this ioctl is called with mode=1, the interface is put to the intelligent
+(coprocessor) mode. NOTE! The MIDI port will be reset when this ioctl is called.
+It could have some strange effects if not called immediately after open. This
+vall returns EINVAL if the midi port doesn't support the MPU-401 intelligent
+mode.
+
+ioctl(fd, SNDCTL_MIDI_MPUCMD, &cmdstruct) is valid only if the MIDI port
+is put to the coprocessor mode using ioctl(SNDCTL_MIDI_MPUMODE). It's used to
+send commands to a MPU-401 compatible MIDI cards. Please refer to the
+MPU-401 Technical Reference Manual (or Music Quest Technical Reference
+Manual) for descriptions of the commands.
+
+The argument of SNDCTL_MIDI_MPUCOMMAND is of type mpu_command_rec. It
+has the following fields:
+
+typedef struct {
+ unsigned char cmd;
+
+ char nr_args, nr_returns;
+ unsigned char data[30];
+ } mpu_command_rec;
+
+where:
+ cmd Contains the command number.
+ nr_args Number of arguments of the command.
+ MUST BE INITIALIZED BEFORE CALL
+ nr_returns Number of bytes returned by the command.
+ MUST BE INITIALIZED BEFORE CALL
+ data Buffer for the command arguments and returned
+ data.
+
+Be extremely carefull with the nr_args and nr_returns fields. They
+must match the command. An incorrect value will put the card and
+the driver out of sync. Refer to the MPU-401/MQX-32M documentation for further
+datails.
+
+
+
+/dev/music (/dev/sequencer2)
+----------------------------
+
+This device file works much like the /dev/sequencer which has been present
+since the beginning. The main differences are the following:
+
+- /dev/sequencer makes the MIDI ports to look like the synth devices. In fact
+the result is somewhere between the MIDI specification and the synth devices of
+/dev/sequencer. Both kind of devices are accessed using the SEQ_START_NOTE()
+like macros. The voice number parameters of the API macros have been redefined
+to denote MIDI channels. This means that the driver allocates voices for
+the channels automaticly (this is a responsibility/right of an application
+with /dev/sequencer). The result is that a SEQ_START_NOTE() macro has
+similar effects for a synth channel than on a MIDI port. This kind of
+solution provides better device independence than the /dev/sequencer. The
+drawback is that the new interface doesn't permit so low level access to the
+device as the /dev/sequencer does. An application developer must choose between
+these two interfaces. I think the old /dev/sequencer is better for applications
+like module players while the new one is better for making generic sequencer
+programs.
+
+- There are no separate MIDI devices with the /dev/sequencer2. The
+ioctl(SNDCTL_SEQ_NRMIDIS) returns always zero. Instead the MIDI ports are
+shown as synth devices. ioctl(SNDCTL_SEQ_NRSYNTHS) on /dev/sequencer2 will
+return sum of internal synthesizers (GUS, OPL3) and MIDI ports in the systems.
+
+- The new interface is used much like the ordinary /dev/sequencer. The
+event format is new so you have to use the API macros defined in the
+sys/soundcard.h. The interface is will propably change before the final 3.0
+release but using the API macros should ensure compatibility in source level.
+The new event format is not recognized by version 2.X so don't try to
+distribute binaries compiled with soundcard.h of v3.X.
+
+- The basic API useage is similar to the current one. There are some new
+macros but the older ones should work as earlier. The most important
+incompatibility is that the /dev/sequencer2 driver allocates voices itself.
+The other one is that the application must send SEQ_START_TIMER() as it's
+first event. Otherwise the timer is not started and the application waits
+infinitely.
+
+
+There are several new features but I don't document them here. There are
+some info in the soundcard.h (near the end). I have also included some
+sample code in the directory v30. Full documentation will
+appear in the Hacker's Guide later.
+
+Don't hesitate to contact me in case you have questions or comments.
+
+Hannu Savolainen
+hannu@voxware.pp.fi
diff --git a/sys/pc98/pc98/sound/ad1848.c b/sys/pc98/pc98/sound/ad1848.c
new file mode 100644
index 0000000..eeeddae
--- /dev/null
+++ b/sys/pc98/pc98/sound/ad1848.c
@@ -0,0 +1,1525 @@
+/*
+ * sound/ad1848.c
+ *
+ * The low level driver for the AD1848/CS4248 codec chip which
+ * is used for example in the MS Sound System.
+ *
+ * The CS4231 which is used in the GUS MAX and some other cards is
+ * upwards compatible with AD1848 and this driver is able to drive it.
+ *
+ * Copyright by Hannu Savolainen 1994, 1995
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Modified:
+ * Riccardo Facchetti 24 Mar 1995
+ * - Added the Audio Excel DSP 16 initialization routine.
+ */
+
+#define DEB(x)
+#define DEB1(x)
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AD1848)
+
+#include "ad1848_mixer.h"
+
+#define IMODE_NONE 0
+#define IMODE_OUTPUT 1
+#define IMODE_INPUT 2
+#define IMODE_INIT 3
+#define IMODE_MIDI 4
+
+typedef struct
+ {
+ int base;
+ int irq;
+ int dma_capture, dma_playback;
+ unsigned char MCE_bit;
+ unsigned char saved_regs[16];
+
+ int speed;
+ unsigned char speed_bits;
+ int channels;
+ int audio_format;
+ unsigned char format_bits;
+
+ int xfer_count;
+ int irq_mode;
+ int intr_active;
+ int opened;
+ char *chip_name;
+ int mode;
+
+ /* Mixer parameters */
+ int recmask;
+ int supported_devices;
+ int supported_rec_devices;
+ unsigned short levels[32];
+ }
+
+ad1848_info;
+
+static int nr_ad1848_devs = 0;
+static char irq2dev[16] =
+{-1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1};
+
+static char mixer2codec[MAX_MIXER_DEV] =
+{0};
+
+static int ad_format_mask[3 /*devc->mode */ ] =
+{
+ 0,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
+ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM
+};
+
+static ad1848_info dev_info[MAX_AUDIO_DEV];
+
+#define io_Index_Addr(d) ((d)->base)
+#define io_Indexed_Data(d) ((d)->base+1)
+#define io_Status(d) ((d)->base+2)
+#define io_Polled_IO(d) ((d)->base+3)
+
+static int ad1848_open (int dev, int mode);
+static void ad1848_close (int dev);
+static int ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local);
+static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static int ad1848_prepare_for_IO (int dev, int bsize, int bcount);
+static void ad1848_reset (int dev);
+static void ad1848_halt (int dev);
+
+static int
+ad_read (ad1848_info * devc, int reg)
+{
+ unsigned long flags;
+ int x;
+ int timeout = 100;
+
+ while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */
+ timeout--;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc));
+ x = INB (io_Indexed_Data (devc));
+ /* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
+ RESTORE_INTR (flags);
+
+ return x;
+}
+
+static void
+ad_write (ad1848_info * devc, int reg, int data)
+{
+ unsigned long flags;
+ int timeout = 100;
+
+ while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */
+ timeout--;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc));
+ OUTB ((unsigned char) (data & 0xff), io_Indexed_Data (devc));
+ /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */
+ RESTORE_INTR (flags);
+}
+
+static void
+wait_for_calibration (ad1848_info * devc)
+{
+ int timeout = 0;
+
+ /*
+ * Wait until the auto calibration process has finished.
+ *
+ * 1) Wait until the chip becomes ready (reads don't return 0x80).
+ * 2) Wait until the ACI bit of I11 gets on and then off.
+ */
+
+ timeout = 100000;
+#ifdef PC98
+ while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80)
+ timeout--;
+ if ((INB (devc->base) & 0x80) == 0x80)
+#else
+ while (timeout > 0 && INB (devc->base) & 0x80)
+ timeout--;
+ if (INB (devc->base) & 0x80)
+#endif
+ printk ("ad1848: Auto calibration timed out(1).\n");
+
+ timeout = 100;
+ while (timeout > 0 && !(ad_read (devc, 11) & 0x20))
+ timeout--;
+ if (!(ad_read (devc, 11) & 0x20))
+ return;
+
+ timeout = 10000;
+ while (timeout > 0 && ad_read (devc, 11) & 0x20)
+ timeout--;
+ if (ad_read (devc, 11) & 0x20)
+ printk ("ad1848: Auto calibration timed out(3).\n");
+}
+
+static void
+ad_mute (ad1848_info * devc)
+{
+ int i;
+ unsigned char prev;
+
+ /*
+ * Save old register settings and mute output channels
+ */
+ for (i = 6; i < 8; i++)
+ {
+ prev = devc->saved_regs[i] = ad_read (devc, i);
+ ad_write (devc, i, prev | 0x80);
+ }
+}
+
+static void
+ad_unmute (ad1848_info * devc)
+{
+ int i;
+
+ /*
+ * Restore back old volume registers (unmute)
+ */
+ for (i = 6; i < 8; i++)
+ {
+ ad_write (devc, i, devc->saved_regs[i] & ~0x80);
+ }
+}
+
+static void
+ad_enter_MCE (ad1848_info * devc)
+{
+ unsigned long flags;
+ int timeout = 1000;
+ unsigned short prev;
+
+ while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */
+ timeout--;
+
+ DISABLE_INTR (flags);
+
+ devc->MCE_bit = 0x40;
+ prev = INB (io_Index_Addr (devc));
+ if (prev & 0x40)
+ {
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ OUTB (devc->MCE_bit, io_Index_Addr (devc));
+ RESTORE_INTR (flags);
+}
+
+static void
+ad_leave_MCE (ad1848_info * devc)
+{
+ unsigned long flags;
+ unsigned char prev;
+ int timeout = 1000;
+
+ while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */
+ timeout--;
+
+ DISABLE_INTR (flags);
+
+ devc->MCE_bit = 0x00;
+ prev = INB (io_Index_Addr (devc));
+ OUTB (0x00, io_Index_Addr (devc)); /* Clear the MCE bit */
+
+ if (prev & 0x40 == 0) /* Not in MCE mode */
+ {
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ OUTB (0x00, io_Index_Addr (devc)); /* Clear the MCE bit */
+ wait_for_calibration (devc);
+ RESTORE_INTR (flags);
+}
+
+
+static int
+ad1848_set_recmask (ad1848_info * devc, int mask)
+{
+ unsigned char recdev;
+ int i, n;
+
+ mask &= devc->supported_rec_devices;
+
+ n = 0;
+ for (i = 0; i < 32; i++) /* Count selected device bits */
+ if (mask & (1 << i))
+ n++;
+
+ if (n == 0)
+ mask = SOUND_MASK_MIC;
+ else if (n != 1) /* Too many devices selected */
+ {
+ mask &= ~devc->recmask; /* Filter out active settings */
+
+ n = 0;
+ for (i = 0; i < 32; i++) /* Count selected device bits */
+ if (mask & (1 << i))
+ n++;
+
+ if (n != 1)
+ mask = SOUND_MASK_MIC;
+ }
+
+ switch (mask)
+ {
+ case SOUND_MASK_MIC:
+ recdev = 2;
+ break;
+
+ case SOUND_MASK_LINE:
+ case SOUND_MASK_LINE3:
+ recdev = 0;
+ break;
+
+ case SOUND_MASK_CD:
+ case SOUND_MASK_LINE1:
+ recdev = 1;
+ break;
+
+ default:
+ mask = SOUND_MASK_MIC;
+ recdev = 2;
+ }
+
+ recdev <<= 6;
+ ad_write (devc, 0, (ad_read (devc, 0) & 0x3f) | recdev);
+ ad_write (devc, 1, (ad_read (devc, 1) & 0x3f) | recdev);
+
+ devc->recmask = mask;
+ return mask;
+}
+
+static void
+change_bits (unsigned char *regval, int dev, int chn, int newval)
+{
+ unsigned char mask;
+ int shift;
+
+ if (mix_devices[dev][chn].polarity == 1) /* Reverse */
+ newval = 100 - newval;
+
+ mask = (1 << mix_devices[dev][chn].nbits) - 1;
+ shift = mix_devices[dev][chn].bitpos;
+ newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
+
+ *regval &= ~(mask << shift); /* Clear bits */
+ *regval |= (newval & mask) << shift; /* Set new value */
+}
+
+static int
+ad1848_mixer_get (ad1848_info * devc, int dev)
+{
+ if (!((1 << dev) & devc->supported_devices))
+ return RET_ERROR (EINVAL);
+
+ return devc->levels[dev];
+}
+
+static int
+ad1848_mixer_set (ad1848_info * devc, int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+
+ int regoffs;
+ unsigned char val;
+
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (dev > 31)
+ return RET_ERROR (EINVAL);
+
+ if (!(devc->supported_devices & (1 << dev)))
+ return RET_ERROR (EINVAL);
+
+ if (mix_devices[dev][LEFT_CHN].nbits == 0)
+ return RET_ERROR (EINVAL);
+
+ /*
+ * Set the left channel
+ */
+
+ regoffs = mix_devices[dev][LEFT_CHN].regno;
+ val = ad_read (devc, regoffs);
+ change_bits (&val, dev, LEFT_CHN, left);
+ devc->levels[dev] = left | (left << 8);
+ ad_write (devc, regoffs, val);
+ devc->saved_regs[regoffs] = val;
+
+ /*
+ * Set the left right
+ */
+
+ if (mix_devices[dev][RIGHT_CHN].nbits == 0)
+ return left | (left << 8); /* Was just a mono channel */
+
+ regoffs = mix_devices[dev][RIGHT_CHN].regno;
+ val = ad_read (devc, regoffs);
+ change_bits (&val, dev, RIGHT_CHN, right);
+ ad_write (devc, regoffs, val);
+ devc->saved_regs[regoffs] = val;
+
+ devc->levels[dev] = left | (right << 8);
+ return left | (right << 8);
+}
+
+static void
+ad1848_mixer_reset (ad1848_info * devc)
+{
+ int i;
+
+ devc->recmask = 0;
+ if (devc->mode == 2)
+ devc->supported_devices = MODE2_MIXER_DEVICES;
+ else
+ devc->supported_devices = MODE1_MIXER_DEVICES;
+
+ devc->supported_rec_devices = MODE1_REC_DEVICES;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ ad1848_mixer_set (devc, i, devc->levels[i] = default_mixer_levels[i]);
+ ad1848_set_recmask (devc, SOUND_MASK_MIC);
+}
+
+static int
+ad1848_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ ad1848_info *devc;
+
+ int codec_dev = mixer2codec[dev];
+
+ if (!codec_dev)
+ return RET_ERROR (ENXIO);
+
+ codec_dev--;
+
+ devc = (ad1848_info *) audio_devs[codec_dev]->devc;
+
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, ad1848_set_recmask (devc, IOCTL_IN (arg)));
+ break;
+
+ default:
+ return IOCTL_OUT (arg, ad1848_mixer_set (devc, cmd & 0xff, IOCTL_IN (arg)));
+ }
+ else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, devc->recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, devc->supported_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, devc->supported_devices &
+ ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, devc->supported_rec_devices);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, SOUND_CAP_EXCL_INPUT);
+ break;
+
+ default:
+ return IOCTL_OUT (arg, ad1848_mixer_get (devc, cmd & 0xff));
+ }
+ }
+ else
+ return RET_ERROR (EINVAL);
+}
+
+static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] =
+{
+ {
+ "Generic AD1848 codec",
+ DMA_AUTOMODE,
+ AFMT_U8, /* Will be set later */
+ NULL,
+ ad1848_open,
+ ad1848_close,
+ ad1848_output_block,
+ ad1848_start_input,
+ ad1848_ioctl,
+ ad1848_prepare_for_IO,
+ ad1848_prepare_for_IO,
+ ad1848_reset,
+ ad1848_halt,
+ NULL,
+ NULL
+ }};
+
+static struct mixer_operations ad1848_mixer_operations =
+{
+ "AD1848/CS4248/CS4231",
+ ad1848_mixer_ioctl
+};
+
+static int
+ad1848_open (int dev, int mode)
+{
+ int err;
+ ad1848_info *devc = NULL;
+ unsigned long flags;
+
+ DEB (printk ("ad1848_open(int mode = %X)\n", mode));
+
+ if (dev < 0 || dev >= num_audiodevs)
+ return RET_ERROR (ENXIO);
+
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ DISABLE_INTR (flags);
+ if (devc->opened)
+ {
+ RESTORE_INTR (flags);
+ printk ("ad1848: Already opened\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (devc->irq) /* Not managed by another driver */
+ if ((err = snd_set_irq_handler (devc->irq, ad1848_interrupt,
+ audio_devs[dev]->name)) < 0)
+ {
+ printk ("ad1848: IRQ in use\n");
+ RESTORE_INTR (flags);
+ return err;
+ }
+
+ if (DMAbuf_open_dma (dev) < 0)
+ {
+ RESTORE_INTR (flags);
+ printk ("ad1848: DMA in use\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ devc->intr_active = 0;
+ devc->opened = 1;
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+static void
+ad1848_close (int dev)
+{
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ DEB (printk ("ad1848_close(void)\n"));
+
+ DISABLE_INTR (flags);
+
+ devc->intr_active = 0;
+ if (devc->irq) /* Not managed by another driver */
+ snd_release_irq (devc->irq);
+ ad1848_reset (dev);
+ DMAbuf_close_dma (dev);
+ devc->opened = 0;
+
+ RESTORE_INTR (flags);
+}
+
+static int
+set_speed (ad1848_info * devc, int arg)
+{
+ /*
+ * The sampling speed is encoded in the least significant nible of I8. The
+ * LSB selects the clock source (0=24.576 MHz, 1=16.9344 Mhz) and other
+ * three bits select the divisor (indirectly):
+ *
+ * The available speeds are in the following table. Keep the speeds in
+ * the increasing order.
+ */
+ typedef struct
+ {
+ int speed;
+ unsigned char bits;
+ }
+ speed_struct;
+
+ static speed_struct speed_table[] =
+ {
+ {5510, (0 << 1) | 1},
+ {5510, (0 << 1) | 1},
+ {6620, (7 << 1) | 1},
+ {8000, (0 << 1) | 0},
+ {9600, (7 << 1) | 0},
+ {11025, (1 << 1) | 1},
+ {16000, (1 << 1) | 0},
+ {18900, (2 << 1) | 1},
+ {22050, (3 << 1) | 1},
+ {27420, (2 << 1) | 0},
+ {32000, (3 << 1) | 0},
+ {33075, (6 << 1) | 1},
+ {37800, (4 << 1) | 1},
+ {44100, (5 << 1) | 1},
+ {48000, (6 << 1) | 0}
+ };
+
+ int i, n, selected = -1;
+
+ n = sizeof (speed_table) / sizeof (speed_struct);
+
+ if (arg < speed_table[0].speed)
+ selected = 0;
+ if (arg > speed_table[n - 1].speed)
+ selected = n - 1;
+
+ for (i = 1 /*really */ ; selected == -1 && i < n; i++)
+ if (speed_table[i].speed == arg)
+ selected = i;
+ else if (speed_table[i].speed > arg)
+ {
+ int diff1, diff2;
+
+ diff1 = arg - speed_table[i - 1].speed;
+ diff2 = speed_table[i].speed - arg;
+
+ if (diff1 < diff2)
+ selected = i - 1;
+ else
+ selected = i;
+ }
+
+ if (selected == -1)
+ {
+ printk ("ad1848: Can't find speed???\n");
+ selected = 3;
+ }
+
+ devc->speed = speed_table[selected].speed;
+ devc->speed_bits = speed_table[selected].bits;
+ return devc->speed;
+}
+
+static int
+set_channels (ad1848_info * devc, int arg)
+{
+ if (arg != 1 && arg != 2)
+ return devc->channels;
+
+ devc->channels = arg;
+ return arg;
+}
+
+static int
+set_format (ad1848_info * devc, int arg)
+{
+
+ static struct format_tbl
+ {
+ int format;
+ unsigned char bits;
+ }
+ format2bits[] =
+ {
+ {
+ 0, 0
+ }
+ ,
+ {
+ AFMT_MU_LAW, 1
+ }
+ ,
+ {
+ AFMT_A_LAW, 3
+ }
+ ,
+ {
+ AFMT_IMA_ADPCM, 5
+ }
+ ,
+ {
+ AFMT_U8, 0
+ }
+ ,
+ {
+ AFMT_S16_LE, 2
+ }
+ ,
+ {
+ AFMT_S16_BE, 6
+ }
+ ,
+ {
+ AFMT_S8, 0
+ }
+ ,
+ {
+ AFMT_U16_LE, 0
+ }
+ ,
+ {
+ AFMT_U16_BE, 0
+ }
+ };
+ int i, n = sizeof (format2bits) / sizeof (struct format_tbl);
+
+ if (!(arg & ad_format_mask[devc->mode]))
+ arg = AFMT_U8;
+
+ devc->audio_format = arg;
+
+ for (i = 0; i < n; i++)
+ if (format2bits[i].format == arg)
+ {
+ if ((devc->format_bits = format2bits[i].bits) == 0)
+ return devc->audio_format = AFMT_U8; /* Was not supported */
+
+ return arg;
+ }
+
+ /* Still hanging here. Something must be terribly wrong */
+ devc->format_bits = 0;
+ return devc->audio_format = AFMT_U8;
+}
+
+static int
+ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return set_speed (devc, arg);
+ return IOCTL_OUT (arg, set_speed (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return devc->speed;
+ return IOCTL_OUT (arg, devc->speed);
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return set_channels (devc, arg + 1) - 1;
+ return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg) + 1) - 1);
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return set_channels (devc, arg);
+ return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return devc->channels;
+ return IOCTL_OUT (arg, devc->channels);
+
+ case SNDCTL_DSP_SAMPLESIZE:
+ if (local)
+ return set_format (devc, arg);
+ return IOCTL_OUT (arg, set_format (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return devc->audio_format;
+ return IOCTL_OUT (arg, devc->audio_format);
+
+ default:;
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static void
+ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ cnt = count;
+
+ if (devc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ }
+ else
+ {
+ if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (devc->channels > 1)
+ cnt >>= 1;
+ cnt--;
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->irq_mode = IMODE_OUTPUT;
+ devc->intr_active = 1;
+ return; /*
+ * Auto DMA mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ /* ad1848_halt (dev); */
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+ }
+
+ ad_enter_MCE (devc);
+
+ ad_write (devc, 15, (unsigned char) (cnt & 0xff));
+ ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
+
+ ad_write (devc, 9, 0x0d); /*
+ * Playback enable, single DMA channel mode,
+ * auto calibration on.
+ */
+
+ ad_leave_MCE (devc); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ ad_unmute (devc);
+
+ devc->xfer_count = cnt;
+ devc->irq_mode = IMODE_OUTPUT;
+ devc->intr_active = 1;
+ INB (io_Status (devc));
+ OUTB (0, io_Status (devc)); /* Clear pending interrupts */
+ RESTORE_INTR (flags);
+}
+
+static void
+ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ int count_reg = 14; /* (devc->mode == 1) ? 14 : 30; */
+
+ cnt = count;
+ if (devc->audio_format == AFMT_IMA_ADPCM)
+ {
+ cnt /= 4;
+ }
+ else
+ {
+ if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
+ cnt >>= 1;
+ }
+ if (devc->channels > 1)
+ cnt >>= 1;
+ cnt--;
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == devc->xfer_count)
+ {
+ devc->irq_mode = IMODE_INPUT;
+ devc->intr_active = 1;
+ return; /*
+ * Auto DMA mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ /* ad1848_halt (dev); */
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+ }
+
+ ad_enter_MCE (devc);
+
+ ad_write (devc, count_reg + 1, (unsigned char) (cnt & 0xff));
+ ad_write (devc, count_reg, (unsigned char) ((cnt >> 8) & 0xff));
+
+ ad_write (devc, 9, 0x0e); /*
+ * Capture enable, single DMA channel mode,
+ * auto calibration on.
+ */
+
+ ad_leave_MCE (devc); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ ad_unmute (devc);
+
+ devc->xfer_count = cnt;
+ devc->irq_mode = IMODE_INPUT;
+ devc->intr_active = 1;
+ INB (io_Status (devc));
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+ RESTORE_INTR (flags);
+}
+
+static int
+ad1848_prepare_for_IO (int dev, int bsize, int bcount)
+{
+ int timeout;
+ unsigned char fs;
+ unsigned long flags;
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ DISABLE_INTR (flags);
+ ad_enter_MCE (devc); /* Enables changes to the format select reg */
+ fs = devc->speed_bits | (devc->format_bits << 5);
+
+ if (devc->channels > 1)
+ fs |= 0x10;
+
+ ad_write (devc, 8, fs);
+ /*
+ * Write to I8 starts resyncronization. Wait until it completes.
+ */
+ timeout = 10000;
+#ifdef PC98
+ while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80)
+#else
+ while (timeout > 0 && INB (devc->base) == 0x80)
+#endif
+ timeout--;
+
+#ifdef PC98
+ ad_write (devc, 8, fs);
+ /*
+ * Write to I8 starts resyncronization. Wait until it completes.
+ */
+ timeout = 10000;
+ while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80)
+ timeout--;
+#endif
+
+ /*
+ * If mode == 2 (CS4231), set I28 also. It's the capture format register.
+ */
+ if (devc->mode == 2)
+ {
+ ad_write (devc, 28, fs);
+
+ /*
+ * Write to I28 starts resyncronization. Wait until it completes.
+ */
+ timeout = 10000;
+#ifdef PC98
+ while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80)
+#else
+ while (timeout > 0 && INB (devc->base) == 0x80)
+#endif
+ timeout--;
+
+ }
+
+#ifdef PC98
+ ad_write (devc, 28, fs);
+
+ /*
+ * Write to I28 starts resyncronization. Wait until it completes.
+ */
+ timeout = 10000;
+ while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80)
+ timeout--;
+#endif
+
+ ad_leave_MCE (devc); /*
+ * Starts the calibration process and
+ * enters playback mode after it.
+ */
+ RESTORE_INTR (flags);
+ devc->xfer_count = 0;
+ return 0;
+}
+
+static void
+ad1848_reset (int dev)
+{
+ ad1848_halt (dev);
+}
+
+static void
+ad1848_halt (int dev)
+{
+ ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
+
+ ad_mute (devc);
+#ifdef PC98
+ ad_enter_MCE (devc);
+#endif
+ ad_write (devc, 9, ad_read (devc, 9) & ~0x03); /* Stop DMA */
+#ifdef PC98
+ ad_leave_MCE (devc);
+#endif
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+
+ ad_enter_MCE (devc);
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+ ad_write (devc, 15, 0); /* Clear DMA counter */
+ ad_write (devc, 14, 0); /* Clear DMA counter */
+
+ if (devc->mode == 2)
+ {
+ ad_write (devc, 30, 0); /* Clear DMA counter */
+ ad_write (devc, 31, 0); /* Clear DMA counter */
+ }
+
+ ad_write (devc, 9, ad_read (devc, 9) & ~0x03); /* Stop DMA */
+
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+ ad_leave_MCE (devc);
+
+ DMAbuf_reset_dma (dev);
+}
+
+int
+ad1848_detect (int io_base)
+{
+
+ unsigned char tmp;
+ int i;
+ ad1848_info *devc = &dev_info[nr_ad1848_devs];
+ unsigned char tmp1 = 0xff, tmp2 = 0xff;
+
+ if (nr_ad1848_devs >= MAX_AUDIO_DEV)
+ {
+ AUDIO_DDB (printk ("ad1848 detect error - step 0\n"));
+ return 0;
+ }
+
+ devc->base = io_base;
+ devc->MCE_bit = 0x40;
+ devc->irq = 0;
+ devc->dma_capture = 0;
+ devc->dma_playback = 0;
+ devc->opened = 0;
+ devc->chip_name = "AD1848";
+ devc->mode = 1; /* MODE1 = original AD1848 */
+
+ /*
+ * Check that the I/O address is in use.
+ *
+ * The bit 0x80 of the base I/O port is known to be 0 after the
+ * chip has performed it's power on initialization. Just assume
+ * this has happened before the OS is starting.
+ *
+ * If the I/O address is unused, it typically returns 0xff.
+ */
+
+ if ((INB (devc->base) & 0x80) != 0x00) /* Not a AD1884 */
+ {
+ AUDIO_DDB (printk ("ad1848 detect error - step A\n"));
+ return 0;
+ }
+
+ /*
+ * Test if it's possible to change contents of the indirect registers.
+ * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
+ * so try to avoid using it.
+ */
+
+ ad_write (devc, 0, 0xaa);
+ ad_write (devc, 1, 0x45); /* 0x55 with bit 0x10 clear */
+
+ if ((tmp1 = ad_read (devc, 0)) != 0xaa || (tmp2 = ad_read (devc, 1)) != 0x45)
+ {
+ AUDIO_DDB (printk ("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+
+ ad_write (devc, 0, 0x45);
+ ad_write (devc, 1, 0xaa);
+
+ if ((tmp1 = ad_read (devc, 0)) != 0x45 || (tmp2 = ad_read (devc, 1)) != 0xaa)
+ {
+ AUDIO_DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
+ return 0;
+ }
+
+ /*
+ * The indirect register I12 has some read only bits. Lets
+ * try to change them.
+ */
+
+ tmp = ad_read (devc, 12);
+ ad_write (devc, 12, (~tmp) & 0x0f);
+
+ if ((tmp & 0x0f) != ((tmp1 = ad_read (devc, 12)) & 0x0f))
+ {
+ AUDIO_DDB (printk ("ad1848 detect error - step D (%x)\n", tmp1));
+ return 0;
+ }
+
+ /*
+ * NOTE! Last 4 bits of the reg I12 tell the chip revision.
+ * 0x01=RevB and 0x0A=RevC.
+ */
+
+ /*
+ * The original AD1848/CS4248 has just 15 indirect registers. This means
+ * that I0 and I16 should return the same value (etc.).
+ * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
+ * with CS4231.
+ */
+
+ ad_write (devc, 12, 0); /* Mode2=disabled */
+
+ for (i = 0; i < 16; i++)
+ if ((tmp1 = ad_read (devc, i)) != (tmp2 = ad_read (devc, i + 16)))
+ {
+ AUDIO_DDB (printk ("ad1848 detect error - step F(%d/%x/%x)\n", i, tmp1, tmp2));
+ return 0;
+ }
+
+ /*
+ * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
+ * The bit 0x80 is always 1 in CS4248 and CS4231.
+ */
+
+ ad_write (devc, 12, 0x40); /* Set mode2, clear 0x80 */
+
+ tmp1 = ad_read (devc, 12);
+ if (tmp1 & 0x80)
+ devc->chip_name = "CS4248"; /* Our best knowledge just now */
+
+ if ((tmp1 & 0xc0) == (0x80 | 0x40))
+ {
+ /*
+ * CS4231 detected - is it?
+ *
+ * Verify that setting I0 doesn't change I16.
+ */
+ ad_write (devc, 16, 0); /* Set I16 to known value */
+
+ ad_write (devc, 0, 0x45);
+ if ((tmp1 = ad_read (devc, 16)) != 0x45) /* No change -> CS4231? */
+ {
+
+ ad_write (devc, 0, 0xaa);
+ if ((tmp1 = ad_read (devc, 16)) == 0xaa) /* Rotten bits? */
+ {
+ AUDIO_DDB (printk ("ad1848 detect error - step H(%x)\n", tmp1));
+ return 0;
+ }
+
+ /*
+ * Verify that some bits of I25 are read only.
+ */
+
+ tmp1 = ad_read (devc, 25); /* Original bits */
+ ad_write (devc, 25, ~tmp1); /* Invert all bits */
+ if ((ad_read (devc, 25) & 0xe7) == (tmp1 & 0xe7))
+ {
+ /*
+ * It's a CS4231
+ */
+ devc->chip_name = "CS4231";
+
+
+#ifdef MOZART_PORT
+ if (devc->base != MOZART_PORT)
+#endif
+ devc->mode = 2;
+
+
+ }
+ ad_write (devc, 25, tmp1); /* Restore bits */
+ }
+ }
+
+ return 1;
+}
+
+void
+ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture)
+{
+ /*
+ * NOTE! If irq < 0, there is another driver which has allocated the IRQ
+ * so that this driver doesn't need to allocate/deallocate it.
+ * The actually used IRQ is ABS(irq).
+ */
+
+ /*
+ * Initial values for the indirect registers of CS4248/AD1848.
+ */
+ static int init_values[] =
+ {
+ 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x80, 0x80,
+ 0x00, 0x08, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00,
+
+ /* Positions 16 to 31 just for CS4231 */
+ 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ int i, my_dev;
+ ad1848_info *devc = &dev_info[nr_ad1848_devs];
+
+ if (!ad1848_detect (io_base))
+ return;
+
+ devc->irq = (irq > 0) ? irq : 0;
+ devc->dma_capture = dma_playback;
+ devc->dma_playback = dma_capture;
+ devc->opened = 0;
+
+ if (nr_ad1848_devs != 0)
+ {
+ memcpy ((char *) &ad1848_pcm_operations[nr_ad1848_devs],
+ (char *) &ad1848_pcm_operations[0],
+ sizeof (struct audio_operations));
+ }
+
+ for (i = 0; i < 16; i++)
+ ad_write (devc, i, init_values[i]);
+
+ ad_mute (devc);
+
+ if (devc->mode == 2)
+ {
+ ad_write (devc, 12, ad_read (devc, 12) | 0x40); /* Mode2 = enabled */
+ for (i = 16; i < 32; i++)
+ ad_write (devc, i, init_values[i]);
+ }
+
+ OUTB (0, io_Status (devc)); /* Clear pending interrupts */
+
+ if (name[0] != 0)
+ sprintf (ad1848_pcm_operations[nr_ad1848_devs].name,
+ "%s (%s)", name, devc->chip_name);
+ else
+ sprintf (ad1848_pcm_operations[nr_ad1848_devs].name,
+ "Generic audio codec (%s)", devc->chip_name);
+
+#if defined(__FreeBSD__)
+ if (strcmp(name, "MS Sound System")) /* *sigh* */
+ printk ("\ngus0: <%s>", ad1848_pcm_operations[nr_ad1848_devs].name);
+ else
+ printk ("mss0: <%s>", ad1848_pcm_operations[nr_ad1848_devs].name);
+#else
+ printk (" <%s>", ad1848_pcm_operations[nr_ad1848_devs].name);
+#endif
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_dev = num_audiodevs++] = &ad1848_pcm_operations[nr_ad1848_devs];
+ if (irq > 0)
+ irq2dev[irq] = my_dev;
+ else if (irq < 0)
+ irq2dev[-irq] = my_dev;
+
+ audio_devs[my_dev]->dmachan = dma_playback;
+ audio_devs[my_dev]->buffcount = 1;
+ audio_devs[my_dev]->buffsize = DSP_BUFFSIZE;
+ audio_devs[my_dev]->devc = devc;
+ audio_devs[my_dev]->format_mask = ad_format_mask[devc->mode];
+ nr_ad1848_devs++;
+
+ /*
+ * Toggle the MCE bit. It completes the initialization phase.
+ */
+
+ ad_enter_MCE (devc); /* In case the bit was off */
+ ad_leave_MCE (devc);
+
+ if (num_mixers < MAX_MIXER_DEV)
+ {
+ mixer2codec[num_mixers] = my_dev + 1;
+ audio_devs[my_dev]->mixer_dev = num_mixers;
+ mixer_devs[num_mixers++] = &ad1848_mixer_operations;
+ ad1848_mixer_reset (devc);
+ }
+ }
+ else
+ printk ("AD1848: Too many PCM devices available\n");
+}
+
+void
+ad1848_interrupt (INT_HANDLER_PARMS (irq, dummy))
+{
+ unsigned char status;
+ ad1848_info *devc;
+ int dev;
+
+ if (irq < 0 || irq > 15)
+ return; /* Bogus irq */
+ dev = irq2dev[irq];
+ if (dev < 0 || dev >= num_audiodevs)
+ return; /* Bogus dev */
+
+ devc = (ad1848_info *) audio_devs[dev]->devc;
+ status = INB (io_Status (devc));
+
+ if (status == 0x80)
+ printk ("ad1848_interrupt: Why?\n");
+
+ if (status & 0x01)
+ {
+ if (devc->opened && devc->irq_mode == IMODE_OUTPUT)
+ {
+ DMAbuf_outputintr (dev, 1);
+ }
+
+ if (devc->opened && devc->irq_mode == IMODE_INPUT)
+ DMAbuf_inputintr (dev);
+ }
+
+ OUTB (0, io_Status (devc)); /* Clear interrupt status */
+
+ status = INB (io_Status (devc));
+ if (status == 0x80 || status & 0x01)
+ {
+ printk ("ad1848: Problems when clearing interrupt, status=%x\n", status);
+ OUTB (0, io_Status (devc)); /* Try again */
+ }
+}
+
+#ifdef MOZART_PORT
+/*
+ * Experimental initialization sequence for Mozart soundcard
+ * (OAK OTI-601 sound chip).
+ * by Gregor Hoffleit <flight@mathi.uni-heidelberg.de>
+ * Some comments by Hannu Savolainen.
+ */
+
+int
+mozart_init (int io_base)
+{
+ int i;
+ unsigned char byte;
+ static int mozart_detected_here = 0;
+
+ /*
+ * Valid ports are 0x530 and 0xf40. The DOS based software doesn't allow
+ * other ports. The OTI-601 preliminary specification says that
+ * 0xe80 and 0x604 are also possible but it's safest to ignore them.
+ */
+
+ if ((io_base != 0x530) && (io_base != 0xf40))
+ {
+ printk ("Mozart: invalid io_base(%x)\n", io_base);
+ return 0;
+ }
+
+ if (mozart_detected_here == io_base) /* Already detected this card */
+ return 1;
+
+ if (mozart_detected_here != 0)
+ return 0; /* Don't allow detecting another Mozart card. */
+
+ /*
+ * The Mozart chip (OAK OTI-601) must be enabled before _each_ write
+ * by writing a secret password (0xE2) to the password register (0xf8f).
+ * Any I/O cycle after writing the password closes the gate and disbles
+ * further access.
+ */
+
+ if (INB (0xf88) != 0) /* Appears to return 0 while the gate is closed */
+ {
+ AUDIO_DDB (printk ("No Mozart signature detected on port 0xf88\n"));
+ return 0;
+ }
+
+ OUTB (0xe2, 0xf8f); /* A secret password which opens the gate */
+ OUTB (0x10, 0xf91); /* Enable access to codec registers during SB mode */
+ for (i = 0; i < 100; i++) /* Delay */
+ tenmicrosec ();
+ OUTB (0xe2, 0xf8f); /* Sesam */
+ byte = INB (0xf8d); /* Read MC1 (Mode control register) */
+
+ /* Read the same register again but with gate closed at this time. */
+ if (INB (0xf8d) == 0xff) /* Bus float. Should be 0 if Mozart present */
+ {
+ AUDIO_DDB (printk ("Seems to be no Mozart chip set\n"));
+ return 0;
+ }
+ AUDIO_DDB (printk ("mozart_init: read 0x%x on 0xf8d\n", byte));
+ byte = byte | 0x80; /* Switch to WSS mode (disables SB) */
+ byte = byte & 0xcf; /* Clear sound base, disable CD, enable joystick */
+
+ if (io_base == 0xf40)
+ byte = byte | 0x20;
+ for (i = 0; i < 100; i++)
+ tenmicrosec ();
+ OUTB (0xe2, 0xf8f); /* Open the gate again */
+ OUTB (byte, 0xf8d); /* Write the modified value back to MC1 */
+ AUDIO_DDB (printk ("mozart_init: wrote 0x%x on 0xf8d\n", byte));
+ OUTB (0xe2, 0xf8f); /* Here we come again */
+ OUTB (0x20, 0xf91); /* Protect WSS shadow registers against write */
+
+ for (i = 0; i < 1000; i++)
+ tenmicrosec ();
+
+ return 1;
+}
+
+#endif /* MOZART_PORT */
+
+#ifdef OPTI_MAD16_PORT
+#include "mad16.h"
+#endif
+
+/*
+ * Some extra code for the MS Sound System
+ */
+
+int
+probe_ms_sound (struct address_info *hw_config)
+{
+#if !defined(EXCLUDE_AEDSP16) && defined(AEDSP16_MSS)
+ /*
+ * Initialize Audio Excel DSP 16 to MSS: before any operation
+ * we must enable MSS I/O ports.
+ */
+
+ InitAEDSP16_MSS (hw_config);
+#endif
+
+ /*
+ * Check if the IO port returns valid signature. The original MS Sound
+ * system returns 0x04 while some cards (AudioTriX Pro for example)
+ * return 0x00.
+ */
+
+#ifdef MOZART_PORT
+ if (hw_config->io_base == MOZART_PORT)
+ mozart_init (hw_config->io_base);
+#endif
+
+#ifdef OPTI_MAD16_PORT
+ if (hw_config->io_base == OPTI_MAD16_PORT)
+ mad16init (hw_config->io_base);
+#endif
+
+ if ((INB (hw_config->io_base + 3) & 0x3f) != 0x04 &&
+ (INB (hw_config->io_base + 3) & 0x3f) != 0x00)
+ {
+ AUDIO_DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n",
+ hw_config->io_base, INB (hw_config->io_base + 3)));
+ return 0;
+ }
+
+#ifdef PC98
+ if (hw_config->irq > 12)
+#else
+ if (hw_config->irq > 11)
+#endif
+ {
+ printk ("MSS: Bad IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+
+ if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+ {
+ printk ("MSS: Bad DMA %d\n", hw_config->dma);
+ return 0;
+ }
+
+ /*
+ * Check that DMA0 is not in use with a 8 bit board.
+ */
+
+ if (hw_config->dma == 0 && INB (hw_config->io_base + 3) & 0x80)
+ {
+ printk ("MSS: Can't use DMA0 with a 8 bit card/slot\n");
+ return 0;
+ }
+
+ if (hw_config->irq > 7 && hw_config->irq != 9 && INB (hw_config->io_base + 3) & 0x80)
+ {
+ printk ("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
+ return 0;
+ }
+
+ return ad1848_detect (hw_config->io_base + 4);
+}
+
+long
+attach_ms_sound (long mem_start, struct address_info *hw_config)
+{
+#ifdef PC98
+ static char interrupt_bits[13] =
+ {
+ -1, -1, -1, 0x08, -1, 0x10, -1, -1, -1, -1, 0x18, -1, 0x20
+ };
+#else
+ static char interrupt_bits[12] =
+ {
+ -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
+ };
+#endif
+ char bits;
+
+ static char dma_bits[4] =
+ {
+ 1, 2, 0, 3
+ };
+
+ int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
+
+ if (!ad1848_detect (hw_config->io_base + 4))
+ return mem_start;
+
+ /*
+ * Set the IRQ and DMA addresses.
+ */
+
+ bits = interrupt_bits[hw_config->irq];
+ if (bits == -1)
+ return mem_start;
+
+ OUTB (bits | 0x40, config_port);
+ if ((INB (version_port) & 0x40) == 0)
+ printk ("[IRQ Conflict?]");
+
+ OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */
+
+ ad1848_init ("MS Sound System", hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma);
+ return mem_start;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/ad1848_mixer.h b/sys/pc98/pc98/sound/ad1848_mixer.h
new file mode 100644
index 0000000..2c1bddb
--- /dev/null
+++ b/sys/pc98/pc98/sound/ad1848_mixer.h
@@ -0,0 +1,130 @@
+/*
+ * sound/ad1848_mixer.h
+ *
+ * Definitions for the mixer of AD1848 and compatible codecs.
+ *
+ * Copyright by Hannu Savolainen 1994
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+/*
+ * The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
+ * Soundcard manufacturers have connected actual inputs (CD, synth, line,
+ * etc) to these inputs in different order. Therefore it's difficult
+ * to assign mixer channels to to these inputs correctly. The following
+ * contains two alternative mappings. The first one is for GUS MAX and
+ * the second is just a generic one (line1, line2 and line3).
+ * (Actually this is not a mapping but rather some kind of interleaving
+ * solution).
+ */
+#ifdef GUSMAX_MIXER
+#define MODE1_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD)
+
+#define MODE1_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | \
+ SOUND_MASK_IGAIN | \
+ SOUND_MASK_PCM|SOUND_MASK_IMIX)
+
+#define MODE2_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_SPEAKER | \
+ SOUND_MASK_IGAIN | \
+ SOUND_MASK_PCM | SOUND_MASK_IMIX)
+#else /* Generic mapping */
+#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \
+ SOUND_MASK_LINE1)
+
+#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \
+ SOUND_MASK_LINE2 | \
+ SOUND_MASK_IGAIN | \
+ SOUND_MASK_PCM | SOUND_MASK_IMIX)
+
+#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | SOUND_MASK_MIC | \
+ SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
+ SOUND_MASK_IGAIN | \
+ SOUND_MASK_PCM | SOUND_MASK_IMIX)
+#endif
+
+struct mixer_def {
+ unsigned int regno: 7;
+ unsigned int polarity:1; /* 0=normal, 1=reversed */
+ unsigned int bitpos:4;
+ unsigned int nbits:4;
+};
+
+
+typedef struct mixer_def mixer_ent;
+
+/*
+ * Most of the mixer entries work in backwards. Setting the polarity field
+ * makes them to work correctly.
+ *
+ * The channel numbering used by individual soundcards is not fixed. Some
+ * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
+ * The current version doesn't try to compensate this.
+ */
+
+#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r) \
+ {{reg_l, pola_l, pos_r, len_l}, {reg_r, pola_r, pos_r, len_r}}
+
+static mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */
+MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6),
+MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_MIC, 0, 1, 5, 1, 1, 1, 5, 1),
+MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4),
+MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5),
+MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5)
+};
+
+static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x5a5a, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x4b4b, /* FM */
+ 0x6464, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x1010, /* Mic */
+ 0x4b4b, /* CD */
+ 0x0000, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b, /* Recording level */
+ 0x4b4b, /* Input gain */
+ 0x4b4b, /* Output gain */
+ 0x4b4b, /* Line1 */
+ 0x4b4b, /* Line2 */
+ 0x4b4b /* Line3 */
+};
+
+#define LEFT_CHN 0
+#define RIGHT_CHN 1
diff --git a/sys/pc98/pc98/sound/adlib_card.c b/sys/pc98/pc98/sound/adlib_card.c
new file mode 100644
index 0000000..6365069
--- /dev/null
+++ b/sys/pc98/pc98/sound/adlib_card.c
@@ -0,0 +1,51 @@
+/*
+ * sound/adlib_card.c
+ *
+ * Detection routine for the AdLib card.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
+
+long
+attach_adlib_card (long mem_start, struct address_info *hw_config)
+{
+
+ if (opl3_detect (FM_MONO))
+ {
+ mem_start = opl3_init (mem_start);
+ }
+ return mem_start;
+}
+
+int
+probe_adlib (struct address_info *hw_config)
+{
+ return opl3_detect (FM_MONO);
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/aedsp16.c b/sys/pc98/pc98/sound/aedsp16.c
new file mode 100644
index 0000000..b14a246
--- /dev/null
+++ b/sys/pc98/pc98/sound/aedsp16.c
@@ -0,0 +1,838 @@
+/*
+ sound/aedsp16.c
+
+ Audio Excel DSP 16 software configuration routines
+
+ Copyright (C) 1995 Riccardo Facchetti (riccardo@cdc8g5.cdc.polimi.it)
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+
+ READ THIS
+
+ This module is intended for Audio Excel DSP 16 Sound Card.
+
+ Audio Excel DSP 16 is an SB pro II, Microsoft Sound System
+ and MPU-401 compatible card.
+ It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
+ so before this module, the only way to configure the DSP under linux was
+ boot the MS-BAU loading the sound.sys device driver (this driver soft-
+ configure the sound board hardware by massaging someone of its registers),
+ and then ctrl-alt-del to boot linux with the DSP configured by the DOG
+ driver.
+
+ This module works configuring your Audio Excel DSP 16's
+ irq, dma and mpu-401-irq. The voxware probe routines rely on the
+ fact that if the hardware is there, they can detect it. The problem
+ with AEDSP16 is that no hardware can be found by the probe routines
+ if the sound card is not well configured. Sometimes the kernel probe
+ routines can find an SBPRO even when the card is not configured (this
+ is the standard setup of the card), but the SBPRO emulation don't work
+ well if the card is not properly initialized. For this reason
+
+ InitAEDSP16_...()
+
+ routines are called before the voxware probe routines try to detect the
+ hardware.
+
+ NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
+
+ The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
+ the voxware sound driver can be configured for SBPRO and MSS cards
+ at the same time, but the aedsp16 can't be two cards!!
+ When we configure it, we have to choose the SBPRO or the MSS emulation
+ for AEDSP16. We also can install a *REAL* card of the other type
+ (see [1], not tested but I can't see any reason for it to fail).
+
+ NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
+ please let me know if it works.
+
+ The MPU-401 support can be compiled in together with one of the other
+ two operating modes.
+
+ The board configuration calls, are in the probe_...() routines because
+ we have to configure the board before probing it for a particular
+ hardware. After card configuration, we can probe the hardware.
+
+ NOTE: This is something like plug-and-play: we have only to plug
+ the AEDSP16 board in the socket, and then configure and compile
+ a kernel that uses the AEDSP16 software configuration capability.
+ No jumper setting is needed!
+
+ For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
+ you have just to make config the voxware package, configuring
+ the SBPro sound card with that parameters, then when configure
+ asks if you have an AEDSP16, answer yes. That's it.
+ Compile the kernel and run it.
+
+ NOTE: This means that you can choose irq and dma, but not the
+ I/O addresses. To change I/O addresses you have to set them
+ with jumpers.
+
+ NOTE: InitAEDSP16_...() routines get as parameter the hw_config,
+ the hardware configuration of the - to be configured - board.
+ The InitAEDSP16() routine, configure the board following our
+ wishes, that are in the hw_config structure.
+
+ You can change the irq/dma/mirq settings WITHOUT THE NEED to open
+ your computer and massage the jumpers (there are no irq/dma/mirq
+ jumpers to be configured anyway, only I/O port ones have to be
+ configured with jumpers)
+
+ For some ununderstandable reason, the card default of irq 7, dma 1,
+ don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
+ HDD work, the kernel start to erupt out a lot of messages like:
+
+ 'Sound: DMA timed out - IRQ/DRQ config error?'
+
+ For what I can say, I have NOT any conflict at irq 7 (under linux I'm
+ using the lp polling driver), and dma line 1 is unused as stated by
+ /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
+ I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
+ Anyway a setting of irq 10, dma 3 works really fine.
+
+ NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
+ the emulation mode, all the installed hardware and the hardware
+ configuration (irq and dma settings of all the hardware).
+
+ This init module should work with SBPRO+MSS, when one of the two is
+ the AEDSP16 emulation and the other the real card. (see [1])
+ For example:
+
+ AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
+ AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
+
+ MPU401 should work. (see [1])
+
+ [1] Not tested by me for lack of hardware.
+
+ TODO, WISHES AND TECH
+
+ May be there's lot of redundant delays, but for now I want to leave it
+ this way.
+
+ Should be interesting eventually write down a new ioctl for the
+ aedsp16, to let the suser() change the irq/dma/mirq on the fly.
+ The thing is not trivial.
+ In the real world, there's no need to have such an ioctl because
+ when we configure the kernel for compile, we can choose the config
+ parameters. If we change our mind, we can easily re-config the kernel
+ and re-compile.
+ Why let the suser() change the config parameters on the fly ?
+ If anyone have a reasonable answer to this question, I will write down
+ the code to do it.
+
+ More integration with voxware, using voxware low level routines to
+ read-write dsp is not possible because you may want to have MSS
+ support and in that case we can not rely on the functions included
+ in sb_dsp.c to control 0x2yy I/O ports. I will continue to use my
+ own I/O functions.
+
+ - About I/O ports allocation -
+
+ The request_region should be done at device probe in every sound card
+ module. This module is not the best site for requesting regions.
+ When the request_region code will be added to the main modules such as
+ sb, adlib, gus, ad1848, etc, the requesting code in this module should
+ go away.
+
+ I think the request regions should be done this way:
+
+ if (check_region(...))
+ return ERR; // I/O region alredy reserved
+ device_probe(...);
+ device_attach(...);
+ request_region(...); // reserve only when we are sure all is okay
+
+ Request the 2x0h region in any case if we are using this card.
+
+ NOTE: the "(sbpro)" string with which we are requesting the aedsp16 region
+ (see code) does not mean necessarly that we are emulating sbpro.
+ It mean that the region is the sbpro I/O ports region. We use this
+ region to access the control registers of the card, and if emulating
+ sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
+ registers are not used, in no way, to emulate an sbpro: they are
+ used only for configuration pourposes.
+
+ Someone pointed out that should be possible use both the SBPRO and MSS
+ modes because the sound card have all the two chipsets, supposing that
+ the card is really two cards. I have tried something to have the two
+ modes work together, but, for some reason unknown to me, without success.
+
+ I think all the soft-config only cards have an init sequence similar to
+ this. If you have a card that is not an aedsp16, you can try to start
+ with this module changing it (mainly in the CMD? I think) to fit your
+ needs.
+
+ Started Fri Mar 17 16:13:18 MET 1995
+
+ v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c)
+ - Initial code.
+ v0.2 (ALPHA)
+ - Cleanups.
+ - Integrated with Linux voxware v 2.90-2 kernel sound driver.
+ - SoundBlaster Pro mode configuration.
+ - Microsoft Sound System mode configuration.
+ - MPU-401 mode configuration.
+ v0.3 (ALPHA)
+ - Cleanups.
+ - Rearranged the code to let InitAEDSP16 be more general.
+ - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
+ inclusion too. We rely on os.h
+ - Used the INB and OUTB #defined in os.h instead of inb and outb.
+ - Corrected the code for GetCardName (DSP Copyright) to get a variable
+ len string (we are not sure about the len of Copyright string).
+ This works with any SB and compatible.
+ - Added the code to request_region at device init (should go in
+ the main body of voxware).
+ v0.4 (BETA)
+ - Better configure.c patch for aedsp16 configuration (better
+ logic of inclusion of AEDSP16 support)
+ - Modified the conditional compilation to better support more than
+ one sound card of the emulated type (read the NOTES above)
+ - Moved the sb init routine from the attach to the very first
+ probe in sb_card.c
+ - Rearrangemens and cleanups
+ - Wiped out some unnecessary code and variables: this is kernel
+ code so it is better save some TEXT and DATA
+ - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
+ I/O ports in any case because they are used to access the DSP
+ configuration registers and we can not allow anyone to get them.
+ v0.5
+ - cleanups on comments
+ - prep for diffs against v3.0-proto-950402
+
+ */
+
+/*
+ * Include the main voxware header file. It include all the os/voxware/etc
+ * headers needed by this source.
+ */
+#include "sound_config.h"
+/*
+ * all but ioport.h :)
+ */
+#include <linux/ioport.h>
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AEDSP16)
+
+#define VERSION "0.5" /* Version of Audio Excel DSP 16 driver */
+
+#undef AEDSP16_DEBUG /* Define this to enable debug code */
+/* Actually no debug code is activated */
+
+/*
+ * Hardware related defaults
+ */
+#define IRQ 7 /* 5 7(default) 9 10 11 */
+#define MIRQ 0 /* 5 7 9 10 0(default), 0 means disable */
+#define DMA 1 /* 0 1(default) 3 */
+
+/*
+ * Commands of AEDSP16's DSP (SBPRO+special).
+ * For now they are CMDn, in the future they may change.
+ */
+#define CMD1 0xe3 /* Get DSP Copyright */
+#define CMD2 0xe1 /* Get DSP Version */
+#define CMD3 0x88 /* */
+#define CMD4 0x5c /* */
+#define CMD5 0x50 /* Set M&I&DRQ mask (the real config) */
+#define CMD6 0x8c /* Enable Microsoft Sound System mode */
+
+/*
+ * Offsets of AEDSP16 DSP I/O ports. The offest is added to portbase
+ * to have the actual I/O port.
+ * Register permissions are:
+ * (wo) == Write Only
+ * (ro) == Read Only
+ * (w-) == Write
+ * (r-) == Read
+ */
+#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */
+#define DSP_READ 0x0a /* offset of DSP READ (ro) */
+#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */
+#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */
+#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */
+#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */
+
+
+#define RETRY 10 /* Various retry values on I/O opera- */
+#define STATUSRETRY 1000 /* tions. Sometimes we have to */
+#define HARDRETRY 500000 /* wait for previous cmd to complete */
+
+/*
+ * Size of character arrays that store name and version of sound card
+ */
+#define CARDNAMELEN 15 /* Size of the card's name in chars */
+#define CARDVERLEN 2 /* Size of the card's version in chars */
+
+/*
+ * Bit mapped flags for calling InitAEDSP16(), and saving the current
+ * emulation mode.
+ */
+#define INIT_NONE (0 )
+#define INIT_SBPRO (1<<0)
+#define INIT_MSS (1<<1)
+#define INIT_MPU401 (1<<2)
+#define RESET_DSP16 (1<<3)
+
+/* Base HW Port for Audio Card */
+static int portbase = AEDSP16_BASE;
+static int irq = IRQ; /* irq for DSP I/O */
+static int mirq = MIRQ; /* irq for MPU-401 I/O */
+static int dma = DMA; /* dma for DSP I/O */
+
+/* Init status of the card */
+static int ae_init = INIT_NONE; /* (bitmapped variable) */
+static int oredparams = 0; /* Will contain or'ed values of params */
+static int gc = 0; /* generic counter (utility counter) */
+struct orVals
+ { /* Contain the values to be or'ed */
+ int val; /* irq|mirq|dma */
+ int or; /* oredparams |= TheStruct.or */
+ };
+
+/*
+ * Magic values that the DSP will eat when configuring irq/mirq/dma
+ */
+/* DSP IRQ conversion array */
+static struct orVals orIRQ[] =
+{
+ {0x05, 0x28},
+ {0x07, 0x08},
+ {0x09, 0x10},
+ {0x0a, 0x18},
+ {0x0b, 0x20},
+ {0x00, 0x00}
+};
+
+/* MPU-401 IRQ conversion array */
+static struct orVals orMIRQ[] =
+{
+ {0x05, 0x04},
+ {0x07, 0x44},
+ {0x09, 0x84},
+ {0x0a, 0xc4},
+ {0x00, 0x00}
+};
+
+/* DMA Channels conversion array */
+static struct orVals orDMA[] =
+{
+ {0x00, 0x01},
+ {0x01, 0x02},
+ {0x03, 0x03},
+ {0x00, 0x00}
+};
+
+/*
+ * Buffers to store audio card informations
+ */
+static char AudioExcelName[CARDNAMELEN + 1];
+static char AudioExcelVersion[CARDVERLEN + 1];
+
+static void
+tenmillisec (void)
+{
+
+ for (gc = 0; gc < 1000; gc++)
+ tenmicrosec ();
+}
+
+static int
+WaitForDataAvail (int port)
+{
+ int loop = STATUSRETRY;
+ unsigned char ret = 0;
+
+ do
+ {
+ ret = INB (port + DSP_DATAVAIL);
+ /*
+ * Wait for data available (bit 7 of ret == 1)
+ */
+ }
+ while (!(ret & 0x80) && loop--);
+
+ if (ret & 0x80)
+ return 0;
+
+ return -1;
+}
+
+static int
+ReadData (int port)
+{
+ if (WaitForDataAvail (port))
+ return -1;
+ return INB (port + DSP_READ);
+}
+
+static int
+CheckDSPOkay (int port)
+{
+ return ((ReadData (port) == 0xaa) ? 0 : -1);
+}
+
+static int
+ResetBoard (int port)
+{
+ /*
+ * Reset DSP
+ */
+ OUTB (1, (port + DSP_RESET));
+ tenmicrosec ();
+ OUTB (0, (port + DSP_RESET));
+ tenmicrosec ();
+ tenmicrosec ();
+ return CheckDSPOkay (port);
+}
+
+static int
+WriteDSPCommand (int port, int cmd)
+{
+ unsigned char ret;
+ int loop = HARDRETRY;
+
+ do
+ {
+ ret = INB (port + DSP_STATUS);
+ /*
+ * DSP ready to receive data if bit 7 of ret == 0
+ */
+ if (!(ret & 0x80))
+ {
+ OUTB (cmd, port + DSP_COMMAND);
+ return 0;
+ }
+ }
+ while (loop--);
+
+ printk ("[aedsp16] DSP Command (0x%x) timeout.\n", cmd);
+ return -1;
+}
+
+int
+InitMSS (int port)
+{
+
+ tenmillisec ();
+
+ if (WriteDSPCommand (port, CMD6))
+ {
+ printk ("[aedsp16] CMD 0x%x: failed!\n", CMD6);
+ return -1;
+ }
+
+ tenmillisec ();
+
+ return 0;
+}
+
+static int
+SetUpBoard (int port)
+{
+ int loop = RETRY;
+
+ do
+ {
+ if (WriteDSPCommand (portbase, CMD3))
+ {
+ printk ("[aedsp16] CMD 0x%x: failed!\n", CMD3);
+ return -1;
+ }
+
+ tenmillisec ();
+
+ }
+ while (WaitForDataAvail (port) && loop--);
+
+#if defined(THIS_SHOULD_GO_AWAY)
+ if (CheckDSPOkay (port))
+ {
+ printk ("[aedsp16] CheckDSPOkay: failed\n");
+ return -1;
+ }
+#else
+ if (ReadData (port) == -1)
+ {
+ printk ("[aedsp16] ReadData after CMD 0x%x: failed\n", CMD3);
+ return -1;
+ }
+#endif
+
+ if (WriteDSPCommand (portbase, CMD4))
+ {
+ printk ("[aedsp16] CMD 0x%x: failed!\n", CMD4);
+ return -1;
+ }
+
+ if (WriteDSPCommand (portbase, CMD5))
+ {
+ printk ("[aedsp16] CMD 0x%x: failed!\n", CMD5);
+ return -1;
+ }
+
+ if (WriteDSPCommand (portbase, oredparams))
+ {
+ printk ("[aedsp16] Initialization of (M)IRQ and DMA: failed!\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+GetCardVersion (int port)
+{
+ int len = 0;
+ int ret;
+ int ver[3];
+
+ do
+ {
+ if ((ret = ReadData (port)) == -1)
+ return -1;
+ /*
+ * We alredy know how many int are stored (2), so we know when the
+ * string is finished.
+ */
+ ver[len++] = ret;
+ }
+ while (len < CARDVERLEN);
+ sprintf (AudioExcelVersion, "%d.%d", ver[0], ver[1]);
+ return 0;
+}
+
+static int
+GetCardName (int port)
+{
+ int len = 0;
+ int ret;
+
+ do
+ {
+ if ((ret = ReadData (port)) == -1)
+ /*
+ * If no more data availabe, return to the caller, no error if len>0.
+ * We have no other way to know when the string is finished.
+ */
+ return (len ? 0 : -1);
+
+ AudioExcelName[len++] = ret;
+
+ }
+ while (len < CARDNAMELEN);
+ return 0;
+}
+
+static void
+InitializeHardParams (void)
+{
+
+ memset (AudioExcelName, 0, CARDNAMELEN + 1);
+ memset (AudioExcelVersion, 0, CARDVERLEN + 1);
+
+ for (gc = 0; orIRQ[gc].or; gc++)
+ if (orIRQ[gc].val == irq)
+ oredparams |= orIRQ[gc].or;
+
+ for (gc = 0; orMIRQ[gc].or; gc++)
+ if (orMIRQ[gc].or == mirq)
+ oredparams |= orMIRQ[gc].or;
+
+ for (gc = 0; orDMA[gc].or; gc++)
+ if (orDMA[gc].val == dma)
+ oredparams |= orDMA[gc].or;
+}
+
+static int
+InitAEDSP16 (int which)
+{
+ static char *InitName = NULL;
+
+ InitializeHardParams ();
+
+ if (ResetBoard (portbase))
+ {
+ printk ("[aedsp16] ResetBoard: failed!\n");
+ return -1;
+ }
+
+#if defined(THIS_SHOULD_GO_AWAY)
+ if (CheckDSPOkay (portbase))
+ {
+ printk ("[aedsp16] CheckDSPOkay: failed!\n");
+ return -1;
+ }
+#endif
+
+ if (WriteDSPCommand (portbase, CMD1))
+ {
+ printk ("[aedsp16] CMD 0x%x: failed!\n", CMD1);
+ return -1;
+ }
+
+ if (GetCardName (portbase))
+ {
+ printk ("[aedsp16] GetCardName: failed!\n");
+ return -1;
+ }
+
+ /*
+ * My AEDSP16 card return SC-6000 in AudioExcelName, so
+ * if we have something different, we have to be warned.
+ */
+ if (strcmp ("SC-6000", AudioExcelName))
+ printk ("[aedsp16] Warning: non SC-6000 audio card!\n");
+
+ if (WriteDSPCommand (portbase, CMD2))
+ {
+ printk ("[aedsp16] CMD 0x%x: failed!\n", CMD2);
+ return -1;
+ }
+
+ if (GetCardVersion (portbase))
+ {
+ printk ("[aedsp16] GetCardVersion: failed!\n");
+ return -1;
+ }
+
+ if (SetUpBoard (portbase))
+ {
+ printk ("[aedsp16] SetUpBoard: failed!\n");
+ return -1;
+ }
+
+ if (which == INIT_MSS)
+ {
+ if (InitMSS (portbase))
+ {
+ printk ("[aedsp16] Can't initialize Microsoft Sound System mode.\n");
+ return -1;
+ }
+ }
+
+ /*
+ * If we are resetting, do not print any message because we may be
+ * in playing and we do not want lost too much time.
+ */
+ if (!(which & RESET_DSP16))
+ {
+ if (which & INIT_MPU401)
+ InitName = "MPU401";
+ else if (which & INIT_SBPRO)
+ InitName = "SBPro";
+ else if (which & INIT_MSS)
+ InitName = "MSS";
+ else
+ InitName = "None";
+
+ printk ("Audio Excel DSP 16 init v%s (%s %s) [%s]\n",
+ VERSION, AudioExcelName,
+ AudioExcelVersion, InitName);
+ }
+
+ tenmillisec ();
+
+ return 0;
+}
+
+#if defined(AEDSP16_SBPRO)
+
+int
+InitAEDSP16_SBPRO (struct address_info *hw_config)
+{
+ /*
+ * If the card is alredy init'ed MSS, we can not init it to SBPRO too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+ if (ae_init & INIT_MSS)
+ return -1;
+ if (ae_init & INIT_SBPRO)
+ return 0;
+
+ /*
+ * For now we will leave this
+ * code included only when INCLUDE_AEDSP16 is configured in, but it should
+ * be better include it every time.
+ */
+ if (!(ae_init & INIT_MPU401))
+ {
+ if (check_region (hw_config->io_base, 0x0f))
+ {
+ printk ("AEDSP16/SBPRO I/O port region is alredy in use.\n");
+ return -1;
+ }
+ }
+
+ /*
+ * Set up the internal hardware parameters, to let the driver reach
+ * the Sound Card.
+ */
+ portbase = hw_config->io_base;
+ irq = hw_config->irq;
+ dma = hw_config->dma;
+ if (InitAEDSP16 (INIT_SBPRO))
+ return -1;
+
+ if (!(ae_init & INIT_MPU401))
+ request_region (hw_config->io_base, 0x0f, "aedsp16 (sbpro)");
+
+ ae_init |= INIT_SBPRO;
+ return 0;
+}
+
+#endif /* AEDSP16_SBPRO */
+
+#if defined(AEDSP16_MSS)
+
+int
+InitAEDSP16_MSS (struct address_info *hw_config)
+{
+ /*
+ * If the card is alredy init'ed SBPRO, we can not init it to MSS too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+ if (ae_init & INIT_SBPRO)
+ return -1;
+ if (ae_init & INIT_MSS)
+ return 0;
+
+ /*
+ * For now we will leave this
+ * code included only when INCLUDE_AEDSP16 is configured in, but it should
+ * be better include it every time.
+ */
+ if (check_region (hw_config->io_base, 0x08))
+ {
+ printk ("MSS I/O port region is alredy in use.\n");
+ return -1;
+ }
+
+ /*
+ * We must allocate the AEDSP16 region too because these are the I/O ports
+ * to access card's control registers.
+ */
+ if (!(ae_init & INIT_MPU401))
+ {
+ if (check_region (AEDSP16_BASE, 0x0f))
+ {
+ printk ("AEDSP16 I/O port region is alredy in use.\n");
+ return -1;
+ }
+ }
+
+
+ /*
+ * If we are configuring the card for MSS, the portbase for card configuration
+ * is the default one (0x220 unless you have changed the factory default
+ * with board switches), so no need to modify the portbase variable.
+ * The default is AEDSP16_BASE, that is the right value.
+ */
+ irq = hw_config->irq;
+ dma = hw_config->dma;
+ if (InitAEDSP16 (INIT_MSS))
+ return -1;
+
+ request_region (hw_config->io_base, 0x08, "aedsp16 (mss)");
+
+ if (!(ae_init & INIT_MPU401))
+ request_region (AEDSP16_BASE, 0x0f, "aedsp16 (sbpro)");
+
+ ae_init |= INIT_MSS;
+ return 0;
+}
+
+#endif /* AEDSP16_MSS */
+
+#if defined(AEDSP16_MPU401)
+
+int
+InitAEDSP16_MPU401 (struct address_info *hw_config)
+{
+ if (ae_init & INIT_MPU401)
+ return 0;
+
+ /*
+ * For now we will leave this
+ * code included only when INCLUDE_AEDSP16 is configured in, but it should
+ * be better include it every time.
+ */
+ if (check_region (hw_config->io_base, 0x02))
+ {
+ printk ("SB I/O port region is alredy in use.\n");
+ return -1;
+ }
+
+ /*
+ * We must allocate the AEDSP16 region too because these are the I/O ports
+ * to access card's control registers.
+ */
+ if (!(ae_init & (INIT_MSS | INIT_SBPRO)))
+ {
+ if (check_region (AEDSP16_BASE, 0x0f))
+ {
+ printk ("AEDSP16 I/O port region is alredy in use.\n");
+ return -1;
+ }
+ }
+
+ /*
+ * If mpu401, the irq and dma are not important, do not touch it
+ * because we may use the default if sbpro is not yet configured,
+ * we may use the sbpro ones if configured, and nothing wrong
+ * should happen.
+ *
+ * The mirq default is 0, but once set it to non-0 value, we should
+ * not touch it anymore (unless I write an ioctl to do it, of course).
+ */
+ mirq = hw_config->irq;
+ if (InitAEDSP16 (INIT_MPU401))
+ return -1;
+
+ request_region (hw_config->io_base, 0x02, "aedsp16 (mpu401)");
+
+ if (!(ae_init & (INIT_MSS | INIT_SBPRO)))
+ request_region (AEDSP16_BASE, 0x0f, "aedsp16 (sbpro)");
+
+ ae_init |= INIT_MPU401;
+ return 0;
+}
+
+#endif /* AEDSP16_MPU401 */
+
+#if 0 /* Leave it out for now. We are not using this portion of code. */
+
+/*
+ * Entry point for a reset function.
+ * May be I will write the infamous ioctl :)
+ */
+int
+ResetAEDSP16 (void)
+{
+#if defined(AEDSP16_DEBUG)
+ printk ("[aedsp16] ResetAEDSP16 called.\n");
+#endif
+ return InitAEDSP16 (RESET_DSP16);
+}
+
+#endif /* 0 */
+
+#endif /* !EXCLUDE_AEDSP16 */
diff --git a/sys/pc98/pc98/sound/audio.c b/sys/pc98/pc98/sound/audio.c
new file mode 100644
index 0000000..770babc
--- /dev/null
+++ b/sys/pc98/pc98/sound/audio.c
@@ -0,0 +1,583 @@
+/*
+ * sound/audio.c
+ *
+ * Device file manager for /dev/audio
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+#ifndef EXCLUDE_AUDIO
+
+#include "ulaw.h"
+#include "coproc.h"
+
+#define ON 1
+#define OFF 0
+
+static int wr_buff_no[MAX_AUDIO_DEV]; /*
+
+ * != -1, if there is
+ * a incomplete output
+ * block in the queue.
+ */
+static int wr_buff_size[MAX_AUDIO_DEV], wr_buff_ptr[MAX_AUDIO_DEV];
+
+static int audio_mode[MAX_AUDIO_DEV];
+static int dev_nblock[MAX_AUDIO_DEV]; /* 1 if in noblocking mode */
+
+#define AM_NONE 0
+#define AM_WRITE 1
+#define AM_READ 2
+
+static char *wr_dma_buf[MAX_AUDIO_DEV];
+static int audio_format[MAX_AUDIO_DEV];
+static int local_conversion[MAX_AUDIO_DEV];
+
+static int
+set_format (int dev, int fmt)
+{
+ if (fmt != AFMT_QUERY)
+ {
+
+ local_conversion[dev] = 0;
+
+ if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
+ if (fmt == AFMT_MU_LAW)
+ {
+ fmt = AFMT_U8;
+ local_conversion[dev] = AFMT_MU_LAW;
+ }
+ else
+ fmt = AFMT_U8; /* This is always supported */
+
+ audio_format[dev] = DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, fmt, 1);
+ }
+
+ if (local_conversion[dev]) /* This shadows the HW format */
+ return local_conversion[dev];
+
+ return audio_format[dev];
+}
+
+int
+audio_open (int dev, struct fileinfo *file)
+{
+ int ret;
+ int bits;
+ int dev_type = dev & 0x0f;
+ int mode = file->mode & O_ACCMODE;
+
+ dev = dev >> 4;
+
+ if (dev_type == SND_DEV_DSP16)
+ bits = 16;
+ else
+ bits = 8;
+
+ if ((ret = DMAbuf_open (dev, mode)) < 0)
+ return ret;
+
+ if (audio_devs[dev]->coproc)
+ if ((ret = audio_devs[dev]->coproc->
+ open (audio_devs[dev]->coproc->devc, COPR_PCM)) < 0)
+ {
+ audio_release (dev, file);
+ printk ("Sound: Can't access coprocessor device\n");
+
+ return ret;
+ }
+
+ local_conversion[dev] = 0;
+
+ if (DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, bits, 1) != bits)
+ {
+ audio_release (dev, file);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (dev_type == SND_DEV_AUDIO)
+ {
+ set_format (dev, AFMT_MU_LAW);
+ }
+ else
+ set_format (dev, bits);
+
+ wr_buff_no[dev] = -1;
+ audio_mode[dev] = AM_NONE;
+ wr_buff_size[dev] = wr_buff_ptr[dev] = 0;
+ dev_nblock[dev] = 0;
+
+ return ret;
+}
+
+void
+audio_release (int dev, struct fileinfo *file)
+{
+ int mode;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+
+ if (audio_devs[dev]->coproc)
+ audio_devs[dev]->coproc->close (audio_devs[dev]->coproc->devc, COPR_PCM);
+ DMAbuf_release (dev, mode);
+}
+
+#ifdef NO_INLINE_ASM
+static void
+translate_bytes (const unsigned char *table, unsigned char *buff, unsigned long n)
+{
+ unsigned long i;
+
+ for (i = 0; i < n; ++i)
+ buff[i] = table[buff[i]];
+}
+
+#else
+static inline void
+translate_bytes (const void *table, void *buff, unsigned long n)
+{
+ __asm__ ("cld\n"
+ "1:\tlodsb\n\t"
+ "xlatb\n\t"
+ "stosb\n\t"
+"loop 1b\n\t":
+: "b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff)
+: "bx", "cx", "di", "si", "ax");
+}
+
+#endif
+
+int
+audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c, p, l;
+ int err;
+
+ dev = dev >> 4;
+
+ p = 0;
+ c = count;
+
+ if (audio_mode[dev] == AM_READ) /*
+ * Direction changed
+ */
+ {
+ wr_buff_no[dev] = -1;
+ }
+
+ audio_mode[dev] = AM_WRITE;
+
+ if (!count) /*
+ * Flush output
+ */
+ {
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ return 0;
+ }
+
+ while (c)
+ { /*
+ * Perform output blocking
+ */
+ if (wr_buff_no[dev] < 0) /*
+ * There is no incomplete buffers
+ */
+ {
+ if ((wr_buff_no[dev] = DMAbuf_getwrbuffer (dev, &wr_dma_buf[dev],
+ &wr_buff_size[dev],
+ dev_nblock[dev])) < 0)
+ {
+ /* Handle nonblocking mode */
+#if defined(__FreeBSD__)
+ if (dev_nblock[dev] && wr_buff_no[dev] == RET_ERROR (EWOULDBLOCK))
+ return wr_buff_no[dev]; /*
+ * XXX Return error, write() will
+ * supply # of accepted bytes.
+ * In fact, in FreeBSD the check
+ * above should not be needed
+ */
+#else
+ if (dev_nblock[dev] && wr_buff_no[dev] == RET_ERROR (EAGAIN))
+ return p; /* No more space. Return # of accepted bytes */
+#endif
+ return wr_buff_no[dev];
+ }
+ wr_buff_ptr[dev] = 0;
+ }
+
+ l = c;
+ if (l > (wr_buff_size[dev] - wr_buff_ptr[dev]))
+ l = (wr_buff_size[dev] - wr_buff_ptr[dev]);
+
+ if (!audio_devs[dev]->copy_from_user)
+ { /*
+ * No device specific copy routine
+ */
+ COPY_FROM_USER (&wr_dma_buf[dev][wr_buff_ptr[dev]], buf, p, l);
+ }
+ else
+ audio_devs[dev]->copy_from_user (dev,
+ wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l);
+
+
+ /*
+ * Insert local processing here
+ */
+
+ if (local_conversion[dev] == AFMT_MU_LAW)
+ {
+#ifdef linux
+ /*
+ * This just allows interrupts while the conversion is running
+ */
+ __asm__ ("sti");
+#endif
+ translate_bytes (ulaw_dsp, (unsigned char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l);
+ }
+
+ c -= l;
+ p += l;
+ wr_buff_ptr[dev] += l;
+
+ if (wr_buff_ptr[dev] >= wr_buff_size[dev])
+ {
+ if ((err = DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0)
+ {
+ return err;
+ }
+
+ wr_buff_no[dev] = -1;
+ }
+
+ }
+
+ return count;
+}
+
+int
+audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c, p, l;
+ char *dmabuf;
+ int buff_no;
+
+ dev = dev >> 4;
+ p = 0;
+ c = count;
+
+ if (audio_mode[dev] == AM_WRITE)
+ {
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ }
+
+ audio_mode[dev] = AM_READ;
+
+ while (c)
+ {
+ if ((buff_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l,
+ dev_nblock[dev])) < 0)
+ {
+ /* Nonblocking mode handling. Return current # of bytes */
+
+#if defined(__FreeBSD__)
+ if (dev_nblock[dev] && buff_no == RET_ERROR (EWOULDBLOCK))
+ return buff_no; /*
+ * XXX Return error, read() will supply
+ * # of bytes actually read. In fact,
+ * in FreeBSD the check above should not
+ * be needed
+ */
+#else
+ if (dev_nblock[dev] && buff_no == RET_ERROR (EAGAIN))
+ return p;
+#endif
+
+ return buff_no;
+ }
+
+ if (l > c)
+ l = c;
+
+ /*
+ * Insert any local processing here.
+ */
+
+ if (local_conversion[dev] == AFMT_MU_LAW)
+ {
+#ifdef linux
+ /*
+ * This just allows interrupts while the conversion is running
+ */
+ __asm__ ("sti");
+#endif
+
+ translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l);
+ }
+
+ COPY_TO_USER (buf, p, dmabuf, l);
+
+ DMAbuf_rmchars (dev, buff_no, l);
+
+ p += l;
+ c -= l;
+ }
+
+ return count - c;
+}
+
+int
+audio_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+
+ dev = dev >> 4;
+
+ if (((cmd >> 8) & 0xff) == 'C')
+ {
+ if (audio_devs[dev]->coproc) /* Coprocessor ioctl */
+ return audio_devs[dev]->coproc->ioctl (audio_devs[dev]->coproc->devc, cmd, arg, 0);
+ else
+ printk ("/dev/dsp%d: No coprocessor for this device\n", dev);
+
+ return RET_ERROR (EREMOTEIO);
+ }
+ else
+ switch (cmd)
+ {
+ case SNDCTL_DSP_SYNC:
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+ break;
+
+ case SNDCTL_DSP_POST:
+ if (wr_buff_no[dev] >= 0)
+ {
+ DMAbuf_start_output (dev, wr_buff_no[dev], wr_buff_ptr[dev]);
+
+ wr_buff_no[dev] = -1;
+ }
+ return 0;
+ break;
+
+ case SNDCTL_DSP_RESET:
+ wr_buff_no[dev] = -1;
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+ break;
+
+ case SNDCTL_DSP_GETFMTS:
+ return IOCTL_OUT (arg, audio_devs[dev]->format_mask);
+ break;
+
+ case SNDCTL_DSP_SETFMT:
+ return IOCTL_OUT (arg, set_format (dev, IOCTL_IN (arg)));
+
+ case SNDCTL_DSP_GETISPACE:
+ if (audio_mode[dev] == AM_WRITE)
+ return RET_ERROR (EBUSY);
+
+ {
+ audio_buf_info info;
+
+ int err = DMAbuf_ioctl (dev, cmd, (unsigned long) &info, 1);
+
+ if (err < 0)
+ return err;
+
+ if (wr_buff_no[dev] != -1)
+ info.bytes += wr_buff_ptr[dev];
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) &info, sizeof (info));
+ return 0;
+ }
+
+ case SNDCTL_DSP_GETOSPACE:
+ if (audio_mode[dev] == AM_READ)
+ return RET_ERROR (EBUSY);
+
+ {
+ audio_buf_info info;
+
+ int err = DMAbuf_ioctl (dev, cmd, (unsigned long) &info, 1);
+
+ if (err < 0)
+ return err;
+
+ if (wr_buff_no[dev] != -1)
+ info.bytes += wr_buff_size[dev] - wr_buff_ptr[dev];
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) &info, sizeof (info));
+ return 0;
+ }
+
+ case SNDCTL_DSP_NONBLOCK:
+ dev_nblock[dev] = 1;
+ return 0;
+ break;
+
+#ifdef __FreeBSD__
+ case FIONBIO: /* XXX Is this the same in Linux? */
+ if (*(int *)arg)
+ dev_nblock[dev] = 1;
+ else
+ dev_nblock[dev] = 0;
+ return 0;
+ break;
+
+ case FIOASYNC:
+ return 0; /* XXX Useful for ampling input notification? */
+ break;
+#endif
+
+ default:
+ return DMAbuf_ioctl (dev, cmd, arg, 0);
+ }
+}
+
+long
+audio_init (long mem_start)
+{
+ /*
+ * NOTE! This routine could be called several times during boot.
+ */
+ return mem_start;
+}
+
+#ifdef ALLOW_SELECT
+int
+audio_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ int l;
+ char *dmabuf;
+
+ dev = dev >> 4;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (audio_mode[dev] != AM_READ && /* Wrong direction */
+ audio_mode[dev] != AM_NONE)
+ return 0;
+
+ if (DMAbuf_getrdbuffer (dev, &dmabuf, &l,
+ 1 /* Don't block */ ) >= 0)
+ return 1; /* We have data */
+
+ return DMAbuf_select (dev, file, sel_type, wait);
+ break;
+
+ case SEL_OUT:
+ if (audio_mode[dev] != AM_WRITE && /* Wrong direction */
+ audio_mode[dev] != AM_NONE)
+ return 0;
+
+ if (wr_buff_no[dev] != -1)
+ return 1; /* There is space in the current buffer */
+
+ return DMAbuf_select (dev, file, sel_type, wait);
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
+
+ return 0;
+}
+
+#endif /* ALLOW_SELECT */
+
+#else /* EXCLUDE_AUDIO */
+/*
+ * Stub versions
+ */
+
+int
+audio_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+audio_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+audio_open (int dev, struct fileinfo *file)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+audio_release (int dev, struct fileinfo *file)
+{
+};
+int
+audio_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+audio_init (long mem_start)
+{
+ return mem_start;
+}
+
+#endif
+
+#endif
diff --git a/sys/pc98/pc98/sound/coproc.h b/sys/pc98/pc98/sound/coproc.h
new file mode 100644
index 0000000..f902382
--- /dev/null
+++ b/sys/pc98/pc98/sound/coproc.h
@@ -0,0 +1,12 @@
+/*
+ * Definitions for various on board processors on the soundcards. For
+ * example DSP processors.
+ */
+
+/*
+ * Coprocessor access types
+ */
+#define COPR_CUSTOM 0x0001 /* Custom applications */
+#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */
+#define COPR_PCM 0x0004 /* Digitized voice applications */
+#define COPR_SYNTH 0x0008 /* Music synthesis */
diff --git a/sys/pc98/pc98/sound/dev_table.c b/sys/pc98/pc98/sound/dev_table.c
new file mode 100644
index 0000000..bfd8e58
--- /dev/null
+++ b/sys/pc98/pc98/sound/dev_table.c
@@ -0,0 +1,182 @@
+/*
+ * sound/dev_table.c
+ *
+ * Device call tables.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#define _DEV_TABLE_C_
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+int
+snd_find_driver (int type)
+{
+ int i, n = sizeof (sound_drivers) / sizeof (struct driver_info);
+
+ for (i = 0; i < (n - 1); i++)
+ if (sound_drivers[i].card_type == type)
+ return i;
+
+ return -1; /*
+ * Not found
+ */
+}
+
+static long
+sndtable_init (long mem_start)
+{
+ int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+ int drv;
+
+ printk ("Sound initialization started\n");
+
+ for (i = 0; i < (n - 1); i++)
+ if (snd_installed_cards[i].enabled)
+ if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ else if (sound_drivers[drv].probe (&snd_installed_cards[i].config))
+ {
+#ifndef SHORT_BANNERS
+ printk ("snd%d",
+ snd_installed_cards[i].card_type);
+#endif
+
+ mem_start = sound_drivers[drv].attach (mem_start, &snd_installed_cards[i].config);
+#ifndef SHORT_BANNERS
+ printk (" at 0x%x irq %d drq %d\n",
+ snd_installed_cards[i].config.io_base,
+ snd_installed_cards[i].config.irq,
+ snd_installed_cards[i].config.dma);
+#endif
+ }
+ else
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ printk ("Sound initialization complete\n");
+ return mem_start;
+}
+
+int
+sndtable_probe (int unit, struct address_info *hw_config)
+{
+ int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+
+ if (!unit)
+ return TRUE;
+
+ for (i = 0; i < (n - 1); i++)
+ if (snd_installed_cards[i].enabled)
+ if (snd_installed_cards[i].card_type == unit)
+ {
+ int drv;
+
+ snd_installed_cards[i].config.io_base = hw_config->io_base;
+ snd_installed_cards[i].config.irq = hw_config->irq;
+ snd_installed_cards[i].config.dma = hw_config->dma;
+ if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not
+ * detected
+ */
+ else if (sound_drivers[drv].probe (hw_config))
+ return 1;
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ return 0;
+ }
+
+ return FALSE;
+}
+
+int
+sndtable_init_card (int unit, struct address_info *hw_config)
+{
+ int i, n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+
+ if (!unit)
+ {
+ if (sndtable_init (0) != 0)
+ panic ("snd: Invalid memory allocation\n");
+ return TRUE;
+ }
+
+ for (i = 0; i < (n - 1); i++)
+ if (snd_installed_cards[i].card_type == unit)
+ {
+ int drv;
+
+ snd_installed_cards[i].config.io_base = hw_config->io_base;
+ snd_installed_cards[i].config.irq = hw_config->irq;
+ snd_installed_cards[i].config.dma = hw_config->dma;
+
+ if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) == -1)
+ snd_installed_cards[i].enabled = 0; /*
+ * Mark as not detected
+ */
+ else if (sound_drivers[drv].attach (0, hw_config) != 0)
+ panic ("snd#: Invalid memory allocation\n");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int
+sndtable_get_cardcount (void)
+{
+ return num_audiodevs + num_mixers + num_synths + num_midis;
+}
+
+struct address_info *
+sound_getconf (int card_type)
+{
+ int j, ptr;
+ int n = sizeof (snd_installed_cards) / sizeof (struct card_info);
+
+ ptr = -1;
+ for (j = 0; j < n && ptr == -1; j++)
+ if (snd_installed_cards[j].card_type == card_type)
+ ptr = j;
+
+ if (ptr == -1)
+ return (struct address_info *) NULL;
+
+ return &snd_installed_cards[ptr].config;
+}
+
+#else
+
+void
+sound_setup (char *str, int *ints)
+{
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/dev_table.h b/sys/pc98/pc98/sound/dev_table.h
new file mode 100644
index 0000000..5269f63
--- /dev/null
+++ b/sys/pc98/pc98/sound/dev_table.h
@@ -0,0 +1,429 @@
+/*
+ * dev_table.h
+ *
+ * Global definitions for device call tables
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+
+*/
+
+#ifndef _DEV_TABLE_H_
+#define _DEV_TABLE_H_
+
+/*
+ * NOTE! NOTE! NOTE! NOTE!
+ *
+ * If you modify this file, please check the dev_table.c also.
+ *
+ * NOTE! NOTE! NOTE! NOTE!
+ */
+
+struct driver_info {
+ int card_type; /* From soundcard.h */
+ char *name;
+ long (*attach) (long mem_start, struct address_info *hw_config);
+ int (*probe) (struct address_info *hw_config);
+};
+
+struct card_info {
+ int card_type; /* Link (search key) to the driver list */
+ struct address_info config;
+ int enabled;
+};
+
+/*
+ * Device specific parameters (used only by dmabuf.c)
+ */
+#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR)
+
+#define DMODE_NONE 0
+#define DMODE_OUTPUT 1
+#define DMODE_INPUT 2
+
+struct dma_buffparms {
+ int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
+
+ /*
+ * Pointers to raw buffers
+ */
+
+ char *raw_buf[DSP_BUFFCOUNT];
+ unsigned long raw_buf_phys[DSP_BUFFCOUNT];
+ int raw_count;
+
+ /*
+ * Device state tables
+ */
+
+ unsigned long flags;
+#define DMA_BUSY 0x00000001
+#define DMA_RESTART 0x00000002
+#define DMA_ACTIVE 0x00000004
+#define DMA_STARTED 0x00000008
+#define DMA_ALLOC_DONE 0x00000020
+
+ int open_mode;
+
+ /*
+ * Queue parameters.
+ */
+ int qlen;
+ int qhead;
+ int qtail;
+
+ int nbufs;
+ int counts[MAX_SUB_BUFFERS];
+ int subdivision;
+ char *buf[MAX_SUB_BUFFERS];
+ unsigned long buf_phys[MAX_SUB_BUFFERS];
+
+ int fragment_size;
+ int max_fragments;
+
+ int bytes_in_use;
+
+ int underrun_count;
+};
+
+/*
+ * Structure for use with various microcontrollers and DSP processors
+ * in the recent soundcards.
+ */
+typedef struct coproc_operations {
+ char name[32];
+ int (*open) (void *devc, int sub_device);
+ void (*close) (void *devc, int sub_device);
+ int (*ioctl) (void *devc, unsigned int cmd, unsigned int arg, int local);
+ void (*reset) (void *devc);
+
+ void *devc; /* Driver specific info */
+ } coproc_operations;
+
+struct audio_operations {
+ char name[32];
+ int flags;
+#define NOTHING_SPECIAL 0
+#define NEEDS_RESTART 1
+#define DMA_AUTOMODE 2
+ int format_mask; /* Bitmask for supported audio formats */
+ void *devc; /* Driver specific info */
+ int (*open) (int dev, int mode);
+ void (*close) (int dev);
+ void (*output_block) (int dev, unsigned long buf,
+ int count, int intrflag, int dma_restart);
+ void (*start_input) (int dev, unsigned long buf,
+ int count, int intrflag, int dma_restart);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg, int local);
+ int (*prepare_for_input) (int dev, int bufsize, int nbufs);
+ int (*prepare_for_output) (int dev, int bufsize, int nbufs);
+ void (*reset) (int dev);
+ void (*halt_xfer) (int dev);
+ int (*local_qlen)(int dev);
+ void (*copy_from_user)(int dev, char *localbuf, int localoffs,
+ snd_rw_buf *userbuf, int useroffs, int len);
+ int buffcount;
+ long buffsize;
+ int dmachan;
+ struct dma_buffparms *dmap;
+ struct coproc_operations *coproc;
+ int mixer_dev;
+};
+
+struct mixer_operations {
+ char name[32];
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+};
+
+struct synth_operations {
+ struct synth_info *info;
+ int midi_dev;
+ int synth_type;
+ int synth_subtype;
+
+ int (*open) (int dev, int mode);
+ void (*close) (int dev);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+ int (*kill_note) (int dev, int voice, int note, int velocity);
+ int (*start_note) (int dev, int voice, int note, int velocity);
+ int (*set_instr) (int dev, int voice, int instr);
+ void (*reset) (int dev);
+ void (*hw_control) (int dev, unsigned char *event);
+ int (*load_patch) (int dev, int format, snd_rw_buf *addr,
+ int offs, int count, int pmgr_flag);
+ void (*aftertouch) (int dev, int voice, int pressure);
+ void (*controller) (int dev, int voice, int ctrl_num, int value);
+ void (*panning) (int dev, int voice, int value);
+ void (*volume_method) (int dev, int mode);
+ int (*pmgr_interface) (int dev, struct patmgr_info *info);
+ void (*bender) (int dev, int chn, int value);
+ int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc);
+ void (*setup_voice) (int dev, int voice, int chn);
+
+ struct voice_alloc_info alloc;
+ struct channel_info chn_info[16];
+};
+
+struct midi_input_info { /* MIDI input scanner variables */
+#define MI_MAX 10
+ int m_busy;
+ unsigned char m_buf[MI_MAX];
+ unsigned char m_prev_status; /* For running status */
+ int m_ptr;
+#define MST_INIT 0
+#define MST_DATA 1
+#define MST_SYSEX 2
+ int m_state;
+ int m_left;
+ };
+
+struct midi_operations {
+ struct midi_info info;
+ struct synth_operations *converter;
+ struct midi_input_info in_info;
+ int (*open) (int dev, int mode,
+ void (*inputintr)(int dev, unsigned char data),
+ void (*outputintr)(int dev)
+ );
+ void (*close) (int dev);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+ int (*putc) (int dev, unsigned char data);
+ int (*start_read) (int dev);
+ int (*end_read) (int dev);
+ void (*kick)(int dev);
+ int (*command) (int dev, unsigned char *data);
+ int (*buffer_status) (int dev);
+ int (*prefix_cmd) (int dev, unsigned char status);
+ struct coproc_operations *coproc;
+};
+
+struct sound_timer_operations {
+ struct sound_timer_info info;
+ int priority;
+ int devlink;
+ int (*open)(int dev, int mode);
+ void (*close)(int dev);
+ int (*event)(int dev, unsigned char *ev);
+ unsigned long (*get_time)(int dev);
+ int (*ioctl) (int dev, unsigned int cmd, unsigned int arg);
+ void (*arm_timer)(int dev, long time);
+};
+
+#ifdef _DEV_TABLE_C_
+ struct audio_operations *audio_devs[MAX_AUDIO_DEV] = {NULL}; int num_audiodevs = 0;
+ struct mixer_operations *mixer_devs[MAX_MIXER_DEV] = {NULL}; int num_mixers = 0;
+ struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0;
+ struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
+
+#ifndef EXCLUDE_SEQUENCER
+ extern struct sound_timer_operations default_sound_timer;
+ struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] =
+ {&default_sound_timer, NULL};
+ int num_sound_timers = 1;
+#else
+ struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] =
+ {NULL};
+ int num_sound_timers = 0;
+#endif
+
+/*
+ * List of low level drivers compiled into the kernel.
+ */
+
+ struct driver_info sound_drivers[] = {
+#ifndef EXCLUDE_PSS
+ {SNDCARD_PSS, "Echo Personal Sound System PSS (ESC614)", attach_pss, probe_pss},
+# ifdef PSS_MPU_BASE
+ {SNDCARD_PSS_MPU, "PSS-MPU", attach_pss_mpu, probe_pss_mpu},
+# endif
+# ifdef PSS_MSS_BASE
+ {SNDCARD_PSS_MSS, "PSS-MSS", attach_pss_mss, probe_pss_mss},
+# endif
+#endif
+#ifndef EXCLUDE_YM3812
+ {SNDCARD_ADLIB, "OPL-2/OPL-3 FM", attach_adlib_card, probe_adlib},
+#endif
+#ifndef EXCLUDE_PAS
+ {SNDCARD_PAS, "ProAudioSpectrum", attach_pas_card, probe_pas},
+#endif
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
+ {SNDCARD_MPU401,"Roland MPU-401", attach_mpu401, probe_mpu401},
+#endif
+#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
+ {SNDCARD_UART6850,"6860 UART Midi", attach_uart6850, probe_uart6850},
+#endif
+#ifndef EXCLUDE_SB
+ {SNDCARD_SB, "SoundBlaster", attach_sb_card, probe_sb},
+#endif
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16)
+#ifndef EXCLUDE_AUDIO
+ {SNDCARD_SB16, "SoundBlaster16", sb16_dsp_init, sb16_dsp_detect},
+#endif
+#ifndef EXCLUDE_MIDI
+ {SNDCARD_SB16MIDI,"SB16 MIDI", attach_sb16midi, probe_sb16midi},
+#endif
+#endif
+#ifndef EXCLUDE_GUS16
+ {SNDCARD_GUS16, "Ultrasound 16-bit opt.", attach_gus_db16, probe_gus_db16},
+#endif
+#ifndef EXCLUDE_MSS
+ {SNDCARD_MSS, "MS Sound System", attach_ms_sound, probe_ms_sound},
+#endif
+#ifndef EXCLUDE_GUS
+ {SNDCARD_GUS, "Gravis Ultrasound", attach_gus_card, probe_gus},
+#endif
+#ifndef EXCLUDE_SSCAPE
+ {SNDCARD_SSCAPE, "Ensoniq Soundscape", attach_sscape, probe_sscape},
+ {SNDCARD_SSCAPE_MSS, "MS Sound System (SoundScape)", attach_ss_ms_sound, probe_ss_ms_sound},
+#endif
+#ifndef EXCLUDE_TRIX
+ {SNDCARD_TRXPRO, "MediaTriX AudioTriX Pro", attach_trix_wss, probe_trix_wss},
+ {SNDCARD_TRXPRO_SB, "AudioTriX (SB mode)", attach_trix_sb, probe_trix_sb},
+ {SNDCARD_TRXPRO_MPU, "AudioTriX MIDI", attach_trix_mpu, probe_trix_mpu},
+#endif
+#ifdef PC98
+#ifndef EXCLUDE_PCM86
+ {SNDCARD_PCM86, "PC-9801-86/73", attach_pcm86, probe_pcm86},
+#endif
+#endif
+ {0, "*?*", NULL, NULL}
+ };
+
+#if defined(linux) || defined(__FreeBSD__)
+/*
+ * List of devices actually configured in the system.
+ *
+ * Note! The detection order is significant. Don't change it.
+ */
+
+ struct card_info snd_installed_cards[] = {
+#ifndef EXCLUDE_PSS
+ {SNDCARD_PSS, {PSS_BASE, PSS_IRQ, PSS_DMA}, SND_DEFAULT_ENABLE},
+# ifdef PSS_MPU_BASE
+ {SNDCARD_PSS_MPU, {PSS_MPU_BASE, PSS_MPU_IRQ, 0}, SND_DEFAULT_ENABLE},
+# endif
+# ifdef PSS_MSS_BASE
+ {SNDCARD_PSS_MSS, {PSS_MSS_BASE, PSS_MSS_IRQ, PSS_MSS_DMA}, SND_DEFAULT_ENABLE},
+# endif
+#endif
+#ifndef EXCLUDE_TRIX
+ {SNDCARD_TRXPRO, {TRIX_BASE, TRIX_IRQ, TRIX_DMA}, SND_DEFAULT_ENABLE},
+# ifdef TRIX_SB_BASE
+ {SNDCARD_TRXPRO_SB, {TRIX_SB_BASE, TRIX_SB_IRQ, TRIX_SB_DMA}, SND_DEFAULT_ENABLE},
+# endif
+# ifdef TRIX_MPU_BASE
+ {SNDCARD_TRXPRO_MPU, {TRIX_MPU_BASE, TRIX_MPU_IRQ, 0}, SND_DEFAULT_ENABLE},
+# endif
+#endif
+#ifndef EXCLUDE_SSCAPE
+ {SNDCARD_SSCAPE, {SSCAPE_BASE, SSCAPE_IRQ, SSCAPE_DMA}, SND_DEFAULT_ENABLE},
+ {SNDCARD_SSCAPE_MSS, {SSCAPE_MSS_BASE, SSCAPE_MSS_IRQ, SSCAPE_MSS_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#ifndef EXCLUDE_MSS
+ {SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA}, SND_DEFAULT_ENABLE},
+# ifdef MSS2_BASE
+ {SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA}, SND_DEFAULT_ENABLE},
+# endif
+#endif
+
+#ifndef EXCLUDE_PAS
+ {SNDCARD_PAS, {PAS_BASE, PAS_IRQ, PAS_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#ifndef EXCLUDE_SB
+ {SNDCARD_SB, {SBC_BASE, SBC_IRQ, SBC_DMA}, SND_DEFAULT_ENABLE},
+#endif
+
+#if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI)
+ {SNDCARD_MPU401, {MPU_BASE, MPU_IRQ, 0}, SND_DEFAULT_ENABLE},
+#ifdef MPU2_BASE
+ {SNDCARD_MPU401, {MPU2_BASE, MPU2_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+#ifdef MPU3_BASE
+ {SNDCARD_MPU401, {MPU3_BASE, MPU2_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+#endif
+
+#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
+ {SNDCARD_UART6850, {U6850_BASE, U6850_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16)
+#ifndef EXCLUDE_AUDIO
+ {SNDCARD_SB16, {SBC_BASE, SBC_IRQ, SB16_DMA}, SND_DEFAULT_ENABLE},
+#endif
+#ifndef EXCLUDE_MIDI
+ {SNDCARD_SB16MIDI,{SB16MIDI_BASE, SBC_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+#endif
+
+#ifndef EXCLUDE_GUS
+#ifndef EXCLUDE_GUS16
+ {SNDCARD_GUS16, {GUS16_BASE, GUS16_IRQ, GUS16_DMA, GUS_DMA_READ}, SND_DEFAULT_ENABLE},
+#endif
+ {SNDCARD_GUS, {GUS_BASE, GUS_IRQ, GUS_DMA, GUS_DMA_READ}, SND_DEFAULT_ENABLE},
+#endif
+
+#ifndef EXCLUDE_YM3812
+ {SNDCARD_ADLIB, {FM_MONO, 0, 0}, SND_DEFAULT_ENABLE},
+#endif
+#ifdef PC98
+#ifndef EXCLUDE_PCM86
+ {SNDCARD_PCM86, {0, 0, 0}, SND_DEFAULT_ENABLE},
+#endif
+#endif
+ {0, {0}, 0}
+ };
+
+ int num_sound_cards =
+ sizeof(snd_installed_cards) / sizeof (struct card_info);
+
+#else
+ int num_sound_cards = 0;
+#endif /* linux */
+
+ int num_sound_drivers =
+ sizeof(sound_drivers) / sizeof (struct driver_info);
+
+#else
+ extern struct audio_operations * audio_devs[MAX_AUDIO_DEV]; extern int num_audiodevs;
+ extern struct mixer_operations * mixer_devs[MAX_MIXER_DEV]; extern int num_mixers;
+ extern struct synth_operations * synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; extern int num_synths;
+ extern struct midi_operations * midi_devs[MAX_MIDI_DEV]; extern int num_midis;
+ extern struct sound_timer_operations * sound_timer_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; extern int num_sound_timers;
+
+ extern struct driver_info sound_drivers[];
+ extern int num_sound_drivers;
+ extern struct card_info snd_installed_cards[];
+ extern int num_sound_cards;
+#endif /* _DEV_TABLE_C_ */
+
+int sndtable_probe(int unit, struct address_info *hw_config);
+int sndtable_init_card(int unit, struct address_info *hw_config);
+int sndtable_get_cardcount (void);
+struct address_info *sound_getconf(int card_type);
+int snd_find_driver(int type);
+
+#endif /* _DEV_TABLE_H_ */
diff --git a/sys/pc98/pc98/sound/dmabuf.c b/sys/pc98/pc98/sound/dmabuf.c
new file mode 100644
index 0000000..ee88219
--- /dev/null
+++ b/sys/pc98/pc98/sound/dmabuf.c
@@ -0,0 +1,1130 @@
+/*
+ * sound/dmabuf.c
+ *
+ * The DMA buffer manager for digitized voice applications
+ *
+ * Copyright by Hannu Savolainen 1993, 1994, 1995
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS)
+
+DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]);
+
+static struct dma_buffparms dmaps[MAX_AUDIO_DEV] =
+{
+ {0}}; /*
+
+ * Primitive way to allocate
+ * such a large array.
+ * Needs dynamic run-time alloction.
+ */
+
+static void
+reorganize_buffers (int dev)
+{
+ /*
+ * This routine breaks the physical device buffers to logical ones.
+ */
+
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+ struct audio_operations *dsp_dev = audio_devs[dev];
+
+ unsigned i, p, n;
+ unsigned sr, nc, sz, bsz;
+
+ if (dmap->fragment_size == 0)
+ { /* Compute the fragment size using the default algorithm */
+
+ sr = dsp_dev->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
+ nc = dsp_dev->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
+ sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1);
+
+ if (sr < 1 || nc < 1 || sz < 1)
+ {
+ printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n",
+ dev, sr, nc, sz);
+ sr = DSP_DEFAULT_SPEED;
+ nc = 1;
+ sz = 8;
+ }
+
+ sz = sr * nc * sz;
+
+ sz /= 8; /* #bits -> #bytes */
+
+ /*
+ * Compute a buffer size for time not exeeding 1 second.
+ * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
+ * of sound (using the current speed, sample size and #channels).
+ */
+
+ bsz = dsp_dev->buffsize;
+ while (bsz > sz)
+ bsz /= 2;
+
+ if (dsp_dev->buffcount == 1 && bsz == dsp_dev->buffsize)
+ bsz /= 2; /* Needs at least 2 buffers */
+
+ if (dmap->subdivision == 0) /* Not already set */
+ dmap->subdivision = 1; /* Init to default value */
+ else
+ bsz /= dmap->subdivision;
+
+ if (bsz < 16)
+ bsz = 16; /* Just a sanity check */
+
+ while ((dsp_dev->buffsize * dsp_dev->buffcount) / bsz > MAX_SUB_BUFFERS)
+ bsz *= 2;
+
+ dmap->fragment_size = bsz;
+ }
+ else
+ {
+ /*
+ * The process has specified the buffer sice with SNDCTL_DSP_SETFRAGMENT or
+ * the buffer sice computation has already been done.
+ */
+ if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2))
+ dmap->fragment_size = (audio_devs[dev]->buffsize / 2);
+ bsz = dmap->fragment_size;
+ }
+
+ bsz &= ~0x03; /* Force size which is multiple of 4 bytes */
+
+ /*
+ * Now computing addresses for the logical buffers
+ */
+
+ n = 0;
+ for (i = 0; i < dmap->raw_count &&
+ n < dmap->max_fragments &&
+ n < MAX_SUB_BUFFERS; i++)
+ {
+ p = 0;
+
+ while ((p + bsz) <= dsp_dev->buffsize &&
+ n < dmap->max_fragments &&
+ n < MAX_SUB_BUFFERS)
+ {
+ dmap->buf[n] = dmap->raw_buf[i] + p;
+ dmap->buf_phys[n] = dmap->raw_buf_phys[i] + p;
+ p += bsz;
+ n++;
+ }
+ }
+
+ dmap->nbufs = n;
+ dmap->bytes_in_use = n * bsz;
+
+ for (i = 0; i < dmap->nbufs; i++)
+ {
+ dmap->counts[i] = 0;
+ }
+
+ dmap->flags |= DMA_ALLOC_DONE;
+}
+
+static void
+dma_init_buffers (int dev)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap = &dmaps[dev];
+
+ RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]);
+
+ dmap->flags = DMA_BUSY; /* Other flags off */
+ dmap->qlen = dmap->qhead = dmap->qtail = 0;
+ dmap->nbufs = 1;
+ dmap->bytes_in_use = audio_devs[dev]->buffsize;
+
+ dmap->dma_mode = DMODE_NONE;
+}
+
+int
+DMAbuf_open (int dev, int mode)
+{
+ int retval;
+ struct dma_buffparms *dmap = NULL;
+
+ if (dev >= num_audiodevs)
+ {
+ printk ("PCM device %d not installed.\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!audio_devs[dev])
+ {
+ printk ("PCM device %d not initialized\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ dmap = audio_devs[dev]->dmap = &dmaps[dev];
+
+ if (dmap->flags & DMA_BUSY)
+ return RET_ERROR (EBUSY);
+
+#ifdef USE_RUNTIME_DMAMEM
+ dmap->raw_buf[0] = NULL;
+ sound_dma_malloc (dev);
+#endif
+
+ if (dmap->raw_buf[0] == NULL)
+ return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */
+
+ if ((retval = audio_devs[dev]->open (dev, mode)) < 0)
+ return retval;
+
+ dmap->open_mode = mode;
+ dmap->subdivision = dmap->underrun_count = 0;
+ dmap->fragment_size = 0;
+ dmap->max_fragments = 65536; /* Just a large value */
+
+ dma_init_buffers (dev);
+ audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1);
+ audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1);
+ audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1);
+
+ return 0;
+}
+
+static void
+dma_reset (int dev)
+{
+ int retval;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ audio_devs[dev]->reset (dev);
+ audio_devs[dev]->close (dev);
+
+ if ((retval = audio_devs[dev]->open (dev, audio_devs[dev]->dmap->open_mode)) < 0)
+ printk ("Sound: Reset failed - Can't reopen device\n");
+ RESTORE_INTR (flags);
+
+ dma_init_buffers (dev);
+ reorganize_buffers (dev);
+}
+
+static int
+dma_sync (int dev)
+{
+ unsigned long flags;
+
+ if (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT)
+ {
+ DISABLE_INTR (flags);
+
+ while (!PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])
+ && audio_devs[dev]->dmap->qlen)
+ {
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ RESTORE_INTR (flags);
+ return audio_devs[dev]->dmap->qlen;
+ }
+ }
+ RESTORE_INTR (flags);
+
+ /*
+ * Some devices such as GUS have huge amount of on board RAM for the
+ * audio data. We have to wait until the device has finished playing.
+ */
+
+ DISABLE_INTR (flags);
+ if (audio_devs[dev]->local_qlen) /* Device has hidden buffers */
+ {
+ while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ && audio_devs[dev]->local_qlen (dev))
+ {
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ);
+ }
+ }
+ RESTORE_INTR (flags);
+ }
+ return audio_devs[dev]->dmap->qlen;
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+ unsigned long flags;
+
+ if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ && (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT))
+ {
+ dma_sync (dev);
+ }
+
+#ifdef USE_RUNTIME_DMAMEM
+ sound_dma_free (dev);
+#endif
+
+ DISABLE_INTR (flags);
+ audio_devs[dev]->reset (dev);
+
+ audio_devs[dev]->close (dev);
+
+ audio_devs[dev]->dmap->dma_mode = DMODE_NONE;
+ audio_devs[dev]->dmap->flags &= ~DMA_BUSY;
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
+{
+ unsigned long flags;
+ int err = EIO;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ DISABLE_INTR (flags);
+ if (!dmap->qlen)
+ {
+ if (dmap->flags & DMA_RESTART)
+ {
+ dma_reset (dev);
+ dmap->flags &= ~DMA_RESTART;
+ }
+
+ if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ dmap->dma_mode = DMODE_NONE;
+ }
+
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers (dev);
+
+ if (!dmap->dma_mode)
+ {
+ int err;
+
+ if ((err = audio_devs[dev]->prepare_for_input (dev,
+ dmap->fragment_size, dmap->nbufs)) < 0)
+ {
+ RESTORE_INTR (flags);
+ return err;
+ }
+ dmap->dma_mode = DMODE_INPUT;
+ }
+
+ if (!(dmap->flags & DMA_ACTIVE))
+ {
+ audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail],
+ dmap->fragment_size, 0,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
+ !(dmap->flags & DMA_STARTED));
+ dmap->flags |= DMA_ACTIVE | DMA_STARTED;
+ }
+
+ if (dontblock)
+ {
+ RESTORE_INTR (flags);
+#if defined(__FreeBSD__)
+ return RET_ERROR (EWOULDBLOCK);
+#else
+ return RET_ERROR (EAGAIN);
+#endif
+ }
+
+ /* Wait for the next block */
+
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+ dma_reset (dev);
+ err = EIO;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else
+ err = EINTR;
+ }
+ RESTORE_INTR (flags);
+
+ if (!dmap->qlen)
+ return RET_ERROR (err);
+
+ *buf = &dmap->buf[dmap->qhead][dmap->counts[dmap->qhead]];
+ *len = dmap->fragment_size - dmap->counts[dmap->qhead];
+
+ return dmap->qhead;
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ int p = dmap->counts[dmap->qhead] + c;
+
+ if (p >= dmap->fragment_size)
+ { /* This buffer is completely empty */
+ dmap->counts[dmap->qhead] = 0;
+ if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+ printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n",
+ dev, dmap->qlen, dmap->nbufs);
+ dmap->qlen--;
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ }
+ else
+ dmap->counts[dmap->qhead] = p;
+
+ return 0;
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ switch (cmd)
+ {
+ case SNDCTL_DSP_RESET:
+ dma_reset (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_SYNC:
+ dma_sync (dev);
+ dma_reset (dev);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers (dev);
+
+ return IOCTL_OUT (arg, dmap->fragment_size);
+ break;
+
+ case SNDCTL_DSP_SETBLKSIZE:
+ {
+ int size = IOCTL_IN(arg);
+
+ if(!(dmap->flags & DMA_ALLOC_DONE) && size)
+ {
+ dmap->fragment_size = size;
+ return 0;
+ }
+ else
+ return RET_ERROR (EINVAL); /* Too late to change */
+ }
+ break;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ {
+ int fact = IOCTL_IN (arg);
+
+ if (fact == 0)
+ {
+ fact = dmap->subdivision;
+ if (fact == 0)
+ fact = 1;
+ return IOCTL_OUT (arg, fact);
+ }
+
+ if (dmap->subdivision != 0 ||
+ dmap->fragment_size) /* Loo late to change */
+ return RET_ERROR (EINVAL);
+
+ if (fact > MAX_REALTIME_FACTOR)
+ return RET_ERROR (EINVAL);
+
+ if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+ return RET_ERROR (EINVAL);
+
+ dmap->subdivision = fact;
+ return IOCTL_OUT (arg, fact);
+ }
+ break;
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ {
+ int fact = IOCTL_IN (arg);
+ int bytes, count;
+
+ if (fact == 0)
+ return RET_ERROR (EIO);
+
+ if (dmap->subdivision != 0 ||
+ dmap->fragment_size) /* Loo late to change */
+ return RET_ERROR (EINVAL);
+
+ bytes = fact & 0xffff;
+ count = (fact >> 16) & 0xffff;
+
+ if (count == 0)
+ count = MAX_SUB_BUFFERS;
+
+ if (bytes < 7 || bytes > 17) /* <64 || > 128k */
+ return RET_ERROR (EINVAL);
+
+ if (count < 2)
+ return RET_ERROR (EINVAL);
+
+ dmap->fragment_size = (1 << bytes);
+ dmap->max_fragments = count;
+
+ if (dmap->fragment_size > audio_devs[dev]->buffsize)
+ dmap->fragment_size = audio_devs[dev]->buffsize;
+
+ if (dmap->fragment_size == audio_devs[dev]->buffsize &&
+ audio_devs[dev]->flags & DMA_AUTOMODE)
+ dmap->fragment_size /= 2; /* Needs at least 2 buffers */
+
+ dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */
+ return IOCTL_OUT (arg, bytes | (count << 16));
+ }
+ break;
+
+ case SNDCTL_DSP_GETISPACE:
+ case SNDCTL_DSP_GETOSPACE:
+ if (!local)
+ return RET_ERROR (EINVAL);
+
+ {
+ audio_buf_info *info = (audio_buf_info *) arg;
+
+ info->fragments = dmap->qlen;
+ info->fragsize = dmap->fragment_size;
+ info->bytes = dmap->qlen * dmap->fragment_size;
+ }
+ return 0;
+
+ default:
+ return audio_devs[dev]->ioctl (dev, cmd, arg, local);
+ }
+
+}
+
+static int
+space_in_queue (int dev)
+{
+ int len, max, tmp;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (dmap->qlen >= dmap->nbufs) /* No space at all */
+ return 0;
+
+ /*
+ * Verify that there are no more pending buffers than the limit
+ * defined by the process.
+ */
+
+ max = dmap->max_fragments;
+ len = dmap->qlen;
+
+ if (audio_devs[dev]->local_qlen)
+ {
+ tmp = audio_devs[dev]->local_qlen (dev);
+ if (tmp & len)
+ tmp--; /*
+ * This buffer has been counted twice
+ */
+ len += tmp;
+ }
+
+ if (len >= max)
+ return 0;
+ return 1;
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
+{
+ unsigned long flags;
+ int abort, err = EIO;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (dmap->dma_mode == DMODE_INPUT) /* Direction change */
+ {
+ dma_reset (dev);
+ dmap->dma_mode = DMODE_NONE;
+ }
+ else if (dmap->flags & DMA_RESTART) /* Restart buffering */
+ {
+ dma_sync (dev);
+ dma_reset (dev);
+ }
+
+ dmap->flags &= ~DMA_RESTART;
+
+ if (!(dmap->flags & DMA_ALLOC_DONE))
+ reorganize_buffers (dev);
+
+ if (!dmap->dma_mode)
+ {
+ int err;
+
+ dmap->dma_mode = DMODE_OUTPUT;
+ if ((err = audio_devs[dev]->prepare_for_output (dev,
+ dmap->fragment_size, dmap->nbufs)) < 0)
+ return err;
+ }
+
+ DISABLE_INTR (flags);
+
+ abort = 0;
+ while (!space_in_queue (dev) &&
+ !abort)
+ {
+
+ if (dontblock)
+ {
+ RESTORE_INTR (flags);
+ return RET_ERROR (EAGAIN);
+ }
+
+ /*
+ * Wait for free space
+ */
+ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ);
+ if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ printk ("Sound: DMA timed out - IRQ/DRQ config error?\n");
+ dma_reset (dev);
+ err = EIO;
+ abort = 1;
+ SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ err = EINTR;
+ abort = 1;
+ }
+ }
+ RESTORE_INTR (flags);
+
+ if (!space_in_queue (dev))
+ {
+ return RET_ERROR (err); /* Caught a signal ? */
+ }
+
+ *buf = dmap->buf[dmap->qtail];
+ *size = dmap->fragment_size;
+ dmap->counts[dmap->qtail] = 0;
+
+ return dmap->qtail;
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+ if (buff_no != dmap->qtail)
+ printk ("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail);
+
+ dmap->qlen++;
+ if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+ printk ("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n",
+ dev, dmap->qlen, dmap->nbufs);
+
+ dmap->counts[dmap->qtail] = l;
+
+ if ((l != dmap->fragment_size) &&
+ ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
+ audio_devs[dev]->flags & NEEDS_RESTART))
+ dmap->flags |= DMA_RESTART;
+ else
+ dmap->flags &= ~DMA_RESTART;
+
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+
+ if (!(dmap->flags & DMA_ACTIVE))
+ {
+ dmap->flags |= DMA_ACTIVE;
+ audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead],
+ dmap->counts[dmap->qhead], 0,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
+ !(dmap->flags & DMA_STARTED));
+ dmap->flags |= DMA_STARTED;
+ }
+
+ return 0;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ int chan = audio_devs[dev]->dmachan;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+ unsigned long flags;
+
+ /*
+ * This function is not as portable as it should be.
+ */
+
+ /*
+ * The count must be one less than the actual size. This is handled by
+ * set_dma_addr()
+ */
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE)
+ { /*
+ * Auto restart mode. Transfer the whole *
+ * buffer
+ */
+#ifdef linux
+ DISABLE_INTR (flags);
+ disable_dma (chan);
+ clear_dma_ff (chan);
+ set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
+ set_dma_addr (chan, dmap->raw_buf_phys[0]);
+ set_dma_count (chan, dmap->bytes_in_use);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+#else
+
+#if defined(__FreeBSD__)
+
+#ifdef PC98
+ pc98_dmastart (B_RAW | ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE),
+#else
+ isa_dmastart (B_RAW | ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE),
+#endif
+ (caddr_t)dmap->raw_buf_phys[0],
+ dmap->bytes_in_use,
+ chan);
+#else /* else __FreeBSD__ */
+#if defined(GENERIC_SYSV)
+#ifndef DMAMODE_AUTO
+ printk ("sound: Invalid DMA mode for device %d\n", dev);
+#endif
+#if defined(SVR42)
+
+ /*
+ ** send full count to snd_dma_prog, it will take care of subtracting
+ ** one if it is required.
+ */
+ snd_dma_prog (chan, dmap->raw_buf_phys[0], dmap->bytes_in_use,
+ dma_mode, TRUE);
+
+#else /* !SVR42 */
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode)
+#ifdef DMAMODE_AUTO
+ | DMAMODE_AUTO
+#endif
+ ,
+ dmap->raw_buf_phys[0], dmap->bytes_in_use - 1);
+ dma_enable (chan);
+#endif /* ! SVR42 */
+#else
+#error This routine is not valid for this OS.
+#endif
+#endif
+
+#endif
+ }
+ else
+ {
+#ifdef linux
+ DISABLE_INTR (flags);
+ disable_dma (chan);
+ clear_dma_ff (chan);
+ set_dma_mode (chan, dma_mode);
+ set_dma_addr (chan, physaddr);
+ set_dma_count (chan, count);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+#else
+#if defined(__FreeBSD__)
+#ifdef PC98
+ pc98_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+#else
+ isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE,
+#endif
+ (caddr_t)physaddr,
+ count,
+ chan);
+#else /* FreeBSD */
+
+#if defined(GENERIC_SYSV)
+#if defined(SVR42)
+
+ snd_dma_prog (chan, physaddr, count, dma_mode, FALSE);
+
+#else /* ! SVR42 */
+ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode),
+ physaddr, count);
+ dma_enable (chan);
+#endif /* SVR42 */
+#else
+#error This routine is not valid for this OS.
+#endif /* GENERIC_SYSV */
+#endif
+
+#endif
+ }
+
+ return count;
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+ int dev;
+
+#if defined(SVR42)
+ snd_dma_init ();
+#endif /* SVR42 */
+
+ /*
+ * NOTE! This routine could be called several times.
+ */
+
+ for (dev = 0; dev < num_audiodevs; dev++)
+ audio_devs[dev]->dmap = &dmaps[dev];
+ return mem_start;
+}
+
+void
+DMAbuf_outputintr (int dev, int event_type)
+{
+ /*
+ * Event types:
+ * 0 = DMA transfer done. Device still has more data in the local
+ * buffer.
+ * 1 = DMA transfer done. Device doesn't have local buffer or it's
+ * empty now.
+ * 2 = No DMA transfer but the device has now more space in it's local
+ * buffer.
+ */
+
+ unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+#if defined(SVR42)
+ snd_dma_intr (audio_devs[dev]->dmachan);
+#endif /* SVR42 */
+
+ if (event_type != 2)
+ {
+ if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+ {
+ printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n",
+ dev, dmap->qlen, dmap->nbufs);
+ return;
+ }
+
+ dmap->qlen--;
+ dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+ dmap->flags &= ~DMA_ACTIVE;
+
+#ifdef __FreeBSD__
+#ifdef PC98
+ pc98_dmadone(0, 0, 0, audio_devs[dev]->dmachan);
+#else
+ isa_dmadone(0, 0, 0, audio_devs[dev]->dmachan);
+#endif
+#endif
+
+ if (dmap->qlen)
+ {
+ audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead],
+ dmap->counts[dmap->qhead], 1,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE));
+ dmap->flags |= DMA_ACTIVE;
+ }
+ else if (event_type == 1)
+ {
+ dmap->underrun_count++;
+ audio_devs[dev]->halt_xfer (dev);
+ if ((audio_devs[dev]->flags & DMA_AUTOMODE) &&
+ audio_devs[dev]->flags & NEEDS_RESTART)
+ dmap->flags |= DMA_RESTART;
+ else
+ dmap->flags &= ~DMA_RESTART;
+ }
+ } /* event_type != 2 */
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+#if defined(__FreeBSD__)
+ if(selinfo[dev].si_pid)
+ selwakeup(&selinfo[dev]);
+#endif
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+ unsigned long flags;
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+
+#if defined(SVR42)
+ snd_dma_intr (audio_devs[dev]->dmachan);
+#endif /* SVR42 */
+
+#ifdef __FreeBSD__
+#ifdef PC98
+ pc98_dmadone(0, 0, 0, audio_devs[dev]->dmachan);
+#else
+ isa_dmadone(0, 0, 0, audio_devs[dev]->dmachan);
+#endif
+#endif
+
+ if (dmap->qlen == (dmap->nbufs - 1))
+ {
+#if !defined(__FreeBSD__) /* ignore console message. */
+ printk ("Sound: Recording overrun\n");
+#endif
+ dmap->underrun_count++;
+ audio_devs[dev]->halt_xfer (dev);
+ dmap->flags &= ~DMA_ACTIVE;
+ if (audio_devs[dev]->flags & DMA_AUTOMODE)
+ dmap->flags |= DMA_RESTART;
+ else
+ dmap->flags &= ~DMA_RESTART;
+ }
+ else
+ {
+ dmap->qlen++;
+ if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
+ printk ("\nSound: Audio queue4 corrupted for dev%d (%d/%d)\n",
+ dev, dmap->qlen, dmap->nbufs);
+ dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+
+ audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail],
+ dmap->fragment_size, 1,
+ !(audio_devs[dev]->flags & DMA_AUTOMODE));
+ dmap->flags |= DMA_ACTIVE;
+ }
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
+ {
+ WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
+ }
+ RESTORE_INTR (flags);
+#if defined(__FreeBSD__)
+ if(selinfo[dev].si_pid)
+ selwakeup(&selinfo[dev]);
+#endif
+}
+
+int
+DMAbuf_open_dma (int dev)
+{
+ unsigned long flags;
+ int chan = audio_devs[dev]->dmachan;
+
+ if (ALLOC_DMA_CHN (chan, audio_devs[dev]->name))
+ {
+#if 0
+ /* Enough already! This error is reported twice elsewhere */
+ printk ("Unable to grab DMA%d for the audio driver\n", chan);
+#endif
+ return RET_ERROR (EBUSY);
+ }
+
+ DISABLE_INTR (flags);
+#ifdef linux
+ disable_dma (chan);
+ clear_dma_ff (chan);
+#endif
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+void
+DMAbuf_close_dma (int dev)
+{
+ int chan = audio_devs[dev]->dmachan;
+
+ DMAbuf_reset_dma (dev);
+ RELEASE_DMA_CHN (chan);
+}
+
+void
+DMAbuf_reset_dma (int dev)
+{
+}
+
+#ifdef ALLOW_SELECT
+int
+DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ struct dma_buffparms *dmap = audio_devs[dev]->dmap;
+ unsigned long flags;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (dmap->dma_mode != DMODE_INPUT)
+ return 0;
+
+ DISABLE_INTR (flags);
+ if (!dmap->qlen)
+ {
+#if defined(__FreeBSD__)
+ selrecord(wait, &selinfo[dev]);
+#else
+ dev_sleep_flag[dev].mode = WK_SLEEP;
+ select_wait (&dev_sleeper[dev], wait);
+#endif
+ RESTORE_INTR (flags);
+ return 0;
+ }
+ RESTORE_INTR (flags);
+ return 1;
+ break;
+
+ case SEL_OUT:
+ if (dmap->dma_mode == DMODE_INPUT)
+ return 0;
+
+ if (dmap->dma_mode == DMODE_NONE)
+ return 1;
+
+ DISABLE_INTR (flags);
+ if (!space_in_queue (dev))
+ {
+#if defined(__FreeBSD__)
+ selrecord(wait, &selinfo[dev]);
+#else
+ dev_sleep_flag[dev].mode = WK_SLEEP;
+ select_wait (&dev_sleeper[dev], wait);
+#endif
+ RESTORE_INTR (flags);
+ return 0;
+ }
+ RESTORE_INTR (flags);
+ return 1;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
+
+ return 0;
+}
+
+#endif /* ALLOW_SELECT */
+
+#else /* EXCLUDE_AUDIO */
+/*
+ * Stub versions if audio services not included
+ */
+
+int
+DMAbuf_open (int dev, int mode)
+{
+ return RET_ERROR (ENXIO);
+}
+
+int
+DMAbuf_release (int dev, int mode)
+{
+ return 0;
+}
+
+int
+DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_rmchars (int dev, int buff_no, int c)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_start_output (int dev, int buff_no, int l)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+DMAbuf_init (long mem_start)
+{
+ return mem_start;
+}
+
+int
+DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+DMAbuf_open_dma (int dev)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+DMAbuf_close_dma (int dev)
+{
+ return;
+}
+
+void
+DMAbuf_reset_dma (int dev)
+{
+ return;
+}
+
+void
+DMAbuf_inputintr (int dev)
+{
+ return;
+}
+
+void
+DMAbuf_outputintr (int dev, int underrun_flag)
+{
+ return;
+}
+
+#endif
+
+#endif
diff --git a/sys/pc98/pc98/sound/finetune.h b/sys/pc98/pc98/sound/finetune.h
new file mode 100644
index 0000000..b86a0eb
--- /dev/null
+++ b/sys/pc98/pc98/sound/finetune.h
@@ -0,0 +1,49 @@
+#ifdef SEQUENCER_C
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+ unsigned short finetune_table[128] =
+ {
+/* 0 */ 9439, 9447, 9456, 9464, 9473, 9481, 9490, 9499,
+/* 8 */ 9507, 9516, 9524, 9533, 9542, 9550, 9559, 9567,
+/* 16 */ 9576, 9585, 9593, 9602, 9611, 9619, 9628, 9637,
+/* 24 */ 9645, 9654, 9663, 9672, 9680, 9689, 9698, 9707,
+/* 32 */ 9715, 9724, 9733, 9742, 9750, 9759, 9768, 9777,
+/* 40 */ 9786, 9795, 9803, 9812, 9821, 9830, 9839, 9848,
+/* 48 */ 9857, 9866, 9874, 9883, 9892, 9901, 9910, 9919,
+/* 56 */ 9928, 9937, 9946, 9955, 9964, 9973, 9982, 9991,
+/* 64 */ 10000, 10009, 10018, 10027, 10036, 10045, 10054, 10063,
+/* 72 */ 10072, 10082, 10091, 10100, 10109, 10118, 10127, 10136,
+/* 80 */ 10145, 10155, 10164, 10173, 10182, 10191, 10201, 10210,
+/* 88 */ 10219, 10228, 10237, 10247, 10256, 10265, 10274, 10284,
+/* 96 */ 10293, 10302, 10312, 10321, 10330, 10340, 10349, 10358,
+/* 104 */ 10368, 10377, 10386, 10396, 10405, 10415, 10424, 10433,
+/* 112 */ 10443, 10452, 10462, 10471, 10481, 10490, 10499, 10509,
+/* 120 */ 10518, 10528, 10537, 10547, 10556, 10566, 10576, 10585
+ };
+#else
+ extern unsigned short finetune_table[128];
+#endif
diff --git a/sys/pc98/pc98/sound/gus_card.c b/sys/pc98/pc98/sound/gus_card.c
new file mode 100644
index 0000000..4a07fd3
--- /dev/null
+++ b/sys/pc98/pc98/sound/gus_card.c
@@ -0,0 +1,197 @@
+/*
+ * sound/gus_card.c
+ *
+ * Detection routine for the Gravis Ultrasound.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#include "gus_hw.h"
+
+int gus_base, gus_irq, gus_dma;
+extern int gus_wave_volume;
+extern int gus_pcm_volume;
+extern int have_gus_max;
+
+long
+attach_gus_card (long mem_start, struct address_info *hw_config)
+{
+ int io_addr;
+
+ snd_set_irq_handler (hw_config->irq, gusintr, "Gravis Ultrasound");
+
+ if (gus_wave_detect (hw_config->io_base)) /*
+ * Try first the default
+ */
+ {
+ mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma,
+ hw_config->dma_read);
+#ifndef EXCLUDE_MIDI
+ mem_start = gus_midi_init (mem_start);
+#endif
+#ifndef EXCLUDE_SEQUENCER
+ sound_timer_init (hw_config->io_base + 8);
+#endif
+ return mem_start;
+ }
+
+#ifndef EXCLUDE_GUS_IODETECT
+
+ /*
+ * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+ */
+
+ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+ if (io_addr != hw_config->io_base) /*
+ * Already tested
+ */
+ if (gus_wave_detect (io_addr))
+ {
+ printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base);
+ mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma,
+ hw_config->dma_read);
+#ifndef EXCLUDE_MIDI
+ mem_start = gus_midi_init (mem_start);
+#endif
+#ifndef EXCLUDE_SEQUENCER
+ sound_timer_init (io_addr + 8);
+#endif
+ return mem_start;
+ }
+
+#endif
+
+ return mem_start; /*
+ * Not detected
+ */
+}
+
+int
+probe_gus (struct address_info *hw_config)
+{
+ int io_addr;
+
+ if (gus_wave_detect (hw_config->io_base))
+ return 1;
+
+#ifndef EXCLUDE_GUS_IODETECT
+
+ /*
+ * Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
+ */
+
+ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
+ if (io_addr != hw_config->io_base) /*
+ * Already tested
+ */
+ if (gus_wave_detect (io_addr))
+ return 1;
+
+#endif
+
+ return 0;
+}
+
+void
+gusintr (INT_HANDLER_PARMS (irq, dummy))
+{
+ unsigned char src;
+
+#ifdef linux
+ sti ();
+#endif
+
+#ifndef EXCLUDE_GUSMAX
+ if (have_gus_max)
+# if defined(__FreeBSD__)
+ ad1848_interrupt (INT_HANDLER_CALL (gus_irq));
+# else
+ ad1848_interrupt (INT_HANDLER_CALL (irq));
+# endif
+#endif
+
+ while (1)
+ {
+ if (!(src = INB (u_IrqStatus)))
+ return;
+
+ if (src & DMA_TC_IRQ)
+ {
+ guswave_dma_irq ();
+ }
+
+ if (src & (MIDI_TX_IRQ | MIDI_RX_IRQ))
+ {
+#ifndef EXCLUDE_MIDI
+ gus_midi_interrupt (0);
+#endif
+ }
+
+ if (src & (GF1_TIMER1_IRQ | GF1_TIMER2_IRQ))
+ {
+#ifndef EXCLUDE_SEQUENCER
+ sound_timer_interrupt ();
+#else
+ gus_write8 (0x45, 0); /* Stop timers */
+#endif
+ }
+
+ if (src & (WAVETABLE_IRQ | ENVELOPE_IRQ))
+ {
+ gus_voice_irq ();
+ }
+ }
+}
+
+#endif
+
+/*
+ * Some extra code for the 16 bit sampling option
+ */
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS16)
+
+int
+probe_gus_db16 (struct address_info *hw_config)
+{
+ return ad1848_detect (hw_config->io_base);
+}
+
+long
+attach_gus_db16 (long mem_start, struct address_info *hw_config)
+{
+ gus_pcm_volume = 100;
+ gus_wave_volume = 90;
+
+ ad1848_init ("GUS 16 bit sampling", hw_config->io_base,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma);
+ return mem_start;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/gus_hw.h b/sys/pc98/pc98/sound/gus_hw.h
new file mode 100644
index 0000000..f97a0b8
--- /dev/null
+++ b/sys/pc98/pc98/sound/gus_hw.h
@@ -0,0 +1,50 @@
+
+/*
+ * I/O addresses
+ */
+
+#define u_Base (gus_base + 0x000)
+#define u_Mixer u_Base
+#define u_Status (gus_base + 0x006)
+#define u_TimerControl (gus_base + 0x008)
+#define u_TimerData (gus_base + 0x009)
+#define u_IRQDMAControl (gus_base + 0x00b)
+#define u_MidiControl (gus_base + 0x100)
+#define MIDI_RESET 0x03
+#define MIDI_ENABLE_XMIT 0x20
+#define MIDI_ENABLE_RCV 0x80
+#define u_MidiStatus u_MidiControl
+#define MIDI_RCV_FULL 0x01
+#define MIDI_XMIT_EMPTY 0x02
+#define MIDI_FRAME_ERR 0x10
+#define MIDI_OVERRUN 0x20
+#define MIDI_IRQ_PEND 0x80
+#define u_MidiData (gus_base + 0x101)
+#define u_Voice (gus_base + 0x102)
+#define u_Command (gus_base + 0x103)
+#define u_DataLo (gus_base + 0x104)
+#define u_DataHi (gus_base + 0x105)
+#define u_MixData (gus_base + 0x106) /* Rev. 3.7+ mixing */
+#define u_MixSelect (gus_base + 0x506) /* registers. */
+#define u_IrqStatus u_Status
+# define MIDI_TX_IRQ 0x01 /* pending MIDI xmit IRQ */
+# define MIDI_RX_IRQ 0x02 /* pending MIDI recv IRQ */
+# define GF1_TIMER1_IRQ 0x04 /* general purpose timer */
+# define GF1_TIMER2_IRQ 0x08 /* general purpose timer */
+# define WAVETABLE_IRQ 0x20 /* pending wavetable IRQ */
+# define ENVELOPE_IRQ 0x40 /* pending volume envelope IRQ */
+# define DMA_TC_IRQ 0x80 /* pending dma tc IRQ */
+
+#define ICS2101 1
+# define ICS_MIXDEVS 6
+# define DEV_MIC 0
+# define DEV_LINE 1
+# define DEV_CD 2
+# define DEV_GF1 3
+# define DEV_UNUSED 4
+# define DEV_VOL 5
+
+# define CHN_LEFT 0
+# define CHN_RIGHT 1
+#define CS4231 2
+#define u_DRAMIO (gus_base + 0x107)
diff --git a/sys/pc98/pc98/sound/gus_linearvol.h b/sys/pc98/pc98/sound/gus_linearvol.h
new file mode 100644
index 0000000..7ad0c30
--- /dev/null
+++ b/sys/pc98/pc98/sound/gus_linearvol.h
@@ -0,0 +1,18 @@
+static unsigned short gus_linearvol[128] = {
+ 0x0000, 0x08ff, 0x09ff, 0x0a80, 0x0aff, 0x0b40, 0x0b80, 0x0bc0,
+ 0x0bff, 0x0c20, 0x0c40, 0x0c60, 0x0c80, 0x0ca0, 0x0cc0, 0x0ce0,
+ 0x0cff, 0x0d10, 0x0d20, 0x0d30, 0x0d40, 0x0d50, 0x0d60, 0x0d70,
+ 0x0d80, 0x0d90, 0x0da0, 0x0db0, 0x0dc0, 0x0dd0, 0x0de0, 0x0df0,
+ 0x0dff, 0x0e08, 0x0e10, 0x0e18, 0x0e20, 0x0e28, 0x0e30, 0x0e38,
+ 0x0e40, 0x0e48, 0x0e50, 0x0e58, 0x0e60, 0x0e68, 0x0e70, 0x0e78,
+ 0x0e80, 0x0e88, 0x0e90, 0x0e98, 0x0ea0, 0x0ea8, 0x0eb0, 0x0eb8,
+ 0x0ec0, 0x0ec8, 0x0ed0, 0x0ed8, 0x0ee0, 0x0ee8, 0x0ef0, 0x0ef8,
+ 0x0eff, 0x0f04, 0x0f08, 0x0f0c, 0x0f10, 0x0f14, 0x0f18, 0x0f1c,
+ 0x0f20, 0x0f24, 0x0f28, 0x0f2c, 0x0f30, 0x0f34, 0x0f38, 0x0f3c,
+ 0x0f40, 0x0f44, 0x0f48, 0x0f4c, 0x0f50, 0x0f54, 0x0f58, 0x0f5c,
+ 0x0f60, 0x0f64, 0x0f68, 0x0f6c, 0x0f70, 0x0f74, 0x0f78, 0x0f7c,
+ 0x0f80, 0x0f84, 0x0f88, 0x0f8c, 0x0f90, 0x0f94, 0x0f98, 0x0f9c,
+ 0x0fa0, 0x0fa4, 0x0fa8, 0x0fac, 0x0fb0, 0x0fb4, 0x0fb8, 0x0fbc,
+ 0x0fc0, 0x0fc4, 0x0fc8, 0x0fcc, 0x0fd0, 0x0fd4, 0x0fd8, 0x0fdc,
+ 0x0fe0, 0x0fe4, 0x0fe8, 0x0fec, 0x0ff0, 0x0ff4, 0x0ff8, 0x0ffc
+};
diff --git a/sys/pc98/pc98/sound/gus_midi.c b/sys/pc98/pc98/sound/gus_midi.c
new file mode 100644
index 0000000..66c81df
--- /dev/null
+++ b/sys/pc98/pc98/sound/gus_midi.c
@@ -0,0 +1,312 @@
+/*
+ * sound/gus2_midi.c
+ *
+ * The low level driver for the GUS Midi Interface.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "gus_hw.h"
+
+#if !defined(EXCLUDE_GUS) && !defined(EXCLUDE_MIDI)
+
+static int midi_busy = 0, input_opened = 0;
+static int my_dev;
+static int output_used = 0;
+static volatile unsigned char gus_midi_control;
+
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+extern int gus_base, gus_irq, gus_dma;
+
+#define GUS_MIDI_STATUS() INB(u_MidiStatus)
+
+static int
+gus_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+
+ if (midi_busy)
+ {
+ printk ("GUS: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ OUTB (MIDI_RESET, u_MidiControl);
+ gus_delay ();
+
+ gus_midi_control = 0;
+ input_opened = 0;
+
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ {
+ gus_midi_control |= MIDI_ENABLE_RCV;
+ input_opened = 1;
+ }
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ {
+ gus_midi_control |= MIDI_ENABLE_XMIT;
+ }
+
+ OUTB (gus_midi_control, u_MidiControl); /*
+ * Enable
+ */
+
+ midi_busy = 1;
+ qlen = qhead = qtail = output_used = 0;
+ midi_input_intr = input;
+
+ return 0;
+}
+
+static int
+dump_to_midi (unsigned char midi_byte)
+{
+ unsigned long flags;
+ int ok = 0;
+
+ output_used = 1;
+
+ DISABLE_INTR (flags);
+
+ if (GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY)
+ {
+ ok = 1;
+ OUTB (midi_byte, u_MidiData);
+ }
+ else
+ {
+ /*
+ * Enable Midi xmit interrupts (again)
+ */
+ gus_midi_control |= MIDI_ENABLE_XMIT;
+ OUTB (gus_midi_control, u_MidiControl);
+ }
+
+ RESTORE_INTR (flags);
+ return ok;
+}
+
+static void
+gus_midi_close (int dev)
+{
+ /*
+ * Reset FIFO pointers, disable intrs
+ */
+
+ OUTB (MIDI_RESET, u_MidiControl);
+ midi_busy = 0;
+}
+
+static int
+gus_midi_out (int dev, unsigned char midi_byte)
+{
+
+ unsigned long flags;
+
+ /*
+ * Drain the local queue first
+ */
+
+ DISABLE_INTR (flags);
+
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Output the byte if the local queue is empty.
+ */
+
+ if (!qlen)
+ if (dump_to_midi (midi_byte))
+ return 1; /*
+ * OK
+ */
+
+ /*
+ * Put to the local queue
+ */
+
+ if (qlen >= 256)
+ return 0; /*
+ * Local queue full
+ */
+
+ DISABLE_INTR (flags);
+
+ tmp_queue[qtail] = midi_byte;
+ qlen++;
+ qtail++;
+
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+static int
+gus_midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+gus_midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+gus_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+gus_midi_kick (int dev)
+{
+}
+
+static int
+gus_midi_buffer_status (int dev)
+{
+ unsigned long flags;
+
+ if (!output_used)
+ return 0;
+
+ DISABLE_INTR (flags);
+
+ if (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+
+ return (qlen > 0) | !(GUS_MIDI_STATUS () & MIDI_XMIT_EMPTY);
+}
+
+#define MIDI_SYNTH_NAME "Gravis Ultrasound Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations gus_midi_operations =
+{
+ {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS},
+ &std_midi_synth,
+ {0},
+ gus_midi_open,
+ gus_midi_close,
+ gus_midi_ioctl,
+ gus_midi_out,
+ gus_midi_start_read,
+ gus_midi_end_read,
+ gus_midi_kick,
+ NULL, /*
+ * command
+ */
+ gus_midi_buffer_status,
+ NULL
+};
+
+long
+gus_midi_init (long mem_start)
+{
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ OUTB (MIDI_RESET, u_MidiControl);
+
+ std_midi_synth.midi_dev = my_dev = num_midis;
+ midi_devs[num_midis++] = &gus_midi_operations;
+ return mem_start;
+}
+
+void
+gus_midi_interrupt (int dummy)
+{
+ unsigned char stat, data;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ stat = GUS_MIDI_STATUS ();
+
+ if (stat & MIDI_RCV_FULL)
+ {
+ data = INB (u_MidiData);
+ if (input_opened)
+ midi_input_intr (my_dev, data);
+ }
+
+ if (stat & MIDI_XMIT_EMPTY)
+ {
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ if (!qlen)
+ {
+ /*
+ * Disable Midi output interrupts, since no data in the buffer
+ */
+ gus_midi_control &= ~MIDI_ENABLE_XMIT;
+ OUTB (gus_midi_control, u_MidiControl);
+ }
+ }
+
+#if 0
+ if (stat & MIDI_FRAME_ERR)
+ printk ("GUS: Midi framing error\n");
+ if (stat & MIDI_OVERRUN && input_opened)
+ printk ("GUS: Midi input overrun\n");
+#endif
+
+ RESTORE_INTR (flags);
+}
+
+#endif
+
+#endif
diff --git a/sys/pc98/pc98/sound/gus_vol.c b/sys/pc98/pc98/sound/gus_vol.c
new file mode 100644
index 0000000..3e58dce
--- /dev/null
+++ b/sys/pc98/pc98/sound/gus_vol.c
@@ -0,0 +1,150 @@
+/*
+ * gus_vol.c - Compute volume for GUS.
+ *
+ * Greg Lee 1993.
+ */
+#include "sound_config.h"
+#ifndef EXCLUDE_GUS
+#include "gus_linearvol.h"
+
+extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev);
+extern unsigned short gus_linear_vol (int vol, int mainvol);
+
+#define GUS_VOLUME gus_wave_volume
+
+
+extern int gus_wave_volume;
+
+/*
+ * Calculate gus volume from note velocity, main volume, expression, and
+ * intrinsic patch volume given in patch library. Expression is multiplied
+ * in, so it emphasizes differences in note velocity, while main volume is
+ * added in -- I don't know whether this is right, but it seems reasonable to
+ * me. (In the previous stage, main volume controller messages were changed
+ * to expression controller messages, if they were found to be used for
+ * dynamic volume adjustments, so here, main volume can be assumed to be
+ * constant throughout a song.)
+ *
+ * Intrinsic patch volume is added in, but if over 64 is also multiplied in, so
+ * we can give a big boost to very weak voices like nylon guitar and the
+ * basses. The normal value is 64. Strings are assigned lower values.
+ */
+unsigned short
+gus_adagio_vol (int vel, int mainv, int xpn, int voicev)
+{
+ int i, m, n, x;
+
+
+ /*
+ * A voice volume of 64 is considered neutral, so adjust the main volume if
+ * something other than this neutral value was assigned in the patch
+ * library.
+ */
+ x = 256 + 6 * (voicev - 64);
+
+ /*
+ * Boost expression by voice volume above neutral.
+ */
+ if (voicev > 65)
+ xpn += voicev - 64;
+ xpn += (voicev - 64) / 2;
+
+ /*
+ * Combine multiplicative and level components.
+ */
+ x = vel * xpn * 6 + (voicev / 4) * x;
+
+#ifdef GUS_VOLUME
+ /*
+ * Further adjustment by installation-specific master volume control
+ * (default 60).
+ */
+ x = (x * GUS_VOLUME * GUS_VOLUME) / 10000;
+#endif
+
+#ifdef GUS_USE_CHN_MAIN_VOLUME
+ /*
+ * Experimental support for the channel main volume
+ */
+
+ mainv = (mainv / 2) + 64; /* Scale to 64 to 127 */
+ x = (x * mainv * mainv) / 16384;
+#endif
+
+ if (x < 2)
+ return (0);
+ else if (x >= 65535)
+ return ((15 << 8) | 255);
+
+ /*
+ * Convert to gus's logarithmic form with 4 bit exponent i and 8 bit
+ * mantissa m.
+ */
+ n = x;
+ i = 7;
+ if (n < 128)
+ {
+ while (i > 0 && n < (1 << i))
+ i--;
+ }
+ else
+ while (n > 255)
+ {
+ n >>= 1;
+ i++;
+ }
+ /*
+ * Mantissa is part of linear volume not expressed in exponent. (This is
+ * not quite like real logs -- I wonder if it's right.)
+ */
+ m = x - (1 << i);
+
+ /*
+ * Adjust mantissa to 8 bits.
+ */
+ if (m > 0)
+ {
+ if (i > 8)
+ m >>= i - 8;
+ else if (i < 8)
+ m <<= 8 - i;
+ }
+
+ return ((i << 8) + m);
+}
+
+/*
+ * Volume-values are interpreted as linear values. Volume is based on the
+ * value supplied with SEQ_START_NOTE(), channel main volume (if compiled in)
+ * and the volume set by the mixer-device (default 60%).
+ */
+
+unsigned short
+gus_linear_vol (int vol, int mainvol)
+{
+ int mixer_mainvol;
+
+ if (vol <= 0)
+ vol = 0;
+ else if (vol >= 127)
+ vol = 127;
+
+#ifdef GUS_VOLUME
+ mixer_mainvol = GUS_VOLUME;
+#else
+ mixer_mainvol = 100;
+#endif
+
+#ifdef GUS_USE_CHN_MAIN_VOLUME
+ if (mainvol <= 0)
+ mainvol = 0;
+ else if (mainvol >= 127)
+ mainvol = 127;
+#else
+ mainvol = 128;
+#endif
+
+ return gus_linearvol[(((vol * mainvol) / 128) * mixer_mainvol) / 100];
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/gus_wave.c b/sys/pc98/pc98/sound/gus_wave.c
new file mode 100644
index 0000000..fe88539
--- /dev/null
+++ b/sys/pc98/pc98/sound/gus_wave.c
@@ -0,0 +1,3445 @@
+/*
+ * sound/gus_wave.c
+ *
+ * Driver for the Gravis UltraSound wave table synth.
+ *
+ * Copyright by Hannu Savolainen 1993, 1994
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+#include <machine/ultrasound.h>
+#include "gus_hw.h"
+
+static unsigned char gus_look8 __P((int reg));
+static unsigned short gus_read16 __P((int reg));
+static void gus_write_addr __P((int reg, unsigned long address, int is16bit));
+static void gus_write16 __P((int reg, unsigned int data));
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#define MAX_SAMPLE 150
+#define MAX_PATCH 256
+
+struct voice_info
+ {
+ unsigned long orig_freq;
+ unsigned long current_freq;
+ unsigned long mode;
+ int bender;
+ int bender_range;
+ int panning;
+ int midi_volume;
+ unsigned int initial_volume;
+ unsigned int current_volume;
+ int loop_irq_mode, loop_irq_parm;
+#define LMODE_FINISH 1
+#define LMODE_PCM 2
+#define LMODE_PCM_STOP 3
+ int volume_irq_mode, volume_irq_parm;
+#define VMODE_HALT 1
+#define VMODE_ENVELOPE 2
+#define VMODE_START_NOTE 3
+
+ int env_phase;
+ unsigned char env_rate[6];
+ unsigned char env_offset[6];
+
+ /*
+ * Volume computation parameters for gus_adagio_vol()
+ */
+ int main_vol, expression_vol, patch_vol;
+
+ /* Variables for "Ultraclick" removal */
+ int dev_pending, note_pending, volume_pending, sample_pending;
+ char kill_pending;
+ long offset_pending;
+
+ };
+
+static struct voice_alloc_info *voice_alloc;
+
+extern int gus_base;
+extern int gus_irq, gus_dma;
+static long gus_mem_size = 0;
+static long free_mem_ptr = 0;
+static int gus_busy[MAX_AUDIO_DEV], gus_dspnum=0;
+static int gus_dma_read=0;
+static int nr_voices = 0;
+static int gus_devnum = 0;
+static int volume_base, volume_scale, volume_method;
+static int gus_recmask = SOUND_MASK_MIC;
+static int recording_active = 0;
+static int only_read_access = 0;
+
+int gus_wave_volume = 60;
+static int gus_pcm_volume = 80;
+int have_gus_max = 0;
+static int gus_line_vol = 100, gus_mic_vol = 0;
+static unsigned char mix_image = 0x00;
+
+/*
+ * Current version of this driver doesn't allow synth and PCM functions
+ * at the same time. The active_device specifies the active driver
+ */
+static int active_device = 0;
+
+#define GUS_DEV_WAVE 1 /* Wave table synth */
+#define GUS_DEV_PCM_DONE 2 /* PCM device, transfer done */
+#define GUS_DEV_PCM_CONTINUE 3 /* PCM device, transfer done ch. 1/2 */
+
+static int gus_sampling_speed;
+static int gus_sampling_channels;
+static int gus_sampling_bits;
+
+DEFINE_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
+
+/*
+ * Variables and buffers for PCM output
+ */
+#define MAX_PCM_BUFFERS (32*MAX_REALTIME_FACTOR) /* Don't change */
+
+static int pcm_bsize, pcm_nblk, pcm_banksize;
+static int pcm_datasize[MAX_PCM_BUFFERS];
+static volatile int pcm_head, pcm_tail, pcm_qlen;
+static volatile int pcm_active;
+static volatile int dma_active;
+static int pcm_opened = 0;
+static int pcm_current_dev;
+static int pcm_current_block;
+static unsigned long pcm_current_buf;
+static int pcm_current_count;
+static int pcm_current_intrflag;
+
+#if defined(__FreeBSD__)
+static char *gus_copy_buf;
+#endif
+
+static struct voice_info voices[32];
+
+static int freq_div_table[] =
+{
+ 44100, /* 14 */
+ 41160, /* 15 */
+ 38587, /* 16 */
+ 36317, /* 17 */
+ 34300, /* 18 */
+ 32494, /* 19 */
+ 30870, /* 20 */
+ 29400, /* 21 */
+ 28063, /* 22 */
+ 26843, /* 23 */
+ 25725, /* 24 */
+ 24696, /* 25 */
+ 23746, /* 26 */
+ 22866, /* 27 */
+ 22050, /* 28 */
+ 21289, /* 29 */
+ 20580, /* 30 */
+ 19916, /* 31 */
+ 19293 /* 32 */
+};
+
+static struct patch_info *samples;
+static long sample_ptrs[MAX_SAMPLE + 1];
+static int sample_map[32];
+static int free_sample;
+
+
+static int patch_table[MAX_PATCH];
+static int patch_map[32];
+
+static struct synth_info gus_info =
+{"Gravis UltraSound", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH};
+
+static void gus_poke (long addr, unsigned char data);
+static void compute_and_set_volume (int voice, int volume, int ramp_time);
+extern unsigned short gus_adagio_vol (int vel, int mainv, int xpn, int voicev);
+extern unsigned short gus_linear_vol (int vol, int mainvol);
+static void compute_volume (int voice, int volume);
+static void do_volume_irq (int voice);
+static void set_input_volumes (void);
+
+#define INSTANT_RAMP -1 /* Instant change. No ramping */
+#define FAST_RAMP 0 /* Fastest possible ramp */
+
+static void
+reset_sample_memory (void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_SAMPLE; i++)
+ sample_ptrs[i] = -1;
+ for (i = 0; i < 32; i++)
+ sample_map[i] = -1;
+ for (i = 0; i < 32; i++)
+ patch_map[i] = -1;
+
+ gus_poke (0, 0); /* Put a silent sample to the beginning */
+ gus_poke (1, 0);
+ free_mem_ptr = 2;
+
+ free_sample = 0;
+
+ for (i = 0; i < MAX_PATCH; i++)
+ patch_table[i] = -1;
+}
+
+void
+gus_delay (void)
+{
+ int i;
+
+ for (i = 0; i < 7; i++)
+ INB (u_DRAMIO);
+}
+
+static void
+gus_poke (long addr, unsigned char data)
+{ /* Writes a byte to the DRAM */
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB (0x43, u_Command);
+ OUTB (addr & 0xff, u_DataLo);
+ OUTB ((addr >> 8) & 0xff, u_DataHi);
+
+ OUTB (0x44, u_Command);
+ OUTB ((addr >> 16) & 0xff, u_DataHi);
+ OUTB (data, u_DRAMIO);
+ RESTORE_INTR (flags);
+}
+
+static unsigned char
+gus_peek (long addr)
+{ /* Reads a byte from the DRAM */
+ unsigned long flags;
+ unsigned char tmp;
+
+ DISABLE_INTR (flags);
+ OUTB (0x43, u_Command);
+ OUTB (addr & 0xff, u_DataLo);
+ OUTB ((addr >> 8) & 0xff, u_DataHi);
+
+ OUTB (0x44, u_Command);
+ OUTB ((addr >> 16) & 0xff, u_DataHi);
+ tmp = INB (u_DRAMIO);
+ RESTORE_INTR (flags);
+
+ return tmp;
+}
+
+void
+gus_write8 (int reg, unsigned int data)
+{ /* Writes to an indirect register (8 bit) */
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg, u_Command);
+ OUTB ((unsigned char) (data & 0xff), u_DataHi);
+
+ RESTORE_INTR (flags);
+}
+
+static unsigned char
+gus_read8 (int reg)
+{ /* Reads from an indirect register (8 bit). Offset 0x80. */
+ unsigned long flags;
+ unsigned char val;
+
+ DISABLE_INTR (flags);
+ OUTB (reg | 0x80, u_Command);
+ val = INB (u_DataHi);
+ RESTORE_INTR (flags);
+
+ return val;
+}
+
+static unsigned char
+gus_look8 (int reg)
+{ /* Reads from an indirect register (8 bit). No additional offset. */
+ unsigned long flags;
+ unsigned char val;
+
+ DISABLE_INTR (flags);
+ OUTB (reg, u_Command);
+ val = INB (u_DataHi);
+ RESTORE_INTR (flags);
+
+ return val;
+}
+
+static void
+gus_write16 (int reg, unsigned int data)
+{ /* Writes to an indirect register (16 bit) */
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg, u_Command);
+
+ OUTB ((unsigned char) (data & 0xff), u_DataLo);
+ OUTB ((unsigned char) ((data >> 8) & 0xff), u_DataHi);
+
+ RESTORE_INTR (flags);
+}
+
+static unsigned short
+gus_read16 (int reg)
+{ /* Reads from an indirect register (16 bit). Offset 0x80. */
+ unsigned long flags;
+ unsigned char hi, lo;
+
+ DISABLE_INTR (flags);
+
+ OUTB (reg | 0x80, u_Command);
+
+ lo = INB (u_DataLo);
+ hi = INB (u_DataHi);
+
+ RESTORE_INTR (flags);
+
+ return ((hi << 8) & 0xff00) | lo;
+}
+
+static void
+gus_write_addr (int reg, unsigned long address, int is16bit)
+{ /* Writes an 24 bit memory address */
+ unsigned long hold_address;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (is16bit)
+ {
+ /*
+ * Special processing required for 16 bit patches
+ */
+
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+
+ gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
+ gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
+ /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */
+ gus_delay ();
+ gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
+ gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
+ RESTORE_INTR (flags);
+}
+
+static void
+gus_select_voice (int voice)
+{
+ if (voice < 0 || voice > 31)
+ return;
+
+ OUTB (voice, u_Voice);
+}
+
+static void
+gus_select_max_voices (int nvoices)
+{
+ if (nvoices < 14)
+ nvoices = 14;
+ if (nvoices > 32)
+ nvoices = 32;
+
+ voice_alloc->max_voice = nr_voices = nvoices;
+
+ gus_write8 (0x0e, (nvoices - 1) | 0xc0);
+}
+
+static void
+gus_voice_on (unsigned int mode)
+{
+ gus_write8 (0x00, (unsigned char) (mode & 0xfc));
+ gus_delay ();
+ gus_write8 (0x00, (unsigned char) (mode & 0xfc));
+}
+
+static void
+gus_voice_off (void)
+{
+ gus_write8 (0x00, gus_read8 (0x00) | 0x03);
+}
+
+static void
+gus_voice_mode (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x00, (gus_read8 (0x00) & 0x03) |
+ (mode & 0xfc)); /* Don't touch last two bits */
+ gus_delay ();
+ gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc));
+}
+
+static void
+gus_voice_freq (unsigned long freq)
+{
+ unsigned long divisor = freq_div_table[nr_voices - 14];
+ unsigned short fc;
+
+ fc = (unsigned short) (((freq << 9) + (divisor >> 1)) / divisor);
+ fc = fc << 1;
+
+ gus_write16 (0x01, fc);
+}
+
+static void
+gus_voice_volume (unsigned int vol)
+{
+ gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */
+ gus_write16 (0x09, (unsigned short) (vol << 4));
+}
+
+static void
+gus_voice_balance (unsigned int balance)
+{
+ gus_write8 (0x0c, (unsigned char) (balance & 0xff));
+}
+
+static void
+gus_ramp_range (unsigned int low, unsigned int high)
+{
+ gus_write8 (0x07, (unsigned char) ((low >> 4) & 0xff));
+ gus_write8 (0x08, (unsigned char) ((high >> 4) & 0xff));
+}
+
+static void
+gus_ramp_rate (unsigned int scale, unsigned int rate)
+{
+ gus_write8 (0x06, (unsigned char) (((scale & 0x03) << 6) | (rate & 0x3f)));
+}
+
+static void
+gus_rampon (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x0d, mode & 0xfc);
+ gus_delay ();
+ gus_write8 (0x0d, mode & 0xfc);
+}
+
+static void
+gus_ramp_mode (unsigned int m)
+{
+ unsigned char mode = (unsigned char) (m & 0xff);
+
+ gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) |
+ (mode & 0xfc)); /* Leave the last 2 bits alone */
+ gus_delay ();
+ gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc));
+}
+
+static void
+gus_rampoff (void)
+{
+ gus_write8 (0x0d, 0x03);
+}
+
+static void
+gus_set_voice_pos (int voice, long position)
+{
+ int sample_no;
+
+ if ((sample_no = sample_map[voice]) != -1)
+ if (position < samples[sample_no].len)
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ voices[voice].offset_pending = position;
+ else
+ gus_write_addr (0x0a, sample_ptrs[sample_no] + position,
+ samples[sample_no].mode & WAVE_16_BITS);
+}
+
+static void
+gus_voice_init (int voice)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_volume (0);
+ gus_voice_off ();
+ gus_write_addr (0x0a, 0, 0); /* Set current position to 0 */
+ gus_write8 (0x00, 0x03); /* Voice off */
+ gus_write8 (0x0d, 0x03); /* Ramping off */
+ voice_alloc->map[voice] = 0;
+ voice_alloc->alloc_times[voice] = 0;
+ RESTORE_INTR (flags);
+
+}
+
+static void
+gus_voice_init2 (int voice)
+{
+ voices[voice].panning = 0;
+ voices[voice].mode = 0;
+ voices[voice].orig_freq = 20000;
+ voices[voice].current_freq = 20000;
+ voices[voice].bender = 0;
+ voices[voice].bender_range = 200;
+ voices[voice].initial_volume = 0;
+ voices[voice].current_volume = 0;
+ voices[voice].loop_irq_mode = 0;
+ voices[voice].loop_irq_parm = 0;
+ voices[voice].volume_irq_mode = 0;
+ voices[voice].volume_irq_parm = 0;
+ voices[voice].env_phase = 0;
+ voices[voice].main_vol = 127;
+ voices[voice].patch_vol = 127;
+ voices[voice].expression_vol = 127;
+ voices[voice].sample_pending = -1;
+}
+
+static void
+step_envelope (int voice)
+{
+ unsigned vol, prev_vol, phase;
+ unsigned char rate;
+ long int flags;
+
+ if (voices[voice].mode & WAVE_SUSTAIN_ON && voices[voice].env_phase == 2)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ return;
+ /*
+ * Sustain phase begins. Continue envelope after receiving note off.
+ */
+ }
+
+ if (voices[voice].env_phase >= 5)
+ { /* Envelope finished. Shoot the voice down */
+ gus_voice_init (voice);
+ return;
+ }
+
+ prev_vol = voices[voice].current_volume;
+ phase = ++voices[voice].env_phase;
+ compute_volume (voice, voices[voice].midi_volume);
+ vol = voices[voice].initial_volume * voices[voice].env_offset[phase] / 255;
+ rate = voices[voice].env_rate[phase];
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ gus_voice_volume (prev_vol);
+
+
+ gus_write8 (0x06, rate); /* Ramping rate */
+
+ voices[voice].volume_irq_mode = VMODE_ENVELOPE;
+
+ if (((vol - prev_vol) / 64) == 0) /* No significant volume change */
+ {
+ RESTORE_INTR (flags);
+ step_envelope (voice); /* Continue the envelope on the next step */
+ return;
+ }
+
+ if (vol > prev_vol)
+ {
+ if (vol >= (4096 - 64))
+ vol = 4096 - 65;
+ gus_ramp_range (0, vol);
+ gus_rampon (0x20); /* Increasing volume, with IRQ */
+ }
+ else
+ {
+ if (vol <= 64)
+ vol = 65;
+ gus_ramp_range (vol, 4030);
+ gus_rampon (0x60); /* Decreasing volume, with IRQ */
+ }
+ voices[voice].current_volume = vol;
+ RESTORE_INTR (flags);
+}
+
+static void
+init_envelope (int voice)
+{
+ voices[voice].env_phase = -1;
+ voices[voice].current_volume = 64;
+
+ step_envelope (voice);
+}
+
+static void
+start_release (int voice, long int flags)
+{
+ if (gus_read8 (0x00) & 0x03)
+ return; /* Voice already stopped */
+
+ voices[voice].env_phase = 2; /* Will be incremented by step_envelope */
+
+ voices[voice].current_volume =
+ voices[voice].initial_volume =
+ gus_read16 (0x09) >> 4; /* Get current volume */
+
+ voices[voice].mode &= ~WAVE_SUSTAIN_ON;
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ step_envelope (voice);
+}
+
+static void
+gus_voice_fade (int voice)
+{
+ int instr_no = sample_map[voice], is16bits;
+ long int flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ if (instr_no < 0 || instr_no > MAX_SAMPLE)
+ {
+ gus_write8 (0x00, 0x03); /* Hard stop */
+ voice_alloc->map[voice] = 0;
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ is16bits = (samples[instr_no].mode & WAVE_16_BITS) ? 1 : 0; /* 8 or 16 bits */
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ start_release (voice, flags);
+ return;
+ }
+
+ /*
+ * Ramp the volume down but not too quickly.
+ */
+ if ((int) (gus_read16 (0x09) >> 4) < 100) /* Get current volume */
+ {
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_init (voice);
+ return;
+ }
+
+ gus_ramp_range (65, 4030);
+ gus_ramp_rate (2, 4);
+ gus_rampon (0x40 | 0x20); /* Down, once, with IRQ */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+ RESTORE_INTR (flags);
+}
+
+static void
+gus_reset (void)
+{
+ int i;
+
+ gus_select_max_voices (24);
+ volume_base = 3071;
+ volume_scale = 4;
+ volume_method = VOL_METHOD_ADAGIO;
+
+ for (i = 0; i < 32; i++)
+ {
+ gus_voice_init (i); /* Turn voice off */
+ gus_voice_init2 (i);
+ }
+
+ INB (u_Status); /* Touch the status register */
+
+ gus_look8 (0x41); /* Clear any pending DMA IRQs */
+ gus_look8 (0x49); /* Clear any pending sample IRQs */
+
+ gus_read8 (0x0f); /* Clear pending IRQs */
+
+}
+
+static void
+gus_initialize (void)
+{
+ unsigned long flags;
+ unsigned char dma_image, irq_image, tmp;
+
+ static unsigned char gus_irq_map[16] =
+ {0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7};
+
+ static unsigned char gus_dma_map[8] =
+ {0, 1, 0, 2, 0, 3, 4, 5};
+
+ DISABLE_INTR (flags);
+ gus_write8 (0x4c, 0); /* Reset GF1 */
+ gus_delay ();
+ gus_delay ();
+
+ gus_write8 (0x4c, 1); /* Release Reset */
+ gus_delay ();
+ gus_delay ();
+
+ /*
+ * Clear all interrupts
+ */
+
+ gus_write8 (0x41, 0); /* DMA control */
+ gus_write8 (0x45, 0); /* Timer control */
+ gus_write8 (0x49, 0); /* Sample control */
+
+ gus_select_max_voices (24);
+
+ INB (u_Status); /* Touch the status register */
+
+ gus_look8 (0x41); /* Clear any pending DMA IRQs */
+ gus_look8 (0x49); /* Clear any pending sample IRQs */
+ gus_read8 (0x0f); /* Clear pending IRQs */
+
+ gus_reset (); /* Resets all voices */
+
+ gus_look8 (0x41); /* Clear any pending DMA IRQs */
+ gus_look8 (0x49); /* Clear any pending sample IRQs */
+ gus_read8 (0x0f); /* Clear pending IRQs */
+
+ gus_write8 (0x4c, 7); /* Master reset | DAC enable | IRQ enable */
+
+ /*
+ * Set up for Digital ASIC
+ */
+
+ OUTB (0x05, gus_base + 0x0f);
+
+ mix_image |= 0x02; /* Disable line out */
+ OUTB (mix_image, u_Mixer);
+
+ OUTB (0x00, u_IRQDMAControl);
+
+ OUTB (0x00, gus_base + 0x0f);
+
+ /*
+ * Now set up the DMA and IRQ interface
+ *
+ * The GUS supports two IRQs and two DMAs.
+ *
+ * Just one DMA channel is used. This prevents simultaneous ADC and DAC.
+ * Adding this support requires significant changes to the dmabuf.c, dsp.c
+ * and audio.c also.
+ */
+
+ irq_image = 0;
+ tmp = gus_irq_map[gus_irq];
+ if (!tmp)
+ printk ("Warning! GUS IRQ not selected\n");
+ irq_image |= tmp;
+ irq_image |= 0x40; /* Combine IRQ1 (GF1) and IRQ2 (Midi) */
+
+ dma_image = gus_dma_map[gus_dma_read] << 3;
+ if(!dma_image)
+ printk ("Warning! GUS DMA read channel not selected.\n");
+ if(gus_dma_read == gus_dma)
+ {
+ dma_image = 0x40; /* dual dma inhibited
+ Combine DMA1 (DRAM) and IRQ2 (ADC) */
+ }
+ tmp = gus_dma_map[gus_dma];
+ if (!tmp)
+ printk ("Warning! GUS DMA not selected\n");
+ dma_image |= tmp;
+
+ /*
+ * For some reason the IRQ and DMA addresses must be written twice
+ */
+
+ /*
+ * Doing it first time
+ */
+
+ OUTB (mix_image, u_Mixer); /* Select DMA control */
+ OUTB (dma_image | 0x80, u_IRQDMAControl); /* Set DMA address */
+
+ OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */
+ OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */
+
+ /*
+ * Doing it second time
+ */
+
+ OUTB (mix_image, u_Mixer); /* Select DMA control */
+ OUTB (dma_image, u_IRQDMAControl); /* Set DMA address */
+
+ OUTB (mix_image | 0x40, u_Mixer); /* Select IRQ control */
+ OUTB (irq_image, u_IRQDMAControl); /* Set IRQ address */
+
+ gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
+
+ mix_image &= ~0x02; /* Enable line out */
+ mix_image |= 0x08; /* Enable IRQ */
+ OUTB (mix_image, u_Mixer); /*
+ * Turn mixer channels on
+ * Note! Mic in is left off.
+ */
+
+ gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
+
+ gusintr (INT_HANDLER_CALL (0)); /* Serve pending interrupts */
+ RESTORE_INTR (flags);
+}
+
+int
+gus_wave_detect (int baseaddr)
+{
+ unsigned long i;
+ unsigned long loc;
+
+ gus_base = baseaddr;
+
+ gus_write8 (0x4c, 0); /* Reset GF1 */
+ gus_delay ();
+ gus_delay ();
+
+ gus_write8 (0x4c, 1); /* Release Reset */
+ gus_delay ();
+ gus_delay ();
+
+ /* See if there is first block there.... */
+ gus_poke (0L, 0xaa);
+ if (gus_peek (0L) != 0xaa)
+ return (0);
+
+ /* Now zero it out so that I can check for mirroring .. */
+ gus_poke (0L, 0x00);
+ for (i = 1L; i < 1024L; i++)
+ {
+ int n, failed;
+
+ /* check for mirroring ... */
+ if (gus_peek (0L) != 0)
+ break;
+ loc = i << 10;
+
+ for (n = loc - 1, failed = 0; n <= loc; n++)
+ {
+ gus_poke (loc, 0xaa);
+ if (gus_peek (loc) != 0xaa)
+ failed = 1;
+
+ gus_poke (loc, 0x55);
+ if (gus_peek (loc) != 0x55)
+ failed = 1;
+ }
+
+ if (failed)
+ break;
+ }
+ gus_mem_size = i << 10;
+ return 1;
+}
+
+static int
+guswave_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+
+ switch (cmd)
+ {
+ case SNDCTL_SYNTH_INFO:
+ gus_info.nr_voices = nr_voices;
+ IOCTL_TO_USER ((char *) arg, 0, &gus_info, sizeof (gus_info));
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ reset_sample_memory ();
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_PERCMODE:
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return gus_mem_size - free_mem_ptr - 32;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static int
+guswave_set_instr (int dev, int voice, int instr_no)
+{
+ int sample_no;
+
+ if (instr_no < 0 || instr_no > MAX_PATCH)
+ return RET_ERROR (EINVAL);
+
+ if (voice < 0 || voice > 31)
+ return RET_ERROR (EINVAL);
+
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].sample_pending = instr_no;
+ return 0;
+ }
+
+ sample_no = patch_table[instr_no];
+ patch_map[voice] = -1;
+
+ if (sample_no < 0)
+ {
+ printk ("GUS: Undefined patch %d for voice %d\n", instr_no, voice);
+ return RET_ERROR (EINVAL); /* Patch not defined */
+ }
+
+ if (sample_ptrs[sample_no] == -1) /* Sample not loaded */
+ {
+ printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n",
+ sample_no, instr_no, voice);
+ return RET_ERROR (EINVAL);
+ }
+
+ sample_map[voice] = sample_no;
+ patch_map[voice] = instr_no;
+ return 0;
+}
+
+static int
+guswave_kill_note (int dev, int voice, int note, int velocity)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ /* voice_alloc->map[voice] = 0xffff; */
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ {
+ voices[voice].kill_pending = 1;
+ RESTORE_INTR (flags);
+ }
+ else
+ {
+ RESTORE_INTR (flags);
+ gus_voice_fade (voice);
+ }
+
+ RESTORE_INTR (flags);
+ return 0;
+}
+
+static void
+guswave_aftertouch (int dev, int voice, int pressure)
+{
+#if 0
+ short lo_limit, hi_limit;
+ unsigned long flags;
+
+ if (voice < 0 || voice > 31)
+ return;
+
+ if (voices[voice].mode & WAVE_ENVELOPES && voices[voice].env_phase != 2)
+ return; /* Don't mix with envelopes */
+
+ if (pressure < 32)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ compute_and_set_volume (voice, 255, 0); /* Back to original volume */
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ hi_limit = voices[voice].current_volume;
+ lo_limit = hi_limit * 99 / 100;
+ if (lo_limit < 65)
+ lo_limit = 65;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ if (hi_limit > (4095 - 65))
+ {
+ hi_limit = 4095 - 65;
+ gus_voice_volume (hi_limit);
+ }
+ gus_ramp_range (lo_limit, hi_limit);
+ gus_ramp_rate (3, 8);
+ gus_rampon (0x58); /* Bidirectional, dow, loop */
+ RESTORE_INTR (flags);
+#endif /* 0 */
+}
+
+static void
+guswave_panning (int dev, int voice, int value)
+{
+ if (voice >= 0 || voice < 32)
+ voices[voice].panning = value;
+}
+
+static void
+guswave_volume_method (int dev, int mode)
+{
+ if (mode == VOL_METHOD_LINEAR || mode == VOL_METHOD_ADAGIO)
+ volume_method = mode;
+}
+
+static void
+compute_volume (int voice, int volume)
+{
+ if (volume < 128)
+ voices[voice].midi_volume = volume;
+
+ switch (volume_method)
+ {
+ case VOL_METHOD_ADAGIO:
+ voices[voice].initial_volume =
+ gus_adagio_vol (voices[voice].midi_volume, voices[voice].main_vol,
+ voices[voice].expression_vol,
+ voices[voice].patch_vol);
+ break;
+
+ case VOL_METHOD_LINEAR: /* Totally ignores patch-volume and expression */
+ voices[voice].initial_volume =
+ gus_linear_vol (volume, voices[voice].main_vol);
+ break;
+
+ default:
+ voices[voice].initial_volume = volume_base +
+ (voices[voice].midi_volume * volume_scale);
+ }
+
+ if (voices[voice].initial_volume > 4030)
+ voices[voice].initial_volume = 4030;
+}
+
+static void
+compute_and_set_volume (int voice, int volume, int ramp_time)
+{
+ int current, target, rate;
+ unsigned long flags;
+
+ compute_volume (voice, volume);
+ voices[voice].current_volume = voices[voice].initial_volume;
+
+ DISABLE_INTR (flags);
+ /*
+ * CAUTION! Interrupts disabled. Enable them before returning
+ */
+
+ gus_select_voice (voice);
+
+ current = gus_read16 (0x09) >> 4;
+ target = voices[voice].initial_volume;
+
+ if (ramp_time == INSTANT_RAMP)
+ {
+ gus_rampoff ();
+ gus_voice_volume (target);
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ if (ramp_time == FAST_RAMP)
+ rate = 63;
+ else
+ rate = 16;
+ gus_ramp_rate (0, rate);
+
+ if ((target - current) / 64 == 0) /* Close enough to target. */
+ {
+ gus_rampoff ();
+ gus_voice_volume (target);
+ RESTORE_INTR (flags);
+ return;
+ }
+
+ if (target > current)
+ {
+ if (target > (4095 - 65))
+ target = 4095 - 65;
+ gus_ramp_range (current, target);
+ gus_rampon (0x00); /* Ramp up, once, no IRQ */
+ }
+ else
+ {
+ if (target < 65)
+ target = 65;
+
+ gus_ramp_range (target, current);
+ gus_rampon (0x40); /* Ramp down, once, no irq */
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+dynamic_volume_change (int voice)
+{
+ unsigned char status;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ status = gus_read8 (0x00); /* Get voice status */
+ RESTORE_INTR (flags);
+
+ if (status & 0x03)
+ return; /* Voice was not running */
+
+ if (!(voices[voice].mode & WAVE_ENVELOPES))
+ {
+ compute_and_set_volume (voice, voices[voice].midi_volume, 1);
+ return;
+ }
+
+ /*
+ * Voice is running and has envelopes.
+ */
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ status = gus_read8 (0x0d); /* Ramping status */
+ RESTORE_INTR (flags);
+
+ if (status & 0x03) /* Sustain phase? */
+ {
+ compute_and_set_volume (voice, voices[voice].midi_volume, 1);
+ return;
+ }
+
+ if (voices[voice].env_phase < 0)
+ return;
+
+ compute_volume (voice, voices[voice].midi_volume);
+
+}
+
+static void
+guswave_controller (int dev, int voice, int ctrl_num, int value)
+{
+ unsigned long flags;
+ unsigned long freq;
+
+ if (voice < 0 || voice > 31)
+ return;
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ voices[voice].bender = value;
+
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ {
+ freq = compute_finetune (voices[voice].orig_freq, value,
+ voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_freq (freq);
+ RESTORE_INTR (flags);
+ }
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE:
+ voices[voice].bender_range = value;
+ break;
+ case CTL_EXPRESSION:
+ value /= 128;
+ case CTRL_EXPRESSION:
+ if (volume_method == VOL_METHOD_ADAGIO)
+ {
+ voices[voice].expression_vol = value;
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ dynamic_volume_change (voice);
+ }
+ break;
+
+ case CTL_PAN:
+ voices[voice].panning = (value * 2) - 128;
+ break;
+
+ case CTL_MAIN_VOLUME:
+ value = (value * 100) / 16383;
+
+ case CTRL_MAIN_VOLUME:
+ voices[voice].main_vol = value;
+ if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
+ dynamic_volume_change (voice);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int
+guswave_start_note2 (int dev, int voice, int note_num, int volume)
+{
+ int sample, best_sample, best_delta, delta_freq;
+ int is16bits, samplep, patch, pan;
+ unsigned long note_freq, base_note, freq, flags;
+ unsigned char mode = 0;
+
+ if (voice < 0 || voice > 31)
+ {
+ printk ("GUS: Invalid voice\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ if (note_num == 255)
+ {
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ voices[voice].midi_volume = volume;
+ dynamic_volume_change (voice);
+ return 0;
+ }
+
+ compute_and_set_volume (voice, volume, 1);
+ return 0;
+ }
+
+ if ((patch = patch_map[voice]) == -1)
+ {
+ return RET_ERROR (EINVAL);
+ }
+
+ if ((samplep = patch_table[patch]) == -1)
+ {
+ return RET_ERROR (EINVAL);
+ }
+
+ note_freq = note_to_freq (note_num);
+
+ /*
+ * Find a sample within a patch so that the note_freq is between low_note
+ * and high_note.
+ */
+ sample = -1;
+
+ best_sample = samplep;
+ best_delta = 1000000;
+ while (samplep >= 0 && sample == -1)
+ {
+ delta_freq = note_freq - samples[samplep].base_note;
+ if (delta_freq < 0)
+ delta_freq = -delta_freq;
+ if (delta_freq < best_delta)
+ {
+ best_sample = samplep;
+ best_delta = delta_freq;
+ }
+ if (samples[samplep].low_note <= note_freq &&
+ note_freq <= samples[samplep].high_note)
+ sample = samplep;
+ else
+ samplep = samples[samplep].key; /*
+ * Follow link
+ */
+ }
+ if (sample == -1)
+ sample = best_sample;
+
+ if (sample == -1)
+ {
+ printk ("GUS: Patch %d not defined for note %d\n", patch, note_num);
+ return 0; /* Should play default patch ??? */
+ }
+
+ is16bits = (samples[sample].mode & WAVE_16_BITS) ? 1 : 0;
+ voices[voice].mode = samples[sample].mode;
+ voices[voice].patch_vol = samples[sample].volume;
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ int i;
+
+ for (i = 0; i < 6; i++)
+ {
+ voices[voice].env_rate[i] = samples[sample].env_rate[i];
+ voices[voice].env_offset[i] = samples[sample].env_offset[i];
+ }
+ }
+
+ sample_map[voice] = sample;
+
+ base_note = samples[sample].base_note / 100; /* Try to avoid overflows */
+ note_freq /= 100;
+
+ freq = samples[sample].base_freq * note_freq / base_note;
+
+ voices[voice].orig_freq = freq;
+
+ /*
+ * Since the pitch bender may have been set before playing the note, we
+ * have to calculate the bending now.
+ */
+
+ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender,
+ voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ pan = (samples[sample].panning + voices[voice].panning) / 32;
+ pan += 7;
+ if (pan < 0)
+ pan = 0;
+ if (pan > 15)
+ pan = 15;
+
+ if (samples[sample].mode & WAVE_16_BITS)
+ {
+ mode |= 0x04; /* 16 bits */
+ if ((sample_ptrs[sample] >> 18) !=
+ ((sample_ptrs[sample] + samples[sample].len) >> 18))
+ printk ("GUS: Sample address error\n");
+ }
+
+ /*************************************************************************
+ * CAUTION! Interrupts disabled. Don't return before enabling
+ *************************************************************************/
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_off ();
+ gus_rampoff ();
+
+ RESTORE_INTR (flags);
+
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ {
+ compute_volume (voice, volume);
+ init_envelope (voice);
+ }
+ else
+ compute_and_set_volume (voice, volume, 0);
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ gus_write_addr (0x0a, sample_ptrs[sample] + samples[sample].len -
+ voices[voice].offset_pending, is16bits); /* start=end */
+ else
+ gus_write_addr (0x0a, sample_ptrs[sample] + voices[voice].offset_pending,
+ is16bits); /* Sample start=begin */
+
+ if (samples[sample].mode & WAVE_LOOPING)
+ {
+ mode |= 0x08;
+
+ if (samples[sample].mode & WAVE_BIDIR_LOOP)
+ mode |= 0x10;
+
+ if (samples[sample].mode & WAVE_LOOP_BACK)
+ {
+ gus_write_addr (0x0a,
+ sample_ptrs[sample] + samples[sample].loop_end -
+ voices[voice].offset_pending, is16bits);
+ mode |= 0x40;
+ }
+
+ gus_write_addr (0x02, sample_ptrs[sample] + samples[sample].loop_start,
+ is16bits); /* Loop start location */
+ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].loop_end,
+ is16bits); /* Loop end location */
+ }
+ else
+ {
+ mode |= 0x20; /* Loop IRQ at the end */
+ voices[voice].loop_irq_mode = LMODE_FINISH; /* Ramp down at the end */
+ voices[voice].loop_irq_parm = 1;
+ gus_write_addr (0x02, sample_ptrs[sample],
+ is16bits); /* Loop start location */
+ gus_write_addr (0x04, sample_ptrs[sample] + samples[sample].len - 1,
+ is16bits); /* Loop end location */
+ }
+ gus_voice_freq (freq);
+ gus_voice_balance (pan);
+ gus_voice_on (mode);
+ RESTORE_INTR (flags);
+
+ return 0;
+}
+
+/*
+ * New guswave_start_note by Andrew J. Robinson attempts to minimize clicking
+ * when the note playing on the voice is changed. It uses volume
+ * ramping.
+ */
+
+static int
+guswave_start_note (int dev, int voice, int note_num, int volume)
+{
+ long int flags;
+ int mode;
+ int ret_val = 0;
+
+ DISABLE_INTR (flags);
+ if (note_num == 255)
+ {
+ if (voices[voice].volume_irq_mode == VMODE_START_NOTE)
+ voices[voice].volume_pending = volume;
+ else
+ {
+ ret_val = guswave_start_note2 (gus_devnum, voice, note_num, volume);
+ }
+ }
+ else
+ {
+ gus_select_voice (voice);
+ mode = gus_read8 (0x00);
+ if (mode & 0x20)
+ gus_write8 (0x00, mode & 0xdf); /* No interrupt! */
+
+ voices[voice].offset_pending = 0;
+ voices[voice].kill_pending = 0;
+ voices[voice].volume_irq_mode = 0;
+ voices[voice].loop_irq_mode = 0;
+
+ if (voices[voice].sample_pending >= 0)
+ {
+ RESTORE_INTR (flags); /* Run temporarily with interrupts enabled */
+ guswave_set_instr (voices[voice].dev_pending, voice,
+ voices[voice].sample_pending);
+ voices[voice].sample_pending = -1;
+ DISABLE_INTR (flags);
+ gus_select_voice (voice); /* Reselect the voice (just to be sure) */
+ }
+
+ if ((mode & 0x01) || (int) ((gus_read16 (0x09) >> 4) < 2065))
+ {
+ ret_val = guswave_start_note2 (gus_devnum, voice, note_num, volume);
+ }
+ else
+ {
+ voices[voice].dev_pending = gus_devnum;
+ voices[voice].note_pending = note_num;
+ voices[voice].volume_pending = volume;
+ voices[voice].volume_irq_mode = VMODE_START_NOTE;
+
+ gus_rampoff ();
+ gus_ramp_range (2000, 4065);
+ gus_ramp_rate (0, 63); /* Fastest possible rate */
+ gus_rampon (0x20 | 0x40); /* Ramp down, once, irq */
+ }
+ }
+ RESTORE_INTR (flags);
+ return ret_val;
+}
+
+static void
+guswave_reset (int dev)
+{
+ int i;
+
+ for (i = 0; i < 32; i++)
+ {
+ gus_voice_init (i);
+ gus_voice_init2 (i);
+ }
+}
+
+static int
+guswave_open (int dev, int mode)
+{
+ int err;
+
+ if (mode & OPEN_WRITE && gus_busy[gus_devnum] ||
+ mode & OPEN_READ && gus_busy[gus_dspnum])
+ return RET_ERROR (EBUSY);
+
+ if(gus_busy[gus_devnum] == 0 && gus_busy[gus_dspnum] == 0)
+ gus_initialize ();
+ voice_alloc->timestamp = 0;
+
+ if ((err = DMAbuf_open_dma (gus_devnum)) < 0)
+ return err;
+
+ RESET_WAIT_QUEUE (dram_sleeper, dram_sleep_flag);
+ gus_busy[gus_devnum] = 1;
+ active_device = GUS_DEV_WAVE;
+
+ gus_reset ();
+
+ return 0;
+}
+
+static void
+guswave_close (int dev)
+{
+ gus_busy[gus_devnum] = 0;
+ active_device = 0;
+ gus_reset ();
+
+ DMAbuf_close_dma (gus_devnum);
+}
+
+static int
+guswave_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag)
+{
+ struct patch_info patch;
+ int instr;
+ long sizeof_patch;
+
+ unsigned long blk_size, blk_end, left, src_offs, target;
+
+ sizeof_patch = (long) &patch.data[0] - (long) &patch; /* Header size */
+
+ if (format != GUS_PATCH)
+ {
+ printk ("GUS Error: Invalid patch format (key) 0x%x\n", format);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (count < sizeof_patch)
+ {
+ printk ("GUS Error: Patch header too short\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ count -= sizeof_patch;
+
+ if (free_sample >= MAX_SAMPLE)
+ {
+ printk ("GUS: Sample table full\n");
+ return RET_ERROR (ENOSPC);
+ }
+
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
+
+ COPY_FROM_USER (&((char *) &patch)[offs], addr, offs, sizeof_patch - offs);
+
+ instr = patch.instr_no;
+
+ if (instr < 0 || instr > MAX_PATCH)
+ {
+ printk ("GUS: Invalid patch number %d\n", instr);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (count < patch.len)
+ {
+ printk ("GUS Warning: Patch record too short (%d<%d)\n",
+ count, (int) patch.len);
+ patch.len = count;
+ }
+
+ if (patch.len <= 0 || patch.len > gus_mem_size)
+ {
+ printk ("GUS: Invalid sample length %d\n", (int) patch.len);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (patch.mode & WAVE_LOOPING)
+ {
+ if (patch.loop_start < 0 || patch.loop_start >= patch.len)
+ {
+ printk ("GUS: Invalid loop start\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ if (patch.loop_end < patch.loop_start || patch.loop_end > patch.len)
+ {
+ printk ("GUS: Invalid loop end\n");
+ return RET_ERROR (EINVAL);
+ }
+ }
+
+ free_mem_ptr = (free_mem_ptr + 31) & ~31; /* 32 byte alignment */
+
+#define GUS_BANK_SIZE (256*1024)
+
+ if (patch.mode & WAVE_16_BITS)
+ {
+ /*
+ * 16 bit samples must fit one 256k bank.
+ */
+ if (patch.len >= GUS_BANK_SIZE)
+ {
+ printk ("GUS: Sample (16 bit) too long %d\n", (int) patch.len);
+ return RET_ERROR (ENOSPC);
+ }
+
+ if ((free_mem_ptr / GUS_BANK_SIZE) !=
+ ((free_mem_ptr + patch.len) / GUS_BANK_SIZE))
+ {
+ unsigned long tmp_mem = /* Aling to 256K */
+ ((free_mem_ptr / GUS_BANK_SIZE) + 1) * GUS_BANK_SIZE;
+
+ if ((tmp_mem + patch.len) > gus_mem_size)
+ return RET_ERROR (ENOSPC);
+
+ free_mem_ptr = tmp_mem; /* This leaves unusable memory */
+ }
+ }
+
+ if ((free_mem_ptr + patch.len) > gus_mem_size)
+ return RET_ERROR (ENOSPC);
+
+ sample_ptrs[free_sample] = free_mem_ptr;
+
+ /*
+ * Tremolo is not possible with envelopes
+ */
+
+ if (patch.mode & WAVE_ENVELOPES)
+ patch.mode &= ~WAVE_TREMOLO;
+
+ memcpy ((char *) &samples[free_sample], &patch, sizeof_patch);
+
+ /*
+ * Link this_one sample to the list of samples for patch 'instr'.
+ */
+
+ samples[free_sample].key = patch_table[instr];
+ patch_table[instr] = free_sample;
+
+ /*
+ * Use DMA to transfer the wave data to the DRAM
+ */
+
+ left = patch.len;
+ src_offs = 0;
+ target = free_mem_ptr;
+
+ while (left) /* Not completely transferred yet */
+ {
+ blk_size = audio_devs[gus_devnum]->buffsize;
+ if (blk_size > left)
+ blk_size = left;
+
+ /*
+ * DMA cannot cross 256k bank boundaries. Check for that.
+ */
+ blk_end = target + blk_size;
+
+ if ((target >> 18) != (blk_end >> 18))
+ { /* Split the block */
+
+ blk_end &= ~(256 * 1024 - 1);
+ blk_size = blk_end - target;
+ }
+
+#if defined(GUS_NO_DMA) || defined(GUS_PATCH_NO_DMA)
+ /*
+ * For some reason the DMA is not possible. We have to use PIO.
+ */
+ {
+ long i;
+ unsigned char data;
+
+ for (i = 0; i < blk_size; i++)
+ {
+ GET_BYTE_FROM_USER (data, addr, sizeof_patch + i);
+ if (patch.mode & WAVE_UNSIGNED)
+
+ if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
+ data ^= 0x80; /* Convert to signed */
+ gus_poke (target + i, data);
+ }
+ }
+#else /* GUS_NO_DMA */
+ {
+ unsigned long address, hold_address;
+ unsigned char dma_command;
+ unsigned long flags;
+
+ /*
+ * OK, move now. First in and then out.
+ */
+
+ COPY_FROM_USER (audio_devs[gus_devnum]->dmap->raw_buf[0],
+ addr, sizeof_patch + src_offs,
+ blk_size);
+
+ DISABLE_INTR (flags);
+/******** INTERRUPTS DISABLED NOW ********/
+ gus_write8 (0x41, 0); /* Disable GF1 DMA */
+ DMAbuf_start_dma (gus_devnum,
+ audio_devs[gus_devnum]->dmap->raw_buf_phys[0],
+ blk_size, DMA_MODE_WRITE);
+
+ /*
+ * Set the DRAM address for the wave data
+ */
+
+ address = target;
+
+ if (audio_devs[gus_devnum]->dmachan > 3)
+ {
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+
+ gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
+
+ /*
+ * Start the DMA transfer
+ */
+
+ dma_command = 0x21; /* IRQ enable, DMA start */
+ if (patch.mode & WAVE_UNSIGNED)
+ dma_command |= 0x80; /* Invert MSB */
+ if (patch.mode & WAVE_16_BITS)
+ dma_command |= 0x40; /* 16 bit _DATA_ */
+ if (audio_devs[gus_devnum]->dmachan > 3)
+ dma_command |= 0x04; /* 16 bit DMA _channel_ */
+
+ gus_write8 (0x41, dma_command); /* Lets bo luteet (=bugs) */
+
+ /*
+ * Sleep here until the DRAM DMA done interrupt is served
+ */
+ active_device = GUS_DEV_WAVE;
+
+ DO_SLEEP (dram_sleeper, dram_sleep_flag, HZ);
+ if (TIMED_OUT (dram_sleeper, dram_sleep_flag))
+ printk ("GUS: DMA Transfer timed out\n");
+ RESTORE_INTR (flags);
+ }
+#endif /* GUS_NO_DMA */
+
+ /*
+ * Now the next part
+ */
+
+ left -= blk_size;
+ src_offs += blk_size;
+ target += blk_size;
+
+ gus_write8 (0x41, 0); /* Stop DMA */
+ }
+
+ free_mem_ptr += patch.len;
+
+ if (!pmgr_flag)
+ pmgr_inform (gus_devnum, PM_E_PATCH_LOADED, instr, free_sample, 0, 0);
+ free_sample++;
+ return 0;
+}
+
+static void
+guswave_hw_control (int dev, unsigned char *event)
+{
+ int voice, cmd;
+ unsigned short p1, p2;
+ unsigned long plong, flags;
+
+ cmd = event[2];
+ voice = event[3];
+ p1 = *(unsigned short *) &event[4];
+ p2 = *(unsigned short *) &event[6];
+ plong = *(unsigned long *) &event[4];
+
+ if ((voices[voice].volume_irq_mode == VMODE_START_NOTE) &&
+ (cmd != _GUS_VOICESAMPLE) && (cmd != _GUS_VOICE_POS))
+ do_volume_irq (voice);
+
+ switch (cmd)
+ {
+
+ case _GUS_NUMVOICES:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_select_max_voices (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICESAMPLE:
+ guswave_set_instr (dev, voice, p1);
+ break;
+
+ case _GUS_VOICEON:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_voice_on (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEOFF:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_off ();
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEFADE:
+ gus_voice_fade (voice);
+ break;
+
+ case _GUS_VOICEMODE:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_voice_mode (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEBALA:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_balance (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEFREQ:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_freq (plong);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEVOL:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_volume (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOICEVOL2: /* Just update the software voice level */
+ voices[voice].initial_volume =
+ voices[voice].current_volume = p1;
+ break;
+
+ case _GUS_RAMPRANGE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NO-NO */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_ramp_range (p1, p2);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPRATE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NJET-NJET */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_ramp_rate (p1, p2);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPMODE:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NO-NO */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_ramp_mode (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPON:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* EI-EI */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ p1 &= ~0x20; /* Don't allow interrupts */
+ gus_rampon (p1);
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_RAMPOFF:
+ if (voices[voice].mode & WAVE_ENVELOPES)
+ break; /* NEJ-NEJ */
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ break;
+
+ case _GUS_VOLUME_SCALE:
+ volume_base = p1;
+ volume_scale = p2;
+ break;
+
+ case _GUS_VOICE_POS:
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_set_voice_pos (voice, plong);
+ RESTORE_INTR (flags);
+ break;
+
+ default:;
+ }
+}
+
+static int
+gus_sampling_set_speed (int speed)
+{
+
+ if (speed <= 0)
+ speed = gus_sampling_speed;
+
+ if (speed < 4000)
+ speed = 4000;
+
+ if (speed > 44100)
+ speed = 44100;
+
+ gus_sampling_speed = speed;
+
+ if (only_read_access)
+ {
+ /* Compute nearest valid recording speed and return it */
+
+ speed = (9878400 / (gus_sampling_speed + 2)) / 16;
+ speed = (9878400 / (speed * 16)) - 2;
+ }
+ return speed;
+}
+
+static int
+gus_sampling_set_channels (int channels)
+{
+ if (!channels)
+ return gus_sampling_channels;
+ if (channels > 2)
+ channels = 2;
+ if (channels < 1)
+ channels = 1;
+ gus_sampling_channels = channels;
+ return channels;
+}
+
+static int
+gus_sampling_set_bits (int bits)
+{
+ if (!bits)
+ return gus_sampling_bits;
+
+ if (bits != 8 && bits != 16)
+ bits = 8;
+
+ gus_sampling_bits = bits;
+ return bits;
+}
+
+static int
+gus_sampling_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return gus_sampling_set_speed (arg);
+ return IOCTL_OUT (arg, gus_sampling_set_speed (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return gus_sampling_speed;
+ return IOCTL_OUT (arg, gus_sampling_speed);
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return gus_sampling_set_channels (arg + 1) - 1;
+ return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg) + 1) - 1);
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return gus_sampling_set_channels (arg);
+ return IOCTL_OUT (arg, gus_sampling_set_channels (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return gus_sampling_channels;
+ return IOCTL_OUT (arg, gus_sampling_channels);
+ break;
+
+ case SNDCTL_DSP_SETFMT:
+ if (local)
+ return gus_sampling_set_bits (arg);
+ return IOCTL_OUT (arg, gus_sampling_set_bits (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return gus_sampling_bits;
+ return IOCTL_OUT (arg, gus_sampling_bits);
+
+ case SOUND_PCM_WRITE_FILTER: /* NOT POSSIBLE */
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ break;
+
+ case SOUND_PCM_READ_FILTER:
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ break;
+
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static void
+gus_sampling_reset (int dev)
+{
+}
+
+static int
+gus_sampling_open (int dev, int mode)
+{
+ int dev_flag;
+ int init_flag;
+#ifdef GUS_NO_DMA
+ printk ("GUS: DMA mode not enabled. Device not supported\n");
+ return RET_ERROR (ENXIO);
+#endif
+ dev_flag = 0;
+ init_flag = (gus_busy[gus_devnum] == 0 && gus_busy[gus_dspnum] == 0);
+ if(mode & OPEN_WRITE)
+ {
+ if (gus_busy[gus_devnum])
+ return RET_ERROR(EBUSY);
+ if(dev != gus_devnum)
+ return RET_ERROR(ENXIO);
+ dev_flag = gus_busy[gus_devnum] = 1;
+ }
+ if(mode & OPEN_READ)
+ {
+ if(gus_busy[gus_dspnum]) {
+ if (dev_flag) gus_busy[gus_devnum] = 0;
+ return RET_ERROR(EBUSY);
+ }
+
+ if(dev != gus_dspnum) {
+ if (dev_flag) gus_busy[gus_devnum] = 0;
+ return RET_ERROR(ENXIO);
+ }
+ }
+
+ if(init_flag)
+ {
+ gus_initialize ();
+
+ active_device = 0;
+
+ gus_reset ();
+ reset_sample_memory ();
+ gus_select_max_voices (14);
+
+ pcm_active = 0;
+ dma_active = 0;
+ pcm_opened = 1;
+ }
+ if (mode & OPEN_READ)
+ {
+ recording_active = 1;
+ set_input_volumes ();
+ }
+ only_read_access = !(mode & OPEN_WRITE);
+
+ return 0;
+}
+
+static void
+gus_sampling_close (int dev)
+{
+ gus_busy[dev] = 0;
+ if (gus_busy[gus_devnum] == 0 && gus_busy[gus_dspnum] == 0) {
+ active_device = 0;
+ gus_reset();
+ pcm_opened = 0;
+ }
+
+ if (recording_active)
+ set_input_volumes ();
+
+ recording_active = 0;
+}
+
+static void
+gus_sampling_update_volume (void)
+{
+ unsigned long flags;
+ int voice;
+
+ if (pcm_active && pcm_opened)
+ for (voice = 0; voice < gus_sampling_channels; voice++)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_rampoff ();
+ gus_voice_volume (1530 + (25 * gus_pcm_volume));
+ gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
+ RESTORE_INTR (flags);
+ }
+}
+
+static void
+play_next_pcm_block (void)
+{
+ unsigned long flags;
+ int speed = gus_sampling_speed;
+ int this_one, is16bits, chn;
+ unsigned long dram_loc;
+ unsigned char mode[2], ramp_mode[2];
+
+ if (!pcm_qlen)
+ return;
+
+ this_one = pcm_head;
+
+ for (chn = 0; chn < gus_sampling_channels; chn++)
+ {
+ mode[chn] = 0x00;
+ ramp_mode[chn] = 0x03; /* Ramping and rollover off */
+
+ if (chn == 0)
+ {
+ mode[chn] |= 0x20; /* Loop IRQ */
+ voices[chn].loop_irq_mode = LMODE_PCM;
+ }
+
+ if (gus_sampling_bits != 8)
+ {
+ is16bits = 1;
+ mode[chn] |= 0x04; /* 16 bit data */
+ }
+ else
+ is16bits = 0;
+
+ dram_loc = this_one * pcm_bsize;
+ dram_loc += chn * pcm_banksize;
+
+ if (this_one == (pcm_nblk - 1)) /* Last fragment of the DRAM buffer */
+ {
+ mode[chn] |= 0x08; /* Enable loop */
+ ramp_mode[chn] = 0x03; /* Disable rollover bit */
+ }
+ else
+ {
+ if (chn == 0)
+ ramp_mode[chn] = 0x04; /* Enable rollover bit */
+ }
+
+ DISABLE_INTR (flags);
+ gus_select_voice (chn);
+ gus_voice_freq (speed);
+
+ if (gus_sampling_channels == 1)
+ gus_voice_balance (7); /* mono */
+ else if (chn == 0)
+ gus_voice_balance (0); /* left */
+ else
+ gus_voice_balance (15); /* right */
+
+ if (!pcm_active) /* Playback not already active */
+ {
+ /*
+ * The playback was not started yet (or there has been a pause).
+ * Start the voice (again) and ask for a rollover irq at the end of
+ * this_one block. If this_one one is last of the buffers, use just
+ * the normal loop with irq.
+ */
+
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_volume (1530 + (25 * gus_pcm_volume));
+ gus_ramp_range (65, 1530 + (25 * gus_pcm_volume));
+
+ gus_write_addr (0x0a, dram_loc, is16bits); /* Starting position */
+ gus_write_addr (0x02, chn * pcm_banksize, is16bits); /* Loop start */
+
+ if (chn != 0)
+ gus_write_addr (0x04, pcm_banksize + (pcm_bsize * pcm_nblk) - 1,
+ is16bits); /* Loop end location */
+ }
+
+ if (chn == 0)
+ gus_write_addr (0x04, dram_loc + pcm_datasize[this_one] - 1,
+ is16bits); /* Loop end location */
+ else
+ mode[chn] |= 0x08; /* Enable looping */
+
+ if (pcm_datasize[this_one] != pcm_bsize)
+ {
+ /*
+ * Incompletely filled block. Possibly the last one.
+ */
+ if (chn == 0)
+ {
+ mode[chn] &= ~0x08; /* Disable looping */
+ mode[chn] |= 0x20; /* Enable IRQ at the end */
+ voices[0].loop_irq_mode = LMODE_PCM_STOP;
+ ramp_mode[chn] = 0x03; /* No rollover bit */
+ }
+ else
+ {
+ gus_write_addr (0x04, dram_loc + pcm_datasize[this_one],
+ is16bits); /* Loop end location */
+ mode[chn] &= ~0x08; /* Disable looping */
+ }
+ }
+
+ RESTORE_INTR (flags);
+ }
+
+ for (chn = 0; chn < gus_sampling_channels; chn++)
+ {
+ DISABLE_INTR (flags);
+ gus_select_voice (chn);
+ gus_write8 (0x0d, ramp_mode[chn]);
+ gus_voice_on (mode[chn]);
+ RESTORE_INTR (flags);
+ }
+
+ pcm_active = 1;
+}
+
+static void
+gus_transfer_output_block (int dev, unsigned long buf,
+ int total_count, int intrflag, int chn)
+{
+ /*
+ * This routine transfers one block of audio data to the DRAM. In mono mode
+ * it's called just once. When in stereo mode, this_one routine is called
+ * once for both channels.
+ *
+ * The left/mono channel data is transferred to the beginning of dram and the
+ * right data to the area pointed by gus_page_size.
+ */
+
+ int this_one, count;
+ unsigned long flags;
+ unsigned char dma_command;
+ unsigned long address, hold_address;
+
+ DISABLE_INTR (flags);
+
+ count = total_count / gus_sampling_channels;
+
+ if (chn == 0)
+ {
+ if (pcm_qlen >= pcm_nblk)
+ printk ("GUS Warning: PCM buffers out of sync\n");
+
+ this_one = pcm_current_block = pcm_tail;
+ pcm_qlen++;
+ pcm_tail = (pcm_tail + 1) % pcm_nblk;
+ pcm_datasize[this_one] = count;
+ }
+ else
+ this_one = pcm_current_block;
+
+ gus_write8 (0x41, 0); /* Disable GF1 DMA */
+ DMAbuf_start_dma (gus_devnum, buf + (chn * count), count, DMA_MODE_WRITE);
+
+ address = this_one * pcm_bsize;
+ address += chn * pcm_banksize;
+
+ if (audio_devs[gus_devnum]->dmachan > 3)
+ {
+ hold_address = address;
+ address = address >> 1;
+ address &= 0x0001ffffL;
+ address |= (hold_address & 0x000c0000L);
+ }
+
+ gus_write16 (0x42, (address >> 4) & 0xffff); /* DRAM DMA address */
+
+ dma_command = 0x21; /* IRQ enable, DMA start */
+
+ if (gus_sampling_bits != 8)
+ dma_command |= 0x40; /* 16 bit _DATA_ */
+ else
+ dma_command |= 0x80; /* Invert MSB */
+
+ if (audio_devs[gus_devnum]->dmachan > 3)
+ dma_command |= 0x04; /* 16 bit DMA channel */
+
+ gus_write8 (0x41, dma_command); /* Kickstart */
+
+ if (chn == (gus_sampling_channels - 1)) /* Last channel */
+ {
+ /*
+ * Last (right or mono) channel data
+ */
+ dma_active = 1; /* DMA started. There is a unacknowledged buffer */
+ active_device = GUS_DEV_PCM_DONE;
+ if (!pcm_active && (pcm_qlen > 0 || count < pcm_bsize))
+ {
+ play_next_pcm_block ();
+ }
+ }
+ else
+ {
+ /*
+ * Left channel data. The right channel
+ * is transferred after DMA interrupt
+ */
+ active_device = GUS_DEV_PCM_CONTINUE;
+ }
+
+ RESTORE_INTR (flags);
+}
+
+static void
+gus_sampling_output_block (int dev, unsigned long buf, int total_count,
+ int intrflag, int restart_dma)
+{
+ pcm_current_buf = buf;
+ pcm_current_count = total_count;
+ pcm_current_intrflag = intrflag;
+ pcm_current_dev = gus_devnum;
+ gus_transfer_output_block (gus_devnum, buf, total_count, intrflag, 0);
+}
+
+static void
+gus_sampling_start_input (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags;
+ unsigned char mode;
+
+ DISABLE_INTR (flags);
+
+ DMAbuf_start_dma (gus_dspnum, buf, count, DMA_MODE_READ);
+
+ mode = 0xa0; /* DMA IRQ enabled, invert MSB */
+
+ if (audio_devs[gus_dspnum]->dmachan > 3)
+ mode |= 0x04; /* 16 bit DMA channel */
+ if (gus_sampling_channels > 1)
+ mode |= 0x02; /* Stereo */
+ mode |= 0x01; /* DMA enable */
+
+ gus_write8 (0x49, mode);
+
+ RESTORE_INTR (flags);
+}
+
+static int
+gus_sampling_prepare_for_input (int dev, int bsize, int bcount)
+{
+ unsigned int rate;
+
+ rate = (9878400 / (gus_sampling_speed + 2)) / 16;
+
+ gus_write8 (0x48, rate & 0xff); /* Set sampling rate */
+
+ if (gus_sampling_bits != 8)
+ {
+ printk ("GUS Error: 16 bit recording not supported\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ return 0;
+}
+
+static int
+gus_sampling_prepare_for_output (int dev, int bsize, int bcount)
+{
+ int i;
+
+ long mem_ptr, mem_size;
+
+ mem_ptr = 0;
+ mem_size = gus_mem_size / gus_sampling_channels;
+
+ if (mem_size > (256 * 1024))
+ mem_size = 256 * 1024;
+
+ pcm_bsize = bsize / gus_sampling_channels;
+ pcm_head = pcm_tail = pcm_qlen = 0;
+
+ pcm_nblk = MAX_PCM_BUFFERS;
+ if ((pcm_bsize * pcm_nblk) > mem_size)
+ pcm_nblk = mem_size / pcm_bsize;
+
+ for (i = 0; i < pcm_nblk; i++)
+ pcm_datasize[i] = 0;
+
+ pcm_banksize = pcm_nblk * pcm_bsize;
+
+ if (gus_sampling_bits != 8 && pcm_banksize == (256 * 1024))
+ pcm_nblk--;
+
+ return 0;
+}
+
+static int
+gus_local_qlen (int dev)
+{
+ return pcm_qlen;
+}
+
+static void
+gus_copy_from_user (int dev, char *localbuf, int localoffs,
+ snd_rw_buf * userbuf, int useroffs, int len)
+{
+ if (gus_sampling_channels == 1)
+ {
+ COPY_FROM_USER (&localbuf[localoffs], userbuf, useroffs, len);
+ }
+ else if (gus_sampling_bits == 8)
+#if defined(__FreeBSD__)
+ {
+ char *in_left = gus_copy_buf;
+ char *in_right = in_left + 1;
+ char *out_left = localbuf + (localoffs / 2);
+ char *out_right = out_left + pcm_bsize;
+ int i;
+
+ COPY_FROM_USER (gus_copy_buf, userbuf, useroffs, len);
+
+ len /= 2;
+
+ for (i = 0; i < len; i++)
+ {
+ *out_left++ = *in_left++;
+ in_left++;
+ *out_right++ = *in_right++;
+ in_right++;
+ }
+ }
+ else
+ {
+ short *in_left = (short *)gus_copy_buf;
+ short *in_right = in_left + 1;
+ short *out_left = (short *)localbuf + (localoffs / 4);
+ short *out_right = out_left + (pcm_bsize / 2);
+ int i;
+
+ COPY_FROM_USER (gus_copy_buf, userbuf, useroffs, len);
+
+ len /= 4;
+
+ for (i = 0; i < len; i++)
+ {
+ *out_left++ = *in_left++;
+ in_left++;
+ *out_right++ = *in_right++;
+ in_right++;
+ }
+ }
+#else
+ {
+ int in_left = useroffs;
+ int in_right = useroffs + 1;
+ char *out_left, *out_right;
+ int i;
+
+ len /= 2;
+ localoffs /= 2;
+ out_left = &localbuf[localoffs];
+ out_right = out_left + pcm_bsize;
+
+ for (i = 0; i < len; i++)
+ {
+ GET_BYTE_FROM_USER (*out_left++, userbuf, in_left);
+ in_left += 2;
+ GET_BYTE_FROM_USER (*out_right++, userbuf, in_right);
+ in_right += 2;
+ }
+ }
+ else
+ {
+ int in_left = useroffs / 2;
+ int in_right = useroffs / 2 + 1;
+ short *out_left, *out_right;
+ int i;
+
+ len /= 4;
+ localoffs /= 2;
+
+ out_left = (short *) &localbuf[localoffs];
+ out_right = out_left + (pcm_bsize / 2);
+
+ for (i = 0; i < len; i++)
+ {
+#ifdef __FreeBSD__
+ GET_SHORT_FROM_USER (*out_left++, userbuf, in_left);
+ in_left += 2;
+ GET_SHORT_FROM_USER (*out_right++, userbuf, in_right);
+ in_right += 2;
+#else
+ GET_SHORT_FROM_USER (*out_left++, (short *) userbuf, in_left);
+ in_left += 2;
+ GET_SHORT_FROM_USER (*out_right++, (short *) userbuf, in_right);
+ in_right += 2;
+#endif
+ }
+ }
+#endif
+}
+
+static struct audio_operations gus_sampling_operations =
+{
+ "Gravis UltraSound",
+ NEEDS_RESTART,
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ gus_sampling_open,
+ gus_sampling_close,
+ gus_sampling_output_block,
+ gus_sampling_start_input,
+ gus_sampling_ioctl,
+ gus_sampling_prepare_for_input,
+ gus_sampling_prepare_for_output,
+ gus_sampling_reset,
+ gus_sampling_reset,
+ gus_local_qlen,
+ gus_copy_from_user
+};
+
+static struct audio_operations gus_sampling_operations_read =
+{
+ "Gravis UltraSound - read only",
+ NEEDS_RESTART,
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ gus_sampling_open,
+ gus_sampling_close,
+ gus_sampling_output_block,
+ gus_sampling_start_input,
+ gus_sampling_ioctl,
+ gus_sampling_prepare_for_input,
+ gus_sampling_prepare_for_output,
+ gus_sampling_reset,
+ gus_sampling_reset,
+ gus_local_qlen,
+ gus_copy_from_user
+};
+
+static void
+guswave_setup_voice (int dev, int voice, int chn)
+{
+ struct channel_info *info =
+ &synth_devs[gus_devnum]->chn_info[chn];
+
+ guswave_set_instr (gus_devnum, voice, info->pgm_num);
+
+ voices[voice].expression_vol =
+ info->controllers[CTL_EXPRESSION]; /* Just msb */
+ voices[voice].main_vol =
+ (info->controllers[CTL_MAIN_VOLUME] * 100) / 128;
+ voices[voice].panning =
+ (info->controllers[CTL_PAN] * 2) - 128;
+ voices[voice].bender = info->bender_value;
+}
+
+static void
+guswave_bender (int dev, int voice, int value)
+{
+ int freq;
+ unsigned long flags;
+
+ voices[voice].bender = value - 8192;
+ freq = compute_finetune (voices[voice].orig_freq, value,
+ voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+ gus_voice_freq (freq);
+ RESTORE_INTR (flags);
+}
+
+static int
+guswave_patchmgr (int dev, struct patmgr_info *rec)
+{
+ int i, n;
+
+ switch (rec->command)
+ {
+ case PM_GET_DEVTYPE:
+ rec->parm1 = PMTYPE_WAVE;
+ return 0;
+ break;
+
+ case PM_GET_NRPGM:
+ rec->parm1 = MAX_PATCH;
+ return 0;
+ break;
+
+ case PM_GET_PGMMAP:
+ rec->parm1 = MAX_PATCH;
+
+ for (i = 0; i < MAX_PATCH; i++)
+ {
+ int ptr = patch_table[i];
+
+ rec->data.data8[i] = 0;
+
+ while (ptr >= 0 && ptr < free_sample)
+ {
+ rec->data.data8[i]++;
+ ptr = samples[ptr].key; /* Follow link */
+ }
+ }
+ return 0;
+ break;
+
+ case PM_GET_PGM_PATCHES:
+ {
+ int ptr = patch_table[rec->parm1];
+
+ n = 0;
+
+ while (ptr >= 0 && ptr < free_sample)
+ {
+ rec->data.data32[n++] = ptr;
+ ptr = samples[ptr].key; /* Follow link */
+ }
+ }
+ rec->parm1 = n;
+ return 0;
+ break;
+
+ case PM_GET_PATCH:
+ {
+ int ptr = rec->parm1;
+ struct patch_info *pat;
+
+ if (ptr < 0 || ptr >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ memcpy (rec->data.data8, (char *) &samples[ptr],
+ sizeof (struct patch_info));
+
+ pat = (struct patch_info *) rec->data.data8;
+
+ pat->key = GUS_PATCH; /* Restore patch type */
+ rec->parm1 = sample_ptrs[ptr]; /* DRAM location */
+ rec->parm2 = sizeof (struct patch_info);
+ }
+ return 0;
+ break;
+
+ case PM_SET_PATCH:
+ {
+ int ptr = rec->parm1;
+ struct patch_info *pat;
+
+ if (ptr < 0 || ptr >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ pat = (struct patch_info *) rec->data.data8;
+
+ if (pat->len > samples[ptr].len) /* Cannot expand sample */
+ return RET_ERROR (EINVAL);
+
+ pat->key = samples[ptr].key; /* Ensure the link is correct */
+
+ memcpy ((char *) &samples[ptr], rec->data.data8,
+ sizeof (struct patch_info));
+
+ pat->key = GUS_PATCH;
+ }
+ return 0;
+ break;
+
+ case PM_READ_PATCH: /* Returns a block of wave data from the DRAM */
+ {
+ int sample = rec->parm1;
+ int n;
+ long offs = rec->parm2;
+ int l = rec->parm3;
+
+ if (sample < 0 || sample >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ if (offs < 0 || offs >= samples[sample].len)
+ return RET_ERROR (EINVAL); /* Invalid offset */
+
+ n = samples[sample].len - offs; /* Num of bytes left */
+
+ if (l > n)
+ l = n;
+
+ if (l > sizeof (rec->data.data8))
+ l = sizeof (rec->data.data8);
+
+ if (l <= 0)
+ return RET_ERROR (EINVAL); /*
+ * Was there a bug?
+ */
+
+ offs += sample_ptrs[sample]; /*
+ * Begin offsess + offset to DRAM
+ */
+
+ for (n = 0; n < l; n++)
+ rec->data.data8[n] = gus_peek (offs++);
+ rec->parm1 = n; /*
+ * Nr of bytes copied
+ */
+ }
+ return 0;
+ break;
+
+ case PM_WRITE_PATCH: /*
+ * Writes a block of wave data to the DRAM
+ */
+ {
+ int sample = rec->parm1;
+ int n;
+ long offs = rec->parm2;
+ int l = rec->parm3;
+
+ if (sample < 0 || sample >= free_sample)
+ return RET_ERROR (EINVAL);
+
+ if (offs < 0 || offs >= samples[sample].len)
+ return RET_ERROR (EINVAL); /*
+ * Invalid offset
+ */
+
+ n = samples[sample].len - offs; /*
+ * Nr of bytes left
+ */
+
+ if (l > n)
+ l = n;
+
+ if (l > sizeof (rec->data.data8))
+ l = sizeof (rec->data.data8);
+
+ if (l <= 0)
+ return RET_ERROR (EINVAL); /*
+ * Was there a bug?
+ */
+
+ offs += sample_ptrs[sample]; /*
+ * Begin offsess + offset to DRAM
+ */
+
+ for (n = 0; n < l; n++)
+ gus_poke (offs++, rec->data.data8[n]);
+ rec->parm1 = n; /*
+ * Nr of bytes copied
+ */
+ }
+ return 0;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static int
+guswave_alloc (int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+ int i, p, best = -1, best_time = 0x7fffffff;
+
+ p = alloc->ptr;
+ /*
+ * First look for a completely stopped voice
+ */
+
+ for (i = 0; i < alloc->max_voice; i++)
+ {
+ if (alloc->map[p] == 0)
+ {
+ alloc->ptr = p;
+ return p;
+ }
+ if (alloc->alloc_times[p] < best_time)
+ {
+ best = p;
+ best_time = alloc->alloc_times[p];
+ }
+ p = (p + 1) % alloc->max_voice;
+ }
+
+ /*
+ * Then look for a releasing voice
+ */
+
+ for (i = 0; i < alloc->max_voice; i++)
+ {
+ if (alloc->map[p] == 0xffff)
+ {
+ alloc->ptr = p;
+ return p;
+ }
+ p = (p + 1) % alloc->max_voice;
+ }
+
+ if (best >= 0)
+ p = best;
+
+ alloc->ptr = p;
+ return p;
+}
+
+static struct synth_operations guswave_operations =
+{
+ &gus_info,
+ 0,
+ SYNTH_TYPE_SAMPLE,
+ SAMPLE_TYPE_GUS,
+ guswave_open,
+ guswave_close,
+ guswave_ioctl,
+ guswave_kill_note,
+ guswave_start_note,
+ guswave_set_instr,
+ guswave_reset,
+ guswave_hw_control,
+ guswave_load_patch,
+ guswave_aftertouch,
+ guswave_controller,
+ guswave_panning,
+ guswave_volume_method,
+ guswave_patchmgr,
+ guswave_bender,
+ guswave_alloc,
+ guswave_setup_voice
+};
+
+static void
+set_input_volumes (void)
+{
+ unsigned long flags;
+ unsigned char mask = 0xff & ~0x06; /* Just line out enabled */
+
+ DISABLE_INTR (flags);
+
+ /*
+ * Enable channels having vol > 10%
+ * Note! bit 0x01 means line in DISABLED while 0x04 means
+ * mic in ENABLED.
+ */
+ if (gus_line_vol > 10)
+ mask &= ~0x01;
+ if (gus_mic_vol > 10)
+ mask |= 0x04;
+
+ if (recording_active)
+ {
+ /*
+ * Disable channel, if not selected for recording
+ */
+ if (!(gus_recmask & SOUND_MASK_LINE))
+ mask |= 0x01;
+ if (!(gus_recmask & SOUND_MASK_MIC))
+ mask &= ~0x04;
+ }
+
+ mix_image &= ~0x07;
+ mix_image |= mask & 0x07;
+ OUTB (mix_image, u_Mixer);
+
+ RESTORE_INTR (flags);
+}
+
+int
+gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \
+ SOUND_MASK_SYNTH|SOUND_MASK_PCM)
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ gus_recmask = IOCTL_IN (arg) & MIX_DEVS;
+ if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
+ gus_recmask = SOUND_MASK_MIC;
+ /* Note! Input volumes are updated during next open for recording */
+ return IOCTL_OUT (arg, gus_recmask);
+ break;
+
+ case SOUND_MIXER_MIC:
+ {
+ int vol = IOCTL_IN (arg) & 0xff;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ gus_mic_vol = vol;
+ set_input_volumes ();
+ return IOCTL_OUT (arg, vol | (vol << 8));
+ }
+ break;
+
+ case SOUND_MIXER_LINE:
+ {
+ int vol = IOCTL_IN (arg) & 0xff;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ gus_line_vol = vol;
+ set_input_volumes ();
+ return IOCTL_OUT (arg, vol | (vol << 8));
+ }
+ break;
+
+ case SOUND_MIXER_PCM:
+ gus_pcm_volume = IOCTL_IN (arg) & 0xff;
+ if (gus_pcm_volume < 0)
+ gus_pcm_volume = 0;
+ if (gus_pcm_volume > 100)
+ gus_pcm_volume = 100;
+ gus_sampling_update_volume ();
+ return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ {
+ int voice;
+
+ gus_wave_volume = IOCTL_IN (arg) & 0xff;
+
+ if (gus_wave_volume < 0)
+ gus_wave_volume = 0;
+ if (gus_wave_volume > 100)
+ gus_wave_volume = 100;
+
+ if (active_device == GUS_DEV_WAVE)
+ for (voice = 0; voice < nr_voices; voice++)
+ dynamic_volume_change (voice); /* Apply the new vol */
+
+ return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8));
+ }
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, gus_recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, MIX_DEVS);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return IOCTL_OUT (arg, gus_mic_vol | (gus_mic_vol << 8));
+ break;
+
+ case SOUND_MIXER_LINE:
+ return IOCTL_OUT (arg, gus_line_vol | (gus_line_vol << 8));
+ break;
+
+ case SOUND_MIXER_PCM:
+ return IOCTL_OUT (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return IOCTL_OUT (arg, gus_wave_volume | (gus_wave_volume << 8));
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ }
+ else
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations gus_mixer_operations =
+{
+ "Gravis Ultrasound",
+ gus_default_mixer_ioctl
+};
+
+static long
+gus_default_mixer_init (long mem_start)
+{
+ if (num_mixers < MAX_MIXER_DEV) /*
+ * Don't install if there is another
+ * mixer
+ */
+ mixer_devs[num_mixers++] = &gus_mixer_operations;
+
+ return mem_start;
+}
+
+long
+gus_wave_init (long mem_start, int irq, int dma, int dma_read)
+{
+ unsigned long flags;
+ unsigned char val;
+ char *model_num = "2.4";
+ int gus_type = 0x24; /* 2.4 */
+ int mixer_type = 0;
+
+ if (irq < 0 || irq > 15)
+ {
+ printk ("ERROR! Invalid IRQ#%d. GUS Disabled", irq);
+ return mem_start;
+ }
+
+ if (dma < 0 || dma > 7)
+ {
+ printk ("ERROR! Invalid DMA#%d. GUS Disabled", dma);
+ return mem_start;
+ }
+
+ if (dma_read == 0) dma_read = dma;
+ if (dma_read < 0 || dma_read > 7)
+ {
+ printk ("ERROR! Invalid DMA#%d. GUS DMA-read disabled", dma_read);
+ dma_read = dma;
+ }
+ /*
+ * Try to identify the GUS model.
+ *
+ * Versions < 3.6 don't have the digital ASIC. Try to probe it first.
+ */
+
+ DISABLE_INTR (flags);
+ OUTB (0x20, gus_base + 0x0f);
+ val = INB (gus_base + 0x0f);
+ RESTORE_INTR (flags);
+
+ if (val != 0xff && (val & 0x06)) /* Should be 0x02?? */
+ {
+ /*
+ * It has the digital ASIC so the card is at least v3.4.
+ * Next try to detect the true model.
+ */
+
+ val = INB (u_MixSelect);
+
+ /*
+ * Value 255 means pre-3.7 which don't have mixer.
+ * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer.
+ * 10 and above is GUS MAX which has the CS4231 codec/mixer.
+ *
+ * Sorry. No GUS max support yet but it should be available
+ * soon after the SDK for GUS MAX is available.
+ */
+
+ if (val == 255 || val < 5)
+ {
+ model_num = "3.4";
+ gus_type = 0x34;
+ }
+ else if (val < 10)
+ {
+ model_num = "3.7";
+ gus_type = 0x37;
+ mixer_type = ICS2101;
+ }
+ else
+ {
+ model_num = "MAX";
+ gus_type = 0x40;
+ mixer_type = CS4231;
+ }
+ }
+ else
+ {
+ /*
+ * ASIC not detected so the card must be 2.2 or 2.4.
+ * There could still be the 16-bit/mixer daughter card.
+ * It has the same codec/mixer than MAX.
+ * At this time there is no support for it but it will appear soon.
+ */
+ }
+
+
+#if defined(__FreeBSD__)
+ printk ("gus0: <Gravis UltraSound %s (%dk)>", model_num,
+ (int) gus_mem_size / 1024);
+#else
+ printk (" <Gravis UltraSound %s (%dk)>", model_num, (int) gus_mem_size / 1024);
+#endif
+
+ sprintf (gus_info.name, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024);
+
+ gus_irq = irq;
+ gus_dma = dma;
+ gus_dma_read = dma_read;
+
+ if (num_synths >= MAX_SYNTH_DEV)
+ printk ("GUS Error: Too many synthesizers\n");
+ else
+ {
+ voice_alloc = &guswave_operations.alloc;
+ synth_devs[num_synths++] = &guswave_operations;
+ }
+
+#if defined(__FreeBSD__)
+ PERMANENT_MALLOC (char *, gus_copy_buf, DSP_BUFFSIZE, mem_start);
+#endif
+
+ PERMANENT_MALLOC (struct patch_info *, samples,
+ (MAX_SAMPLE + 1) * sizeof (*samples), mem_start);
+
+ reset_sample_memory ();
+
+ gus_initialize ();
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[gus_devnum = num_audiodevs++] = &gus_sampling_operations;
+ audio_devs[gus_devnum]->dmachan = dma;
+ audio_devs[gus_devnum]->buffcount = 1;
+ audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE;
+ gus_dspnum = gus_devnum;
+ gus_busy[gus_devnum] = 0;
+ gus_busy[gus_dspnum] = 0;
+ }
+ else
+ printk ("GUS: Too many PCM devices available\n");
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ if(dma_read && dma != dma_read)
+ {
+ audio_devs[gus_dspnum = num_audiodevs++]= &gus_sampling_operations_read;
+ audio_devs[gus_dspnum]->dmachan = gus_dma_read;
+ audio_devs[gus_dspnum]->buffcount = 1;
+ audio_devs[gus_dspnum]->buffsize = DSP_BUFFSIZE;
+ gus_busy[gus_dspnum] = 0;
+ }
+ else
+ {
+ gus_dspnum = gus_devnum;
+ }
+ }
+ else
+ printk ("GUS READ: Too many PCM devices available\n");
+
+ /*
+ * Mixer dependent initialization.
+ */
+ switch (mixer_type)
+ {
+ case ICS2101:
+ gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
+ gus_wave_volume = 90;
+ return ics2101_mixer_init (mem_start);
+
+ case CS4231:
+ /* Initialized elsewhere (ad1848.c) */
+#ifndef EXCLUDE_GUSMAX
+ {
+ unsigned char max_config = 0x40; /* Codec enable */
+ long mixer_init_return;
+
+ if (dma > 3)
+ max_config |= 0x30; /* 16 bit playback and capture DMAs */
+
+ max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */
+
+ OUTB (max_config, gus_base + 0x106); /* UltraMax control */
+ mixer_init_return = gus_default_mixer_init(mem_start);
+
+ if (ad1848_detect (gus_base + 0x10c))
+ {
+ gus_mic_vol = gus_line_vol = gus_pcm_volume = 100;
+ gus_wave_volume = 90;
+ have_gus_max = 1;
+ ad1848_init ("GUS MAX", gus_base + 0x10c,
+ -irq,
+ dma_read, /* read write reversed */
+ dma);
+ }
+ else
+ printk ("[Where's the CS4231?]");
+ return mixer_init_return;
+ }
+#endif
+ default:
+ return gus_default_mixer_init (mem_start);
+ }
+}
+
+static void
+do_loop_irq (int voice)
+{
+ unsigned char tmp;
+ int mode, parm;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ gus_select_voice (voice);
+
+ tmp = gus_read8 (0x00);
+ tmp &= ~0x20; /*
+ * Disable wave IRQ for this_one voice
+ */
+ gus_write8 (0x00, tmp);
+
+ if (tmp & 0x03) /* Voice stopped */
+ voice_alloc->map[voice] = 0;
+
+ mode = voices[voice].loop_irq_mode;
+ voices[voice].loop_irq_mode = 0;
+ parm = voices[voice].loop_irq_parm;
+
+ switch (mode)
+ {
+
+ case LMODE_FINISH: /*
+ * Final loop finished, shoot volume down
+ */
+
+ if ((int) (gus_read16 (0x09) >> 4) < 100) /*
+ * Get current volume
+ */
+ {
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_voice_init (voice);
+ break;
+ }
+ gus_ramp_range (65, 4065);
+ gus_ramp_rate (0, 63); /*
+ * Fastest possible rate
+ */
+ gus_rampon (0x20 | 0x40); /*
+ * Ramp down, once, irq
+ */
+ voices[voice].volume_irq_mode = VMODE_HALT;
+ break;
+
+ case LMODE_PCM_STOP:
+ pcm_active = 0; /* Signal to the play_next_pcm_block routine */
+ case LMODE_PCM:
+ {
+ int flag; /* 0 or 2 */
+
+ pcm_qlen--;
+ pcm_head = (pcm_head + 1) % pcm_nblk;
+ if (pcm_qlen && pcm_active)
+ {
+ play_next_pcm_block ();
+ }
+ else
+ { /* Underrun. Just stop the voice */
+ gus_select_voice (0); /* Left channel */
+ gus_voice_off ();
+ gus_rampoff ();
+ gus_select_voice (1); /* Right channel */
+ gus_voice_off ();
+ gus_rampoff ();
+ pcm_active = 0;
+ }
+
+ /*
+ * If the queue was full before this interrupt, the DMA transfer was
+ * suspended. Let it continue now.
+ */
+ if (dma_active)
+ {
+ if (pcm_qlen == 0)
+ flag = 1; /* Underflow */
+ else
+ flag = 0;
+ dma_active = 0;
+ }
+ else
+ flag = 2; /* Just notify the dmabuf.c */
+ DMAbuf_outputintr (gus_devnum, flag);
+ }
+ break;
+
+ default:;
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+do_volume_irq (int voice)
+{
+ unsigned char tmp;
+ int mode, parm;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ gus_select_voice (voice);
+
+ tmp = gus_read8 (0x0d);
+ tmp &= ~0x20; /*
+ * Disable volume ramp IRQ
+ */
+ gus_write8 (0x0d, tmp);
+
+ mode = voices[voice].volume_irq_mode;
+ voices[voice].volume_irq_mode = 0;
+ parm = voices[voice].volume_irq_parm;
+
+ switch (mode)
+ {
+ case VMODE_HALT: /*
+ * Decay phase finished
+ */
+ RESTORE_INTR (flags);
+ gus_voice_init (voice);
+ break;
+
+ case VMODE_ENVELOPE:
+ gus_rampoff ();
+ RESTORE_INTR (flags);
+ step_envelope (voice);
+ break;
+
+ case VMODE_START_NOTE:
+ RESTORE_INTR (flags);
+ guswave_start_note2 (voices[voice].dev_pending, voice,
+ voices[voice].note_pending, voices[voice].volume_pending);
+ if (voices[voice].kill_pending)
+ guswave_kill_note (voices[voice].dev_pending, voice,
+ voices[voice].note_pending, 0);
+
+ if (voices[voice].sample_pending >= 0)
+ {
+ guswave_set_instr (voices[voice].dev_pending, voice,
+ voices[voice].sample_pending);
+ voices[voice].sample_pending = -1;
+ }
+ break;
+
+ default:;
+ }
+}
+
+void
+gus_voice_irq (void)
+{
+ unsigned long wave_ignore = 0, volume_ignore = 0;
+ unsigned long voice_bit;
+
+ unsigned char src, voice;
+
+ while (1)
+ {
+ src = gus_read8 (0x0f); /*
+ * Get source info
+ */
+ voice = src & 0x1f;
+ src &= 0xc0;
+
+ if (src == (0x80 | 0x40))
+ return; /*
+ * No interrupt
+ */
+
+ voice_bit = 1 << voice;
+
+ if (!(src & 0x80)) /*
+ * Wave IRQ pending
+ */
+ if (!(wave_ignore & voice_bit) && (int) voice < nr_voices) /*
+ * Not done
+ * yet
+ */
+ {
+ wave_ignore |= voice_bit;
+ do_loop_irq (voice);
+ }
+
+ if (!(src & 0x40)) /*
+ * Volume IRQ pending
+ */
+ if (!(volume_ignore & voice_bit) && (int) voice < nr_voices) /*
+ * Not done
+ * yet
+ */
+ {
+ volume_ignore |= voice_bit;
+ do_volume_irq (voice);
+ }
+ }
+}
+
+void
+guswave_dma_irq (void)
+{
+ unsigned char status;
+
+ status = gus_look8 (0x41); /* Get DMA IRQ Status */
+ if (status & 0x40) /* DMA interrupt pending */
+ switch (active_device)
+ {
+ case GUS_DEV_WAVE:
+ if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag))
+ WAKE_UP (dram_sleeper, dram_sleep_flag);
+ break;
+
+ case GUS_DEV_PCM_CONTINUE: /* Left channel data transferred */
+ gus_transfer_output_block (pcm_current_dev, pcm_current_buf,
+ pcm_current_count,
+ pcm_current_intrflag, 1);
+ break;
+
+ case GUS_DEV_PCM_DONE: /* Right or mono channel data transferred */
+ if (pcm_qlen < pcm_nblk)
+ {
+ int flag = (1 - dma_active) * 2; /* 0 or 2 */
+
+ if (pcm_qlen == 0)
+ flag = 1; /* Underrun */
+ dma_active = 0;
+ DMAbuf_outputintr (gus_devnum, flag);
+ }
+ break;
+
+ default:;
+ }
+
+ status = gus_look8 (0x49); /*
+ * Get Sampling IRQ Status
+ */
+ if (status & 0x40) /*
+ * Sampling Irq pending
+ */
+ {
+ if (gus_dma_read && gus_dma_read != gus_dma)
+ DMAbuf_inputintr (gus_dspnum);
+ else
+ DMAbuf_inputintr (gus_devnum);
+ }
+
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/hex2hex.h b/sys/pc98/pc98/sound/hex2hex.h
new file mode 100644
index 0000000..ecd7b4c
--- /dev/null
+++ b/sys/pc98/pc98/sound/hex2hex.h
@@ -0,0 +1,97 @@
+/*
+ * This file is a part of configure.c
+ *
+ * hex2hex reads an input file in Intel HEX format and produces
+ * an (unsigned char) array which contains the bytes and writes it to the
+ * output file using C syntax
+ */
+
+#define MAX_SIZE (256*1024)
+#define ABANDON(why) { \
+ fprintf(stderr, "%s: " why "\n", source); \
+ fclose(inf);fclose(outf);return 0; \
+ }
+
+int hex2hex(char *source, char *target, char *varline)
+{
+ FILE *inf, *outf;
+
+ int i,l, c;
+ unsigned char buf[MAX_SIZE];
+
+ if ((inf=fopen(source, "r"))==NULL)
+ {
+ perror(source);
+ return 0;
+ }
+
+ if ((outf=fopen(target, "w"))==NULL)
+ {
+ perror(target);
+ fclose(inf);
+ return 0;
+ }
+
+ l=0;
+
+ while ((c=getc(inf))!=EOF)
+ {
+ if (c == ':') /* Sync with beginning of line */
+ {
+ int n, check;
+ unsigned char sum;
+ int addr;
+ int linetype;
+
+ if (fscanf(inf, "%02x", &n) != 1)
+ ABANDON("File format error");
+ sum = n;
+
+ if (fscanf(inf, "%04x", &addr) != 1)
+ ABANDON("File format error");
+ sum += addr/256;
+ sum += addr%256;
+
+ if (fscanf(inf, "%02x", &linetype) != 1)
+ ABANDON("File format error");
+ sum += linetype;
+
+ if (linetype != 0)
+ continue;
+
+ for (i=0;i<n;i++)
+ {
+ if (fscanf(inf, "%02x", &c) != 1)
+ ABANDON("File format error");
+ if (addr >= MAX_SIZE)
+ ABANDON("File too large");
+ buf[addr++] = c;
+ if (addr > l)
+ l = addr;
+ sum += c;
+ }
+
+ if (fscanf(inf, "%02x", &check) != 1)
+ ABANDON("File format error");
+
+ sum = ~sum + 1;
+ if (check != sum)
+ ABANDON("Line checksum error");
+ }
+ }
+
+ fprintf(outf, "/*\n *\t Computer generated file. Do not edit.\n */\n");
+ fprintf(outf, "%s[] = {\n", varline);
+
+ for (i=0;i<l;i++)
+ {
+ if (i) fprintf(outf, ",");
+ if (i && !(i % 16)) fprintf(outf, "\n");
+ fprintf(outf, "0x%02x", buf[i]);
+ }
+
+ fprintf(outf, "\n};\n\n");
+ fclose(inf);
+ fclose(outf);
+ return 1;
+}
diff --git a/sys/pc98/pc98/sound/ics2101.c b/sys/pc98/pc98/sound/ics2101.c
new file mode 100644
index 0000000..3a76ea9
--- /dev/null
+++ b/sys/pc98/pc98/sound/ics2101.c
@@ -0,0 +1,264 @@
+/*
+ * sound/ics2101.c
+ *
+ * Driver for the ICS2101 mixer of GUS v3.7.
+ *
+ * Copyright by Hannu Savolainen 1994
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
+
+#include <machine/ultrasound.h>
+#include "gus_hw.h"
+
+#define MIX_DEVS (SOUND_MASK_MIC|SOUND_MASK_LINE| \
+ SOUND_MASK_SYNTH| \
+ SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+extern int gus_base;
+static int volumes[ICS_MIXDEVS];
+static int left_fix[ICS_MIXDEVS] =
+{1, 1, 1, 2, 1, 2};
+static int right_fix[ICS_MIXDEVS] =
+{2, 2, 2, 1, 2, 1};
+
+static int
+scale_vol (int vol)
+{
+#if 1
+ /*
+ * Experimental volume scaling by Risto Kankkunen.
+ * This should give smoother volume response than just
+ * a plain multiplication.
+ */
+ int e;
+
+ if (vol < 0)
+ vol = 0;
+ if (vol > 100)
+ vol = 100;
+ vol = (31 * vol + 50) / 100;
+ e = 0;
+ if (vol)
+ {
+ while (vol < 16)
+ {
+ vol <<= 1;
+ e--;
+ }
+ vol -= 16;
+ e += 7;
+ }
+ return ((e << 4) + vol);
+#else
+ return ((vol * 127) + 50) / 100;
+#endif
+}
+
+static void
+write_mix (int dev, int chn, int vol)
+{
+ int *selector;
+ unsigned long flags;
+ int ctrl_addr = dev << 3;
+ int attn_addr = dev << 3;
+
+ vol = scale_vol (vol);
+
+ if (chn == CHN_LEFT)
+ {
+ selector = left_fix;
+ ctrl_addr |= 0x00;
+ attn_addr |= 0x02;
+ }
+ else
+ {
+ selector = right_fix;
+ ctrl_addr |= 0x01;
+ attn_addr |= 0x03;
+ }
+
+ DISABLE_INTR (flags);
+ OUTB (ctrl_addr, u_MixSelect);
+ OUTB (selector[dev], u_MixData);
+ OUTB (attn_addr, u_MixSelect);
+ OUTB ((unsigned char) vol, u_MixData);
+ RESTORE_INTR (flags);
+}
+
+static int
+set_volumes (int dev, int vol)
+{
+ int left = vol & 0x00ff;
+ int right = (vol >> 8) & 0x00ff;
+
+ if (left < 0)
+ left = 0;
+ if (left > 100)
+ left = 100;
+ if (right < 0)
+ right = 0;
+ if (right > 100)
+ right = 100;
+
+ write_mix (dev, CHN_LEFT, left);
+ write_mix (dev, CHN_RIGHT, right);
+
+ vol = left + (right << 8);
+ volumes[dev] = vol;
+ return vol;
+}
+
+static int
+ics2101_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ return gus_default_mixer_ioctl (dev, cmd, arg);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return IOCTL_OUT (arg, set_volumes (DEV_MIC, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_CD:
+ return IOCTL_OUT (arg, set_volumes (DEV_CD, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_LINE:
+ return IOCTL_OUT (arg, set_volumes (DEV_LINE, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return IOCTL_OUT (arg, set_volumes (DEV_GF1, IOCTL_IN (arg)));
+ break;
+
+ case SOUND_MIXER_VOLUME:
+ return IOCTL_OUT (arg, set_volumes (DEV_VOL, IOCTL_IN (arg)));
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return gus_default_mixer_ioctl (dev, cmd, arg);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, MIX_DEVS);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, SOUND_MASK_LINE | SOUND_MASK_CD |
+ SOUND_MASK_SYNTH | SOUND_MASK_VOLUME |
+ SOUND_MASK_MIC);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ case SOUND_MIXER_MIC:
+ return IOCTL_OUT (arg, volumes[DEV_MIC]);
+ break;
+
+ case SOUND_MIXER_LINE:
+ return IOCTL_OUT (arg, volumes[DEV_LINE]);
+ break;
+
+ case SOUND_MIXER_CD:
+ return IOCTL_OUT (arg, volumes[DEV_CD]);
+ break;
+
+ case SOUND_MIXER_VOLUME:
+ return IOCTL_OUT (arg, volumes[DEV_VOL]);
+ break;
+
+ case SOUND_MIXER_SYNTH:
+ return IOCTL_OUT (arg, volumes[DEV_GF1]);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations ics2101_mixer_operations =
+{
+ "ICS2101 Multimedia Mixer",
+ ics2101_mixer_ioctl
+};
+
+long
+ics2101_mixer_init (long mem_start)
+{
+ int i;
+
+ if (num_mixers < MAX_MIXER_DEV)
+ {
+ mixer_devs[num_mixers++] = &ics2101_mixer_operations;
+
+ /*
+ * Some GUS v3.7 cards had some channels flipped. Disable
+ * the flipping feature if the model id is other than 5.
+ */
+
+ if (INB (u_MixSelect) != 5)
+ {
+ for (i = 0; i < ICS_MIXDEVS; i++)
+ left_fix[i] = 1;
+ for (i = 0; i < ICS_MIXDEVS; i++)
+ right_fix[i] = 2;
+ }
+
+ set_volumes (DEV_GF1, 0x5a5a);
+ set_volumes (DEV_CD, 0x5a5a);
+ set_volumes (DEV_MIC, 0x0000);
+ set_volumes (DEV_LINE, 0x5a5a);
+ set_volumes (DEV_VOL, 0x5a5a);
+ set_volumes (DEV_UNUSED, 0x0000);
+ }
+
+ return mem_start;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/local.h b/sys/pc98/pc98/sound/local.h
new file mode 100644
index 0000000..888f2d7
--- /dev/null
+++ b/sys/pc98/pc98/sound/local.h
@@ -0,0 +1,128 @@
+/* for FreeBSD */
+/*
+ * $Id: local.h,v 1.11 1994/11/01 17:26:50 ache Exp
+ */
+
+#define DSP_BUFFSIZE 65536
+#define SELECTED_SOUND_OPTIONS 0xffffffff
+#define SOUND_CONFIG_DATE "Sun Feb 5 14:38:12 EST 1995"
+#define SOUND_CONFIG_BY "freebsd-hackers"
+#define SOUND_CONFIG_HOST "freefall"
+#define SOUND_CONFIG_DOMAIN "cdrom.com"
+
+/* determine if sound code should be compiled */
+#include "snd.h"
+#if NSND > 0
+#define KERNEL_SOUNDCARD
+#endif
+
+#define ALLOW_SELECT
+
+/* PSS code does not work */
+#ifndef EXCLUDE_PSS
+#define EXCLUDE_PSS
+#endif
+
+#include "gus.h"
+#if NGUS == 0 && !defined(EXCLUDE_GUS)
+#define EXCLUDE_GUS
+#endif
+
+#include "gusxvi.h"
+#if NGUSXVI == 0 && !defined(EXCLUDE_GUS16)
+#define EXCLUDE_GUS16
+#endif
+
+#include "mss.h"
+#if NMSS == 0 && !defined(EXCLUDE_MSS)
+#define EXCLUDE_MSS
+#endif
+
+#include "trix.h"
+#if NTRIX == 0 && !defined(EXCLUDE_TRIX)
+#define EXCLUDE_TRIX
+#endif
+
+#include "sscape.h"
+#if NSSCAPE == 0 && !defined(EXCLUDE_SSCAPE)
+#define EXCLUDE_SSCAPE
+#endif
+
+#if NGUS == 0 && !defined(EXCLUDE_GUSMAX)
+# define EXCLUDE_GUSMAX
+# if defined(EXCLUDE_GUS16) && defined(EXCLUDE_MSS) && !defined(EXCLUDE_AD1848)
+# define EXCLUDE_AD1848
+# endif
+#else
+# define GUSMAX_MIXER
+#endif
+
+#include <sb.h>
+#if NSB == 0 && !defined(EXCLUDE_SB)
+#define EXCLUDE_SB
+#endif
+
+#include "sbxvi.h"
+#if NSBXVI == 0 && !defined(EXCLUDE_SB16)
+#define EXCLUDE_SB16
+#endif
+
+#include "sbmidi.h"
+#if NSBMIDI == 0 && !defined(EXCLUDE_SB16MIDI)
+#define EXCLUDE_SB16MIDI
+#endif
+
+#include <pas.h>
+#if NPAS == 0 && !defined(EXCLUDE_PAS)
+#define EXCLUDE_PAS
+#endif
+
+#include "mpu.h"
+#if NMPU == 0 && !defined(EXCLUDE_MPU401)
+#define EXCLUDE_MPU401
+#endif
+
+#include "opl.h"
+#if NOPL == 0 && !defined(EXCLUDE_YM3812)
+#define EXCLUDE_YM3812
+#endif
+
+#include "uart.h"
+#if NUART == 0 && !defined(EXCLUDE_UART6850)
+#define EXCLUDE_UART6850
+#endif
+
+#ifdef PC98
+#include "pcm.h"
+#if NPCM == 0 && !defined(EXCLUDE_PCM86)
+#define EXCLUDE_PCM86
+#endif
+#endif
+
+
+/* nothing but a sequencer (Adlib/OPL) ? */
+#if NGUS == 0 && NSB == 0 && NSBMIDI == 0 && NPAS == 0 && NMPU == 0 && \
+ NUART == 0 && NMSS == 0
+#ifndef EXCLUDE_MIDI
+#define EXCLUDE_MIDI
+#endif
+#ifndef EXCLUDE_AUDIO
+#if !defined(PC98) || defined(EXCLUDE_PCM86) && defined(EXCLUDE_MSS)
+#define EXCLUDE_AUDIO
+#endif
+#endif
+#endif
+
+/* nothing but a Midi (MPU/UART) ? */
+#if NGUS == 0 && NSB == 0 && NSBMIDI == 0 && NPAS == 0 && NOPL == 0 && \
+ NMSS == 0
+/* MPU depends on sequencer timer */
+#if NMPU == 0 && !defined(EXCLUDE_SEQUENCER)
+#define EXCLUDE_SEQUENCER
+#endif
+#ifndef EXCLUDE_AUDIO
+#if !defined(PC98) || defined(EXCLUDE_PCM86) && defined(EXCLUDE_MSS)
+#define EXCLUDE_AUDIO
+#endif
+#endif
+#endif
diff --git a/sys/pc98/pc98/sound/mad16.h b/sys/pc98/pc98/sound/mad16.h
new file mode 100644
index 0000000..0370973
--- /dev/null
+++ b/sys/pc98/pc98/sound/mad16.h
@@ -0,0 +1,91 @@
+
+/*
+ * Initialization code for OPTI MAD16 interface chip by
+ * Davor Jadrijevic <davor@emard.pub.hr>
+ * (Included by ad1848.c when MAD16 support is enabled)
+ *
+ * It looks like MAD16 is similar than the Mozart chip (OAK OTI-601).
+ * It could be even possible that these chips are exactly the same. Can
+ * anybody confirm this?
+ */
+
+static void wr_a_mad16(int base, int v, int a)
+{
+ OUTB(a, base + 0xf);
+ OUTB(v, base + 0x11);
+}
+
+static void wr_b_mad16(int base, int v, int a)
+{
+ OUTB(a, base + 0xf);
+ OUTB(v, base + 0xd);
+}
+
+/*
+static int rd_a_mad16(int base, int a)
+{
+ OUTB(a, base + 0xf);
+ return INB(base + 0x11);
+}
+*/
+
+static int rd_b_mad16(int base, int a)
+{
+ OUTB(a, base + 0xf);
+ return INB(base + 0xd);
+}
+
+/*
+static int rd_0_mad16(int base, int a)
+{
+ OUTB(a, base + 0xf);
+ return INB(base + 0xf);
+}
+
+static void wr_ad(int base, int v, int a)
+{
+ OUTB(a, base + 4);
+ OUTB(v, base + 5);
+}
+
+static int rd_ad(int base, int a)
+{
+ OUTB(a, base + 4);
+ return INB(base + 5);
+}
+*/
+
+static int mad16init(int adr)
+{
+ int j;
+ long i;
+
+ static int ad1848_bases[] =
+{ 0x220, -1, -1, 0x240, -1, -1, -1, -1, 0x530, 0xE80, 0xF40, 0x604, 0 };
+
+ int mad16_base = 0xf80, ad1848_base;
+
+
+ for(j = 0; (j < 16) && (ad1848_bases[j] != 0); j++)
+ if(adr == ad1848_bases[j])
+ break;
+
+ if( (ad1848_base = ad1848_bases[j]) < 0x530)
+ {
+ printk("Unknown MAD16 setting 0x%3X\n", adr);
+ return -1;
+ }
+
+ /* printk("OPTi MAD16 WSS at 0x%3X\n", ad1848_base); */
+
+ rd_b_mad16(mad16_base, 0xe2);
+ wr_a_mad16(mad16_base, 0x1a, 0xe2);
+ wr_b_mad16(mad16_base, j * 16 + 1, 0xe2);
+ wr_a_mad16(mad16_base, 0x1a, 0xe2);
+ for( i = 0; i < 10000; i++)
+ if( (INB(ad1848_base+4) & 0x80) == 0 )
+ break;
+
+ return 0;
+};
+
diff --git a/sys/pc98/pc98/sound/midi_ctrl.h b/sys/pc98/pc98/sound/midi_ctrl.h
new file mode 100644
index 0000000..8b68c7d
--- /dev/null
+++ b/sys/pc98/pc98/sound/midi_ctrl.h
@@ -0,0 +1,22 @@
+static unsigned char ctrl_def_values[128] =
+{
+ 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 0 to 7 */
+ 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 8 to 15 */
+ 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 16 to 23 */
+ 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 24 to 31 */
+
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */
+
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */
+
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 96 to 103 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */
+};
diff --git a/sys/pc98/pc98/sound/midi_synth.c b/sys/pc98/pc98/sound/midi_synth.c
new file mode 100644
index 0000000..e2a1c2c
--- /dev/null
+++ b/sys/pc98/pc98/sound/midi_synth.c
@@ -0,0 +1,644 @@
+/*
+ * sound/midi_synth.c
+ *
+ * High level midi sequencer manager for dumb MIDI interfaces.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#define USE_SEQ_MACROS
+#define USE_SIMPLE_MACROS
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI)
+
+#define _MIDI_SYNTH_C_
+
+DEFINE_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag);
+
+#include "midi_synth.h"
+
+static int midi2synth[MAX_MIDI_DEV];
+static unsigned char prev_out_status[MAX_MIDI_DEV];
+
+#define STORE(cmd) \
+{ \
+ int len; \
+ unsigned char obuf[8]; \
+ cmd; \
+ seq_input_event(obuf, len); \
+}
+#define _seqbuf obuf
+#define _seqbufptr 0
+#define _SEQ_ADVBUF(x) len=x
+
+void
+do_midi_msg (int synthno, unsigned char *msg, int mlen)
+{
+ switch (msg[0] & 0xf0)
+ {
+ case 0x90:
+ if (msg[2] != 0)
+ {
+ STORE (SEQ_START_NOTE (synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+ }
+ msg[2] = 64;
+
+ case 0x80:
+ STORE (SEQ_STOP_NOTE (synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+
+ case 0xA0:
+ STORE (SEQ_KEY_PRESSURE (synthno, msg[0] & 0x0f, msg[1], msg[2]));
+ break;
+
+ case 0xB0:
+ STORE (SEQ_CONTROL (synthno, msg[0] & 0x0f,
+ msg[1], msg[2]));
+ break;
+
+ case 0xC0:
+ STORE (SEQ_SET_PATCH (synthno, msg[0] & 0x0f, msg[1]));
+ break;
+
+ case 0xD0:
+ STORE (SEQ_CHN_PRESSURE (synthno, msg[0] & 0x0f, msg[1]));
+ break;
+
+ case 0xE0:
+ STORE (SEQ_BENDER (synthno, msg[0] & 0x0f,
+ (msg[1] % 0x7f) | ((msg[2] & 0x7f) << 7)));
+ break;
+
+ default:
+ printk ("MPU: Unknown midi channel message %02x\n", msg[0]);
+ }
+}
+
+static void
+midi_outc (int midi_dev, int data)
+{
+ int timeout;
+
+ for (timeout = 0; timeout < 32000; timeout++)
+ if (midi_devs[midi_dev]->putc (midi_dev, (unsigned char) (data & 0xff)))
+ {
+ if (data & 0x80) /*
+ * Status byte
+ */
+ prev_out_status[midi_dev] =
+ (unsigned char) (data & 0xff); /*
+ * Store for running status
+ */
+ return; /*
+ * Mission complete
+ */
+ }
+
+ /*
+ * Sorry! No space on buffers.
+ */
+ printk ("Midi send timed out\n");
+}
+
+static int
+prefix_cmd (int midi_dev, unsigned char status)
+{
+ if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL)
+ return 1;
+
+ return midi_devs[midi_dev]->prefix_cmd (midi_dev, status);
+}
+
+static void
+midi_synth_input (int dev, unsigned char data)
+{
+ int orig_dev;
+ struct midi_input_info *inc;
+
+ static unsigned char len_tab[] = /* # of data bytes following a status
+ */
+ {
+ 2, /* 8x */
+ 2, /* 9x */
+ 2, /* Ax */
+ 2, /* Bx */
+ 1, /* Cx */
+ 1, /* Dx */
+ 2, /* Ex */
+ 0 /* Fx */
+ };
+
+ if (dev < 0 || dev > num_synths)
+ return;
+
+ if (data == 0xfe) /* Ignore active sensing */
+ return;
+
+ orig_dev = midi2synth[dev];
+ inc = &midi_devs[orig_dev]->in_info;
+
+ switch (inc->m_state)
+ {
+ case MST_INIT:
+ if (data & 0x80) /* MIDI status byte */
+ {
+ if ((data & 0xf0) == 0xf0) /* Common message */
+ {
+ switch (data)
+ {
+ case 0xf0: /* Sysex */
+ inc->m_state = MST_SYSEX;
+ break; /* Sysex */
+
+ case 0xf1: /* MTC quarter frame */
+ case 0xf3: /* Song select */
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 1;
+ inc->m_left = 1;
+ inc->m_buf[0] = data;
+ break;
+
+ case 0xf2: /* Song position pointer */
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 1;
+ inc->m_left = 2;
+ inc->m_buf[0] = data;
+ break;
+
+ default:
+ inc->m_buf[0] = data;
+ inc->m_ptr = 1;
+ do_midi_msg (dev, inc->m_buf, inc->m_ptr);
+ inc->m_ptr = 0;
+ inc->m_left = 0;
+ }
+ }
+ else
+ {
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 1;
+ inc->m_left = len_tab[(data >> 4) - 8];
+ inc->m_buf[0] = inc->m_prev_status = data;
+ }
+ }
+ else if (inc->m_prev_status & 0x80) /* Ignore if no previous status (yet) */
+ { /* Data byte (use running status) */
+ inc->m_state = MST_DATA;
+ inc->m_ptr = 2;
+ inc->m_left = len_tab[(data >> 4) - 8] - 1;
+ inc->m_buf[0] = inc->m_prev_status;
+ inc->m_buf[1] = data;
+ }
+ break; /* MST_INIT */
+
+ case MST_DATA:
+ inc->m_buf[inc->m_ptr++] = data;
+ if (--inc->m_left <= 0)
+ {
+ inc->m_state = MST_INIT;
+ do_midi_msg (dev, inc->m_buf, inc->m_ptr);
+ inc->m_ptr = 0;
+ }
+ break; /* MST_DATA */
+
+ case MST_SYSEX:
+ if (data == 0xf7) /* Sysex end */
+ {
+ inc->m_state = MST_INIT;
+ inc->m_left = 0;
+ inc->m_ptr = 0;
+ }
+ break; /* MST_SYSEX */
+
+ default:
+ printk ("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state,
+ (int) data);
+ inc->m_state = MST_INIT;
+ }
+}
+
+static void
+midi_synth_output (int dev)
+{
+ /*
+ * Currently NOP
+ */
+}
+
+int
+midi_synth_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ /*
+ * int orig_dev = synth_devs[dev]->midi_dev;
+ */
+
+ switch (cmd)
+ {
+
+ case SNDCTL_SYNTH_INFO:
+ IOCTL_TO_USER ((char *) arg, 0, synth_devs[dev]->info,
+ sizeof (struct synth_info));
+
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+int
+midi_synth_kill_note (int dev, int channel, int note, int velocity)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
+
+ if (note < 0 || note > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+ if (velocity < 0)
+ velocity = 0;
+ if (velocity > 127)
+ velocity = 127;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
+ { /*
+ * Use running status
+ */
+ if (!prefix_cmd (orig_dev, note))
+ return 0;
+
+ midi_outc (orig_dev, note);
+
+ if (msg == 0x90) /*
+ * Running status = Note on
+ */
+ midi_outc (orig_dev, 0); /*
+ * Note on with velocity 0 == note
+ * off
+ */
+ else
+ midi_outc (orig_dev, velocity);
+ }
+ else
+ {
+ if (velocity == 64)
+ {
+ if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
+ * Note on
+ */
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, 0); /*
+ * Zero G
+ */
+ }
+ else
+ {
+ if (!prefix_cmd (orig_dev, 0x80 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0x80 | (channel & 0x0f)); /*
+ * Note off
+ */
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, velocity);
+ }
+ }
+
+ return 0;
+}
+
+int
+midi_synth_set_instr (int dev, int channel, int instr_no)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ if (instr_no < 0 || instr_no > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+
+ if (!prefix_cmd (orig_dev, 0xc0 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0xc0 | (channel & 0x0f)); /*
+ * Program change
+ */
+ midi_outc (orig_dev, instr_no);
+
+ return 0;
+}
+
+int
+midi_synth_start_note (int dev, int channel, int note, int velocity)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
+
+ if (note < 0 || note > 127)
+ return 0;
+ if (channel < 0 || channel > 15)
+ return 0;
+ if (velocity < 0)
+ velocity = 0;
+ if (velocity > 127)
+ velocity = 127;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (chn == channel && msg == 0x90)
+ { /*
+ * Use running status
+ */
+ if (!prefix_cmd (orig_dev, note))
+ return 0;
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, velocity);
+ }
+ else
+ {
+ if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
+ return 0;
+ midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
+ * Note on
+ */
+ midi_outc (orig_dev, note);
+ midi_outc (orig_dev, velocity);
+ }
+ return 0;
+}
+
+void
+midi_synth_reset (int dev)
+{
+}
+
+int
+midi_synth_open (int dev, int mode)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int err;
+ unsigned long flags;
+ struct midi_input_info *inc;
+
+ if (orig_dev < 0 || orig_dev > num_midis)
+ return RET_ERROR (ENXIO);
+
+ midi2synth[orig_dev] = dev;
+ prev_out_status[orig_dev] = 0;
+
+ if ((err = midi_devs[orig_dev]->open (orig_dev, mode,
+ midi_synth_input, midi_synth_output)) < 0)
+ return err;
+
+ inc = &midi_devs[orig_dev]->in_info;
+
+ DISABLE_INTR (flags);
+ inc->m_busy = 0;
+ inc->m_state = MST_INIT;
+ inc->m_ptr = 0;
+ inc->m_left = 0;
+ inc->m_prev_status = 0x00;
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+void
+midi_synth_close (int dev)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ /*
+ * Shut up the synths by sending just single active sensing message.
+ */
+ midi_devs[orig_dev]->putc (orig_dev, 0xfe);
+
+ midi_devs[orig_dev]->close (orig_dev);
+}
+
+void
+midi_synth_hw_control (int dev, unsigned char *event)
+{
+}
+
+int
+midi_synth_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+
+ struct sysex_info sysex;
+ int i;
+ unsigned long left, src_offs, eox_seen = 0;
+ int first_byte = 1;
+ int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex;
+
+ if (!prefix_cmd (orig_dev, 0xf0))
+ return 0;
+
+ if (format != SYSEX_PATCH)
+ {
+ printk ("MIDI Error: Invalid patch format (key) 0x%x\n", format);
+ return RET_ERROR (EINVAL);
+ }
+
+ if (count < hdr_size)
+ {
+ printk ("MIDI Error: Patch header too short\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ count -= hdr_size;
+
+ /*
+ * Copy the header from user space but ignore the first bytes which have
+ * been transferred already.
+ */
+
+ COPY_FROM_USER (&((char *) &sysex)[offs], addr, offs, hdr_size - offs);
+
+ if (count < sysex.len)
+ {
+ printk ("MIDI Warning: Sysex record too short (%d<%d)\n",
+ count, (int) sysex.len);
+ sysex.len = count;
+ }
+
+ left = sysex.len;
+ src_offs = 0;
+
+ RESET_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag);
+
+ for (i = 0; i < left && !PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag); i++)
+ {
+ unsigned char data;
+
+ GET_BYTE_FROM_USER (data, addr, hdr_size + i);
+
+ eox_seen = (i > 0 && data & 0x80); /* End of sysex */
+
+ if (eox_seen && data != 0xf7)
+ data = 0xf7;
+
+ if (i == 0)
+ {
+ if (data != 0xf0)
+ {
+ printk ("Error: Sysex start missing\n");
+ return RET_ERROR (EINVAL);
+ }
+ }
+
+ while (!midi_devs[orig_dev]->putc (orig_dev, (unsigned char) (data & 0xff)) &&
+ !PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag))
+ DO_SLEEP (sysex_sleeper, sysex_sleep_flag, 1); /* Wait for timeout */
+
+ if (!first_byte && data & 0x80)
+ return 0;
+ first_byte = 0;
+ }
+
+ if (!eox_seen)
+ midi_outc (orig_dev, 0xf7);
+ return 0;
+}
+
+void
+midi_synth_panning (int dev, int channel, int pressure)
+{
+}
+
+void
+midi_synth_aftertouch (int dev, int channel, int pressure)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, chn;
+
+ if (pressure < 0 || pressure > 127)
+ return;
+ if (channel < 0 || channel > 15)
+ return;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xd0 || chn != channel) /*
+ * Test for running status
+ */
+ {
+ if (!prefix_cmd (orig_dev, 0xd0 | (channel & 0x0f)))
+ return;
+ midi_outc (orig_dev, 0xd0 | (channel & 0x0f)); /*
+ * Channel pressure
+ */
+ }
+ else if (!prefix_cmd (orig_dev, pressure))
+ return;
+
+ midi_outc (orig_dev, pressure);
+}
+
+void
+midi_synth_controller (int dev, int channel, int ctrl_num, int value)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int chn, msg;
+
+ if (ctrl_num < 1 || ctrl_num > 127)
+ return; /* NOTE! Controller # 0 ignored */
+ if (channel < 0 || channel > 15)
+ return;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xb0 || chn != channel)
+ {
+ if (!prefix_cmd (orig_dev, 0xb0 | (channel & 0x0f)))
+ return;
+ midi_outc (orig_dev, 0xb0 | (channel & 0x0f));
+ }
+ else if (!prefix_cmd (orig_dev, ctrl_num))
+ return;
+
+ midi_outc (orig_dev, ctrl_num);
+ midi_outc (orig_dev, value & 0x7f);
+}
+
+int
+midi_synth_patchmgr (int dev, struct patmgr_info *rec)
+{
+ return RET_ERROR (EINVAL);
+}
+
+void
+midi_synth_bender (int dev, int channel, int value)
+{
+ int orig_dev = synth_devs[dev]->midi_dev;
+ int msg, prev_chn;
+
+ if (channel < 0 || channel > 15)
+ return;
+
+ if (value < 0 || value > 16383)
+ return;
+
+ msg = prev_out_status[orig_dev] & 0xf0;
+ prev_chn = prev_out_status[orig_dev] & 0x0f;
+
+ if (msg != 0xd0 || prev_chn != channel) /*
+ * Test for running status
+ */
+ {
+ if (!prefix_cmd (orig_dev, 0xe0 | (channel & 0x0f)))
+ return;
+ midi_outc (orig_dev, 0xe0 | (channel & 0x0f));
+ }
+ else if (!prefix_cmd (orig_dev, value & 0x7f))
+ return;
+
+ midi_outc (orig_dev, value & 0x7f);
+ midi_outc (orig_dev, (value >> 7) & 0x7f);
+}
+
+void
+midi_synth_setup_voice (int dev, int voice, int channel)
+{
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/midi_synth.h b/sys/pc98/pc98/sound/midi_synth.h
new file mode 100644
index 0000000..22f56ac
--- /dev/null
+++ b/sys/pc98/pc98/sound/midi_synth.h
@@ -0,0 +1,48 @@
+int midi_synth_ioctl (int dev,
+ unsigned int cmd, unsigned int arg);
+int midi_synth_kill_note (int dev, int channel, int note, int velocity);
+int midi_synth_set_instr (int dev, int channel, int instr_no);
+int midi_synth_start_note (int dev, int channel, int note, int volume);
+void midi_synth_reset (int dev);
+int midi_synth_open (int dev, int mode);
+void midi_synth_close (int dev);
+void midi_synth_hw_control (int dev, unsigned char *event);
+int midi_synth_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag);
+void midi_synth_panning (int dev, int channel, int pressure);
+void midi_synth_aftertouch (int dev, int channel, int pressure);
+void midi_synth_controller (int dev, int channel, int ctrl_num, int value);
+int midi_synth_patchmgr (int dev, struct patmgr_info *rec);
+void midi_synth_bender (int dev, int chn, int value);
+void midi_synth_setup_voice (int dev, int voice, int chn);
+
+
+#ifndef _MIDI_SYNTH_C_
+static struct synth_info std_synth_info =
+{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS};
+
+static struct synth_operations std_midi_synth =
+{
+ &std_synth_info,
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ midi_synth_open,
+ midi_synth_close,
+ midi_synth_ioctl,
+ midi_synth_kill_note,
+ midi_synth_start_note,
+ midi_synth_set_instr,
+ midi_synth_reset,
+ midi_synth_hw_control,
+ midi_synth_load_patch,
+ midi_synth_aftertouch,
+ midi_synth_controller,
+ midi_synth_panning,
+ NULL,
+ midi_synth_patchmgr,
+ midi_synth_bender,
+ NULL, /* alloc_voice */
+ midi_synth_setup_voice
+};
+#endif
diff --git a/sys/pc98/pc98/sound/midibuf.c b/sys/pc98/pc98/sound/midibuf.c
new file mode 100644
index 0000000..f1a5cf2
--- /dev/null
+++ b/sys/pc98/pc98/sound/midibuf.c
@@ -0,0 +1,470 @@
+/*
+ * sound/midibuf.c
+ *
+ * Device file manager for /dev/midi#
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+static void drain_midi_queue __P((int dev));
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI)
+
+/*
+ * Don't make MAX_QUEUE_SIZE larger than 4000
+ */
+
+#define MAX_QUEUE_SIZE 4000
+
+DEFINE_WAIT_QUEUES (midi_sleeper[MAX_MIDI_DEV], midi_sleep_flag[MAX_MIDI_DEV]);
+DEFINE_WAIT_QUEUES (input_sleeper[MAX_MIDI_DEV], input_sleep_flag[MAX_MIDI_DEV]);
+
+struct midi_buf
+ {
+ int len, head, tail;
+ unsigned char queue[MAX_QUEUE_SIZE];
+ };
+
+struct midi_parms
+ {
+ int prech_timeout; /*
+ * Timeout before the first ch
+ */
+ };
+
+static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] =
+{NULL};
+static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] =
+{NULL};
+static struct midi_parms parms[MAX_MIDI_DEV];
+
+static void midi_poll (unsigned long dummy);
+
+DEFINE_TIMER (poll_timer, midi_poll);
+static volatile int open_devs = 0;
+
+#define DATA_AVAIL(q) (q->len)
+#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len)
+
+#define QUEUE_BYTE(q, data) \
+ if (SPACE_AVAIL(q)) \
+ { \
+ unsigned long flags; \
+ DISABLE_INTR(flags); \
+ q->queue[q->tail] = (data); \
+ q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \
+ RESTORE_INTR(flags); \
+ }
+
+#define REMOVE_BYTE(q, data) \
+ if (DATA_AVAIL(q)) \
+ { \
+ unsigned long flags; \
+ DISABLE_INTR(flags); \
+ data = q->queue[q->head]; \
+ q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \
+ RESTORE_INTR(flags); \
+ }
+
+static void
+drain_midi_queue (int dev)
+{
+
+ /*
+ * Give the Midi driver time to drain its output queues
+ */
+
+ if (midi_devs[dev]->buffer_status != NULL)
+ while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) &&
+ midi_devs[dev]->buffer_status (dev))
+ DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], HZ / 10);
+}
+
+static void
+midi_input_intr (int dev, unsigned char data)
+{
+ if (midi_in_buf[dev] == NULL)
+ return;
+
+ if (data == 0xfe) /*
+ * Active sensing
+ */
+ return; /*
+ * Ignore
+ */
+
+ if (SPACE_AVAIL (midi_in_buf[dev]))
+ {
+ QUEUE_BYTE (midi_in_buf[dev], data);
+ if (SOMEONE_WAITING (input_sleeper[dev], input_sleep_flag[dev]))
+ WAKE_UP (input_sleeper[dev], input_sleep_flag[dev]);
+ }
+#if defined(__FreeBSD__)
+ if (selinfo[dev].si_pid)
+ selwakeup(&selinfo[dev]);
+#endif
+}
+
+static void
+midi_output_intr (int dev)
+{
+ /*
+ * Currently NOP
+ */
+#if defined(__FreeBSD__)
+ if (selinfo[dev].si_pid)
+ selwakeup(&selinfo[dev]);
+#endif
+}
+
+static void
+midi_poll (unsigned long dummy)
+{
+ unsigned long flags;
+ int dev;
+
+ DISABLE_INTR (flags);
+ if (open_devs)
+ {
+ for (dev = 0; dev < num_midis; dev++)
+ if (midi_out_buf[dev] != NULL)
+ {
+ while (DATA_AVAIL (midi_out_buf[dev]) &&
+ midi_devs[dev]->putc (dev,
+ midi_out_buf[dev]->queue[midi_out_buf[dev]->head]))
+ {
+ midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
+ midi_out_buf[dev]->len--;
+ }
+
+ if (DATA_AVAIL (midi_out_buf[dev]) < 100 &&
+ SOMEONE_WAITING (midi_sleeper[dev], midi_sleep_flag[dev]))
+ WAKE_UP (midi_sleeper[dev], midi_sleep_flag[dev]);
+ }
+ ACTIVATE_TIMER (poll_timer, midi_poll, 1); /*
+ * Come back later
+ */
+ }
+ RESTORE_INTR (flags);
+}
+
+int
+MIDIbuf_open (int dev, struct fileinfo *file)
+{
+ int mode, err;
+ unsigned long flags;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ if (num_midis > MAX_MIDI_DEV)
+ {
+ printk ("Sound: FATAL ERROR: Too many midi interfaces\n");
+ num_midis = MAX_MIDI_DEV;
+ }
+
+ if (dev < 0 || dev >= num_midis)
+ {
+ printk ("Sound: Nonexistent MIDI interface %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ /*
+ * Interrupts disabled. Be careful
+ */
+
+ DISABLE_INTR (flags);
+ if ((err = midi_devs[dev]->open (dev, mode,
+ midi_input_intr, midi_output_intr)) < 0)
+ {
+ RESTORE_INTR (flags);
+ return err;
+ }
+
+ parms[dev].prech_timeout = 0;
+
+ RESET_WAIT_QUEUE (midi_sleeper[dev], midi_sleep_flag[dev]);
+ RESET_WAIT_QUEUE (input_sleeper[dev], input_sleep_flag[dev]);
+
+ midi_in_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf));
+
+ if (midi_in_buf[dev] == NULL)
+ {
+ printk ("midi: Can't allocate buffer\n");
+ midi_devs[dev]->close (dev);
+ RESTORE_INTR (flags);
+ return RET_ERROR (EIO);
+ }
+ midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
+
+ midi_out_buf[dev] = (struct midi_buf *) KERNEL_MALLOC (sizeof (struct midi_buf));
+
+ if (midi_out_buf[dev] == NULL)
+ {
+ printk ("midi: Can't allocate buffer\n");
+ midi_devs[dev]->close (dev);
+ KERNEL_FREE (midi_in_buf[dev]);
+ midi_in_buf[dev] = NULL;
+ RESTORE_INTR (flags);
+ return RET_ERROR (EIO);
+ }
+ midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
+ if (!open_devs)
+ ACTIVATE_TIMER (poll_timer, midi_poll, 1); /*
+ * Come back later
+ */
+ open_devs++;
+ RESTORE_INTR (flags);
+
+ return err;
+}
+
+void
+MIDIbuf_release (int dev, struct fileinfo *file)
+{
+ int mode;
+ unsigned long flags;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ DISABLE_INTR (flags);
+
+ /*
+ * Wait until the queue is empty
+ */
+
+ if (mode != OPEN_READ)
+ {
+ midi_devs[dev]->putc (dev, 0xfe); /*
+ * Active sensing to shut the
+ * devices
+ */
+
+ while (!PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]) &&
+ DATA_AVAIL (midi_out_buf[dev]))
+ DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0); /*
+ * Sync
+ */
+
+ drain_midi_queue (dev); /*
+ * Ensure the output queues are empty
+ */
+ }
+
+ midi_devs[dev]->close (dev);
+ KERNEL_FREE (midi_in_buf[dev]);
+ KERNEL_FREE (midi_out_buf[dev]);
+ midi_in_buf[dev] = NULL;
+ midi_out_buf[dev] = NULL;
+ open_devs--;
+ RESTORE_INTR (flags);
+}
+
+int
+MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned long flags;
+ int c, n, i;
+ unsigned char tmp_data;
+
+ dev = dev >> 4;
+
+ if (!count)
+ return 0;
+
+ DISABLE_INTR (flags);
+
+ c = 0;
+
+ while (c < count)
+ {
+ n = SPACE_AVAIL (midi_out_buf[dev]);
+
+ if (n == 0) /*
+ * No space just now. We have to sleep
+ */
+ {
+ DO_SLEEP (midi_sleeper[dev], midi_sleep_flag[dev], 0);
+ if (PROCESS_ABORTING (midi_sleeper[dev], midi_sleep_flag[dev]))
+ {
+ RESTORE_INTR (flags);
+ return RET_ERROR (EINTR);
+ }
+
+ n = SPACE_AVAIL (midi_out_buf[dev]);
+ }
+
+ if (n > (count - c))
+ n = count - c;
+
+ for (i = 0; i < n; i++)
+ {
+ COPY_FROM_USER (&tmp_data, buf, c, 1);
+ QUEUE_BYTE (midi_out_buf[dev], tmp_data);
+ c++;
+ }
+ }
+
+ RESTORE_INTR (flags);
+
+ return c;
+}
+
+
+int
+MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int n, c = 0;
+ unsigned long flags;
+ unsigned char tmp_data;
+
+ dev = dev >> 4;
+
+ DISABLE_INTR (flags);
+
+ if (!DATA_AVAIL (midi_in_buf[dev])) /*
+ * No data yet, wait
+ */
+ {
+ DO_SLEEP (input_sleeper[dev], input_sleep_flag[dev],
+ parms[dev].prech_timeout);
+ if (PROCESS_ABORTING (input_sleeper[dev], input_sleep_flag[dev]))
+ c = RET_ERROR (EINTR); /*
+ * The user is getting restless
+ */
+ }
+
+ if (c == 0 && DATA_AVAIL (midi_in_buf[dev])) /*
+ * Got some bytes
+ */
+ {
+ n = DATA_AVAIL (midi_in_buf[dev]);
+ if (n > count)
+ n = count;
+ c = 0;
+
+ while (c < n)
+ {
+ REMOVE_BYTE (midi_in_buf[dev], tmp_data);
+ COPY_TO_USER (buf, c, &tmp_data, 1);
+ c++;
+ }
+ }
+
+ RESTORE_INTR (flags);
+
+ return c;
+}
+
+int
+MIDIbuf_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ int val;
+
+ dev = dev >> 4;
+
+ if (((cmd >> 8) & 0xff) == 'C')
+ {
+ if (midi_devs[dev]->coproc) /* Coprocessor ioctl */
+ return midi_devs[dev]->coproc->ioctl (midi_devs[dev]->coproc->devc, cmd, arg, 0);
+ else
+ printk ("/dev/midi%d: No coprocessor for this device\n", dev);
+
+ return RET_ERROR (EREMOTEIO);
+ }
+ else
+ switch (cmd)
+ {
+
+ case SNDCTL_MIDI_PRETIME:
+ val = IOCTL_IN (arg);
+ if (val < 0)
+ val = 0;
+
+ val = (HZ * val) / 10;
+ parms[dev].prech_timeout = val;
+ return IOCTL_OUT (arg, val);
+ break;
+
+ default:
+ return midi_devs[dev]->ioctl (dev, cmd, arg);
+ }
+}
+
+#ifdef ALLOW_SELECT
+int
+MIDIbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ dev = dev >> 4;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ if (!DATA_AVAIL (midi_in_buf[dev]))
+ {
+#if defined(__FreeBSD__)
+ selrecord(wait, &selinfo[dev]);
+#else
+ input_sleep_flag[dev].mode = WK_SLEEP;
+ select_wait (&input_sleeper[dev], wait);
+#endif
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_OUT:
+ if (SPACE_AVAIL (midi_out_buf[dev]))
+ {
+#if defined(__FreeBSD__)
+ selrecord(wait, &selinfo[dev]);
+#else
+ midi_sleep_flag[dev].mode = WK_SLEEP;
+ select_wait (&midi_sleeper[dev], wait);
+#endif
+ return 0;
+ }
+ return 1;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
+
+ return 0;
+}
+
+#endif /* ALLOW_SELECT */
+
+long
+MIDIbuf_init (long mem_start)
+{
+ return mem_start;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/mpu401.c b/sys/pc98/pc98/sound/mpu401.c
new file mode 100644
index 0000000..7b1616d
--- /dev/null
+++ b/sys/pc98/pc98/sound/mpu401.c
@@ -0,0 +1,1777 @@
+/*
+ * sound/mpu401.c
+ *
+ * The low level driver for Roland MPU-401 compatible Midi cards.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Modified:
+ * Riccardo Facchetti 24 Mar 1995
+ * - Added the Audio Excel DSP 16 initialization routine.
+ */
+
+#define USE_SEQ_MACROS
+#define USE_SIMPLE_MACROS
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if (!defined(EXCLUDE_MPU401) || !defined(EXCLUDE_MPU_EMU)) && !defined(EXCLUDE_MIDI)
+#include "coproc.h"
+
+static int init_sequence[20]; /* NOTE! pos 0 = len, start pos 1. */
+static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL;
+
+struct mpu_config
+ {
+ int base; /*
+ * I/O base
+ */
+ int irq;
+ int opened; /*
+ * Open mode
+ */
+ int devno;
+ int synthno;
+ int uart_mode;
+ int initialized;
+ int mode;
+#define MODE_MIDI 1
+#define MODE_SYNTH 2
+ unsigned char version, revision;
+ unsigned int capabilities;
+#define MPU_CAP_INTLG 0x10000000
+#define MPU_CAP_SYNC 0x00000010
+#define MPU_CAP_FSK 0x00000020
+#define MPU_CAP_CLS 0x00000040
+#define MPU_CAP_SMPTE 0x00000080
+#define MPU_CAP_2PORT 0x00000001
+ int timer_flag;
+
+#define MBUF_MAX 10
+#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \
+ {printk("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;}
+ int m_busy;
+ unsigned char m_buf[MBUF_MAX];
+ int m_ptr;
+ int m_state;
+ int m_left;
+ unsigned char last_status;
+ void (*inputintr) (int dev, unsigned char data);
+ int shared_irq;
+ };
+
+#define DATAPORT(base) (base)
+#define COMDPORT(base) (base+1)
+#define STATPORT(base) (base+1)
+
+#define mpu401_status(base) INB(STATPORT(base))
+#define input_avail(base) (!(mpu401_status(base)&INPUT_AVAIL))
+#define output_ready(base) (!(mpu401_status(base)&OUTPUT_READY))
+#define write_command(base, cmd) OUTB(cmd, COMDPORT(base))
+#define read_data(base) INB(DATAPORT(base))
+
+#define write_data(base, byte) OUTB(byte, DATAPORT(base))
+
+#define OUTPUT_READY 0x40
+#define INPUT_AVAIL 0x80
+#define MPU_ACK 0xF7
+#define MPU_RESET 0xFF
+#define UART_MODE_ON 0x3F
+
+static struct mpu_config dev_conf[MAX_MIDI_DEV] =
+{
+ {0}};
+
+static int n_mpu_devs = 0;
+static int irq2dev[16] =
+{-1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1};
+
+static int reset_mpu401 (struct mpu_config *devc);
+static void set_uart_mode (int dev, struct mpu_config *devc, int arg);
+static void mpu_timer_init (int midi_dev);
+static void mpu_timer_interrupt (void);
+static void timer_ext_event (struct mpu_config *devc, int event, int parm);
+
+static struct synth_info mpu_synth_info_proto =
+{"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, SYNTH_CAP_INPUT};
+
+static struct synth_info mpu_synth_info[MAX_MIDI_DEV];
+
+/*
+ * States for the input scanner
+ */
+
+#define ST_INIT 0 /* Ready for timing byte or msg */
+#define ST_TIMED 1 /* Leading timing byte rcvd */
+#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */
+
+#define ST_SYSMSG 100 /* System message (sysx etc). */
+#define ST_SYSEX 101 /* System exclusive msg */
+#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */
+#define ST_SONGSEL 103 /* Song select */
+#define ST_SONGPOS 104 /* Song position pointer */
+
+static unsigned char len_tab[] = /* # of data bytes following a status
+ */
+{
+ 2, /* 8x */
+ 2, /* 9x */
+ 2, /* Ax */
+ 2, /* Bx */
+ 1, /* Cx */
+ 1, /* Dx */
+ 2, /* Ex */
+ 0 /* Fx */
+};
+
+#define STORE(cmd) \
+{ \
+ int len; \
+ unsigned char obuf[8]; \
+ cmd; \
+ seq_input_event(obuf, len); \
+}
+#define _seqbuf obuf
+#define _seqbufptr 0
+#define _SEQ_ADVBUF(x) len=x
+
+static int
+mpu_input_scanner (struct mpu_config *devc, unsigned char midic)
+{
+
+ switch (devc->m_state)
+ {
+ case ST_INIT:
+ switch (midic)
+ {
+ case 0xf8:
+ /* Timer overflow */
+ break;
+
+ case 0xfc:
+ printk ("<all end>");
+ break;
+
+ case 0xfd:
+ if (devc->timer_flag)
+ mpu_timer_interrupt ();
+ break;
+
+ case 0xfe:
+ return MPU_ACK;
+ break;
+
+ case 0xf0:
+ case 0xf1:
+ case 0xf2:
+ case 0xf3:
+ case 0xf4:
+ case 0xf5:
+ case 0xf6:
+ case 0xf7:
+ printk ("<Trk data rq #%d>", midic & 0x0f);
+ break;
+
+ case 0xf9:
+ printk ("<conductor rq>");
+ break;
+
+ case 0xff:
+ devc->m_state = ST_SYSMSG;
+ break;
+
+ default:
+ if (midic <= 0xef)
+ {
+ /* printk("mpu time: %d ", midic); */
+ devc->m_state = ST_TIMED;
+ }
+ else
+ printk ("<MPU: Unknown event %02x> ", midic);
+ }
+ break;
+
+ case ST_TIMED:
+ {
+ int msg = (midic & 0xf0) >> 4;
+
+ devc->m_state = ST_DATABYTE;
+
+ if (msg < 8) /* Data byte */
+ {
+ /* printk("midi msg (running status) "); */
+ msg = (devc->last_status & 0xf0) >> 4;
+ msg -= 8;
+ devc->m_left = len_tab[msg] - 1;
+
+ devc->m_ptr = 2;
+ devc->m_buf[0] = devc->last_status;
+ devc->m_buf[1] = midic;
+
+ if (devc->m_left <= 0)
+ {
+ devc->m_state = ST_INIT;
+ do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr);
+ devc->m_ptr = 0;
+ }
+ }
+ else if (msg == 0xf) /* MPU MARK */
+ {
+ devc->m_state = ST_INIT;
+
+ switch (midic)
+ {
+ case 0xf8:
+ /* printk("NOP "); */
+ break;
+
+ case 0xf9:
+ /* printk("meas end "); */
+ break;
+
+ case 0xfc:
+ /* printk("data end "); */
+ break;
+
+ default:
+ printk ("Unknown MPU mark %02x\n", midic);
+ }
+ }
+ else
+ {
+ devc->last_status = midic;
+ /* printk ("midi msg "); */
+ msg -= 8;
+ devc->m_left = len_tab[msg];
+
+ devc->m_ptr = 1;
+ devc->m_buf[0] = midic;
+
+ if (devc->m_left <= 0)
+ {
+ devc->m_state = ST_INIT;
+ do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr);
+ devc->m_ptr = 0;
+ }
+ }
+ }
+ break;
+
+ case ST_SYSMSG:
+ switch (midic)
+ {
+ case 0xf0:
+ printk ("<SYX>");
+ devc->m_state = ST_SYSEX;
+ break;
+
+ case 0xf1:
+ devc->m_state = ST_MTC;
+ break;
+
+ case 0xf2:
+ devc->m_state = ST_SONGPOS;
+ devc->m_ptr = 0;
+ break;
+
+ case 0xf3:
+ devc->m_state = ST_SONGSEL;
+ break;
+
+ case 0xf6:
+ /* printk("tune_request\n"); */
+ devc->m_state = ST_INIT;
+
+ /*
+ * Real time messages
+ */
+ case 0xf8:
+ /* midi clock */
+ devc->m_state = ST_INIT;
+ timer_ext_event (devc, TMR_CLOCK, 0);
+ break;
+
+ case 0xfA:
+ devc->m_state = ST_INIT;
+ timer_ext_event (devc, TMR_START, 0);
+ break;
+
+ case 0xFB:
+ devc->m_state = ST_INIT;
+ timer_ext_event (devc, TMR_CONTINUE, 0);
+ break;
+
+ case 0xFC:
+ devc->m_state = ST_INIT;
+ timer_ext_event (devc, TMR_STOP, 0);
+ break;
+
+ case 0xFE:
+ /* active sensing */
+ devc->m_state = ST_INIT;
+ break;
+
+ case 0xff:
+ /* printk("midi hard reset"); */
+ devc->m_state = ST_INIT;
+ break;
+
+ default:
+ printk ("unknown MIDI sysmsg %0x\n", midic);
+ devc->m_state = ST_INIT;
+ }
+ break;
+
+ case ST_MTC:
+ devc->m_state = ST_INIT;
+ printk ("MTC frame %x02\n", midic);
+ break;
+
+ case ST_SYSEX:
+ if (midic == 0xf7)
+ {
+ printk ("<EOX>");
+ devc->m_state = ST_INIT;
+ }
+ else
+ printk ("%02x ", midic);
+ break;
+
+ case ST_SONGPOS:
+ BUFTEST (devc);
+ devc->m_buf[devc->m_ptr++] = midic;
+ if (devc->m_ptr == 2)
+ {
+ devc->m_state = ST_INIT;
+ devc->m_ptr = 0;
+ timer_ext_event (devc, TMR_SPP,
+ ((devc->m_buf[1] & 0x7f) << 7) |
+ (devc->m_buf[0] & 0x7f));
+ }
+ break;
+
+ case ST_DATABYTE:
+ BUFTEST (devc);
+ devc->m_buf[devc->m_ptr++] = midic;
+ if ((--devc->m_left) <= 0)
+ {
+ devc->m_state = ST_INIT;
+ do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr);
+ devc->m_ptr = 0;
+ }
+ break;
+
+ default:
+ printk ("Bad state %d ", devc->m_state);
+ devc->m_state = ST_INIT;
+ }
+
+ return 1;
+}
+
+static void
+mpu401_input_loop (struct mpu_config *devc)
+{
+ unsigned long flags;
+ int busy;
+ int n;
+
+ DISABLE_INTR (flags);
+ busy = devc->m_busy;
+ devc->m_busy = 1;
+ RESTORE_INTR (flags);
+
+ if (busy) /* Already inside the scanner */
+ return;
+
+ n = 50;
+
+ while (input_avail (devc->base) && n-- > 0)
+ {
+ unsigned char c = read_data (devc->base);
+
+ if (devc->mode == MODE_SYNTH)
+ {
+ mpu_input_scanner (devc, c);
+ }
+ else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
+ devc->inputintr (devc->devno, c);
+ }
+
+ devc->m_busy = 0;
+}
+
+void
+mpuintr (INT_HANDLER_PARMS (irq, dummy))
+{
+ struct mpu_config *devc;
+ int dev;
+
+#ifdef linux
+ sti ();
+#endif
+
+ if (irq < 1 || irq > 15)
+ {
+ printk ("MPU-401: Interrupt #%d?\n", irq);
+ return;
+ }
+
+ dev = irq2dev[irq];
+ if (dev == -1)
+ {
+ /* printk ("MPU-401: Interrupt #%d?\n", irq); */
+ return;
+ }
+
+ devc = &dev_conf[dev];
+
+ if (input_avail (devc->base))
+ if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH))
+ mpu401_input_loop (devc);
+ else
+ {
+ /* Dummy read (just to acknowledge the interrupt) */
+ read_data (devc->base);
+ }
+
+}
+
+static int
+mpu401_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ int err;
+ struct mpu_config *devc;
+
+ if (dev < 0 || dev >= num_midis)
+ return RET_ERROR (ENXIO);
+
+ devc = &dev_conf[dev];
+
+ if (devc->opened)
+ {
+ printk ("MPU-401: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ /*
+ * Verify that the device is really running.
+ * Some devices (such as Ensoniq SoundScape don't
+ * work before the on board processor (OBP) is initialized
+ * by downloadin it's microcode.
+ */
+
+ if (!devc->initialized)
+ {
+ if (mpu401_status (devc->base) == 0xff) /* Bus float */
+ {
+ printk ("MPU-401: Device not initialized properly\n");
+ return RET_ERROR (EIO);
+ }
+ reset_mpu401 (devc);
+ }
+
+ irq2dev[devc->irq] = dev;
+ if (devc->shared_irq == 0)
+ if ((err = snd_set_irq_handler (devc->irq, mpuintr, midi_devs[dev]->info.name) < 0))
+ {
+ return err;
+ }
+
+ if (midi_devs[dev]->coproc)
+ if ((err = midi_devs[dev]->coproc->
+ open (midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0)
+ {
+ if (devc->shared_irq == 0)
+ snd_release_irq (devc->irq);
+ printk ("MPU-401: Can't access coprocessor device\n");
+
+ return err;
+ }
+
+ set_uart_mode (dev, devc, 1);
+ devc->mode = MODE_MIDI;
+ devc->synthno = 0;
+
+ mpu401_input_loop (devc);
+
+ devc->inputintr = input;
+ devc->opened = mode;
+
+ return 0;
+}
+
+static void
+mpu401_close (int dev)
+{
+ struct mpu_config *devc;
+
+ devc = &dev_conf[dev];
+
+ if (devc->uart_mode)
+ reset_mpu401 (devc); /*
+ * This disables the UART mode
+ */
+ devc->mode = 0;
+
+ if (devc->shared_irq == 0)
+ snd_release_irq (devc->irq);
+ devc->inputintr = NULL;
+
+ if (midi_devs[dev]->coproc)
+ midi_devs[dev]->coproc->close (midi_devs[dev]->coproc->devc, COPR_MIDI);
+ devc->opened = 0;
+}
+
+static int
+mpu401_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ struct mpu_config *devc;
+
+ devc = &dev_conf[dev];
+
+#if 0
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ if (input_avail (devc->base))
+ {
+ mpu401_input_loop (devc);
+ }
+#endif
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 3000; timeout > 0 && !output_ready (devc->base); timeout--);
+
+ DISABLE_INTR (flags);
+ if (!output_ready (devc->base))
+ {
+ printk ("MPU-401: Send data timeout\n");
+ RESTORE_INTR (flags);
+ return 0;
+ }
+
+ write_data (devc->base, midi_byte);
+ RESTORE_INTR (flags);
+ return 1;
+}
+
+static int
+mpu401_command (int dev, mpu_command_rec * cmd)
+{
+ int i, timeout, ok;
+ int ret = 0;
+ unsigned long flags;
+ struct mpu_config *devc;
+
+ devc = &dev_conf[dev];
+
+ if (devc->uart_mode) /*
+ * Not possible in UART mode
+ */
+ {
+ printk ("MPU-401 commands not possible in the UART mode\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+ if (input_avail (devc->base))
+ mpu401_input_loop (devc);
+
+ /*
+ * Sometimes it takes about 30000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ timeout = 30000;
+retry:
+ if (timeout-- <= 0)
+ {
+ printk ("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd);
+ return RET_ERROR (EIO);
+ }
+
+ DISABLE_INTR (flags);
+
+ if (!output_ready (devc->base))
+ {
+ RESTORE_INTR (flags);
+ goto retry;
+ }
+
+ write_command (devc->base, cmd->cmd);
+ ok = 0;
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail (devc->base))
+ {
+ if (mpu_input_scanner (devc, read_data (devc->base)) == MPU_ACK)
+ ok = 1;
+ }
+
+ if (!ok)
+ {
+ RESTORE_INTR (flags);
+ /* printk ("MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); */
+ return RET_ERROR (EIO);
+ }
+
+ if (cmd->nr_args)
+ for (i = 0; i < cmd->nr_args; i++)
+ {
+ for (timeout = 3000; timeout > 0 && !output_ready (devc->base); timeout--);
+
+ if (!mpu401_out (dev, cmd->data[i]))
+ {
+ RESTORE_INTR (flags);
+ printk ("MPU: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
+ return RET_ERROR (EIO);
+ }
+ }
+
+ ret = 0;
+ cmd->data[0] = 0;
+
+ if (cmd->nr_returns)
+ for (i = 0; i < cmd->nr_returns; i++)
+ {
+ ok = 0;
+ for (timeout = 5000; timeout > 0 && !ok; timeout--)
+ if (input_avail (devc->base))
+ {
+ cmd->data[i] = read_data (devc->base);
+ ok = 1;
+ }
+
+ if (!ok)
+ {
+ RESTORE_INTR (flags);
+ /* printk ("MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd); */
+ return RET_ERROR (EIO);
+ }
+ }
+
+ RESTORE_INTR (flags);
+
+ return ret;
+}
+
+static int
+exec_cmd (int dev, int cmd, int data)
+{
+ int ret;
+
+ static mpu_command_rec rec;
+
+ rec.cmd = cmd & 0xff;
+ rec.nr_args = ((cmd & 0xf0) == 0xE0);
+ rec.nr_returns = ((cmd & 0xf0) == 0xA0);
+ rec.data[0] = data & 0xff;
+
+ if ((ret = mpu401_command (dev, &rec)) < 0)
+ return ret;
+ return (unsigned char) rec.data[0];
+}
+
+static int
+mpu401_prefix_cmd (int dev, unsigned char status)
+{
+ struct mpu_config *devc = &dev_conf[dev];
+
+ if (devc->uart_mode)
+ return 1;
+
+ if (status < 0xf0)
+ {
+ if (exec_cmd (dev, 0xD0, 0) < 0)
+ return 0;
+
+ return 1;
+ }
+
+ switch (status)
+ {
+ case 0xF0:
+ if (exec_cmd (dev, 0xDF, 0) < 0)
+ return 0;
+
+ return 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int
+mpu401_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+mpu401_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+mpu401_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ struct mpu_config *devc;
+
+ devc = &dev_conf[dev];
+
+ switch (cmd)
+ {
+ case 1:
+ IOCTL_FROM_USER ((char *) &init_sequence, (char *) arg, 0, sizeof (init_sequence));
+ return 0;
+ break;
+
+ case SNDCTL_MIDI_MPUMODE:
+ if (devc->version == 0)
+ {
+ printk ("MPU-401: Intelligent mode not supported by the HW\n");
+ return RET_ERROR (EINVAL);
+ }
+ set_uart_mode (dev, devc, !IOCTL_IN (arg));
+ return 0;
+ break;
+
+ case SNDCTL_MIDI_MPUCMD:
+ {
+ int ret;
+ mpu_command_rec rec;
+
+ IOCTL_FROM_USER ((char *) &rec, (char *) arg, 0, sizeof (rec));
+
+ if ((ret = mpu401_command (dev, &rec)) < 0)
+ return ret;
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) &rec, sizeof (rec));
+ return 0;
+ }
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static void
+mpu401_kick (int dev)
+{
+}
+
+static int
+mpu401_buffer_status (int dev)
+{
+ return 0; /*
+ * No data in buffers
+ */
+}
+
+static int
+mpu_synth_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ int midi_dev;
+ struct mpu_config *devc;
+
+ midi_dev = synth_devs[dev]->midi_dev;
+
+ if (midi_dev < 0 || midi_dev > num_midis)
+ return RET_ERROR (ENXIO);
+
+ devc = &dev_conf[midi_dev];
+
+ switch (cmd)
+ {
+
+ case SNDCTL_SYNTH_INFO:
+ IOCTL_TO_USER ((char *) arg, 0, &mpu_synth_info[midi_dev],
+ sizeof (struct synth_info));
+
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+}
+
+static int
+mpu_synth_open (int dev, int mode)
+{
+ int midi_dev, err;
+ struct mpu_config *devc;
+
+ midi_dev = synth_devs[dev]->midi_dev;
+
+ if (midi_dev < 0 || midi_dev > num_midis)
+ {
+ return RET_ERROR (ENXIO);
+ }
+
+ devc = &dev_conf[midi_dev];
+
+ /*
+ * Verify that the device is really running.
+ * Some devices (such as Ensoniq SoundScape don't
+ * work before the on board processor (OBP) is initialized
+ * by downloadin it's microcode.
+ */
+
+ if (!devc->initialized)
+ {
+ if (mpu401_status (devc->base) == 0xff) /* Bus float */
+ {
+ printk ("MPU-401: Device not initialized properly\n");
+ return RET_ERROR (EIO);
+ }
+ reset_mpu401 (devc);
+ }
+
+ if (devc->opened)
+ {
+ printk ("MPU-401: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ devc->mode = MODE_SYNTH;
+ devc->synthno = dev;
+
+ devc->inputintr = NULL;
+ irq2dev[devc->irq] = midi_dev;
+ if (devc->shared_irq == 0)
+ if ((err = snd_set_irq_handler (devc->irq, mpuintr, midi_devs[midi_dev]->info.name) < 0))
+ {
+ return err;
+ }
+
+ if (midi_devs[midi_dev]->coproc)
+ if ((err = midi_devs[midi_dev]->coproc->
+ open (midi_devs[midi_dev]->coproc->devc, COPR_MIDI)) < 0)
+ {
+ if (devc->shared_irq == 0)
+ snd_release_irq (devc->irq);
+ printk ("MPU-401: Can't access coprocessor device\n");
+
+ return err;
+ }
+
+ devc->opened = mode;
+ reset_mpu401 (devc);
+
+ if (mode & OPEN_READ)
+ {
+ exec_cmd (midi_dev, 0x8B, 0); /* Enable data in stop mode */
+ exec_cmd (midi_dev, 0x34, 0); /* Return timing bytes in stop mode */
+ }
+
+ return 0;
+}
+
+static void
+mpu_synth_close (int dev)
+{
+ int midi_dev;
+ struct mpu_config *devc;
+
+ midi_dev = synth_devs[dev]->midi_dev;
+
+ devc = &dev_conf[midi_dev];
+ exec_cmd (midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */
+ exec_cmd (midi_dev, 0x8a, 0); /* Disable data in stopped mode */
+
+ if (devc->shared_irq == 0)
+ snd_release_irq (devc->irq);
+ devc->inputintr = NULL;
+
+ if (midi_devs[midi_dev]->coproc)
+ midi_devs[midi_dev]->coproc->close (midi_devs[midi_dev]->coproc->devc, COPR_MIDI);
+ devc->opened = 0;
+ devc->mode = 0;
+}
+
+#define MIDI_SYNTH_NAME "MPU-401 UART Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct synth_operations mpu401_synth_proto =
+{
+ NULL,
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ mpu_synth_open,
+ mpu_synth_close,
+ mpu_synth_ioctl,
+ midi_synth_kill_note,
+ midi_synth_start_note,
+ midi_synth_set_instr,
+ midi_synth_reset,
+ midi_synth_hw_control,
+ midi_synth_load_patch,
+ midi_synth_aftertouch,
+ midi_synth_controller,
+ midi_synth_panning,
+ NULL,
+ midi_synth_patchmgr,
+ midi_synth_bender,
+ NULL, /* alloc */
+ midi_synth_setup_voice
+};
+
+static struct synth_operations mpu401_synth_operations[MAX_MIDI_DEV];
+
+static struct midi_operations mpu401_midi_proto =
+{
+ {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
+ NULL,
+ {0},
+ mpu401_open,
+ mpu401_close,
+ mpu401_ioctl,
+ mpu401_out,
+ mpu401_start_read,
+ mpu401_end_read,
+ mpu401_kick,
+ NULL,
+ mpu401_buffer_status,
+ mpu401_prefix_cmd
+};
+
+static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV];
+
+static void
+mpu401_chk_version (struct mpu_config *devc)
+{
+ int tmp;
+
+ devc->version = devc->revision = 0;
+
+ if ((tmp = exec_cmd (num_midis, 0xAC, 0)) < 0)
+ return;
+
+ if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */
+ return;
+
+ devc->version = tmp;
+
+ if ((tmp = exec_cmd (num_midis, 0xAD, 0)) < 0)
+ {
+ devc->version = 0;
+ return;
+ }
+ devc->revision = tmp;
+}
+
+long
+attach_mpu401 (long mem_start, struct address_info *hw_config)
+{
+ unsigned long flags;
+ char revision_char;
+
+ struct mpu_config *devc;
+
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("MPU-401: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ devc = &dev_conf[num_midis];
+
+ devc->base = hw_config->io_base;
+ devc->irq = hw_config->irq;
+ devc->opened = 0;
+ devc->uart_mode = 0;
+ devc->initialized = 0;
+ devc->version = 0;
+ devc->revision = 0;
+ devc->capabilities = 0;
+ devc->timer_flag = 0;
+ devc->m_busy = 0;
+ devc->m_state = ST_INIT;
+ devc->shared_irq = hw_config->always_detect;
+
+ if (!hw_config->always_detect)
+ {
+ /* Verify the hardware again */
+ if (!reset_mpu401 (devc))
+ return mem_start;
+
+ DISABLE_INTR (flags);
+ mpu401_chk_version (devc);
+ if (devc->version == 0)
+ mpu401_chk_version (devc);
+ RESTORE_INTR (flags);
+ }
+
+ if (devc->version == 0)
+ {
+ memcpy ((char *) &mpu401_synth_operations[num_midis],
+ (char *) &std_midi_synth,
+ sizeof (struct synth_operations));
+ }
+ else
+ {
+ devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */
+ memcpy ((char *) &mpu401_synth_operations[num_midis],
+ (char *) &mpu401_synth_proto,
+ sizeof (struct synth_operations));
+ }
+
+ memcpy ((char *) &mpu401_midi_operations[num_midis],
+ (char *) &mpu401_midi_proto,
+ sizeof (struct midi_operations));
+
+ mpu401_midi_operations[num_midis].converter =
+ &mpu401_synth_operations[num_midis];
+
+ memcpy ((char *) &mpu_synth_info[num_midis],
+ (char *) &mpu_synth_info_proto,
+ sizeof (struct synth_info));
+
+ n_mpu_devs++;
+
+ if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */
+ {
+ int ports = (devc->revision & 0x08) ? 32 : 16;
+
+ devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
+ MPU_CAP_CLS | MPU_CAP_2PORT;
+
+ revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
+#if defined(__FreeBSD__)
+ printk ("mpu0: <MQX-%d%c MIDI Interface>",
+#else
+ printk (" <MQX-%d%c MIDI Interface>",
+#endif
+ ports,
+ revision_char);
+ sprintf (mpu_synth_info[num_midis].name,
+ "MQX-%d%c MIDI Interface #%d",
+ ports,
+ revision_char,
+ n_mpu_devs);
+ }
+ else
+ {
+
+ revision_char = devc->revision ? devc->revision + '@' : ' ';
+ if (devc->revision > ('Z' - '@'))
+ revision_char = '+';
+
+ devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
+
+#if defined(__FreeBSD__)
+ printk ("mpu0: <MPU-401 MIDI Interface %d.%d%c>",
+#else
+ printk (" <MPU-401 MIDI Interface %d.%d%c>",
+#endif
+ (devc->version & 0xf0) >> 4,
+ devc->version & 0x0f,
+ revision_char);
+ sprintf (mpu_synth_info[num_midis].name,
+ "MPU-401 %d.%d%c Midi interface #%d",
+ (devc->version & 0xf0) >> 4,
+ devc->version & 0x0f,
+ revision_char,
+ n_mpu_devs);
+ }
+
+ strcpy (mpu401_midi_operations[num_midis].info.name,
+ mpu_synth_info[num_midis].name);
+
+ mpu401_synth_operations[num_midis].midi_dev = devc->devno = num_midis;
+ mpu401_synth_operations[devc->devno].info =
+ &mpu_synth_info[devc->devno];
+
+ if (devc->capabilities & MPU_CAP_INTLG) /* Has timer */
+ mpu_timer_init (num_midis);
+
+ irq2dev[devc->irq] = num_midis;
+ midi_devs[num_midis++] = &mpu401_midi_operations[devc->devno];
+ return mem_start;
+}
+
+static int
+reset_mpu401 (struct mpu_config *devc)
+{
+ unsigned long flags;
+ int ok, timeout, n;
+ int timeout_limit;
+
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ * (If the device is in the UART mode, it will not ack the reset cmd).
+ */
+
+ ok = 0;
+
+ timeout_limit = devc->initialized ? 30000 : 100000;
+ devc->initialized = 1;
+
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
+ ok = output_ready (devc->base);
+
+ write_command (devc->base, MPU_RESET); /*
+ * Send MPU-401 RESET Command
+ */
+
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+
+ for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
+ {
+ DISABLE_INTR (flags);
+ if (input_avail (devc->base))
+ if (read_data (devc->base) == MPU_ACK)
+ ok = 1;
+ RESTORE_INTR (flags);
+ }
+
+ }
+
+ devc->m_state = ST_INIT;
+ devc->m_ptr = 0;
+ devc->m_left = 0;
+ devc->last_status = 0;
+ devc->uart_mode = 0;
+
+ return ok;
+}
+
+static void
+set_uart_mode (int dev, struct mpu_config *devc, int arg)
+{
+
+ if (!arg && devc->version == 0)
+ {
+ return;
+ }
+
+ if ((devc->uart_mode == 0) == (arg == 0))
+ {
+ return; /* Already set */
+ }
+
+ reset_mpu401 (devc); /* This exits the uart mode */
+
+ if (arg)
+ {
+ if (exec_cmd (dev, UART_MODE_ON, 0) < 0)
+ {
+ printk ("MPU%d: Can't enter UART mode\n", devc->devno);
+ devc->uart_mode = 0;
+ return;
+ }
+ }
+ devc->uart_mode = arg;
+
+}
+
+int
+probe_mpu401 (struct address_info *hw_config)
+{
+ int ok = 0;
+ struct mpu_config tmp_devc;
+
+ tmp_devc.base = hw_config->io_base;
+ tmp_devc.irq = hw_config->irq;
+ tmp_devc.initialized = 0;
+
+#if !defined(EXCLUDE_AEDSP16) && defined(AEDSP16_MPU401)
+ /*
+ * Initialize Audio Excel DSP 16 to MPU-401, before any operation.
+ */
+ InitAEDSP16_MPU401 (hw_config);
+#endif
+
+ if (hw_config->always_detect)
+ return 1;
+
+ if (INB (hw_config->io_base + 1) == 0xff)
+ return 0; /* Just bus float? */
+
+ ok = reset_mpu401 (&tmp_devc);
+
+ return ok;
+}
+
+/*****************************************************
+ * Timer stuff
+ ****************************************************/
+
+#if !defined(EXCLUDE_SEQUENCER)
+
+static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0;
+static volatile int curr_tempo, curr_timebase, hw_timebase;
+static int max_timebase = 8; /* 8*24=192 ppqn */
+static volatile unsigned long next_event_time;
+static volatile unsigned long curr_ticks, curr_clocks;
+static unsigned long prev_event_time;
+static int metronome_mode;
+
+static unsigned long
+clocks2ticks (unsigned long clocks)
+{
+ /*
+ * The MPU-401 supports just a limited set of possible timebase values.
+ * Since the applications require more choices, the driver has to
+ * program the HW to do it's best and to convert between the HW and
+ * actual timebases.
+ */
+
+ return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;
+}
+
+static void
+set_timebase (int midi_dev, int val)
+{
+ int hw_val;
+
+ if (val < 48)
+ val = 48;
+ if (val > 1000)
+ val = 1000;
+
+ hw_val = val;
+ hw_val = (hw_val + 23) / 24;
+ if (hw_val > max_timebase)
+ hw_val = max_timebase;
+
+ if (exec_cmd (midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)
+ {
+ printk ("MPU: Can't set HW timebase to %d\n", hw_val * 24);
+ return;
+ }
+ hw_timebase = hw_val * 24;
+ curr_timebase = val;
+
+}
+
+static void
+tmr_reset (void)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ next_event_time = 0xffffffff;
+ prev_event_time = 0;
+ curr_ticks = curr_clocks = 0;
+ RESTORE_INTR (flags);
+}
+
+static void
+set_timer_mode (int midi_dev)
+{
+ if (timer_mode & TMR_MODE_CLS)
+ exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */
+ else if (timer_mode & TMR_MODE_SMPTE)
+ exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */
+
+ if (timer_mode & TMR_INTERNAL)
+ {
+ exec_cmd (midi_dev, 0x80, 0); /* Use MIDI sync */
+ }
+ else
+ {
+ if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
+ {
+ exec_cmd (midi_dev, 0x82, 0); /* Use MIDI sync */
+ exec_cmd (midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */
+ }
+ else if (timer_mode & TMR_MODE_FSK)
+ exec_cmd (midi_dev, 0x81, 0); /* Use FSK sync */
+ }
+}
+
+static void
+stop_metronome (int midi_dev)
+{
+ exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */
+}
+
+static void
+setup_metronome (int midi_dev)
+{
+ int numerator, denominator;
+ int clks_per_click, num_32nds_per_beat;
+ int beats_per_measure;
+
+ numerator = ((unsigned) metronome_mode >> 24) & 0xff;
+ denominator = ((unsigned) metronome_mode >> 16) & 0xff;
+ clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff;
+ num_32nds_per_beat = (unsigned) metronome_mode & 0xff;
+ beats_per_measure = (numerator * 4) >> denominator;
+
+ if (!metronome_mode)
+ exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */
+ else
+ {
+ exec_cmd (midi_dev, 0xE4, clks_per_click);
+ exec_cmd (midi_dev, 0xE6, beats_per_measure);
+ exec_cmd (midi_dev, 0x83, 0); /* Enable metronome without accents */
+ }
+}
+
+static int
+start_timer (int midi_dev)
+{
+ tmr_reset ();
+ set_timer_mode (midi_dev);
+
+ if (tmr_running)
+ return TIMER_NOT_ARMED; /* Already running */
+
+ if (timer_mode & TMR_INTERNAL)
+ {
+ exec_cmd (midi_dev, 0x02, 0); /* Send MIDI start */
+ tmr_running = 1;
+ return TIMER_NOT_ARMED;
+ }
+ else
+ {
+ exec_cmd (midi_dev, 0x35, 0); /* Enable mode messages to PC */
+ exec_cmd (midi_dev, 0x38, 0); /* Enable sys common messages to PC */
+ exec_cmd (midi_dev, 0x39, 0); /* Enable real time messages to PC */
+ exec_cmd (midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */
+ }
+
+ return TIMER_ARMED;
+}
+
+static int
+mpu_timer_open (int dev, int mode)
+{
+ int midi_dev = sound_timer_devs[dev]->devlink;
+
+ if (timer_open)
+ return RET_ERROR (EBUSY);
+
+ tmr_reset ();
+ curr_tempo = 50;
+ exec_cmd (midi_dev, 0xE0, 50);
+ curr_timebase = hw_timebase = 120;
+ set_timebase (midi_dev, 120);
+ timer_open = 1;
+ metronome_mode = 0;
+ set_timer_mode (midi_dev);
+
+ exec_cmd (midi_dev, 0xe7, 0x04); /* Send all clocks to host */
+ exec_cmd (midi_dev, 0x95, 0); /* Enable clock to host */
+
+ return 0;
+}
+
+static void
+mpu_timer_close (int dev)
+{
+ int midi_dev = sound_timer_devs[dev]->devlink;
+
+ timer_open = tmr_running = 0;
+ exec_cmd (midi_dev, 0x15, 0); /* Stop all */
+ exec_cmd (midi_dev, 0x94, 0); /* Disable clock to host */
+ exec_cmd (midi_dev, 0x8c, 0); /* Disable measure end messages to host */
+ stop_metronome (midi_dev);
+}
+
+static int
+mpu_timer_event (int dev, unsigned char *event)
+{
+ unsigned char command = event[1];
+ unsigned long parm = *(unsigned int *) &event[4];
+ int midi_dev = sound_timer_devs[dev]->devlink;
+
+ switch (command)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ if (tmr_running)
+ break;
+ return start_timer (midi_dev);
+ break;
+
+ case TMR_STOP:
+ exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */
+ stop_metronome (midi_dev);
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ if (tmr_running)
+ break;
+ exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */
+ setup_metronome (midi_dev);
+ tmr_running = 1;
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+
+ if (exec_cmd (midi_dev, 0xE0, parm) < 0)
+ printk ("MPU: Can't set tempo to %d\n", (int) parm);
+ curr_tempo = parm;
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input (event, 8);
+ break;
+
+ case TMR_TIMESIG:
+ if (metronome_mode) /* Metronome enabled */
+ {
+ metronome_mode = parm;
+ setup_metronome (midi_dev);
+ }
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+mpu_timer_get_time (int dev)
+{
+ if (!timer_open)
+ return 0;
+
+ return curr_ticks;
+}
+
+static int
+mpu_timer_ioctl (int dev,
+ unsigned int command, unsigned int arg)
+{
+ int midi_dev = sound_timer_devs[dev]->devlink;
+
+ switch (command)
+ {
+ case SNDCTL_TMR_SOURCE:
+ {
+ int parm = IOCTL_IN (arg) & timer_caps;
+
+ if (parm != 0)
+ {
+ timer_mode = parm;
+
+ if (timer_mode & TMR_MODE_CLS)
+ exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */
+ else if (timer_mode & TMR_MODE_SMPTE)
+ exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */
+ }
+
+ return IOCTL_OUT (arg, timer_mode);
+ }
+ break;
+
+ case SNDCTL_TMR_START:
+ start_timer (midi_dev);
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */
+ stop_metronome (midi_dev);
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ if (tmr_running)
+ return 0;
+ tmr_running = 1;
+ exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ set_timebase (midi_dev, val);
+
+ return IOCTL_OUT (arg, curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val = IOCTL_IN (arg);
+ int ret;
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ if ((ret = exec_cmd (midi_dev, 0xE0, val)) < 0)
+ {
+ printk ("MPU: Can't set tempo to %d\n", (int) val);
+ return ret;
+ }
+
+ curr_tempo = val;
+ }
+
+ return IOCTL_OUT (arg, curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (IOCTL_IN (arg) != 0) /* Can't change */
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ metronome_mode = IOCTL_IN (arg);
+ setup_metronome (midi_dev);
+ return 0;
+ break;
+
+ default:
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+mpu_timer_arm (int dev, long time)
+{
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
+
+ next_event_time = prev_event_time = time;
+
+ return;
+}
+
+static struct sound_timer_operations mpu_timer =
+{
+ {"MPU-401 Timer", 0},
+ 10, /* Priority */
+ 0, /* Local device link */
+ mpu_timer_open,
+ mpu_timer_close,
+ mpu_timer_event,
+ mpu_timer_get_time,
+ mpu_timer_ioctl,
+ mpu_timer_arm
+};
+
+static void
+mpu_timer_interrupt (void)
+{
+
+ if (!timer_open)
+ return;
+
+ if (!tmr_running)
+ return;
+
+ curr_clocks++;
+ curr_ticks = clocks2ticks (curr_clocks);
+
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = 0xffffffff;
+ sequencer_timer ();
+ }
+}
+
+static void
+timer_ext_event (struct mpu_config *devc, int event, int parm)
+{
+ int midi_dev = devc->devno;
+
+ if (!devc->timer_flag)
+ return;
+
+ switch (event)
+ {
+ case TMR_CLOCK:
+ printk ("<MIDI clk>");
+ break;
+
+ case TMR_START:
+ printk ("Ext MIDI start\n");
+ if (!tmr_running)
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ tmr_running = 1;
+ setup_metronome (midi_dev);
+ next_event_time = 0;
+ STORE (SEQ_START_TIMER ());
+ }
+ break;
+
+ case TMR_STOP:
+ printk ("Ext MIDI stop\n");
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ tmr_running = 0;
+ stop_metronome (midi_dev);
+ STORE (SEQ_STOP_TIMER ());
+ }
+ break;
+
+ case TMR_CONTINUE:
+ printk ("Ext MIDI continue\n");
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ tmr_running = 1;
+ setup_metronome (midi_dev);
+ STORE (SEQ_CONTINUE_TIMER ());
+ }
+ break;
+
+ case TMR_SPP:
+ printk ("Songpos: %d\n", parm);
+ if (timer_mode & TMR_EXTERNAL)
+ {
+ STORE (SEQ_SONGPOS (parm));
+ }
+ break;
+ }
+}
+
+static void
+mpu_timer_init (int midi_dev)
+{
+ struct mpu_config *devc;
+ int n;
+
+ devc = &dev_conf[midi_dev];
+
+ if (timer_initialized)
+ return; /* There is already a similar timer */
+
+ timer_initialized = 1;
+
+ mpu_timer.devlink = midi_dev;
+ dev_conf[midi_dev].timer_flag = 1;
+
+#if 1
+ if (num_sound_timers >= MAX_TIMER_DEV)
+ n = 0; /* Overwrite the system timer */
+ else
+ n = num_sound_timers++;
+#else
+ n = 0;
+#endif
+ sound_timer_devs[n] = &mpu_timer;
+
+ if (devc->version < 0x20) /* Original MPU-401 */
+ timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI;
+ else
+ {
+ /*
+ * The version number 2.0 is used (at least) by the
+ * MusicQuest cards and the Roland Super-MPU.
+ *
+ * MusicQuest has given a special meaning to the bits of the
+ * revision number. The Super-MPU returns 0.
+ */
+
+ if (devc->revision)
+ timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI;
+
+ if (devc->revision & 0x02)
+ timer_caps |= TMR_MODE_CLS;
+
+#if 0
+ if (devc->revision & 0x04)
+ timer_caps |= TMR_MODE_SMPTE;
+#endif
+
+ if (devc->revision & 0x40)
+ max_timebase = 10; /* Has the 216 and 240 ppqn modes */
+ }
+
+ timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;
+
+}
+
+#endif
+
+#endif
+
+#endif
diff --git a/sys/pc98/pc98/sound/opl3.c b/sys/pc98/pc98/sound/opl3.c
new file mode 100644
index 0000000..4b30e0a
--- /dev/null
+++ b/sys/pc98/pc98/sound/opl3.c
@@ -0,0 +1,1292 @@
+/*
+ * sound/opl3.c
+ *
+ * A low level driver for Yamaha YM3812 and OPL-3 -chips
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+/*
+ * Major improvements to the FM handling 30AUG92 by Rob Hooft,
+ */
+/*
+ * hooft@chem.ruu.nl
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_YM3812)
+
+#include "opl3.h"
+
+#define MAX_VOICE 18
+#define OFFS_4OP 11 /*
+ * * * Definitions for the operators OP3 and
+ * * OP4 * * begin here */
+
+static int opl3_enabled = 0;
+static int opl4_enabled = 0;
+#ifdef PC98
+static int left_address = 0x28d2, right_address = 0x28d2, both_address = 0;
+#else
+static int left_address = 0x388, right_address = 0x388, both_address = 0;
+#endif
+
+static int nr_voices = 9;
+static int logical_voices[MAX_VOICE] =
+{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+
+struct voice_info
+ {
+ unsigned char keyon_byte;
+ long bender;
+ long bender_range;
+ unsigned long orig_freq;
+ unsigned long current_freq;
+ int mode;
+ };
+
+static struct voice_info voices[MAX_VOICE];
+static struct voice_alloc_info *voice_alloc;
+static struct channel_info *chn_info;
+
+static struct sbi_instrument *instrmap;
+static struct sbi_instrument *active_instrument[MAX_VOICE] =
+{NULL};
+
+static struct synth_info fm_info =
+{"OPL-2", 0, SYNTH_TYPE_FM, FM_TYPE_ADLIB, 0, 9, 0, SBFM_MAXINSTR, 0};
+
+static int already_initialized = 0;
+
+static int opl3_ok = 0;
+static int opl3_busy = 0;
+static int fm_model = 0; /*
+
+
+ * * * * 0=no fm, 1=mono, 2=SB Pro 1, 3=SB
+ * Pro 2 * * */
+
+static int store_instr (int instr_no, struct sbi_instrument *instr);
+static void freq_to_fnum (int freq, int *block, int *fnum);
+static void opl3_command (int io_addr, unsigned int addr, unsigned int val);
+static int opl3_kill_note (int dev, int voice, int note, int velocity);
+static unsigned char connection_mask = 0x00;
+
+void
+enable_opl3_mode (int left, int right, int both)
+{
+ if (opl3_enabled)
+ return;
+
+ opl3_enabled = 1;
+ left_address = left;
+ right_address = right;
+ both_address = both;
+ fm_info.capabilities = SYNTH_CAP_OPL3;
+ fm_info.synth_subtype = FM_TYPE_OPL3;
+}
+
+static void
+enter_4op_mode (void)
+{
+ int i;
+ static int voices_4op[MAX_VOICE] =
+ {0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17};
+
+ connection_mask = 0x3f; /* Connect all possible 4 OP voices */
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x3f);
+
+ for (i = 0; i < 3; i++)
+ physical_voices[i].voice_mode = 4;
+ for (i = 3; i < 6; i++)
+ physical_voices[i].voice_mode = 0;
+
+ for (i = 9; i < 12; i++)
+ physical_voices[i].voice_mode = 4;
+ for (i = 12; i < 15; i++)
+ physical_voices[i].voice_mode = 0;
+
+ for (i = 0; i < 12; i++)
+ logical_voices[i] = voices_4op[i];
+ voice_alloc->max_voice = nr_voices = 12;
+}
+
+static int
+opl3_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ switch (cmd)
+ {
+
+ case SNDCTL_FM_LOAD_INSTR:
+ {
+ struct sbi_instrument ins;
+
+ IOCTL_FROM_USER ((char *) &ins, (char *) arg, 0, sizeof (ins));
+
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+ {
+ printk ("FM Error: Invalid instrument number %d\n", ins.channel);
+ return RET_ERROR (EINVAL);
+ }
+
+ pmgr_inform (dev, PM_E_PATCH_LOADED, ins.channel, 0, 0, 0);
+ return store_instr (ins.channel, &ins);
+ }
+ break;
+
+ case SNDCTL_SYNTH_INFO:
+ fm_info.nr_voices = (nr_voices == 12) ? 6 : nr_voices;
+
+ IOCTL_TO_USER ((char *) arg, 0, &fm_info, sizeof (fm_info));
+ return 0;
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+
+ case SNDCTL_FM_4OP_ENABLE:
+ if (opl3_enabled)
+ enter_4op_mode ();
+ return 0;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+}
+
+int
+opl3_detect (int ioaddr)
+{
+ /*
+ * This function returns 1 if the FM chicp is present at the given I/O port
+ * The detection algorithm plays with the timer built in the FM chip and
+ * looks for a change in the status register.
+ *
+ * Note! The timers of the FM chip are not connected to AdLib (and compatible)
+ * boards.
+ *
+ * Note2! The chip is initialized if detected.
+ */
+
+ unsigned char stat1, stat2, signature;
+ int i;
+
+ if (already_initialized)
+ {
+ return 0; /*
+ * Do avoid duplicate initializations
+ */
+ }
+
+ if (opl3_enabled)
+ ioaddr = left_address;
+
+ /* Reset timers 1 and 2 */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
+
+ /* Reset the IRQ of the FM chip */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
+
+ signature = stat1 = INB (ioaddr); /* Status register */
+
+ if ((stat1 & 0xE0) != 0x00)
+ {
+ return 0; /*
+ * Should be 0x00
+ */
+ }
+
+ opl3_command (ioaddr, TIMER1_REGISTER, 0xff); /* Set timer1 to 0xff */
+
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER,
+ TIMER2_MASK | TIMER1_START); /*
+ * Unmask and start timer 1
+ */
+
+ /*
+ * Now we have to delay at least 80 usec
+ */
+
+ for (i = 0; i < 50; i++)
+ tenmicrosec ();
+
+ stat2 = INB (ioaddr); /*
+ * Read status after timers have expired
+ */
+
+ /*
+ * Stop the timers
+ */
+
+ /* Reset timers 1 and 2 */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
+ /* Reset the IRQ of the FM chip */
+ opl3_command (ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
+
+ if ((stat2 & 0xE0) != 0xc0)
+ {
+ return 0; /*
+ * There is no YM3812
+ */
+ }
+
+ /*
+ * There is a FM chicp in this address. Detect the type (OPL2 to OPL4)
+ */
+
+ if (signature == 0x06) /* OPL2 */
+ {
+ opl3_enabled = 0;
+ }
+ else if (signature == 0x00) /* OPL3 or OPL4 */
+ {
+ unsigned char tmp;
+
+ if (!opl3_enabled) /* Was not already enabled */
+ {
+ left_address = ioaddr;
+ right_address = ioaddr + 2;
+ opl3_enabled = 1;
+ }
+
+ /*
+ * Detect availability of OPL4 (_experimental_). Works propably
+ * only after a cold boot. In addition the OPL4 port
+ * of the chip may not be connected to the PC bus at all.
+ */
+
+ opl3_command (right_address, OPL3_MODE_REGISTER, 0x00);
+ opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);
+
+ if ((tmp = INB (ioaddr)) == 0x02) /* Have a OPL4 */
+ {
+ opl4_enabled = 1;
+ }
+ opl3_command (right_address, OPL3_MODE_REGISTER, 0);
+
+ }
+
+ for (i = 0; i < 9; i++)
+ opl3_command (ioaddr, KEYON_BLOCK + i, 0); /*
+ * Note off
+ */
+
+ opl3_command (ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
+ opl3_command (ioaddr, PERCUSSION_REGISTER, 0x00); /*
+ * Melodic mode.
+ */
+
+ return 1;
+}
+
+static int
+opl3_kill_note (int dev, int voice, int note, int velocity)
+{
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return 0;
+
+ voice_alloc->map[voice] = 0;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ DEB (printk ("Kill note %d\n", voice));
+
+ if (map->voice_mode == 0)
+ return 0;
+
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, voices[voice].keyon_byte & ~0x20);
+
+ voices[voice].keyon_byte = 0;
+ voices[voice].bender = 0;
+ voices[voice].bender_range = 200; /*
+ * 200 cents = 2 semitones
+ */
+ voices[voice].orig_freq = 0;
+ voices[voice].current_freq = 0;
+ voices[voice].mode = 0;
+
+ return 0;
+}
+
+#define HIHAT 0
+#define CYMBAL 1
+#define TOMTOM 2
+#define SNARE 3
+#define BDRUM 4
+#define UNDEFINED TOMTOM
+#define DEFAULT TOMTOM
+
+static int
+store_instr (int instr_no, struct sbi_instrument *instr)
+{
+
+ if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled))
+ printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
+ memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr));
+
+ return 0;
+}
+
+static int
+opl3_set_instr (int dev, int voice, int instr_no)
+{
+ if (voice < 0 || voice >= nr_voices)
+ return 0;
+
+ if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
+ return 0;
+
+ active_instrument[voice] = &instrmap[instr_no];
+ return 0;
+}
+
+/*
+ * The next table looks magical, but it certainly is not. Its values have
+ * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
+ * for i=0. This log-table converts a linear volume-scaling (0..127) to a
+ * logarithmic scaling as present in the FM-synthesizer chips. so : Volume
+ * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative
+ * volume -8 it was implemented as a table because it is only 128 bytes and
+ * it saves a lot of log() calculations. (RH)
+ */
+static char fm_volume_table[128] =
+{-64, -48, -40, -35, -32, -29, -27, -26, /*
+ * 0 - 7
+ */
+ -24, -23, -21, -20, -19, -18, -18, -17, /*
+ * 8 - 15
+ */
+ -16, -15, -15, -14, -13, -13, -12, -12, /*
+ * 16 - 23
+ */
+ -11, -11, -10, -10, -10, -9, -9, -8, /*
+ * 24 - 31
+ */
+ -8, -8, -7, -7, -7, -6, -6, -6, /*
+ * 32 - 39
+ */
+ -5, -5, -5, -5, -4, -4, -4, -4, /*
+ * 40 - 47
+ */
+ -3, -3, -3, -3, -2, -2, -2, -2, /*
+ * 48 - 55
+ */
+ -2, -1, -1, -1, -1, 0, 0, 0, /*
+ * 56 - 63
+ */
+ 0, 0, 0, 1, 1, 1, 1, 1, /*
+ * 64 - 71
+ */
+ 1, 2, 2, 2, 2, 2, 2, 2, /*
+ * 72 - 79
+ */
+ 3, 3, 3, 3, 3, 3, 3, 4, /*
+ * 80 - 87
+ */
+ 4, 4, 4, 4, 4, 4, 4, 5, /*
+ * 88 - 95
+ */
+ 5, 5, 5, 5, 5, 5, 5, 5, /*
+ * 96 - 103
+ */
+ 6, 6, 6, 6, 6, 6, 6, 6, /*
+ * 104 - 111
+ */
+ 6, 7, 7, 7, 7, 7, 7, 7, /*
+ * 112 - 119
+ */
+ 7, 7, 7, 8, 8, 8, 8, 8}; /*
+
+
+ * * * * 120 - 127 */
+
+static void
+calc_vol (unsigned char *regbyte, int volume)
+{
+ int level = (~*regbyte & 0x3f);
+
+ if (level)
+ level += fm_volume_table[volume];
+
+ if (level > 0x3f)
+ level = 0x3f;
+ if (level < 0)
+ level = 0;
+
+ *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
+}
+
+static void
+set_voice_volume (int voice, int volume)
+{
+ unsigned char vol1, vol2, vol3, vol4;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ instr = active_instrument[voice];
+
+ if (!instr)
+ instr = &instrmap[0];
+
+ if (instr->channel < 0)
+ return;
+
+ if (voices[voice].mode == 0)
+ return;
+
+ if (voices[voice].mode == 2)
+ { /*
+ * 2 OP voice
+ */
+
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+
+ if ((instr->operators[10] & 0x01))
+ { /*
+ * Additive synthesis
+ */
+ calc_vol (&vol1, volume);
+ calc_vol (&vol2, volume);
+ }
+ else
+ { /*
+ * FM synthesis
+ */
+ calc_vol (&vol2, volume);
+ }
+
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1); /*
+ * Modulator
+ * volume
+ */
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2); /*
+ * Carrier
+ * volume
+ */
+ }
+ else
+ { /*
+ * 4 OP voice
+ */
+ int connection;
+
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+ vol3 = instr->operators[OFFS_4OP + 2];
+ vol4 = instr->operators[OFFS_4OP + 3];
+
+ /*
+ * The connection method for 4 OP voices is defined by the rightmost
+ * bits at the offsets 10 and 10+OFFS_4OP
+ */
+
+ connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+ switch (connection)
+ {
+ case 0:
+ calc_vol (&vol4, volume); /*
+ * Just the OP 4 is carrier
+ */
+ break;
+
+ case 1:
+ calc_vol (&vol2, volume);
+ calc_vol (&vol4, volume);
+ break;
+
+ case 2:
+ calc_vol (&vol1, volume);
+ calc_vol (&vol4, volume);
+ break;
+
+ case 3:
+ calc_vol (&vol1, volume);
+ calc_vol (&vol3, volume);
+ calc_vol (&vol4, volume);
+ break;
+
+ default: /*
+ * Why ??
+ */ ;
+ }
+
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], vol1);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], vol2);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], vol3);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], vol4);
+ }
+}
+
+static int
+opl3_start_note (int dev, int voice, int note, int volume)
+{
+ unsigned char data, fpc;
+ int block, fnum, freq, voice_mode;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return 0;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ if (map->voice_mode == 0)
+ return 0;
+
+ if (note == 255) /*
+ * Just change the volume
+ */
+ {
+ set_voice_volume (voice, volume);
+ return 0;
+ }
+
+ /*
+ * Kill previous note before playing
+ */
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /*
+ * Carrier
+ * volume to
+ * min
+ */
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /*
+ * Modulator
+ * volume to
+ */
+
+ if (map->voice_mode == 4)
+ {
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
+ opl3_command (map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
+ }
+
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /*
+ * Note
+ * off
+ */
+
+ instr = active_instrument[voice];
+
+ if (!instr)
+ instr = &instrmap[0];
+
+ if (instr->channel < 0)
+ {
+ printk (
+ "OPL3: Initializing voice %d with undefined instrument\n",
+ voice);
+ return 0;
+ }
+
+ if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
+ return 0; /*
+ * Cannot play
+ */
+
+ voice_mode = map->voice_mode;
+
+ if (voice_mode == 4)
+ {
+ int voice_shift;
+
+ voice_shift = (map->ioaddr == left_address) ? 0 : 3;
+ voice_shift += map->voice_num;
+
+ if (instr->key != OPL3_PATCH) /*
+ * Just 2 OP patch
+ */
+ {
+ voice_mode = 2;
+ connection_mask &= ~(1 << voice_shift);
+ }
+ else
+ {
+ connection_mask |= (1 << voice_shift);
+ }
+
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
+ }
+
+ /*
+ * Set Sound Characteristics
+ */
+ opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
+ opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
+
+ /*
+ * Set Attack/Decay
+ */
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
+
+ /*
+ * Set Sustain/Release
+ */
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
+
+ /*
+ * Set Wave Select
+ */
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
+
+ /*
+ * Set Feedback/Connection
+ */
+ fpc = instr->operators[10];
+ if (!(fpc & 0x30))
+ fpc |= 0x30; /*
+ * Ensure that at least one chn is enabled
+ */
+ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num,
+ fpc);
+
+ /*
+ * If the voice is a 4 OP one, initialize the operators 3 and 4 also
+ */
+
+ if (voice_mode == 4)
+ {
+
+ /*
+ * Set Sound Characteristics
+ */
+ opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
+ opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
+
+ /*
+ * Set Attack/Decay
+ */
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
+ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
+
+ /*
+ * Set Sustain/Release
+ */
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
+ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
+
+ /*
+ * Set Wave Select
+ */
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
+ opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
+
+ /*
+ * Set Feedback/Connection
+ */
+ fpc = instr->operators[OFFS_4OP + 10];
+ if (!(fpc & 0x30))
+ fpc |= 0x30; /*
+ * Ensure that at least one chn is enabled
+ */
+ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
+ }
+
+ voices[voice].mode = voice_mode;
+
+ set_voice_volume (voice, volume);
+
+ freq = voices[voice].orig_freq = note_to_freq (note) / 1000;
+
+ /*
+ * Since the pitch bender may have been set before playing the note, we
+ * have to calculate the bending now.
+ */
+
+ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ freq_to_fnum (freq, &block, &fnum);
+
+ /*
+ * Play note
+ */
+
+ data = fnum & 0xff; /*
+ * Least significant bits of fnumber
+ */
+ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+ data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
+ voices[voice].keyon_byte = data;
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+ if (voice_mode == 4)
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
+
+ return 0;
+}
+
+static void
+freq_to_fnum (int freq, int *block, int *fnum)
+{
+ int f, octave;
+
+ /*
+ * Converts the note frequency to block and fnum values for the FM chip
+ */
+ /*
+ * First try to compute the block -value (octave) where the note belongs
+ */
+
+ f = freq;
+
+ octave = 5;
+
+ if (f == 0)
+ octave = 0;
+ else if (f < 261)
+ {
+ while (f < 261)
+ {
+ octave--;
+ f <<= 1;
+ }
+ }
+ else if (f > 493)
+ {
+ while (f > 493)
+ {
+ octave++;
+ f >>= 1;
+ }
+ }
+
+ if (octave > 7)
+ octave = 7;
+
+ *fnum = freq * (1 << (20 - octave)) / 49716;
+ *block = octave;
+}
+
+static void
+opl3_command (int io_addr, unsigned int addr, unsigned int val)
+{
+ int i;
+
+ /*
+ * The original 2-OP synth requires a quite long delay after writing to a
+ * register. The OPL-3 survives with just two INBs
+ */
+
+ OUTB ((unsigned char) (addr & 0xff), io_addr); /*
+ * Select register
+ *
+ */
+
+ if (!opl3_enabled)
+ tenmicrosec ();
+ else
+ for (i = 0; i < 2; i++)
+ INB (io_addr);
+
+#ifdef PC98
+ OUTB ((unsigned char) (val & 0xff), io_addr + 0x100);
+#else
+ OUTB ((unsigned char) (val & 0xff), io_addr + 1); /*
+ * Write to register
+ *
+ */
+#endif
+
+ if (!opl3_enabled)
+ {
+ tenmicrosec ();
+ tenmicrosec ();
+ tenmicrosec ();
+ }
+ else
+ for (i = 0; i < 2; i++)
+ INB (io_addr);
+}
+
+static void
+opl3_reset (int dev)
+{
+ int i;
+
+ for (i = 0; i < nr_voices; i++)
+ {
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[0], 0xff);
+
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff);
+
+ if (physical_voices[logical_voices[i]].voice_mode == 4)
+ {
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff);
+
+ opl3_command (physical_voices[logical_voices[i]].ioaddr,
+ KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff);
+ }
+
+ opl3_kill_note (dev, i, 0, 64);
+ }
+
+ if (opl3_enabled)
+ {
+ voice_alloc->max_voice = nr_voices = 18;
+
+ for (i = 0; i < 18; i++)
+ logical_voices[i] = i;
+
+ for (i = 0; i < 18; i++)
+ physical_voices[i].voice_mode = 2;
+
+ }
+
+}
+
+static int
+opl3_open (int dev, int mode)
+{
+ int i;
+
+ if (!opl3_ok)
+ return RET_ERROR (ENXIO);
+ if (opl3_busy)
+ return RET_ERROR (EBUSY);
+ opl3_busy = 1;
+
+ voice_alloc->max_voice = nr_voices = opl3_enabled ? 18 : 9;
+ voice_alloc->timestamp = 0;
+
+ for (i = 0; i < 18; i++)
+ {
+ voice_alloc->map[i] = 0;
+ voice_alloc->alloc_times[i] = 0;
+ }
+
+ connection_mask = 0x00; /*
+ * Just 2 OP voices
+ */
+ if (opl3_enabled)
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask);
+ return 0;
+}
+
+static void
+opl3_close (int dev)
+{
+ opl3_busy = 0;
+ voice_alloc->max_voice = nr_voices = opl3_enabled ? 18 : 9;
+
+ fm_info.nr_drums = 0;
+ fm_info.perc_mode = 0;
+
+ opl3_reset (dev);
+}
+
+static void
+opl3_hw_control (int dev, unsigned char *event)
+{
+}
+
+static int
+opl3_load_patch (int dev, int format, snd_rw_buf * addr,
+ int offs, int count, int pmgr_flag)
+{
+ struct sbi_instrument ins;
+
+ if (count < sizeof (ins))
+ {
+ printk ("FM Error: Patch record too short\n");
+ return RET_ERROR (EINVAL);
+ }
+
+ COPY_FROM_USER (&((char *) &ins)[offs], (char *) addr, offs, sizeof (ins) - offs);
+
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
+ {
+ printk ("FM Error: Invalid instrument number %d\n", ins.channel);
+ return RET_ERROR (EINVAL);
+ }
+ ins.key = format;
+
+ return store_instr (ins.channel, &ins);
+}
+
+static void
+opl3_panning (int dev, int voice, int pressure)
+{
+}
+
+static void
+opl3_volume_method (int dev, int mode)
+{
+}
+
+#define SET_VIBRATO(cell) { \
+ tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
+ if (pressure > 110) \
+ tmp |= 0x40; /* Vibrato on */ \
+ opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
+
+static void
+opl3_aftertouch (int dev, int voice, int pressure)
+{
+ int tmp;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ DEB (printk ("Aftertouch %d\n", voice));
+
+ if (map->voice_mode == 0)
+ return;
+
+ /*
+ * Adjust the amount of vibrato depending the pressure
+ */
+
+ instr = active_instrument[voice];
+
+ if (!instr)
+ instr = &instrmap[0];
+
+ if (voices[voice].mode == 4)
+ {
+ int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+
+ switch (connection)
+ {
+ case 0:
+ SET_VIBRATO (4);
+ break;
+
+ case 1:
+ SET_VIBRATO (2);
+ SET_VIBRATO (4);
+ break;
+
+ case 2:
+ SET_VIBRATO (1);
+ SET_VIBRATO (4);
+ break;
+
+ case 3:
+ SET_VIBRATO (1);
+ SET_VIBRATO (3);
+ SET_VIBRATO (4);
+ break;
+
+ }
+ /*
+ * Not implemented yet
+ */
+ }
+ else
+ {
+ SET_VIBRATO (1);
+
+ if ((instr->operators[10] & 0x01)) /*
+ * Additive synthesis
+ */
+ SET_VIBRATO (2);
+ }
+}
+
+#undef SET_VIBRATO
+
+static void
+bend_pitch (int dev, int voice, int value)
+{
+ unsigned char data;
+ int block, fnum, freq;
+ struct physical_voice_info *map;
+
+ map = &physical_voices[logical_voices[voice]];
+
+ if (map->voice_mode == 0)
+ return;
+
+ voices[voice].bender = value;
+ if (!value)
+ return;
+ if (!(voices[voice].keyon_byte & 0x20))
+ return; /*
+ * Not keyed on
+ */
+
+ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range);
+ voices[voice].current_freq = freq;
+
+ freq_to_fnum (freq, &block, &fnum);
+
+ data = fnum & 0xff; /*
+ * Least significant bits of fnumber
+ */
+ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data);
+
+ data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /*
+ * *
+ * KEYON|OCTAVE|MS
+ *
+ * * bits * *
+ * of * f-num
+ *
+ */
+ voices[voice].keyon_byte = data;
+ opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data);
+}
+
+static void
+opl3_controller (int dev, int voice, int ctrl_num, int value)
+{
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ switch (ctrl_num)
+ {
+ case CTRL_PITCH_BENDER:
+ bend_pitch (dev, voice, value);
+ break;
+
+ case CTRL_PITCH_BENDER_RANGE:
+ voices[voice].bender_range = value;
+ break;
+ }
+}
+
+static int
+opl3_patchmgr (int dev, struct patmgr_info *rec)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+opl3_bender (int dev, int voice, int value)
+{
+ if (voice < 0 || voice >= nr_voices)
+ return;
+
+ bend_pitch (dev, voice, value);
+}
+
+static int
+opl3_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+ int i, p, best, first, avail_voices, best_time = 0x7fffffff;
+ struct sbi_instrument *instr;
+ int is4op;
+ int instr_no;
+
+ if (chn < 0 || chn > 15)
+ instr_no = 0;
+ else
+ instr_no = chn_info[chn].pgm_num;
+
+ instr = &instrmap[instr_no];
+ if (instr->channel < 0 || /* Instrument not loaded */
+ nr_voices != 12) /* Not in 4 OP mode */
+ is4op = 0;
+ else if (nr_voices == 12) /* 4 OP mode */
+ is4op = (instr->key == OPL3_PATCH);
+ else
+ is4op = 0;
+
+ if (is4op)
+ {
+ first = p = 0;
+ avail_voices = 6;
+ }
+ else
+ {
+ if (nr_voices == 12) /* 4 OP mode. Use the '2 OP only' voices first */
+ first = p = 6;
+ else
+ first = p = 0;
+ avail_voices = nr_voices;
+ }
+
+ /*
+ * Now try to find a free voice
+ */
+ best = first;
+
+ for (i = 0; i < avail_voices; i++)
+ {
+ if (alloc->map[p] == 0)
+ {
+ return p;
+ }
+ if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */
+ {
+ best_time = alloc->alloc_times[p];
+ best = p;
+ }
+ p = (p + 1) % avail_voices;
+ }
+
+ /*
+ * Insert some kind of priority mechanism here.
+ */
+
+ if (best < 0)
+ best = 0;
+ if (best > nr_voices)
+ best -= nr_voices;
+
+ return best; /* All voices in use. Select the first one. */
+}
+
+static void
+opl3_setup_voice (int dev, int voice, int chn)
+{
+ struct channel_info *info =
+ &synth_devs[dev]->chn_info[chn];
+
+ opl3_set_instr (dev, voice,
+ info->pgm_num);
+
+ voices[voice].bender = info->bender_value;
+}
+
+static struct synth_operations opl3_operations =
+{
+ &fm_info,
+ 0,
+ SYNTH_TYPE_FM,
+ FM_TYPE_ADLIB,
+ opl3_open,
+ opl3_close,
+ opl3_ioctl,
+ opl3_kill_note,
+ opl3_start_note,
+ opl3_set_instr,
+ opl3_reset,
+ opl3_hw_control,
+ opl3_load_patch,
+ opl3_aftertouch,
+ opl3_controller,
+ opl3_panning,
+ opl3_volume_method,
+ opl3_patchmgr,
+ opl3_bender,
+ opl3_alloc_voice,
+ opl3_setup_voice
+};
+
+long
+opl3_init (long mem_start)
+{
+ int i;
+
+ PERMANENT_MALLOC (struct sbi_instrument *, instrmap,
+ SBFM_MAXINSTR * sizeof (*instrmap), mem_start);
+
+ if (num_synths >= MAX_SYNTH_DEV)
+ printk ("OPL3 Error: Too many synthesizers\n");
+ else
+ {
+ synth_devs[num_synths++] = &opl3_operations;
+ voice_alloc = &opl3_operations.alloc;
+ chn_info = &opl3_operations.chn_info[0];
+ }
+
+ fm_model = 0;
+ opl3_ok = 1;
+ if (opl3_enabled)
+ {
+ if (opl4_enabled)
+#if defined(__FreeBSD__)
+ printk ("opl0: <Yamaha OPL4/OPL3 FM>");
+ else
+ printk ("opl0: <Yamaha OPL-3 FM>");
+#else
+ printk (" <Yamaha OPL4/OPL3 FM>");
+ else
+ printk (" <Yamaha OPL-3 FM>");
+#endif
+
+ fm_model = 2;
+ voice_alloc->max_voice = nr_voices = 18;
+ fm_info.nr_drums = 0;
+ fm_info.capabilities |= SYNTH_CAP_OPL3;
+ strcpy (fm_info.name, "Yamaha OPL-3");
+
+ for (i = 0; i < 18; i++)
+ if (physical_voices[i].ioaddr == USE_LEFT)
+ physical_voices[i].ioaddr = left_address;
+ else
+ physical_voices[i].ioaddr = right_address;
+
+
+ opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE); /*
+ * Enable
+ * OPL-3
+ * mode
+ */
+ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x00); /*
+ * Select
+ * all
+ * 2-OP
+ * *
+ * voices
+ */
+ }
+ else
+ {
+#if defined(__FreeBSD__)
+ printk ("opl0: <Yamaha 2-OP FM>");
+#else
+ printk (" <Yamaha 2-OP FM>");
+#endif
+ fm_model = 1;
+ voice_alloc->max_voice = nr_voices = 9;
+ fm_info.nr_drums = 0;
+
+ for (i = 0; i < 18; i++)
+ physical_voices[i].ioaddr = left_address;
+ };
+
+ already_initialized = 1;
+ for (i = 0; i < SBFM_MAXINSTR; i++)
+ instrmap[i].channel = -1;
+
+ return mem_start;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/opl3.h b/sys/pc98/pc98/sound/opl3.h
new file mode 100644
index 0000000..d25116b
--- /dev/null
+++ b/sys/pc98/pc98/sound/opl3.h
@@ -0,0 +1,261 @@
+/*
+ * opl3.h - Definitions of the OPL-3 registers
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+/*
+ * The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ * of the right side.
+ *
+ * Another special register at the right side is at offset 4. It contains
+ * a bit mask defining which voices are used as 4 OP voices.
+ *
+ * The percussive mode is implemented in the left side only.
+ *
+ * With the above exeptions the both sides can be operated independently.
+ *
+ * A 4 OP voice can be created by setting the corresponding
+ * bit at offset 4 of the right side.
+ *
+ * For example setting the rightmost bit (0x01) changes the
+ * first voice on the right side to the 4 OP mode. The fourth
+ * voice is made inaccessible.
+ *
+ * If a voice is set to the 2 OP mode, it works like 2 OP modes
+ * of the original YM3812 (AdLib). In addition the voice can
+ * be connected the left, right or both stereo channels. It can
+ * even be left unconnected. This works with 4 OP voices also.
+ *
+ * The stereo connection bits are located in the FEEDBACK_CONNECTION
+ * register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ * in the second half of the voice.
+ */
+
+/*
+ * Register numbers for the global registers
+ */
+
+#define TEST_REGISTER 0x01
+#define ENABLE_WAVE_SELECT 0x20
+
+#define TIMER1_REGISTER 0x02
+#define TIMER2_REGISTER 0x03
+#define TIMER_CONTROL_REGISTER 0x04 /* Left side */
+#define IRQ_RESET 0x80
+#define TIMER1_MASK 0x40
+#define TIMER2_MASK 0x20
+#define TIMER1_START 0x01
+#define TIMER2_START 0x02
+
+#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */
+#define RIGHT_4OP_0 0x01
+#define RIGHT_4OP_1 0x02
+#define RIGHT_4OP_2 0x04
+#define LEFT_4OP_0 0x08
+#define LEFT_4OP_1 0x10
+#define LEFT_4OP_2 0x20
+
+#define OPL3_MODE_REGISTER 0x05 /* Right side */
+#define OPL3_ENABLE 0x01
+#define OPL4_ENABLE 0x02
+
+#define KBD_SPLIT_REGISTER 0x08 /* Left side */
+#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
+#define KEYBOARD_SPLIT 0x40
+
+#define PERCUSSION_REGISTER 0xbd /* Left side only */
+#define TREMOLO_DEPTH 0x80
+#define VIBRATO_DEPTH 0x40
+#define PERCUSSION_ENABLE 0x20
+#define BASSDRUM_ON 0x10
+#define SNAREDRUM_ON 0x08
+#define TOMTOM_ON 0x04
+#define CYMBAL_ON 0x02
+#define HIHAT_ON 0x01
+
+/*
+ * Offsets to the register banks for operators. To get the
+ * register number just add the operator offset to the bank offset
+ *
+ * AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+ #define AM_VIB 0x20
+ #define TREMOLO_ON 0x80
+ #define VIBRATO_ON 0x40
+ #define SUSTAIN_ON 0x20
+ #define KSR 0x10 /* Key scaling rate */
+ #define MULTIPLE_MASK 0x0f /* Frequency multiplier */
+
+ /*
+ * KSL/Total level (0x40 to 0x55)
+ */
+#define KSL_LEVEL 0x40
+#define KSL_MASK 0xc0 /* Envelope scaling bits */
+#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */
+
+/*
+ * Attack / Decay rate (0x60 to 0x75)
+ */
+#define ATTACK_DECAY 0x60
+#define ATTACK_MASK 0xf0
+#define DECAY_MASK 0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define SUSTAIN_RELEASE 0x80
+#define SUSTAIN_MASK 0xf0
+#define RELEASE_MASK 0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define WAVE_SELECT 0xe0
+
+/*
+ * Offsets to the register banks for voices. Just add to the
+ * voice number to get the register number.
+ *
+ * F-Number low bits (0xA0 to 0xA8).
+ */
+#define FNUM_LOW 0xa0
+
+/*
+ * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define KEYON_BLOCK 0xb0
+#define KEYON_BIT 0x20
+#define BLOCKNUM_MASK 0x1c
+#define FNUM_HIGH_MASK 0x03
+
+/*
+ * Feedback / Connection (0xc0 to 0xc8)
+ *
+ * These registers have two new bits when the OPL-3 mode
+ * is selected. These bits controls connecting the voice
+ * to the stereo channels. For 4 OP voices this bit is
+ * defined in the second half of the voice (add 3 to the
+ * register offset).
+ *
+ * For 4 OP voices the connection bit is used in the
+ * both halfs (gives 4 ways to connect the operators).
+ */
+#define FEEDBACK_CONNECTION 0xc0
+#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */
+#define CONNECTION_BIT 0x01
+/*
+ * In the 4 OP mode there is four possible configurations how the
+ * operators can be connected together (in 2 OP modes there is just
+ * AM or FM). The 4 OP connection mode is defined by the rightmost
+ * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halfs.
+ *
+ * First half Second half Mode
+ *
+ * +---+
+ * v |
+ * 0 0 >+-1-+--2--3--4-->
+ *
+ *
+ *
+ * +---+
+ * | |
+ * 0 1 >+-1-+--2-+
+ * |->
+ * >--3----4-+
+ *
+ * +---+
+ * | |
+ * 1 0 >+-1-+-----+
+ * |->
+ * >--2--3--4-+
+ *
+ * +---+
+ * | |
+ * 1 1 >+-1-+--+
+ * |
+ * >--2--3-+->
+ * |
+ * >--4----+
+ */
+#define STEREO_BITS 0x30 /* OPL-3 only */
+#define VOICE_TO_LEFT 0x10
+#define VOICE_TO_RIGHT 0x20
+
+/*
+ * Definition table for the physical voices
+ */
+
+struct physical_voice_info {
+ unsigned char voice_num;
+ unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
+ unsigned short ioaddr; /* I/O port (left or right side) */
+ unsigned char op[4]; /* Operator offsets */
+ };
+
+/*
+ * There is 18 possible 2 OP voices
+ * (9 in the left and 9 in the right).
+ * The first OP is the modulator and 2nd is the carrier.
+ *
+ * The first three voices in the both sides may be connected
+ * with another voice to a 4 OP voice. For example voice 0
+ * can be connected with voice 3. The operators of voice 3 are
+ * used as operators 3 and 4 of the new 4 OP voice.
+ * In this case the 2 OP voice number 0 is the 'first half' and
+ * voice 3 is the second.
+ */
+
+#define USE_LEFT 0
+#define USE_RIGHT 1
+
+static struct physical_voice_info physical_voices[18] =
+{
+/* No Mode Side OP1 OP2 OP3 OP4 */
+/* --------------------------------------------------- */
+ { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}},
+ { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}},
+ { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}},
+
+ { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}},
+ { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}},
+ { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}},
+
+ { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */
+ { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */
+ { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */
+
+ { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}},
+ { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}},
+ { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}},
+
+ { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}},
+ { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}},
+ { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}},
+
+ { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}},
+ { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}},
+ { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}}
+};
diff --git a/sys/pc98/pc98/sound/os.h b/sys/pc98/pc98/sound/os.h
new file mode 100644
index 0000000..980163b
--- /dev/null
+++ b/sys/pc98/pc98/sound/os.h
@@ -0,0 +1,361 @@
+#ifndef _OS_H_
+#define _OS_H_
+/*
+ * OS specific settings for FreeBSD
+ *
+ * This chould be used as an example when porting the driver to a new
+ * operating systems.
+ *
+ * What you should do is to rewrite the soundcard.c and os.h (this file).
+ * You should create a new subdirectory and put these two files there.
+ * In addition you have to do a makefile.<OS>.
+ *
+ * If you have to make changes to other than these two files, please contact me
+ * before making the changes. It's possible that I have already made the
+ * change.
+ */
+
+/*
+ * Insert here the includes required by your kernel.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#ifdef PC98
+#include <pc98/pc98/pc98_device.h>
+#else
+#include <i386/isa/isa_device.h>
+#endif
+#include <machine/cpufunc.h>
+#include <sys/signalvar.h>
+
+#if NSND > 0
+#define CONFIGURE_SOUNDCARD
+#else
+#undef CONFIGURED_SOUNDCARD
+#endif
+
+
+/*
+ * Rest of the file is compiled only if the driver is really required.
+ */
+#ifdef CONFIGURE_SOUNDCARD
+
+/*
+ * select() is currently implemented in Linux specific way. Don't enable.
+ * I don't remember what the SHORT_BANNERS means so forget it.
+ */
+
+/*#undef ALLOW_SELECT*/
+#define SHORT_BANNERS
+
+/* The soundcard.h could be in a nonstandard place so inclyde it here. */
+#include <machine/soundcard.h>
+
+/*
+ * Here is the first portability problem. Every OS has it's own way to
+ * pass a pointer to the buffer in read() and write() calls. In Linux it's
+ * just a char*. In BSD it's struct uio. This parameter is passed to
+ * all functions called from read() or write(). Since nothing can be
+ * assumed about this structure, the driver uses set of macros for
+ * accessing the user buffer.
+ *
+ * The driver reads/writes bytes in the user buffer sequentially which
+ * means that calls like uiomove() can be used.
+ *
+ * snd_rw_buf is the type which is passed to the device file specific
+ * read() and write() calls.
+ *
+ * The following macros are used to move date to and from the
+ * user buffer. These macros should be used only when the
+ * target or source parameter has snd_rw_buf type.
+ * The offs parameter is a offset relative to the beginning of
+ * the user buffer. In Linux the offset is required but for example
+ * BSD passes the offset info in the uio structure. It could be usefull
+ * if these macros verify that the offs parameter and the value in
+ * the snd_rw_buf structure are equal.
+ */
+typedef struct uio snd_rw_buf;
+
+/*
+ * Move bytes from the buffer which the application given in a
+ * write() call.
+ * offs is position relative to the beginning of the buffer in
+ * user space. The count is number of bytes to be moved.
+ */
+#define COPY_FROM_USER(target, source, offs, count) \
+ do { if (uiomove((caddr_t ) target, count, (struct uio *)source)) { \
+ printf ("sb: Bad copyin()!\n"); \
+ } } while(0)
+
+/* Like COPY_FOM_USER but for writes. */
+
+#define COPY_TO_USER(target, offs, source, count) \
+ do { if (uiomove(source, count, (struct uio *)target)) { \
+ printf ("sb: Bad copyout()!\n"); \
+ } } while(0)
+/*
+ * The following macros are like COPY_*_USER but work just with one byte (8bit),
+ * short (16 bit) or long (32 bit) at a time.
+ * The same restrictions apply than for COPY_*_USER
+ */
+#define GET_BYTE_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 1, (struct uio *)addr);}
+#define GET_SHORT_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 2, (struct uio *)addr);}
+#define GET_WORD_FROM_USER(target, addr, offs) {uiomove((char*)&(target), 4, (struct uio *)addr);}
+#define PUT_WORD_TO_USER(addr, offs, data) {uiomove((char*)&(data), 4, (struct uio *)addr);}
+
+
+#define EREMOTEIO -1
+
+/*
+ * The way how the ioctl arguments are passed is another nonportable thing.
+ * In Linux the argument is just a pointer directly to the user segment. On
+ * FreeBSD the data is already moved to the kernel space. The following
+ * macros should handle the difference.
+ */
+
+/*
+ * IOCTL_FROM_USER is used to copy a record pointed by the argument to
+ * a buffer in the kernel space. On FreeBSD it can be done just by calling
+ * memcpy. With Linux a memcpy_from_fs should be called instead.
+ * Parameters of the following macros are like in the COPY_*_USER macros.
+ */
+
+/*
+ * When the ioctl argument points to a record or array (longer than 32 bits),
+ * the macros IOCTL_*_USER are used. It's assumed that the source and target
+ * parameters are direct memory addresses.
+ */
+#define IOCTL_FROM_USER(target, source, offs, count) {memcpy(target, &((source)[offs]), count);}
+#define IOCTL_TO_USER(target, offs, source, count) {memcpy(&((target)[offs]), source, count);}
+/* The following macros are used if the ioctl argument points to 32 bit int */
+#define IOCTL_IN(arg) (*(int*)arg)
+#define IOCTL_OUT(arg, ret) *(int*)arg = ret
+
+/*
+ * When the driver displays something to the console, printk() will be called.
+ * The name can be changed here.
+ */
+#define printk printf
+
+/*
+ * The following macros define an interface to the process management.
+ */
+
+struct snd_wait {
+ int mode;
+ int aborting;
+};
+
+/*
+ * DEFINE_WAIT_QUEUE is used where a wait queue is required. It must define
+ * a structure which can be passed as a parameter to a sleep(). The second
+ * parameter is name of a flag variable (must be defined as int).
+ */
+#define DEFINE_WAIT_QUEUE(qname, flag) static int *qname = NULL; \
+ static volatile struct snd_wait flag = {0}
+/* Like the above but defines an array of wait queues and flags */
+#define DEFINE_WAIT_QUEUES(qname, flag) static int *qname = {NULL}; \
+ static volatile struct snd_wait flag = {{0}}
+
+#define RESET_WAIT_QUEUE(q, f) {f.aborting = 0;f.mode = WK_NONE;}
+#define SET_ABORT_FLAG(q, f) f.aborting = 1
+#define TIMED_OUT(q, f) (f.mode & WK_TIMEOUT)
+#define SOMEONE_WAITING(q, f) (f.mode & WK_SLEEP)
+/*
+ * This driver handles interrupts little bit nonstandard way. The following
+ * macro is used to test if the current process has received a signal which
+ * is aborts the process. This macro is called from close() to see if the
+ * buffers should be discarded. If this kind info is not available, a constant
+ * 1 or 0 could be returned (1 should be better than 0).
+ */
+#define PROCESS_ABORTING(q, f) (f.aborting || CURSIG(curproc))
+
+/*
+ * The following macro calls tsleep. It should be implemented such that
+ * the process is resumed if it receives a signal.
+ * The q parameter is a wait_queue defined with DEFINE_WAIT_QUEUE(),
+ * and the second is a workarea parameter. The third is a timeout
+ * in ticks. Zero means no timeout.
+ */
+#define DO_SLEEP(q, f, time_limit) \
+ { \
+ int flag; \
+ f.mode = WK_SLEEP; \
+ flag=tsleep(&q, (PRIBIO-5)|PCATCH, "sndint", time_limit); \
+ f.mode &= ~WK_SLEEP; \
+ if (flag == EWOULDBLOCK) { \
+ f.mode |= WK_TIMEOUT; \
+ f.aborting = 0; \
+ } else \
+ f.aborting = flag; \
+ }
+/* An the following wakes up a process */
+#define WAKE_UP(q, f) wakeup(&q)
+
+/*
+ * Timing macros. This driver assumes that there is a timer running in the
+ * kernel. The timer should return a value which is increased once at every
+ * timer tick. The macro HZ should return the number of such ticks/sec.
+ */
+
+#ifndef HZ
+#define HZ hz
+#endif
+
+/*
+ * GET_TIME() returns current value of the counter incremented at timer
+ * ticks. This can overflow, so the timeout might be real big...
+ *
+ */
+
+extern unsigned long get_time(void);
+#define GET_TIME() get_time()
+/*#define GET_TIME() (lbolt) */ /* Returns current time (1/HZ secs since boot) */
+
+/*
+ * The following three macros are called before and after atomic
+ * code sequences. The flags parameter has always type of unsigned long.
+ * The macro DISABLE_INTR() should ensure that all interrupts which
+ * may invoke any part of the driver (timer, soundcard interrupts) are
+ * disabled.
+ * RESTORE_INTR() should return the interrupt status back to the
+ * state when DISABLE_INTR() was called. The flags parameter is
+ * a variable which can carry 32 bits of state information between
+ * DISABLE_INTR() and RESTORE_INTR() calls.
+ */
+#define DISABLE_INTR(flags) flags = splhigh()
+#define RESTORE_INTR(flags) splx(flags)
+
+/*
+ * INB() and OUTB() should be obvious. NOTE! The order of
+ * paratemeters of OUTB() is different than on some other
+ * operating systems.
+ */
+
+#define INB inb
+#define INW inb
+
+#if 0
+/*
+ * The outb(0, 0x80) is just for slowdown. It's bit unsafe since
+ * this address could be used for something usefull.
+ */
+#ifdef PC98
+#define OUTB(addr, data) {outb(data, addr);outb(0x5f, 0);}
+#define OUTW(addr, data) {outw(data, addr);outb(0x5f, 0);}
+#else
+#define OUTB(addr, data) {outb(data, addr);outb(0, 0x80);}
+#define OUTW(addr, data) {outw(data, addr);outb(0, 0x80);}
+#endif
+#else
+#define OUTB(addr, data) outb(data, addr)
+#define OUTW(addr, data) outw(data, addr)
+#endif
+
+/* memcpy() was not defined on FreeBSD. Lets define it here */
+#define memcpy(d, s, c) bcopy(s, d, c)
+
+/*
+ * When a error (such as EINVAL) is returned by a function,
+ * the following macro is used. The driver assumes that a
+ * error is signalled by returning a negative value.
+ */
+
+#define RET_ERROR(err) -(err)
+
+/*
+ KERNEL_MALLOC() allocates requested number of memory and
+ KERNEL_FREE is used to free it.
+ These macros are never called from interrupt, in addition the
+ nbytes will never be more than 4096 bytes. Generally the driver
+ will allocate memory in blocks of 4k. If the kernel has just a
+ page level memory allocation, 4K can be safely used as the size
+ (the nbytes parameter can be ignored).
+*/
+#define KERNEL_MALLOC(nbytes) malloc(nbytes, M_TEMP, M_WAITOK)
+#define KERNEL_FREE(addr) free(addr, M_TEMP)
+
+/*
+ * The macro PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr)
+ * returns size bytes of
+ * (kernel virtual) memory which will never get freed by the driver.
+ * This macro is called only during boot. The linux_ptr is a linux specific
+ * parameter which should be ignored in other operating systems.
+ * The mem_ptr is a pointer variable where the macro assigns pointer to the
+ * memory area. The type is the type of the mem_ptr.
+ */
+#define PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) \
+ {mem_ptr = (typecast)malloc(size, M_DEVBUF, M_NOWAIT); \
+ if (!mem_ptr)panic("SOUND: Cannot allocate memory\n");}
+
+/*
+ * The macro DEFINE_TIMER defines variables for the ACTIVATE_TIMER if
+ * required. The name is the variable/name to be used and the proc is
+ * the procedure to be called when the timer expires.
+ */
+
+#define DEFINE_TIMER(name, proc)
+
+/*
+ * The ACTIVATE_TIMER requests system to call 'proc' after 'time' ticks.
+ */
+
+#define ACTIVATE_TIMER(name, proc, time) \
+ timeout((timeout_func_t)proc, 0, time);
+/*
+ * The rest of this file is not complete yet. The functions using these
+ * macros will not work
+ */
+#ifdef PC98
+#define ALLOC_DMA_CHN(chn,deviceID) (pc98_dma_acquire(chn))
+#define RELEASE_DMA_CHN(chn) (pc98_dma_release(chn))
+#else
+#define ALLOC_DMA_CHN(chn,deviceID) (isa_dma_acquire(chn))
+#define RELEASE_DMA_CHN(chn) (isa_dma_release(chn))
+#endif
+#define DMA_MODE_READ 0
+#define DMA_MODE_WRITE 1
+#define RELEASE_IRQ(irq_no)
+
+/*
+ * The macro DECLARE_FILE() adds an entry to struct fileinfo referencing the
+ * connected filestructure.
+ * This entry must be initialized in sound_open() in soundcard.c
+ *
+ * ISSET_FILE_FLAG() allows checking of flags like O_NONBLOCK on files
+ *
+ */
+
+#define DECLARE_FILE() struct file *filp
+#ifdef notdef
+#define ISSET_FILE_FLAG(fileinfo, flag) (fileinfo->filp->f_flag & (flag) ? \
+ 1 : 0)
+#else
+#define ISSET_FILE_FLAG(fileinfo, flag) 0
+#endif
+#define INT_HANDLER_PROTO() void(*hndlr)(int)
+#define INT_HANDLER_PARMS(irq, parms) int irq
+#define INT_HANDLER_CALL(irq) irq
+
+/*
+ * For select call...
+ */
+#ifdef ALLOW_SELECT
+typedef struct proc select_table;
+#define SEL_IN FREAD
+#define SEL_OUT FWRITE
+#define SEL_EX 0
+extern struct selinfo selinfo[];
+#endif
+#endif
+#endif
diff --git a/sys/pc98/pc98/sound/pas.h b/sys/pc98/pc98/sound/pas.h
new file mode 100644
index 0000000..c4af2d4
--- /dev/null
+++ b/sys/pc98/pc98/sound/pas.h
@@ -0,0 +1,250 @@
+/* */
+/* Port addresses and bit fields for the Media Vision Pro AudioSpectrum second generation sound cards. */
+/* */
+/* Feel free to use this header file in any application you create that has support for the Media Vision */
+/* Pro AudioSpectrum second generation sound cards. Other uses prohibited without prior permission. */
+/* */
+/* - cmetz@thor.tjhsst.edu */
+/* */
+/* Notes: */
+/* */
+/* * All of these ports go into the MVD101 multimedia controller chip, which then signals the other chips to do */
+/* the actual work. Many ports like the FM ones functionally attach directly to the destination chip though */
+/* they don't actually have a direct connection. */
+/* */
+/* * The PAS2 series cards have an MVD101 multimedia controller chip, the original PAS cards don't. The original */
+/* PAS cards are pretty defunct now, so no attempt is made here to support them. */
+/* */
+/* * The PAS2 series cards are all really different at the hardware level, though the MVD101 hides some of the */
+/* incompatibilities, there still are differences that need to be accounted for. */
+/* */
+/* Card CD-ROM interface PCM chip Mixer chip FM chip */
+/* PAS Plus Sony proprietary (Crystal?) 8-bit DAC National OPL3 */
+/* PAS 16 Zilog SCSI MVA416 16-bit Codec MVA508 OPL3 */
+/* CDPC Sony proprietary Sony 16-bit Codec National OPL3 */
+/* Fusion CD 16 Sony proprietary MVA416 16-bit Codec MVA508 OPL3 */
+/* Fusion CD Sony proprietary (Crystal?) 8-bit DAC National OPL3 */
+/* */
+#define PAS_DEFAULT_BASE 0x388
+
+/* Symbolic Name Value R W Subsystem Description */
+#define SPEAKER_CONTROL 0x61 /* W PC speaker Control register */
+#define SPEAKER_CONTROL_GHOST 0x738B /* R W PC speaker Control ghost register */
+#define SPEAKER_TIMER_CONTROL 0x43 /* W PC speaker Timer control register */
+#define SPEAKER_TIMER_CONTROL_GHOST 0x778B /* R W PC speaker Timer control register ghost */
+#define SPEAKER_TIMER_DATA 0x42 /* W PC speaker Timer data register */
+#define SPEAKER_TIMER_DATA_GHOST 0x138A /* R W PC speaker Timer data register ghost */
+
+#define WARM_BOOT 0x41 /* W Control Used to detect system warm boot */
+#define WARM_BOOT_GHOST 0x7789 /* ? W Control Use to get the card to fake warm boot */
+#define MASTER_DECODE 0x9A01 /* W Control Address >> 2 of card base address */
+#define PRESCALE_DIVIDER 0xBF8A /* R W PCM Ration between Codec clock and master clock */
+#define WAIT_STATE 0xBF88 /* R W Control Four-bit bus wait-state count (~140ns ea.) */
+#define BOARD_REV_ID 0x2789 /* R Control Extended Board Revision ID */
+
+#define CHIP_REV 0xFF88 /* R 0=PAS, 1=PAS+, 2=CDPC, 3=PAS16C, 4=PAS16D */
+
+#define SYSTEM_CONFIGURATION_1 0x8388 /* R W Control */
+ #define S_C_1_PCS_ENABLE 0x01 /* R W PC speaker 1=enable, 0=disable PC speaker emulation */
+ #define S_C_1_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=14.31818Mhz/12, 0=28.224Mhz master clock */
+ #define S_C_1_FM_EMULATE_CLOCK 0x04 /* R W FM 1=use 28.224Mhz/2, 0=use 14.31818Mhz clock */
+ #define S_C_1_PCS_STEREO 0x10 /* R W PC speaker 1=enable PC speaker stereo effect, 0=disable */
+ #define S_C_1_PCS_REALSOUND 0x20 /* R W PC speaker 1=enable RealSound enhancement, 0=disable */
+ #define S_C_1_FORCE_EXT_RESET 0x40 /* R W Control Force external reset */
+ #define S_C_1_FORCE_INT_RESET 0x80 /* R W Control Force internal reset */
+#define SYSTEM_CONFIGURATION_2 0x8389 /* R W Control */
+ #define S_C_2_PCM_OVERSAMPLING 0x03 /* R W PCM 00=0x, 01=2x, 10=4x, 11=reserved */
+ #define S_C_2_PCM_16_BIT 0x04 /* R W PCM 1=16-bit, 0=8-bit samples */
+#define SYSTEM_CONFIGURATION_3 0x838A /* R W Control */
+ #define S_C_3_PCM_CLOCK_SELECT 0x02 /* R W PCM 1=use 1.008Mhz clock for PCM, 0=don't */
+#define SYSTEM_CONFIGURATION_4 0x838B /* R W Control CD-ROM interface controls */
+
+#define IO_CONFIGURATION_1 0xF388 /* R W Control */
+ #define I_C_1_BOOT_RESET_ENABLE 0x80 /* R W Control 1=reset board on warm boot, 0=don't */
+ #define I_C_1_JOYSTICK_ENABLE 0x40 /* R W Control 1=enable joystick port, 0=don't */
+#define IO_CONFIGURATION_2 0xF389 /* R W Control */
+ #define I_C_2_PCM_DMA_DISABLED 0x00 /* R W PCM PCM DMA disabled */
+#define IO_CONFIGURATION_3 0xF38A /* R W Control */
+ #define I_C_3_PCM_IRQ_DISABLED 0x00 /* R W PCM PCM IRQ disabled */
+
+#define COMPATIBILITY_ENABLE 0xF788 /* R W Control */
+ #define C_E_MPU401_ENABLE 0x01 /* R W MIDI 1=enable, 0=disable MPU401 MIDI emulation */
+ #define C_E_SB_ENABLE 0x02 /* R W PCM 1=enable, 0=disable Sound Blaster emulation */
+ #define C_E_SB_ACTIVE 0x04 /* R PCM "Sound Blaster Interrupt active" */
+ #define C_E_MPU401_ACTIVE 0x08 /* R MIDI "MPU UART mode active" */
+ #define C_E_PCM_COMPRESSION 0x10 /* R W PCM 1=enable, 0=disabled compression */
+#define EMULATION_ADDRESS 0xF789 /* R W Control */
+ #define E_A_SB_BASE 0x0f /* R W PCM bits A4-A7 for SB base port */
+ #define E_A_MPU401_BASE 0xf0 /* R W MIDI bits A4-A7 for MPU401 base port */
+#define EMULATION_CONFIGURATION 0xFB8A /* R W ***** Only valid on newer PAS2 cards (?) ***** */
+ #define E_C_MPU401_IRQ 0x07 /* R W MIDI MPU401 emulation IRQ */
+ #define E_C_SB_IRQ 0x38 /* R W PCM SB emulation IRQ */
+ #define E_C_SB_DMA 0xC0 /* R W PCM SB emulation DMA */
+
+#define OPERATION_MODE_1 0xEF8B /* R Control */
+ #define O_M_1_CDROM_TYPE 0x03 /* R CD-ROM 3=SCSI, 2=Sony, 0=no CD-ROM interface */
+ #define O_M_1_FM_TYPE 0x04 /* R FM 1=sterero, 0=mono FM chip */
+ #define O_M_1_PCM_TYPE 0x08 /* R PCM 1=16-bit Codec, 0=8-bit DAC */
+#define OPERATION_MODE_2 0xFF8B /* R Control */
+ #define O_M_2_PCS_ENABLED 0x02 /* R PC speaker PC speaker emulation 1=enabled, 0=disabled */
+ #define O_M_2_BUS_TIMING 0x10 /* R Control 1=AT bus timing, 0=XT bus timing */
+ #define O_M_2_BOARD_REVISION 0xe0 /* R Control Board revision */
+
+#define INTERRUPT_MASK 0x0B8B /* R W Control */
+ #define I_M_FM_LEFT_IRQ_ENABLE 0x01 /* R W FM Enable FM left interrupt */
+ #define I_M_FM_RIGHT_IRQ_ENABLE 0x02 /* R W FM Enable FM right interrupt */
+ #define I_M_PCM_RATE_IRQ_ENABLE 0x04 /* R W PCM Enable Sample Rate interrupt */
+ #define I_M_PCM_BUFFER_IRQ_ENABLE 0x08 /* R W PCM Enable Sample Buffer interrupt */
+ #define I_M_MIDI_IRQ_ENABLE 0x10 /* R W MIDI Enable MIDI interrupt */
+ #define I_M_BOARD_REV 0xE0 /* R Control Board revision */
+
+#define INTERRUPT_STATUS 0x0B89 /* R W Control */
+ #define I_S_FM_LEFT_IRQ 0x01 /* R W FM Left FM Interrupt Pending */
+ #define I_S_FM_RIGHT_IRQ 0x02 /* R W FM Right FM Interrupt Pending */
+ #define I_S_PCM_SAMPLE_RATE_IRQ 0x04 /* R W PCM Sample Rate Interrupt Pending */
+ #define I_S_PCM_SAMPLE_BUFFER_IRQ 0x08 /* R W PCM Sample Buffer Interrupt Pending */
+ #define I_S_MIDI_IRQ 0x10 /* R W MIDI MIDI Interrupt Pending */
+ #define I_S_PCM_CHANNEL 0x20 /* R W PCM 1=right, 0=left */
+ #define I_S_RESET_ACTIVE 0x40 /* R W Control Reset is active (Timed pulse not finished) */
+ #define I_S_PCM_CLIPPING 0x80 /* R W PCM Clipping has occurred */
+
+#define FILTER_FREQUENCY 0x0B8A /* R W Control */
+ #define F_F_FILTER_DISABLED 0x00 /* R W Mixer No filter */
+#if 0
+ struct { /* R W Mixer Filter translation */
+ unsigned int freq:24;
+ unsigned int value:8;
+ } F_F_FILTER_translate[] =
+ { { 73500, 0x01 }, /* 73500Hz - divide by 16 */
+ { 65333, 0x02 }, /* 65333Hz - divide by 18 */
+ { 49000, 0x09 }, /* 49000Hz - divide by 24 */
+ { 36750, 0x11 }, /* 36750Hz - divide by 32 */
+ { 24500, 0x19 }, /* 24500Hz - divide by 48 */
+ { 18375, 0x07 }, /* 18375Hz - divide by 64 */
+ { 12783, 0x0f }, /* 12783Hz - divide by 92 */
+ { 12250, 0x04 }, /* 12250Hz - divide by 96 */
+ { 9188, 0x17 }, /* 9188Hz - divide by 128 */
+ { 6125, 0x1f }, /* 6125Hz - divide by 192 */
+ };
+#endif
+ #define F_F_MIXER_UNMUTE 0x20 /* R W Mixer 1=disable, 0=enable board mute */
+ #define F_F_PCM_RATE_COUNTER 0x40 /* R W PCM 1=enable, 0=disable sample rate counter */
+ #define F_F_PCM_BUFFER_COUNTER 0x80 /* R W PCM 1=enable, 0=disable sample buffer counter */
+
+#define PAS_NONE 0
+#define PAS_PLUS 1
+#define PAS_CDPC 2
+#define PAS_16 3
+#define PAS_16D 4
+
+#ifdef DEFINE_TRANSLATIONS
+static unsigned char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */
+ { 4, 1, 2, 3, 0, 5, 6, 7 };
+static unsigned char I_C_3_PCM_IRQ_translate[] = /* R W PCM PCM IRQ level value translation */
+ { 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 };
+#ifdef unused
+static unsigned char E_C_MPU401_IRQ_translate[] = /* R W MIDI MPU401 emulation IRQ value translation */
+ { 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, 0x05, 0x06, 0x07 };
+#endif
+static unsigned char E_C_SB_IRQ_translate[] = /* R W PCM SB emulation IRQ translate */
+ { 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 };
+static unsigned char E_C_SB_DMA_translate[] = /* R W PCM SB emulation DMA translate */
+ { 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 };
+#ifdef unused
+static unsigned char O_M_1_to_card[] = /* R W Control Translate (OM1 & 0x0f) to card type */
+ { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 0, 2, 3 };
+#endif
+#endif
+
+#define PARALLEL_MIXER 0x078B /* W Mixer Documented for MVD101 as FM Mono Right decode?? */
+ #define P_M_MV508_ADDRESS 0x80 /* W Mixer MVD508 Address/mixer select */
+ #define P_M_MV508_DATA 0x00
+ #define P_M_MV508_LEFT 0x20 /* W Mixer MVD508 Left channel select */
+ #define P_M_MV508_RIGHT 0x40 /* W Mixer MVD508 Right channel select */
+ #define P_M_MV508_BOTH 0x00 /* W Mixer MVD508 Both channel select */
+ #define P_M_MV508_MIXER 0x10 /* W Mixer MVD508 Select a mixer (rather than a volume) */
+ #define P_M_MV508_VOLUME 0x00
+
+ #define P_M_MV508_INPUTMIX 0x20 /* W Mixer MVD508 Select mixer A */
+ #define P_M_MV508_OUTPUTMIX 0x00 /* W Mixer MVD508 Select mixer B */
+
+ #define P_M_MV508_MASTER_A 0x01 /* W Mixer MVD508 Master volume control A (output) */
+ #define P_M_MV508_MASTER_B 0x02 /* W Mixer MVD508 Master volume control B (DSP input) */
+ #define P_M_MV508_BASS 0x03 /* W Mixer MVD508 Bass control */
+ #define P_M_MV508_TREBLE 0x04 /* W Mixer MVD508 Treble control */
+ #define P_M_MV508_MODE 0x05 /* W Mixer MVD508 Master mode control */
+
+ #define P_M_MV508_LOUDNESS 0x04 /* W Mixer MVD508 Mode control - Loudness filter */
+ #define P_M_MV508_ENHANCE_BITS 0x03
+ #define P_M_MV508_ENHANCE_NONE 0x00 /* W Mixer MVD508 Mode control - No stereo enhancement */
+ #define P_M_MV508_ENHANCE_40 0x01 /* W Mixer MVD508 Mode control - 40% stereo enhancement */
+ #define P_M_MV508_ENHANCE_60 0x02 /* W Mixer MVD508 Mode control - 60% stereo enhancement */
+ #define P_M_MV508_ENHANCE_80 0x03 /* W Mixer MVD508 Mode control - 80% stereo enhancement */
+
+ #define P_M_MV508_FM 0x00 /* W Mixer MVD508 Channel 0 - FM */
+ #define P_M_MV508_IMIXER 0x01 /* W Mixer MVD508 Channel 1 - Input mixer (rec monitor) */
+ #define P_M_MV508_LINE 0x02 /* W Mixer MVD508 Channel 2 - Line in */
+ #define P_M_MV508_CDROM 0x03 /* W Mixer MVD508 Channel 3 - CD-ROM */
+ #define P_M_MV508_MIC 0x04 /* W Mixer MVD508 Channel 4 - Microphone */
+ #define P_M_MV508_PCM 0x05 /* W Mixer MVD508 Channel 5 - PCM */
+ #define P_M_MV508_SPEAKER 0x06 /* W Mixer MVD508 Channel 6 - PC Speaker */
+ #define P_M_MV508_SB 0x07 /* W Mixer MVD508 Channel 7 - SB DSP */
+
+#define SERIAL_MIXER 0xB88 /* R W Control Serial mixer control (used other ways) */
+ #define S_M_PCM_RESET 0x01 /* R W PCM Codec/DSP reset */
+ #define S_M_FM_RESET 0x02 /* R W FM FM chip reset */
+ #define S_M_SB_RESET 0x04 /* R W PCM SB emulation chip reset */
+ #define S_M_MIXER_RESET 0x10 /* R W Mixer Mixer chip reset */
+ #define S_M_INTEGRATOR_ENABLE 0x40 /* R W Speaker Enable PC speaker integrator (FORCE RealSound) */
+ #define S_M_OPL3_DUAL_MONO 0x80 /* R W FM Set the OPL-3 to dual mono mode */
+
+#define PCM_CONTROL 0xF8A /* R W PCM PCM Control Register */
+ #define P_C_MIXER_CROSS_FIELD 0x0f
+ #define P_C_MIXER_CROSS_R_TO_R 0x01 /* R W Mixer Connect Right to Right */
+ #define P_C_MIXER_CROSS_L_TO_R 0x02 /* R W Mixer Connect Left to Right */
+ #define P_C_MIXER_CROSS_R_TO_L 0x04 /* R W Mixer Connect Right to Left */
+ #define P_C_MIXER_CROSS_L_TO_L 0x08 /* R W Mixer Connect Left to Left */
+ #define P_C_PCM_DAC_MODE 0x10 /* R W PCM Playback (DAC) mode */
+ #define P_C_PCM_ADC_MODE 0x00 /* R W PCM Record (ADC) mode */
+ #define P_C_PCM_MONO 0x20 /* R W PCM Mono mode */
+ #define P_C_PCM_STEREO 0x00 /* R W PCM Stereo mode */
+ #define P_C_PCM_ENABLE 0x40 /* R W PCM Enable PCM engine */
+ #define P_C_PCM_DMA_ENABLE 0x80 /* R W PCM Enable DRQ */
+
+#define SAMPLE_COUNTER_CONTROL 0x138B /* R W PCM Sample counter control register */
+ #define S_C_C_SQUARE_WAVE 0x04 /* R W PCM Square wave generator (use for sample rate) */
+ #define S_C_C_RATE 0x06 /* R W PCM Rate generator (use for sample buffer count) */
+ #define S_C_C_LSB_THEN_MSB 0x30 /* R W PCM Change all 16 bits, LSB first, then MSB */
+
+ /* MVD101 and SDK documentations have S_C_C_SAMPLE_RATE and S_C_C_SAMPLE_BUFFER transposed. Only one works :-) */
+ #define S_C_C_SAMPLE_RATE 0x00 /* R W PCM Select sample rate timer */
+ #define S_C_C_SAMPLE_BUFFER 0x40 /* R W PCM Select sample buffer counter */
+
+ #define S_C_C_PC_SPEAKER 0x80 /* R W PCM Select PC speaker counter */
+
+#define SAMPLE_RATE_TIMER 0x1388 /* W PCM Sample rate timer register (PCM wait interval) */
+#define SAMPLE_BUFFER_COUNTER 0x1389 /* R W PCM Sample buffer counter (DMA buffer size) */
+
+#define MIDI_CONTROL 0x178b /* R W MIDI Midi control register */
+ #define M_C_ENA_TSTAMP_IRQ 0x01 /* R W MIDI Enable Time Stamp Interrupts */
+ #define M_C_ENA_TME_COMP_IRQ 0x02 /* R W MIDI Enable time compare interrupts */
+ #define M_C_ENA_INPUT_IRQ 0x04 /* R W MIDI Enable input FIFO interrupts */
+ #define M_C_ENA_OUTPUT_IRQ 0x08 /* R W MIDI Enable output FIFO interrupts */
+ #define M_C_ENA_OUTPUT_HALF_IRQ 0x10 /* R W MIDI Enable output FIFO half full interrupts */
+ #define M_C_RESET_INPUT_FIFO 0x20 /* R W MIDI Reset input FIFO pointer */
+ #define M_C_RESET_OUTPUT_FIFO 0x40 /* R W MIDI Reset output FIFO pointer */
+ #define M_C_ENA_THRU_MODE 0x80 /* R W MIDI Echo input to output (THRU) */
+
+#define MIDI_STATUS 0x1B88 /* R W MIDI Midi (interrupt) status register */
+ #define M_S_TIMESTAMP 0x01 /* R W MIDI Midi time stamp interrupt occurred */
+ #define M_S_COMPARE 0x02 /* R W MIDI Midi compare time interrupt occurred */
+ #define M_S_INPUT_AVAIL 0x04 /* R W MIDI Midi input data available interrupt occurred */
+ #define M_S_OUTPUT_EMPTY 0x08 /* R W MIDI Midi output FIFO empty interrupt occurred */
+ #define M_S_OUTPUT_HALF_EMPTY 0x10 /* R W MIDI Midi output FIFO half empty interrupt occurred */
+ #define M_S_INPUT_OVERRUN 0x20 /* R W MIDI Midi input overrun error occurred */
+ #define M_S_OUTPUT_OVERRUN 0x40 /* R W MIDI Midi output overrun error occurred */
+ #define M_S_FRAMING_ERROR 0x80 /* R W MIDI Midi input framing error occurred */
+
+#define MIDI_FIFO_STATUS 0x1B89 /* R W MIDI Midi fifo status */
+#define MIDI_DATA 0x178A /* R W MIDI Midi data register */
+#define MIDI_INPUT_AVAILABLE 0x0f /* RW MIDI */
diff --git a/sys/pc98/pc98/sound/pas2_card.c b/sys/pc98/pc98/sound/pas2_card.c
new file mode 100644
index 0000000..3f72359
--- /dev/null
+++ b/sys/pc98/pc98/sound/pas2_card.c
@@ -0,0 +1,420 @@
+#define _PAS2_CARD_C_
+/*
+ * sound/pas2_card.c
+ *
+ * Detection routine for the Pro Audio Spectrum cards.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
+
+#define DEFINE_TRANSLATIONS
+#include "pas.h"
+
+static int config_pas_hw __P((struct address_info *hw_config));
+static int detect_pas_hw __P((struct address_info *hw_config));
+static void pas2_msg __P((char *foo));
+
+/*
+ * The Address Translation code is used to convert I/O register addresses to
+ * be relative to the given base -register
+ */
+
+int translat_code;
+static int pas_intr_mask = 0;
+static int pas_irq = 0;
+
+char pas_model;
+static char *pas_model_names[] =
+{"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"};
+
+extern void mix_write (unsigned char data, int ioaddr);
+/*
+ * pas_read() and pas_write() are equivalents of INB() and OUTB()
+ */
+/*
+ * These routines perform the I/O address translation required
+ */
+/*
+ * to support other than the default base address
+ */
+
+unsigned char
+pas_read (int ioaddr)
+{
+ return INB (ioaddr ^ translat_code);
+}
+
+void
+pas_write (unsigned char data, int ioaddr)
+{
+ OUTB (data, ioaddr ^ translat_code);
+}
+
+static void
+pas2_msg (char *foo)
+{
+ printk (" PAS2: %s.\n", foo);
+}
+
+/******************* Begin of the Interrupt Handler ********************/
+
+void
+pasintr (INT_HANDLER_PARMS (irq, dummy))
+{
+ int status;
+
+ status = pas_read (INTERRUPT_STATUS);
+ pas_write (status, INTERRUPT_STATUS); /*
+ * Clear interrupt
+ */
+
+ if (status & I_S_PCM_SAMPLE_BUFFER_IRQ)
+ {
+#ifndef EXCLUDE_AUDIO
+ pas_pcm_interrupt (status, 1);
+#endif
+ status &= ~I_S_PCM_SAMPLE_BUFFER_IRQ;
+ }
+ if (status & I_S_MIDI_IRQ)
+ {
+#ifndef EXCLUDE_MIDI
+#ifdef EXCLUDE_PRO_MIDI
+ pas_midi_interrupt ();
+#endif
+#endif
+ status &= ~I_S_MIDI_IRQ;
+ }
+
+}
+
+int
+pas_set_intr (int mask)
+{
+ int err;
+
+ if (!mask)
+ return 0;
+
+ if (!pas_intr_mask)
+ {
+ if ((err = snd_set_irq_handler (pas_irq, pasintr, "PAS16")) < 0)
+ return err;
+ }
+ pas_intr_mask |= mask;
+
+ pas_write (pas_intr_mask, INTERRUPT_MASK);
+ return 0;
+}
+
+int
+pas_remove_intr (int mask)
+{
+ if (!mask)
+ return 0;
+
+ pas_intr_mask &= ~mask;
+ pas_write (pas_intr_mask, INTERRUPT_MASK);
+
+ if (!pas_intr_mask)
+ {
+ snd_release_irq (pas_irq);
+ }
+ return 0;
+}
+
+/******************* End of the Interrupt handler **********************/
+
+/******************* Begin of the Initialization Code ******************/
+
+static int
+config_pas_hw (struct address_info *hw_config)
+{
+ char ok = 1;
+ unsigned int_ptrs; /* scsi/sound interrupt pointers */
+
+ pas_irq = hw_config->irq;
+
+ pas_write (0x00, INTERRUPT_MASK);
+
+ pas_write (0x36, SAMPLE_COUNTER_CONTROL); /*
+ * Local timer control *
+ * register
+ */
+
+ pas_write (0x36, SAMPLE_RATE_TIMER); /*
+ * Sample rate timer (16 bit)
+ */
+ pas_write (0, SAMPLE_RATE_TIMER);
+
+ pas_write (0x74, SAMPLE_COUNTER_CONTROL); /*
+ * Local timer control *
+ * register
+ */
+
+ pas_write (0x74, SAMPLE_BUFFER_COUNTER); /*
+ * Sample count register (16
+ * * bit)
+ */
+ pas_write (0, SAMPLE_BUFFER_COUNTER);
+
+ pas_write (F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER | F_F_MIXER_UNMUTE | 1, FILTER_FREQUENCY);
+ pas_write (P_C_PCM_DMA_ENABLE | P_C_PCM_MONO | P_C_PCM_DAC_MODE | P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, PCM_CONTROL);
+ pas_write (S_M_PCM_RESET | S_M_FM_RESET | S_M_SB_RESET | S_M_MIXER_RESET /*
+ * |
+ * S_M_OPL3_DUAL_MONO
+ */ , SERIAL_MIXER);
+
+ pas_write (I_C_1_BOOT_RESET_ENABLE
+#ifdef PAS_JOYSTICK_ENABLE
+ | I_C_1_JOYSTICK_ENABLE
+#endif
+ ,IO_CONFIGURATION_1);
+
+ if (pas_irq < 0 || pas_irq > 15)
+ {
+ printk ("PAS2: Invalid IRQ %d", pas_irq);
+ ok = 0;
+ }
+ else
+ {
+ int_ptrs = pas_read (IO_CONFIGURATION_3);
+ int_ptrs |= I_C_3_PCM_IRQ_translate[pas_irq] & 0xf;
+ pas_write (int_ptrs, IO_CONFIGURATION_3);
+ if (!I_C_3_PCM_IRQ_translate[pas_irq])
+ {
+ printk ("PAS2: Invalid IRQ %d", pas_irq);
+ ok = 0;
+ }
+ }
+
+ if (hw_config->dma < 0 || hw_config->dma > 7)
+ {
+ printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+ ok = 0;
+ }
+ else
+ {
+ pas_write (I_C_2_PCM_DMA_translate[hw_config->dma], IO_CONFIGURATION_2);
+ if (!I_C_2_PCM_DMA_translate[hw_config->dma])
+ {
+ printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+ ok = 0;
+ }
+ }
+
+ /*
+ * This fixes the timing problems of the PAS due to the Symphony chipset
+ * as per Media Vision. Only define this if your PAS doesn't work correctly.
+ */
+#ifdef SYMPHONY_PAS
+ OUTB (0x05, 0xa8);
+ OUTB (0x60, 0xa9);
+#endif
+
+#ifdef BROKEN_BUS_CLOCK
+ pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1);
+#else
+ /*
+ * pas_write(S_C_1_PCS_ENABLE, SYSTEM_CONFIGURATION_1);
+ */
+ pas_write (S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, SYSTEM_CONFIGURATION_1);
+#endif
+ pas_write (0x18, SYSTEM_CONFIGURATION_3); /*
+ * ???
+ */
+
+ pas_write (F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); /*
+ * Sets mute
+ * off and *
+ * selects
+ * filter
+ * rate * of
+ * 17.897 kHz
+ */
+
+ if (pas_model == PAS_16 || pas_model == PAS_16D)
+ pas_write (8, PRESCALE_DIVIDER);
+ else
+ pas_write (0, PRESCALE_DIVIDER);
+
+ mix_write (P_M_MV508_ADDRESS | 5, PARALLEL_MIXER);
+ mix_write (5, PARALLEL_MIXER);
+
+#if !defined(EXCLUDE_SB_EMULATION) || !defined(EXCLUDE_SB)
+
+ {
+ struct address_info *sb_config;
+
+ if ((sb_config = sound_getconf (SNDCARD_SB)))
+ {
+ unsigned char irq_dma;
+
+ /*
+ * Turn on Sound Blaster compatibility
+ */
+ /*
+ * bit 1 = SB emulation
+ */
+ /*
+ * bit 0 = MPU401 emulation (CDPC only :-( )
+ */
+ pas_write (0x02, COMPATIBILITY_ENABLE);
+
+ /*
+ * "Emulation address"
+ */
+ pas_write ((sb_config->io_base >> 4) & 0x0f, EMULATION_ADDRESS);
+
+ if (!E_C_SB_DMA_translate[sb_config->dma])
+ printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n",
+ sb_config->dma);
+
+ if (!E_C_SB_IRQ_translate[sb_config->irq])
+ printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n",
+ sb_config->irq);
+
+ irq_dma = E_C_SB_DMA_translate[sb_config->dma] |
+ E_C_SB_IRQ_translate[sb_config->irq];
+
+ pas_write (irq_dma, EMULATION_CONFIGURATION);
+ }
+ }
+#endif
+
+ if (!ok)
+ pas2_msg ("Driver not enabled");
+
+ return ok;
+}
+
+static int
+detect_pas_hw (struct address_info *hw_config)
+{
+ unsigned char board_id, foo;
+
+ /*
+ * WARNING: Setting an option like W:1 or so that disables warm boot reset
+ * of the card will screw up this detect code something fierce. Adding code
+ * to handle this means possibly interfering with other cards on the bus if
+ * you have something on base port 0x388. SO be forewarned.
+ */
+
+ OUTB (0xBC, MASTER_DECODE); /*
+ * Talk to first board
+ */
+ OUTB (hw_config->io_base >> 2, MASTER_DECODE); /*
+ * Set base address
+ */
+ translat_code = PAS_DEFAULT_BASE ^ hw_config->io_base;
+ pas_write (1, WAIT_STATE); /*
+ * One wait-state
+ */
+
+ board_id = pas_read (INTERRUPT_MASK);
+
+ if (board_id == 0xff)
+ return 0;
+
+ /*
+ * We probably have a PAS-series board, now check for a PAS2-series board
+ * by trying to change the board revision bits. PAS2-series hardware won't
+ * let you do this - the bits are read-only.
+ */
+
+ foo = board_id ^ 0xe0;
+
+ pas_write (foo, INTERRUPT_MASK);
+ foo = INB (INTERRUPT_MASK);
+ pas_write (board_id, INTERRUPT_MASK);
+
+ if (board_id != foo) /*
+ * Not a PAS2
+ */
+ return 0;
+
+ pas_model = pas_read (CHIP_REV);
+
+ return pas_model;
+}
+
+long
+attach_pas_card (long mem_start, struct address_info *hw_config)
+{
+ pas_irq = hw_config->irq;
+
+ if (detect_pas_hw (hw_config))
+ {
+
+ if (pas_model = pas_read (CHIP_REV))
+ {
+#ifdef __FreeBSD__
+ printk ("pas0: <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID));
+#else
+ printk (" <%s rev %d>", pas_model_names[(int) pas_model], pas_read (BOARD_REV_ID));
+#endif
+ }
+
+ if (config_pas_hw (hw_config))
+ {
+
+#ifndef EXCLUDE_AUDIO
+ mem_start = pas_pcm_init (mem_start, hw_config);
+#endif
+
+#if !defined(EXCLUDE_SB_EMULATION) && !defined(EXCLUDE_SB)
+
+ sb_dsp_disable_midi (); /*
+ * The SB emulation don't support *
+ * midi
+ */
+#endif
+
+#ifndef EXCLUDE_YM3812
+ enable_opl3_mode (0x388, 0x38a, 0);
+#endif
+
+#ifndef EXCLUDE_MIDI
+#ifdef EXCLUDE_PRO_MIDI
+ mem_start = pas_midi_init (mem_start);
+#endif
+#endif
+
+ pas_init_mixer ();
+ }
+ }
+
+ return mem_start;
+}
+
+int
+probe_pas (struct address_info *hw_config)
+{
+ return detect_pas_hw (hw_config);
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/pas2_midi.c b/sys/pc98/pc98/sound/pas2_midi.c
new file mode 100644
index 0000000..6db884b
--- /dev/null
+++ b/sys/pc98/pc98/sound/pas2_midi.c
@@ -0,0 +1,341 @@
+/*
+ * sound/pas2_midi.c
+ *
+ * The low level driver for the PAS Midi Interface.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+
+#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_MIDI) && defined(EXCLUDE_PRO_MIDI)
+
+static int midi_busy = 0, input_opened = 0;
+static int my_dev;
+static volatile int ofifo_bytes = 0;
+
+static unsigned char tmp_queue[256];
+static volatile int qlen;
+static volatile unsigned char qhead, qtail;
+
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static int
+pas_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ int err;
+ unsigned long flags;
+ unsigned char ctrl;
+
+
+ if (midi_busy)
+ {
+ printk ("PAS2: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ /*
+ * Reset input and output FIFO pointers
+ */
+ pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO,
+ MIDI_CONTROL);
+
+ DISABLE_INTR (flags);
+
+ if ((err = pas_set_intr (I_M_MIDI_IRQ_ENABLE)) < 0)
+ return err;
+
+ /*
+ * Enable input available and output FIFO empty interrupts
+ */
+
+ ctrl = 0;
+ input_opened = 0;
+ midi_input_intr = input;
+
+ if (mode == OPEN_READ || mode == OPEN_READWRITE)
+ {
+ ctrl |= M_C_ENA_INPUT_IRQ; /*
+ * Enable input
+ */
+ input_opened = 1;
+ }
+
+ if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ {
+ ctrl |= M_C_ENA_OUTPUT_IRQ | /*
+ * Enable output
+ */
+ M_C_ENA_OUTPUT_HALF_IRQ;
+ }
+
+ pas_write (ctrl,
+ MIDI_CONTROL);
+
+ /*
+ * Acknowledge any pending interrupts
+ */
+
+ pas_write (0xff, MIDI_STATUS);
+ ofifo_bytes = 0;
+
+ RESTORE_INTR (flags);
+
+ midi_busy = 1;
+ qlen = qhead = qtail = 0;
+ return 0;
+}
+
+static void
+pas_midi_close (int dev)
+{
+
+ /*
+ * Reset FIFO pointers, disable intrs
+ */
+ pas_write (M_C_RESET_INPUT_FIFO | M_C_RESET_OUTPUT_FIFO, MIDI_CONTROL);
+
+ pas_remove_intr (I_M_MIDI_IRQ_ENABLE);
+ midi_busy = 0;
+}
+
+static int
+dump_to_midi (unsigned char midi_byte)
+{
+ int fifo_space, x;
+
+ fifo_space = ((x = pas_read (MIDI_FIFO_STATUS)) >> 4) & 0x0f;
+
+ if (fifo_space == 15 || (fifo_space < 2 && ofifo_bytes > 13)) /*
+ * Fifo
+ * full
+ */
+ {
+ return 0; /*
+ * Upper layer will call again
+ */
+ }
+
+ ofifo_bytes++;
+
+ pas_write (midi_byte, MIDI_DATA);
+
+ return 1;
+}
+
+static int
+pas_midi_out (int dev, unsigned char midi_byte)
+{
+
+ unsigned long flags;
+
+ /*
+ * Drain the local queue first
+ */
+
+ DISABLE_INTR (flags);
+
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Output the byte if the local queue is empty.
+ */
+
+ if (!qlen)
+ if (dump_to_midi (midi_byte))
+ return 1; /*
+ * OK
+ */
+
+ /*
+ * Put to the local queue
+ */
+
+ if (qlen >= 256)
+ return 0; /*
+ * Local queue full
+ */
+
+ DISABLE_INTR (flags);
+
+ tmp_queue[qtail] = midi_byte;
+ qlen++;
+ qtail++;
+
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+static int
+pas_midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+pas_midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+pas_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+pas_midi_kick (int dev)
+{
+ ofifo_bytes = 0;
+}
+
+static int
+pas_buffer_status (int dev)
+{
+ return !qlen;
+}
+
+#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations pas_midi_operations =
+{
+ {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
+ &std_midi_synth,
+ {0},
+ pas_midi_open,
+ pas_midi_close,
+ pas_midi_ioctl,
+ pas_midi_out,
+ pas_midi_start_read,
+ pas_midi_end_read,
+ pas_midi_kick,
+ NULL, /*
+ * command
+ */
+ pas_buffer_status,
+ NULL
+};
+
+long
+pas_midi_init (long mem_start)
+{
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ std_midi_synth.midi_dev = my_dev = num_midis;
+ midi_devs[num_midis++] = &pas_midi_operations;
+ return mem_start;
+}
+
+void
+pas_midi_interrupt (void)
+{
+ unsigned char stat;
+ int i, incount;
+ unsigned long flags;
+
+ stat = pas_read (MIDI_STATUS);
+
+ if (stat & M_S_INPUT_AVAIL) /*
+ * Input byte available
+ */
+ {
+ incount = pas_read (MIDI_FIFO_STATUS) & 0x0f; /*
+ * Input FIFO count
+ */
+ if (!incount)
+ incount = 16;
+
+ for (i = 0; i < incount; i++)
+ if (input_opened)
+ {
+ midi_input_intr (my_dev, pas_read (MIDI_DATA));
+ }
+ else
+ pas_read (MIDI_DATA); /*
+ * Flush
+ */
+ }
+
+ if (stat & (M_S_OUTPUT_EMPTY | M_S_OUTPUT_HALF_EMPTY))
+ {
+ if (!(stat & M_S_OUTPUT_EMPTY))
+ {
+ ofifo_bytes = 8;
+ }
+ else
+ {
+ ofifo_bytes = 0;
+ }
+
+ DISABLE_INTR (flags);
+
+ while (qlen && dump_to_midi (tmp_queue[qhead]))
+ {
+ qlen--;
+ qhead++;
+ }
+
+ RESTORE_INTR (flags);
+ }
+
+#if 0
+ if (stat & M_S_FRAMING_ERROR)
+ printk ("MIDI framing error\n");
+#endif
+
+ if (stat & M_S_OUTPUT_OVERRUN)
+ {
+ printk ("MIDI output overrun %x,%x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes);
+ ofifo_bytes = 100;
+ }
+
+ pas_write (stat, MIDI_STATUS); /*
+ * Acknowledge interrupts
+ */
+}
+
+#endif
+
+#endif
diff --git a/sys/pc98/pc98/sound/pas2_mixer.c b/sys/pc98/pc98/sound/pas2_mixer.c
new file mode 100644
index 0000000..f6fd0b6
--- /dev/null
+++ b/sys/pc98/pc98/sound/pas2_mixer.c
@@ -0,0 +1,340 @@
+#define _PAS2_MIXER_C_
+
+/*
+ * sound/pas2_mixer.c
+ *
+ * Mixer routines for the Pro Audio Spectrum cards.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
+
+#include "pas.h"
+
+extern void mix_write __P((unsigned char data, int ioaddr));
+static int pas_mixer_ioctl __P((int dev, unsigned int cmd, unsigned int arg));
+static void set_mode __P((int new_mode));
+
+#define TRACE(what) /* (what) */
+
+extern int translat_code;
+extern char pas_model;
+
+static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */
+static int mode_control = 0;
+
+#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_ALTPCM)
+
+#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
+ SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \
+ SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD)
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x3232, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x5050, /* FM */
+ 0x4b4b, /* PCM */
+ 0x3232, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x4b4b, /* Mic */
+ 0x4b4b, /* CD */
+ 0x6464, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x6464 /* Recording level */
+};
+
+void
+mix_write (unsigned char data, int ioaddr)
+{
+ /*
+ * The Revision D cards have a problem with their MVA508 interface. The
+ * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
+ * MSBs out of the output byte and to do a 16-bit out to the mixer port -
+ * 1. We need to do this because it isn't timing problem but chip access
+ * sequence problem.
+ */
+
+ if (pas_model == PAS_16D)
+ {
+ OUTW (data | (data << 8), (ioaddr ^ translat_code) - 1);
+ OUTB (0x80, 0);
+ }
+ else
+ pas_write (data, ioaddr);
+}
+
+static int
+mixer_output (int right_vol, int left_vol, int div, int bits,
+ int mixer) /* Input or output mixer */
+{
+ int left = left_vol * div / 100;
+ int right = right_vol * div / 100;
+
+
+ if (bits & P_M_MV508_MIXER)
+ { /*
+ * Select input or output mixer
+ */
+ left |= mixer;
+ right |= mixer;
+ }
+
+ if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE)
+ { /*
+ * Bass and treble are mono devices
+ */
+ mix_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER);
+ mix_write (left, PARALLEL_MIXER);
+ right_vol = left_vol;
+ }
+ else
+ {
+ mix_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER);
+ mix_write (left, PARALLEL_MIXER);
+ mix_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER);
+ mix_write (right, PARALLEL_MIXER);
+ }
+
+ return (left_vol | (right_vol << 8));
+}
+
+static void
+set_mode (int new_mode)
+{
+ mix_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER);
+ mix_write (new_mode, PARALLEL_MIXER);
+
+ mode_control = new_mode;
+}
+
+static int
+pas_mixer_set (int whichDev, unsigned int level)
+{
+ int left, right, devmask, changed, i, mixer = 0;
+
+ TRACE (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
+
+ left = level & 0x7f;
+ right = (level & 0x7f00) >> 8;
+
+ if (whichDev < SOUND_MIXER_NRDEVICES)
+ if ((1 << whichDev) & rec_devices)
+ mixer = P_M_MV508_INPUTMIX;
+ else
+ mixer = P_M_MV508_OUTPUTMIX;
+
+ switch (whichDev)
+ {
+ case SOUND_MIXER_VOLUME: /* Master volume (0-63) */
+ levels[whichDev] = mixer_output (right, left, 63, P_M_MV508_MASTER_A, 0);
+ break;
+
+ /*
+ * Note! Bass and Treble are mono devices. Will use just the left
+ * channel.
+ */
+ case SOUND_MIXER_BASS: /* Bass (0-12) */
+ levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_BASS, 0);
+ break;
+ case SOUND_MIXER_TREBLE: /* Treble (0-12) */
+ levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_TREBLE, 0);
+ break;
+
+ case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer);
+ break;
+ case SOUND_MIXER_PCM: /* PAS PCM (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer);
+ break;
+ case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer);
+ break;
+ case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer);
+ break;
+ case SOUND_MIXER_LINE: /* External line (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer);
+ break;
+ case SOUND_MIXER_CD: /* CD (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer);
+ break;
+ case SOUND_MIXER_MIC: /* External microphone (0-31) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer);
+ break;
+ case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */
+ levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER,
+ P_M_MV508_OUTPUTMIX);
+ break;
+ case SOUND_MIXER_RECLEV: /* Recording level (0-15) */
+ levels[whichDev] = mixer_output (right, left, 15, P_M_MV508_MASTER_B, 0);
+ break;
+
+ case SOUND_MIXER_MUTE:
+ return 0;
+ break;
+
+ case SOUND_MIXER_ENHANCE:
+ i = 0;
+ level &= 0x7f;
+ if (level)
+ i = (level / 20) - 1;
+
+ mode_control &= ~P_M_MV508_ENHANCE_BITS;
+ mode_control |= P_M_MV508_ENHANCE_BITS;
+ set_mode (mode_control);
+
+ if (i)
+ i = (i + 1) * 20;
+ return i;
+ break;
+
+ case SOUND_MIXER_LOUD:
+ mode_control &= ~P_M_MV508_LOUDNESS;
+ if (level)
+ mode_control |= P_M_MV508_LOUDNESS;
+ set_mode (mode_control);
+ return !!level; /* 0 or 1 */
+ break;
+
+ case SOUND_MIXER_RECSRC:
+ devmask = level & POSSIBLE_RECORDING_DEVICES;
+
+ changed = devmask ^ rec_devices;
+ rec_devices = devmask;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (changed & (1 << i))
+ {
+ pas_mixer_set (i, levels[i]);
+ }
+ return rec_devices;
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return (levels[whichDev]);
+}
+
+/*****/
+
+static void
+pas_mixer_reset (void)
+{
+ int foo;
+
+ TRACE (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n"));
+
+ for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+ pas_mixer_set (foo, levels[foo]);
+
+ set_mode (P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40);
+}
+
+int
+pas_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ TRACE (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ return IOCTL_OUT (arg, pas_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
+ else
+ { /*
+ * Read parameters
+ */
+
+ switch (cmd & 0xff)
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, rec_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, 0); /* No special capabilities */
+ break;
+
+ case SOUND_MIXER_MUTE:
+ return IOCTL_OUT (arg, 0); /* No mute yet */
+ break;
+
+ case SOUND_MIXER_ENHANCE:
+ if (!(mode_control & P_M_MV508_ENHANCE_BITS))
+ return IOCTL_OUT (arg, 0);
+ return IOCTL_OUT (arg, ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20);
+ break;
+
+ case SOUND_MIXER_LOUD:
+ if (mode_control & P_M_MV508_LOUDNESS)
+ return IOCTL_OUT (arg, 1);
+ return IOCTL_OUT (arg, 0);
+ break;
+
+ default:
+ return IOCTL_OUT (arg, levels[cmd & 0xff]);
+ }
+ }
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations pas_mixer_operations =
+{
+ "Pro Audio Spectrum 16",
+ pas_mixer_ioctl
+};
+
+int
+pas_init_mixer (void)
+{
+ pas_mixer_reset ();
+
+ if (num_mixers < MAX_MIXER_DEV)
+ mixer_devs[num_mixers++] = &pas_mixer_operations;
+ return 1;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/pas2_pcm.c b/sys/pc98/pc98/sound/pas2_pcm.c
new file mode 100644
index 0000000..8a9c172
--- /dev/null
+++ b/sys/pc98/pc98/sound/pas2_pcm.c
@@ -0,0 +1,457 @@
+#define _PAS2_PCM_C_
+/*
+ * sound/pas2_pcm.c
+ *
+ * The low level driver for the Pro Audio Spectrum ADC/DAC.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "pas.h"
+
+static int pcm_set_bits __P((int arg));
+static int pcm_set_channels __P((int arg));
+static int pcm_set_speed __P((int arg));
+
+#if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO)
+
+#define TRACE(WHAT) /*
+ * * * (WHAT) */
+
+#define PAS_PCM_INTRBITS (0x08)
+/*
+ * Sample buffer timer interrupt enable
+ */
+
+#define PCM_NON 0
+#define PCM_DAC 1
+#define PCM_ADC 2
+
+static unsigned long pcm_speed = 0; /* sampling rate */
+static unsigned char pcm_channels = 1; /* channels (1 or 2) */
+static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */
+static unsigned char pcm_filter = 0; /* filter FLAG */
+static unsigned char pcm_mode = PCM_NON;
+static unsigned long pcm_count = 0;
+static unsigned short pcm_bitsok = 8; /* mask of OK bits */
+static int my_devnum = 0;
+
+static int
+pcm_set_speed (int arg)
+{
+ int foo, tmp;
+ unsigned long flags;
+
+ if (arg > 44100)
+ arg = 44100;
+ if (arg < 5000)
+ arg = 5000;
+
+ foo = (1193180 + (arg / 2)) / arg;
+ arg = 1193180 / foo;
+
+ if (pcm_channels & 2)
+ foo = foo >> 1;
+
+ pcm_speed = arg;
+
+ tmp = pas_read (FILTER_FREQUENCY);
+
+ /*
+ * Set anti-aliasing filters according to sample rate. You reall *NEED*
+ * to enable this feature for all normal recording unless you want to
+ * experiment with aliasing effects.
+ * These filters apply to the selected "recording" source.
+ * I (pfw) don't know the encoding of these 5 bits. The values shown
+ * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
+ */
+#if !defined NO_AUTO_FILTER_SET
+ tmp &= 0xe0;
+ if (pcm_speed >= 2 * 17897)
+ tmp |= 0x21;
+ else if (pcm_speed >= 2 * 15909)
+ tmp |= 0x22;
+ else if (pcm_speed >= 2 * 11931)
+ tmp |= 0x29;
+ else if (pcm_speed >= 2 * 8948)
+ tmp |= 0x31;
+ else if (pcm_speed >= 2 * 5965)
+ tmp |= 0x39;
+ else if (pcm_speed >= 2 * 2982)
+ tmp |= 0x24;
+ pcm_filter = tmp;
+#endif
+
+ DISABLE_INTR (flags);
+
+ pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY);
+ pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+ pas_write (foo & 0xff, SAMPLE_RATE_TIMER);
+ pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER);
+ pas_write (tmp, FILTER_FREQUENCY);
+
+ RESTORE_INTR (flags);
+
+ return pcm_speed;
+}
+
+static int
+pcm_set_channels (int arg)
+{
+
+ if ((arg != 1) && (arg != 2))
+ return pcm_channels;
+
+ if (arg != pcm_channels)
+ {
+ pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL);
+
+ pcm_channels = arg;
+ pcm_set_speed (pcm_speed); /*
+ * The speed must be reinitialized
+ */
+ }
+
+ return pcm_channels;
+}
+
+static int
+pcm_set_bits (int arg)
+{
+ if ((arg & pcm_bitsok) != arg)
+ return pcm_bits;
+
+ if (arg != pcm_bits)
+ {
+ pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2);
+
+ pcm_bits = arg;
+ }
+
+ return pcm_bits;
+}
+
+static int
+pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
+
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return pcm_set_speed (arg);
+ return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return pcm_speed;
+ return IOCTL_OUT (arg, pcm_speed);
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return pcm_set_channels (arg + 1) - 1;
+ return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1);
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return pcm_set_channels (arg);
+ return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return pcm_channels;
+ return IOCTL_OUT (arg, pcm_channels);
+ break;
+
+ case SNDCTL_DSP_SETFMT:
+ if (local)
+ return pcm_set_bits (arg);
+ return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return pcm_bits;
+ return IOCTL_OUT (arg, pcm_bits);
+
+ case SOUND_PCM_WRITE_FILTER: /*
+ * NOT YET IMPLEMENTED
+ */
+ if (IOCTL_IN (arg) > 1)
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ break;
+
+ pcm_filter = IOCTL_IN (arg);
+ case SOUND_PCM_READ_FILTER:
+ return IOCTL_OUT (arg, pcm_filter);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+pas_pcm_reset (int dev)
+{
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n"));
+
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
+}
+
+static int
+pas_pcm_open (int dev, int mode)
+{
+ int err;
+
+ TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode));
+
+ if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
+ return err;
+
+ if (DMAbuf_open_dma (dev) < 0)
+ {
+ pas_remove_intr (PAS_PCM_INTRBITS);
+ return RET_ERROR (EBUSY);
+ }
+
+ pcm_count = 0;
+
+ return 0;
+}
+
+static void
+pas_pcm_close (int dev)
+{
+ unsigned long flags;
+
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n"));
+
+ DISABLE_INTR (flags);
+
+ pas_pcm_reset (dev);
+ DMAbuf_close_dma (dev);
+ pas_remove_intr (PAS_PCM_INTRBITS);
+ pcm_mode = PCM_NON;
+
+ RESTORE_INTR (flags);
+}
+
+static void
+pas_pcm_output_block (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags, cnt;
+
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count));
+
+ cnt = count;
+ if (audio_devs[dev]->dmachan > 3)
+ cnt >>= 1;
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == pcm_count)
+ return; /*
+ * Auto mode on. No need to react
+ */
+
+ DISABLE_INTR (flags);
+
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
+ PCM_CONTROL);
+
+ if (restart_dma)
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+ if (audio_devs[dev]->dmachan > 3)
+ count >>= 1;
+
+ if (count != pcm_count)
+ {
+ pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+ pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+ pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+
+ pcm_count = count;
+ }
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
+ pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
+
+ pcm_mode = PCM_DAC;
+
+ RESTORE_INTR (flags);
+}
+
+static void
+pas_pcm_start_input (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags;
+ int cnt;
+
+ TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count));
+
+ cnt = count;
+ if (audio_devs[dev]->dmachan > 3)
+ cnt >>= 1;
+
+ if (audio_devs[my_devnum]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == pcm_count)
+ return; /*
+ * Auto mode on. No need to react
+ */
+
+ DISABLE_INTR (flags);
+
+ if (restart_dma)
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ if (audio_devs[dev]->dmachan > 3)
+ count >>= 1;
+
+ if (count != pcm_count)
+ {
+ pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+ pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
+ pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
+
+ pcm_count = count;
+ }
+ pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
+ pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
+
+ pcm_mode = PCM_ADC;
+
+ RESTORE_INTR (flags);
+}
+
+static int
+pas_pcm_prepare_for_input (int dev, int bsize, int bcount)
+{
+ return 0;
+}
+static int
+pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
+{
+ return 0;
+}
+
+static struct audio_operations pas_pcm_operations =
+{
+ "Pro Audio Spectrum",
+ DMA_AUTOMODE,
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ pas_pcm_open,
+ pas_pcm_close,
+ pas_pcm_output_block,
+ pas_pcm_start_input,
+ pas_pcm_ioctl,
+ pas_pcm_prepare_for_input,
+ pas_pcm_prepare_for_output,
+ pas_pcm_reset,
+ pas_pcm_reset,
+ NULL,
+ NULL
+};
+
+long
+pas_pcm_init (long mem_start, struct address_info *hw_config)
+{
+ TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start));
+
+ pcm_bitsok = 8;
+ if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE)
+ pcm_bitsok |= 16;
+
+ pcm_set_speed (DSP_DEFAULT_SPEED);
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_devnum = num_audiodevs++] = &pas_pcm_operations;
+ audio_devs[my_devnum]->dmachan = hw_config->dma;
+ audio_devs[my_devnum]->buffcount = 1;
+ audio_devs[my_devnum]->buffsize = DSP_BUFFSIZE;
+ }
+ else
+ printk ("PAS2: Too many PCM devices available\n");
+
+ return mem_start;
+}
+
+void
+pas_pcm_interrupt (unsigned char status, int cause)
+{
+ if (cause == 1) /*
+ * PCM buffer done
+ */
+ {
+ /*
+ * Halt the PCM first. Otherwise we don't have time to start a new
+ * block before the PCM chip proceeds to the next sample
+ */
+
+ if (!(audio_devs[my_devnum]->flags & DMA_AUTOMODE))
+ {
+ pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
+ PCM_CONTROL);
+ }
+
+ switch (pcm_mode)
+ {
+
+ case PCM_DAC:
+ DMAbuf_outputintr (my_devnum, 1);
+ break;
+
+ case PCM_ADC:
+ DMAbuf_inputintr (my_devnum);
+ break;
+
+ default:
+ printk ("PAS: Unexpected PCM interrupt\n");
+ }
+ }
+}
+
+#endif
+
+#endif
diff --git a/sys/pc98/pc98/sound/patmgr.c b/sys/pc98/pc98/sound/patmgr.c
new file mode 100644
index 0000000..fc201dd
--- /dev/null
+++ b/sys/pc98/pc98/sound/patmgr.c
@@ -0,0 +1,268 @@
+/*
+ * sound/patmgr.c
+ *
+ * The patch maneger interface for the /dev/sequencer
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#define PATMGR_C
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SEQUENCER)
+
+DEFINE_WAIT_QUEUES (server_procs[MAX_SYNTH_DEV],
+ server_wait_flag[MAX_SYNTH_DEV]);
+
+static struct patmgr_info *mbox[MAX_SYNTH_DEV] =
+{NULL};
+static volatile int msg_direction[MAX_SYNTH_DEV] =
+{0};
+
+static int pmgr_opened[MAX_SYNTH_DEV] =
+{0};
+
+#define A_TO_S 1
+#define S_TO_A 2
+
+DEFINE_WAIT_QUEUE (appl_proc, appl_wait_flag);
+
+int
+pmgr_open (int dev)
+{
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (pmgr_opened[dev])
+ return RET_ERROR (EBUSY);
+ pmgr_opened[dev] = 1;
+
+ RESET_WAIT_QUEUE (server_procs[dev], server_wait_flag[dev]);
+
+ return 0;
+}
+
+void
+pmgr_release (int dev)
+{
+
+ if (mbox[dev]) /*
+ * Killed in action. Inform the client
+ */
+ {
+
+ mbox[dev]->key = PM_ERROR;
+ mbox[dev]->parm1 = RET_ERROR (EIO);
+
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
+ WAKE_UP (appl_proc, appl_wait_flag);
+ }
+
+ pmgr_opened[dev] = 0;
+}
+
+int
+pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned long flags;
+ int ok = 0;
+
+ if (count != sizeof (struct patmgr_info))
+ {
+ printk ("PATMGR%d: Invalid read count\n", dev);
+ return RET_ERROR (EIO);
+ }
+
+ while (!ok && !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
+ {
+ DISABLE_INTR (flags);
+
+ while (!(mbox[dev] && msg_direction[dev] == A_TO_S) &&
+ !PROCESS_ABORTING (server_procs[dev], server_wait_flag[dev]))
+ {
+ DO_SLEEP (server_procs[dev], server_wait_flag[dev], 0);
+ }
+
+ if (mbox[dev] && msg_direction[dev] == A_TO_S)
+ {
+ COPY_TO_USER (buf, 0, (char *) mbox[dev], count);
+ msg_direction[dev] = 0;
+ ok = 1;
+ }
+
+ RESTORE_INTR (flags);
+
+ }
+
+ if (!ok)
+ return RET_ERROR (EINTR);
+ return count;
+}
+
+int
+pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned long flags;
+
+ if (count < 4)
+ {
+ printk ("PATMGR%d: Write count < 4\n", dev);
+ return RET_ERROR (EIO);
+ }
+
+ COPY_FROM_USER (mbox[dev], buf, 0, 4);
+
+ if (*(unsigned char *) mbox[dev] == SEQ_FULLSIZE)
+ {
+ int tmp_dev;
+
+ tmp_dev = ((unsigned short *) mbox[dev])[2];
+ if (tmp_dev != dev)
+ return RET_ERROR (ENXIO);
+
+ return synth_devs[dev]->load_patch (dev, *(unsigned short *) mbox[dev],
+ buf, 4, count, 1);
+ }
+
+ if (count != sizeof (struct patmgr_info))
+ {
+ printk ("PATMGR%d: Invalid write count\n", dev);
+ return RET_ERROR (EIO);
+ }
+
+ /*
+ * If everything went OK, there should be a preallocated buffer in the
+ * mailbox and a client waiting.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (mbox[dev] && !msg_direction[dev])
+ {
+ COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4);
+ msg_direction[dev] = S_TO_A;
+
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
+ {
+ WAKE_UP (appl_proc, appl_wait_flag);
+ }
+ }
+
+ RESTORE_INTR (flags);
+
+ return count;
+}
+
+int
+pmgr_access (int dev, struct patmgr_info *rec)
+{
+ unsigned long flags;
+ int err = 0;
+
+ DISABLE_INTR (flags);
+
+ if (mbox[dev])
+ printk (" PATMGR: Server %d mbox full. Why?\n", dev);
+ else
+ {
+ rec->key = PM_K_COMMAND;
+ mbox[dev] = rec;
+ msg_direction[dev] = A_TO_S;
+
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
+ {
+ WAKE_UP (server_procs[dev], server_wait_flag[dev]);
+ }
+
+ DO_SLEEP (appl_proc, appl_wait_flag, 0);
+
+ if (msg_direction[dev] != S_TO_A)
+ {
+ rec->key = PM_ERROR;
+ rec->parm1 = RET_ERROR (EIO);
+ }
+ else if (rec->key == PM_ERROR)
+ {
+ err = rec->parm1;
+ if (err > 0)
+ err = -err;
+ }
+
+ mbox[dev] = NULL;
+ msg_direction[dev] = 0;
+ }
+
+ RESTORE_INTR (flags);
+
+ return err;
+}
+
+int
+pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2,
+ unsigned long p3, unsigned long p4)
+{
+ unsigned long flags;
+ int err = 0;
+
+ if (!pmgr_opened[dev])
+ return 0;
+
+ DISABLE_INTR (flags);
+
+ if (mbox[dev])
+ printk (" PATMGR: Server %d mbox full. Why?\n", dev);
+ else
+ {
+ if ((mbox[dev] =
+ (struct patmgr_info *) KERNEL_MALLOC (sizeof (struct patmgr_info))) == NULL)
+ {
+ printk ("pmgr: Couldn't allocate memory for a message\n");
+ return 0;
+ }
+
+ mbox[dev]->key = PM_K_EVENT;
+ mbox[dev]->command = event;
+ mbox[dev]->parm1 = p1;
+ mbox[dev]->parm2 = p2;
+ mbox[dev]->parm3 = p3;
+ msg_direction[dev] = A_TO_S;
+
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
+ {
+ WAKE_UP (server_procs[dev], server_wait_flag[dev]);
+ }
+
+ DO_SLEEP (appl_proc, appl_wait_flag, 0);
+ if (mbox[dev])
+ KERNEL_FREE (mbox[dev]);
+ mbox[dev] = NULL;
+ msg_direction[dev] = 0;
+ }
+
+ RESTORE_INTR (flags);
+
+ return err;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/pcm86.c b/sys/pc98/pc98/sound/pcm86.c
new file mode 100644
index 0000000..974bafe
--- /dev/null
+++ b/sys/pc98/pc98/sound/pcm86.c
@@ -0,0 +1,2189 @@
+/*
+ * PC-9801-86 PCM driver for FreeBSD(98).
+ *
+ * Copyright (c) 1995 NAGAO Tadaaki (ABTK)
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR AND 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.
+ *
+ * $Id: pcm86.c,v 2.4 1996/01/24 19:53:34 abtk Exp $
+ */
+
+/*
+ * !! NOTE !! :
+ * This file DOES NOT belong to the VoxWare distribution though it works
+ * as part of the VoxWare drivers. It is FreeBSD(98) original.
+ * -- Nagao (nagao@cs.titech.ac.jp)
+ */
+
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_PCM86) && !defined(EXCLUDE_AUDIO)
+
+
+/*
+ * Constants
+ */
+
+#define YES 1
+#define NO 0
+
+#define IMODE_NONE 0
+#define IMODE_INPUT 1
+#define IMODE_OUTPUT 2
+
+/* PC-9801-86 specific constants */
+#define PCM86_IOBASE 0xa460 /* PCM I/O ports */
+#define PCM86_FIFOSIZE 32768 /* There is a 32kB FIFO buffer on 86-board */
+
+/* XXX -- These values should be chosen appropriately. */
+#define PCM86_INTRSIZE_OUT 1024
+#define PCM86_INTRSIZE_IN (PCM86_FIFOSIZE / 2 - 128)
+#define DEFAULT_VOLUME 15 /* 0(min) - 15(max) */
+
+
+/*
+ * Switches for debugging and experiments
+ */
+
+/* #define PCM86_DEBUG */
+
+#ifdef PCM86_DEBUG
+# ifdef DEB
+# undef DEB
+# endif
+# define DEB(x) x
+#endif
+
+
+/*
+ * Private variables and types
+ */
+
+typedef unsigned char pcm_data;
+
+enum board_type {
+ NO_SUPPORTED_BOARD = 0,
+ PC980186_FAMILY = 1,
+ PC980173_FAMILY = 2
+};
+
+static char *board_name[] = {
+ /* Each must be of the length less than 32 bytes. */
+ "No supported board",
+ "PC-9801-86 soundboard",
+ "PC-9801-73 soundboard"
+};
+
+/* Current status of the driver */
+static struct {
+ int iobase;
+ int irq;
+ enum board_type board_type;
+ int opened;
+ int format;
+ int bytes;
+ int chipspeedno;
+ int chipspeed;
+ int speed;
+ int stereo;
+ int volume;
+ int intr_busy;
+ int intr_size;
+ int intr_mode;
+ int intr_last;
+ int intr_trailer;
+ pcm_data * pdma_buf;
+ int pdma_count;
+ int pdma_chunkcount;
+ int acc;
+ int last_l;
+ int last_r;
+} pcm_s;
+
+static struct {
+ pcm_data buff[4];
+ int size;
+} tmpbuf;
+
+static int my_dev = 0;
+static char pcm_initialized = NO;
+
+/* 86-board supports only the following rates. */
+static int rates_tbl[8] = {
+#ifndef WAVEMASTER_FREQ
+ 44100, 33075, 22050, 16538, 11025, 8269, 5513, 4134
+#else
+ /*
+ * It is said that Q-Vision's WaveMaster of some earlier lot(s?) has
+ * sampling rates incompatible with PC-9801-86.
+ * But I'm not sure whether the following rates are correct, especially
+ * 4000Hz.
+ */
+ 44100, 33075, 22050, 16000, 11025, 8000, 5510, 4000
+#endif
+};
+
+/* u-law to linear translation table */
+static pcm_data ulaw2linear[256] = {
+ 130, 134, 138, 142, 146, 150, 154, 158,
+ 162, 166, 170, 174, 178, 182, 186, 190,
+ 193, 195, 197, 199, 201, 203, 205, 207,
+ 209, 211, 213, 215, 217, 219, 221, 223,
+ 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240,
+ 240, 241, 241, 242, 242, 243, 243, 244,
+ 244, 245, 245, 246, 246, 247, 247, 248,
+ 248, 248, 249, 249, 249, 249, 250, 250,
+ 250, 250, 251, 251, 251, 251, 252, 252,
+ 252, 252, 252, 252, 253, 253, 253, 253,
+ 253, 253, 253, 253, 254, 254, 254, 254,
+ 254, 254, 254, 254, 254, 254, 254, 254,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 125, 121, 117, 113, 109, 105, 101, 97,
+ 93, 89, 85, 81, 77, 73, 69, 65,
+ 62, 60, 58, 56, 54, 52, 50, 48,
+ 46, 44, 42, 40, 38, 36, 34, 32,
+ 30, 29, 28, 27, 26, 25, 24, 23,
+ 22, 21, 20, 19, 18, 17, 16, 15,
+ 15, 14, 14, 13, 13, 12, 12, 11,
+ 11, 10, 10, 9, 9, 8, 8, 7,
+ 7, 7, 6, 6, 6, 6, 5, 5,
+ 5, 5, 4, 4, 4, 4, 3, 3,
+ 3, 3, 3, 3, 2, 2, 2, 2,
+ 2, 2, 2, 2, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* linear to u-law translation table */
+static pcm_data linear2ulaw[256] = {
+ 255, 231, 219, 211, 205, 201, 197, 193,
+ 190, 188, 186, 184, 182, 180, 178, 176,
+ 175, 174, 173, 172, 171, 170, 169, 168,
+ 167, 166, 165, 164, 163, 162, 161, 160,
+ 159, 159, 158, 158, 157, 157, 156, 156,
+ 155, 155, 154, 154, 153, 153, 152, 152,
+ 151, 151, 150, 150, 149, 149, 148, 148,
+ 147, 147, 146, 146, 145, 145, 144, 144,
+ 143, 143, 143, 143, 142, 142, 142, 142,
+ 141, 141, 141, 141, 140, 140, 140, 140,
+ 139, 139, 139, 139, 138, 138, 138, 138,
+ 137, 137, 137, 137, 136, 136, 136, 136,
+ 135, 135, 135, 135, 134, 134, 134, 134,
+ 133, 133, 133, 133, 132, 132, 132, 132,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 129, 129, 129, 129, 128, 128, 128, 128,
+ 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 2, 2, 2, 2, 3, 3, 3,
+ 3, 4, 4, 4, 4, 5, 5, 5,
+ 5, 6, 6, 6, 6, 7, 7, 7,
+ 7, 8, 8, 8, 8, 9, 9, 9,
+ 9, 10, 10, 10, 10, 11, 11, 11,
+ 11, 12, 12, 12, 12, 13, 13, 13,
+ 13, 14, 14, 14, 14, 15, 15, 15,
+ 15, 16, 16, 17, 17, 18, 18, 19,
+ 19, 20, 20, 21, 21, 22, 22, 23,
+ 23, 24, 24, 25, 25, 26, 26, 27,
+ 27, 28, 28, 29, 29, 30, 30, 31,
+ 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 50, 52, 54, 56, 58, 60,
+ 62, 65, 69, 73, 77, 83, 91, 103
+};
+
+
+/*
+ * Prototypes
+ */
+
+static int pcm86_detect(struct address_info *);
+
+static int pcm86_open(int, int);
+static void pcm86_close(int);
+static void pcm86_output_block(int, unsigned long, int, int, int);
+static void pcm86_start_input(int, unsigned long, int, int, int);
+static int pcm86_ioctl(int, unsigned int, unsigned int, int);
+static int pcm86_prepare_for_input(int, int, int);
+static int pcm86_prepare_for_output(int, int, int);
+static void pcm86_reset(int);
+static void pcm86_halt_xfer(int);
+
+static void dsp73_send_command(unsigned char);
+static void dsp73_send_data(unsigned char);
+static void dsp73_init(void);
+static int set_format(int);
+static int set_speed(int);
+static int set_stereo(int);
+static void set_volume(int);
+static void fifo_start(int);
+static void fifo_stop(void);
+static void fifo_reset(void);
+static void fifo_output_block(void);
+static int fifo_send(pcm_data *, int);
+static void fifo_sendtrailer(int);
+static void fifo_send_stereo(pcm_data *, int);
+static void fifo_send_monoral(pcm_data *, int);
+static void fifo_send_stereo_ulaw(pcm_data *, int);
+static void fifo_send_stereo_8(pcm_data *, int, int);
+static void fifo_send_stereo_16le(pcm_data *, int, int);
+static void fifo_send_stereo_16be(pcm_data *, int, int);
+static void fifo_send_mono_ulaw(pcm_data *, int);
+static void fifo_send_mono_8(pcm_data *, int, int);
+static void fifo_send_mono_16le(pcm_data *, int, int);
+static void fifo_send_mono_16be(pcm_data *, int, int);
+static void fifo_input_block(void);
+static void fifo_recv(pcm_data *, int);
+static void fifo_recv_stereo(pcm_data *, int);
+static void fifo_recv_monoral(pcm_data *, int);
+static void fifo_recv_stereo_ulaw(pcm_data *, int);
+static void fifo_recv_stereo_8(pcm_data *, int, int);
+static void fifo_recv_stereo_16le(pcm_data *, int, int);
+static void fifo_recv_stereo_16be(pcm_data *, int, int);
+static void fifo_recv_mono_ulaw(pcm_data *, int);
+static void fifo_recv_mono_8(pcm_data *, int, int);
+static void fifo_recv_mono_16le(pcm_data *, int, int);
+static void fifo_recv_mono_16be(pcm_data *, int, int);
+static void pcm_stop(void);
+static void pcm_init(void);
+
+
+/*
+ * Identity
+ */
+
+static struct audio_operations pcm86_operations =
+{
+ "PC-9801-86 SoundBoard", /* filled in properly by auto configuration */
+ NOTHING_SPECIAL,
+ ( AFMT_MU_LAW |
+ AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE |
+ AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE ),
+ NULL,
+ pcm86_open,
+ pcm86_close,
+ pcm86_output_block,
+ pcm86_start_input,
+ pcm86_ioctl,
+ pcm86_prepare_for_input,
+ pcm86_prepare_for_output,
+ pcm86_reset,
+ pcm86_halt_xfer,
+ NULL,
+ NULL
+};
+
+
+/*
+ * Codes for internal use
+ */
+
+static void
+dsp73_send_command(unsigned char command)
+{
+ /* wait for RDY */
+ while ((inb(pcm_s.iobase + 2) & 0x48) != 8);
+
+ /* command mode */
+ outb(pcm_s.iobase + 2, (inb(pcm_s.iobase + 2) & 0x20) | 3);
+
+ /* wait for RDY */
+ while ((inb(pcm_s.iobase + 2) & 0x48) != 8);
+
+ /* send command */
+ outb(pcm_s.iobase + 4, command);
+}
+
+
+static void
+dsp73_send_data(unsigned char data)
+{
+ /* wait for RDY */
+ while ((inb(pcm_s.iobase + 2) & 0x48) != 8);
+
+ /* data mode */
+ outb(pcm_s.iobase + 2, (inb(pcm_s.iobase + 2) & 0x20) | 0x83);
+
+ /* wait for RDY */
+ while ((inb(pcm_s.iobase + 2) & 0x48) != 8);
+
+ /* send command */
+ outb(pcm_s.iobase + 4, data);
+}
+
+
+static void
+dsp73_init(void)
+{
+ const unsigned char dspinst[15] = {
+ 0x00, 0x00, 0x27,
+ 0x3f, 0xe0, 0x01,
+ 0x00, 0x00, 0x27,
+ 0x36, 0x5a, 0x0d,
+ 0x3e, 0x60, 0x04
+ };
+ unsigned char t;
+ int i;
+
+ /* reset DSP */
+ t = inb(pcm_s.iobase + 2);
+ outb(pcm_s.iobase + 2, (t & 0x80) | 0x23);
+
+ /* mute on */
+ dsp73_send_command(0x04);
+ dsp73_send_data(0x6f);
+ dsp73_send_data(0x3c);
+
+ /* write DSP instructions */
+ dsp73_send_command(0x01);
+ dsp73_send_data(0x00);
+ for (i = 0; i < 16; i++)
+ dsp73_send_data(dspinst[i]);
+
+ /* mute off */
+ dsp73_send_command(0x04);
+ dsp73_send_data(0x6f);
+ dsp73_send_data(0x30);
+
+ /* wait for RDY */
+ while ((inb(pcm_s.iobase + 2) & 0x48) != 8);
+
+ outb(pcm_s.iobase + 2, 3);
+}
+
+
+static int
+set_format(int format)
+{
+ switch (format) {
+ case AFMT_MU_LAW:
+ case AFMT_S8:
+ case AFMT_U8:
+ pcm_s.format = format;
+ pcm_s.bytes = 1; /* 8bit */
+ break;
+ case AFMT_S16_LE:
+ case AFMT_U16_LE:
+ case AFMT_S16_BE:
+ case AFMT_U16_BE:
+ pcm_s.format = format;
+ pcm_s.bytes = 2; /* 16bit */
+ break;
+ case AFMT_QUERY:
+ break;
+ default:
+ return -1;
+ }
+
+ return pcm_s.format;
+}
+
+
+static int
+set_speed(int speed)
+{
+ int i;
+
+ if (speed < 4000) /* Minimum 4000Hz */
+ speed = 4000;
+ if (speed > 44100) /* Maximum 44100Hz */
+ speed = 44100;
+ for (i = 7; i >= 0; i--) {
+ if (speed <= rates_tbl[i]) {
+ pcm_s.chipspeedno = i;
+ pcm_s.chipspeed = rates_tbl[i];
+ break;
+ }
+ }
+ pcm_s.speed = speed;
+
+ return speed;
+}
+
+
+static int
+set_stereo(int stereo)
+{
+ pcm_s.stereo = stereo ? YES : NO;
+
+ return pcm_s.stereo;
+}
+
+
+static void
+set_volume(int volume)
+{
+ if (volume < 0)
+ volume = 0;
+ if (volume > 15)
+ volume = 15;
+ pcm_s.volume = volume;
+
+ outb(pcm_s.iobase + 6, 0xaf - volume); /* D/A -> LINE OUT */
+ outb(0x5f,0);
+ outb(0x5f,0);
+ outb(0x5f,0);
+ outb(0x5f,0);
+ outb(pcm_s.iobase + 6, 0x20); /* FM -> A/D */
+ outb(0x5f,0);
+ outb(0x5f,0);
+ outb(0x5f,0);
+ outb(0x5f,0);
+ outb(pcm_s.iobase + 6, 0x60); /* LINE IN -> A/D */
+ outb(0x5f,0);
+ outb(0x5f,0);
+ outb(0x5f,0);
+ outb(0x5f,0);
+}
+
+
+static void
+fifo_start(int mode)
+{
+ unsigned char tmp;
+
+ /* Set frame length & panpot(LR). */
+ tmp = inb(pcm_s.iobase + 10) & 0x88;
+ outb(pcm_s.iobase + 10, tmp | ((pcm_s.bytes == 1) ? 0x72 : 0x32));
+
+ tmp = pcm_s.chipspeedno;
+ if (mode == IMODE_INPUT)
+ tmp |= 0x40;
+
+ /* Reset intr. flag. */
+ outb(pcm_s.iobase + 8, tmp);
+ outb(pcm_s.iobase + 8, tmp | 0x10);
+
+ /* Enable FIFO intr. */
+ outb(pcm_s.iobase + 8, tmp | 0x30);
+
+ /* Set intr. interval. */
+ outb(pcm_s.iobase + 10, pcm_s.intr_size / 128 - 1);
+
+ /* Start intr. */
+ outb(pcm_s.iobase + 8, tmp | 0xb0);
+}
+
+
+static void
+fifo_stop(void)
+{
+ unsigned char tmp;
+
+ /* Reset intr. flag, and disable FIFO intr. */
+ tmp = inb(pcm_s.iobase + 8) & 0x0f;
+ outb(pcm_s.iobase + 8, tmp);
+}
+
+
+static void
+fifo_reset(void)
+{
+ unsigned char tmp;
+
+ /* Reset FIFO. */
+ tmp = inb(pcm_s.iobase + 8) & 0x77;
+ outb(pcm_s.iobase + 8, tmp | 0x8);
+ outb(pcm_s.iobase + 8, tmp);
+}
+
+
+static void
+fifo_output_block(void)
+{
+ int chunksize, count;
+
+ if (pcm_s.pdma_chunkcount) {
+ /* Update chunksize and then send the next chunk to FIFO. */
+ chunksize = pcm_s.pdma_count / pcm_s.pdma_chunkcount--;
+ count = fifo_send(pcm_s.pdma_buf, chunksize);
+ } else {
+ /* ??? something wrong... */
+ printk("pcm0: chunkcount overrun\n");
+ chunksize = count = 0;
+ }
+
+ if (((audio_devs[my_dev]->dmap->qlen < 2) && (pcm_s.pdma_chunkcount == 0))
+ || (count < pcm_s.intr_size)) {
+ /* The sent chunk seems to be the last one. */
+ fifo_sendtrailer(pcm_s.intr_size);
+ pcm_s.intr_last = YES;
+ }
+
+ pcm_s.pdma_buf += chunksize;
+ pcm_s.pdma_count -= chunksize;
+}
+
+
+static int
+fifo_send(pcm_data *buf, int count)
+{
+ int i, length, r, cnt, rslt;
+ pcm_data *p;
+
+ /* Calculate the length of PCM frames. */
+ cnt = count + tmpbuf.size;
+ length = pcm_s.bytes << pcm_s.stereo;
+ r = cnt % length;
+ cnt -= r;
+
+ if (cnt > 0) {
+ if (pcm_s.stereo)
+ fifo_send_stereo(buf, cnt);
+ else
+ fifo_send_monoral(buf, cnt);
+ /* Carry over extra data which doesn't seem to be a full PCM frame. */
+ p = (pcm_data *)buf + count - r;
+ for (i = 0; i < r; i++)
+ tmpbuf.buff[i] = *p++;
+ } else {
+ /* Carry over extra data which doesn't seem to be a full PCM frame. */
+ p = (pcm_data *)buf;
+ for (i = tmpbuf.size; i < r; i++)
+ tmpbuf.buff[i] = *p++;
+ }
+ tmpbuf.size = r;
+
+ rslt = ((cnt / length) * pcm_s.chipspeed / pcm_s.speed) * pcm_s.bytes * 2;
+#ifdef PCM86_DEBUG
+ printk("fifo_send(): %d bytes sent\n", rslt);
+#endif
+ return rslt;
+}
+
+
+static void
+fifo_sendtrailer(int count)
+{
+ /* Send trailing zeros to the FIFO buffer. */
+ int i;
+
+ for (i = 0; i < count; i++)
+ outb(pcm_s.iobase + 12, 0);
+ pcm_s.intr_trailer = YES;
+
+#ifdef PCM86_DEBUG
+ printk("fifo_sendtrailer(): %d bytes sent\n", count);
+#endif
+}
+
+
+static void
+fifo_send_stereo(pcm_data *buf, int count)
+{
+ /* Convert format and sampling speed. */
+ switch (pcm_s.format) {
+ case AFMT_MU_LAW:
+ fifo_send_stereo_ulaw(buf, count);
+ break;
+ case AFMT_S8:
+ fifo_send_stereo_8(buf, count, NO);
+ break;
+ case AFMT_U8:
+ fifo_send_stereo_8(buf, count, YES);
+ break;
+ case AFMT_S16_LE:
+ fifo_send_stereo_16le(buf, count, NO);
+ break;
+ case AFMT_U16_LE:
+ fifo_send_stereo_16le(buf, count, YES);
+ break;
+ case AFMT_S16_BE:
+ fifo_send_stereo_16be(buf, count, NO);
+ break;
+ case AFMT_U16_BE:
+ fifo_send_stereo_16be(buf, count, YES);
+ break;
+ }
+}
+
+
+static void
+fifo_send_monoral(pcm_data *buf, int count)
+{
+ /* Convert format and sampling speed. */
+ switch (pcm_s.format) {
+ case AFMT_MU_LAW:
+ fifo_send_mono_ulaw(buf, count);
+ break;
+ case AFMT_S8:
+ fifo_send_mono_8(buf, count, NO);
+ break;
+ case AFMT_U8:
+ fifo_send_mono_8(buf, count, YES);
+ break;
+ case AFMT_S16_LE:
+ fifo_send_mono_16le(buf, count, NO);
+ break;
+ case AFMT_U16_LE:
+ fifo_send_mono_16le(buf, count, YES);
+ break;
+ case AFMT_S16_BE:
+ fifo_send_mono_16be(buf, count, NO);
+ break;
+ case AFMT_U16_BE:
+ fifo_send_mono_16be(buf, count, YES);
+ break;
+ }
+}
+
+
+static void
+fifo_send_stereo_ulaw(pcm_data *buf, int count)
+{
+ int i;
+ signed char dl, dl0, dl1, dr, dr0, dr1;
+ pcm_data t[2];
+
+ if (tmpbuf.size > 0)
+ t[0] = ulaw2linear[tmpbuf.buff[0]];
+ else
+ t[0] = ulaw2linear[*buf++];
+ t[1] = ulaw2linear[*buf++];
+
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ outb(pcm_s.iobase + 12, t[0]);
+ outb(pcm_s.iobase + 12, t[1]);
+ count -= 2;
+ for (i = 0; i < count; i++)
+ outb(pcm_s.iobase + 12, ulaw2linear[*buf++]);
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ dl0 = pcm_s.last_l;
+ dr0 = pcm_s.last_r;
+ dl1 = t[0];
+ dr1 = t[1];
+ i = 0;
+ count /= 2;
+ while (i < count) {
+ while (pcm_s.acc >= pcm_s.chipspeed) {
+ pcm_s.acc -= pcm_s.chipspeed;
+ i++;
+ dl0 = dl1;
+ dr0 = dr1;
+ if (i < count) {
+ dl1 = ulaw2linear[*buf++];
+ dr1 = ulaw2linear[*buf++];
+ } else
+ dl1 = dr1 = 0;
+ }
+ dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ outb(pcm_s.iobase + 12, dl);
+ outb(pcm_s.iobase + 12, dr);
+ pcm_s.acc += pcm_s.speed;
+ }
+
+ pcm_s.last_l = dl0;
+ pcm_s.last_r = dr0;
+ }
+}
+
+
+static void
+fifo_send_stereo_8(pcm_data *buf, int count, int uflag)
+{
+ int i;
+ signed char dl, dl0, dl1, dr, dr0, dr1, zlev;
+ pcm_data t[2];
+
+ zlev = uflag ? -128 : 0;
+
+ if (tmpbuf.size > 0)
+ t[0] = tmpbuf.buff[0] + zlev;
+ else
+ t[0] = *buf++ + zlev;
+ t[1] = *buf++ + zlev;
+
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ outb(pcm_s.iobase + 12, t[0]);
+ outb(pcm_s.iobase + 12, t[1]);
+ count -= 2;
+ for (i = 0; i < count; i++)
+ outb(pcm_s.iobase + 12, *buf++ + zlev);
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ dl0 = pcm_s.last_l;
+ dr0 = pcm_s.last_r;
+ dl1 = t[0];
+ dr1 = t[1];
+ i = 0;
+ count /= 2;
+ while (i < count) {
+ while (pcm_s.acc >= pcm_s.chipspeed) {
+ pcm_s.acc -= pcm_s.chipspeed;
+ i++;
+ dl0 = dl1;
+ dr0 = dr1;
+ if (i < count) {
+ dl1 = *buf++ + zlev;
+ dr1 = *buf++ + zlev;
+ } else
+ dl1 = dr1 = 0;
+ }
+ dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ outb(pcm_s.iobase + 12, dl);
+ outb(pcm_s.iobase + 12, dr);
+ pcm_s.acc += pcm_s.speed;
+ }
+
+ pcm_s.last_l = dl0;
+ pcm_s.last_r = dr0;
+ }
+}
+
+
+static void
+fifo_send_stereo_16le(pcm_data *buf, int count, int uflag)
+{
+ int i;
+ short dl, dl0, dl1, dr, dr0, dr1, zlev;
+ pcm_data t[4];
+
+ zlev = uflag ? -128 : 0;
+
+ for (i = 0; i < 4; i++)
+ t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++;
+
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ outb(pcm_s.iobase + 12, t[1] + zlev);
+ outb(pcm_s.iobase + 12, t[0]);
+ outb(pcm_s.iobase + 12, t[3] + zlev);
+ outb(pcm_s.iobase + 12, t[2]);
+ count = count / 2 - 2;
+ for (i = 0; i < count; i++) {
+ outb(pcm_s.iobase + 12, *(buf + 1) + zlev);
+ outb(pcm_s.iobase + 12, *buf);
+ buf += 2;
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ dl0 = pcm_s.last_l;
+ dr0 = pcm_s.last_r;
+ dl1 = t[0] + ((t[1] + zlev) << 8);
+ dr1 = t[2] + ((t[3] + zlev) << 8);
+ i = 0;
+ count /= 4;
+ while (i < count) {
+ while (pcm_s.acc >= pcm_s.chipspeed) {
+ pcm_s.acc -= pcm_s.chipspeed;
+ i++;
+ dl0 = dl1;
+ dr0 = dr1;
+ if (i < count) {
+ dl1 = *buf + ((*(buf + 1) + zlev) << 8);
+ buf += 2;
+ dr1 = *buf + ((*(buf + 1) + zlev) << 8);
+ buf += 2;
+ } else
+ dl1 = dr1 = 0;
+ }
+ dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ outb(pcm_s.iobase + 12, (dl >> 8) & 0xff);
+ outb(pcm_s.iobase + 12, dl & 0xff);
+ outb(pcm_s.iobase + 12, (dr >> 8) & 0xff);
+ outb(pcm_s.iobase + 12, dr & 0xff);
+ pcm_s.acc += pcm_s.speed;
+ }
+
+ pcm_s.last_l = dl0;
+ pcm_s.last_r = dr0;
+ }
+}
+
+
+static void
+fifo_send_stereo_16be(pcm_data *buf, int count, int uflag)
+{
+ int i;
+ short dl, dl0, dl1, dr, dr0, dr1, zlev;
+ pcm_data t[4];
+
+ zlev = uflag ? -128 : 0;
+
+ for (i = 0; i < 4; i++)
+ t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++;
+
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ outb(pcm_s.iobase + 12, t[0] + zlev);
+ outb(pcm_s.iobase + 12, t[1]);
+ outb(pcm_s.iobase + 12, t[2] + zlev);
+ outb(pcm_s.iobase + 12, t[3]);
+ count = count / 2 - 2;
+ for (i = 0; i < count; i++) {
+ outb(pcm_s.iobase + 12, *buf + zlev);
+ outb(pcm_s.iobase + 12, *(buf + 1));
+ buf += 2;
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ dl0 = pcm_s.last_l;
+ dr0 = pcm_s.last_r;
+ dl1 = ((t[0] + zlev) << 8) + t[1];
+ dr1 = ((t[2] + zlev) << 8) + t[3];
+ i = 0;
+ count /= 4;
+ while (i < count) {
+ while (pcm_s.acc >= pcm_s.chipspeed) {
+ pcm_s.acc -= pcm_s.chipspeed;
+ i++;
+ dl0 = dl1;
+ dr0 = dr1;
+ if (i < count) {
+ dl1 = ((*buf + zlev) << 8) + *(buf + 1);
+ buf += 2;
+ dr1 = ((*buf + zlev) << 8) + *(buf + 1);
+ buf += 2;
+ } else
+ dl1 = dr1 = 0;
+ }
+ dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ outb(pcm_s.iobase + 12, (dl >> 8) & 0xff);
+ outb(pcm_s.iobase + 12, dl & 0xff);
+ outb(pcm_s.iobase + 12, (dr >> 8) & 0xff);
+ outb(pcm_s.iobase + 12, dr & 0xff);
+ pcm_s.acc += pcm_s.speed;
+ }
+
+ pcm_s.last_l = dl0;
+ pcm_s.last_r = dr0;
+ }
+}
+
+
+static void
+fifo_send_mono_ulaw(pcm_data *buf, int count)
+{
+ int i;
+ signed char d, d0, d1;
+
+ if (pcm_s.speed == pcm_s.chipspeed)
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < count; i++) {
+ d = ulaw2linear[*buf++];
+ outb(pcm_s.iobase + 12, d);
+ outb(pcm_s.iobase + 12, d);
+ }
+ else {
+ /* Speed conversion with linear interpolation method. */
+ d0 = pcm_s.last_l;
+ d1 = ulaw2linear[*buf++];
+ i = 0;
+ while (i < count) {
+ while (pcm_s.acc >= pcm_s.chipspeed) {
+ pcm_s.acc -= pcm_s.chipspeed;
+ i++;
+ d0 = d1;
+ d1 = (i < count) ? ulaw2linear[*buf++] : 0;
+ }
+ d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ outb(pcm_s.iobase + 12, d);
+ outb(pcm_s.iobase + 12, d);
+ pcm_s.acc += pcm_s.speed;
+ }
+
+ pcm_s.last_l = d0;
+ }
+}
+
+
+static void
+fifo_send_mono_8(pcm_data *buf, int count, int uflag)
+{
+ int i;
+ signed char d, d0, d1, zlev;
+
+ zlev = uflag ? -128 : 0;
+
+ if (pcm_s.speed == pcm_s.chipspeed)
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < count; i++) {
+ d = *buf++ + zlev;
+ outb(pcm_s.iobase + 12, d);
+ outb(pcm_s.iobase + 12, d);
+ }
+ else {
+ /* Speed conversion with linear interpolation method. */
+ d0 = pcm_s.last_l;
+ d1 = *buf++ + zlev;
+ i = 0;
+ while (i < count) {
+ while (pcm_s.acc >= pcm_s.chipspeed) {
+ pcm_s.acc -= pcm_s.chipspeed;
+ i++;
+ d0 = d1;
+ d1 = (i < count) ? *buf++ + zlev : 0;
+ }
+ d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ outb(pcm_s.iobase + 12, d);
+ outb(pcm_s.iobase + 12, d);
+ pcm_s.acc += pcm_s.speed;
+ }
+
+ pcm_s.last_l = d0;
+ }
+}
+
+
+static void
+fifo_send_mono_16le(pcm_data *buf, int count, int uflag)
+{
+ int i;
+ short d, d0, d1, zlev;
+ pcm_data t[2];
+
+ zlev = uflag ? -128 : 0;
+
+ for (i = 0; i < 2; i++)
+ t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++;
+
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ outb(pcm_s.iobase + 12, t[1] + zlev);
+ outb(pcm_s.iobase + 12, t[0]);
+ outb(pcm_s.iobase + 12, t[1] + zlev);
+ outb(pcm_s.iobase + 12, t[0]);
+ count = count / 2 - 1;
+ for (i = 0; i < count; i++) {
+ outb(pcm_s.iobase + 12, *(buf + 1) + zlev);
+ outb(pcm_s.iobase + 12, *buf);
+ outb(pcm_s.iobase + 12, *(buf + 1) + zlev);
+ outb(pcm_s.iobase + 12, *buf);
+ buf += 2;
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ d0 = pcm_s.last_l;
+ d1 = t[0] + ((t[1] + zlev) << 8);
+ i = 0;
+ count /= 2;
+ while (i < count) {
+ while (pcm_s.acc >= pcm_s.chipspeed) {
+ pcm_s.acc -= pcm_s.chipspeed;
+ i++;
+ d0 = d1;
+ if (i < count) {
+ d1 = *buf + ((*(buf + 1) + zlev) << 8);
+ buf += 2;
+ } else
+ d1 = 0;
+ }
+ d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ outb(pcm_s.iobase + 12, (d >> 8) & 0xff);
+ outb(pcm_s.iobase + 12, d & 0xff);
+ outb(pcm_s.iobase + 12, (d >> 8) & 0xff);
+ outb(pcm_s.iobase + 12, d & 0xff);
+ pcm_s.acc += pcm_s.speed;
+ }
+
+ pcm_s.last_l = d0;
+ }
+}
+
+
+static void
+fifo_send_mono_16be(pcm_data *buf, int count, int uflag)
+{
+ int i;
+ short d, d0, d1, zlev;
+ pcm_data t[2];
+
+ zlev = uflag ? -128 : 0;
+
+ for (i = 0; i < 2; i++)
+ t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++;
+
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ outb(pcm_s.iobase + 12, t[0] + zlev);
+ outb(pcm_s.iobase + 12, t[1]);
+ outb(pcm_s.iobase + 12, t[0] + zlev);
+ outb(pcm_s.iobase + 12, t[1]);
+ count = count / 2 - 1;
+ for (i = 0; i < count; i++) {
+ outb(pcm_s.iobase + 12, *buf + zlev);
+ outb(pcm_s.iobase + 12, *(buf + 1));
+ outb(pcm_s.iobase + 12, *buf + zlev);
+ outb(pcm_s.iobase + 12, *(buf + 1));
+ buf += 2;
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ d0 = pcm_s.last_l;
+ d1 = ((t[0] + zlev) << 8) + t[1];
+ i = 0;
+ count /= 2;
+ while (i < count) {
+ while (pcm_s.acc >= pcm_s.chipspeed) {
+ pcm_s.acc -= pcm_s.chipspeed;
+ i++;
+ d0 = d1;
+ if (i < count) {
+ d1 = ((*buf + zlev) << 8) + *(buf + 1);
+ buf += 2;
+ } else
+ d1 = 0;
+ }
+ d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.chipspeed;
+ outb(pcm_s.iobase + 12, d & 0xff);
+ outb(pcm_s.iobase + 12, (d >> 8) & 0xff);
+ outb(pcm_s.iobase + 12, d & 0xff);
+ outb(pcm_s.iobase + 12, (d >> 8) & 0xff);
+ pcm_s.acc += pcm_s.speed;
+ }
+
+ pcm_s.last_l = d0;
+ }
+}
+
+
+static void
+fifo_input_block(void)
+{
+ int chunksize;
+
+ if (pcm_s.pdma_chunkcount) {
+ /* Update chunksize and then receive the next chunk from FIFO. */
+ chunksize = pcm_s.pdma_count / pcm_s.pdma_chunkcount--;
+ fifo_recv(pcm_s.pdma_buf, chunksize);
+ pcm_s.pdma_buf += chunksize;
+ pcm_s.pdma_count -= chunksize;
+ } else
+ /* ??? something wrong... */
+ printk("pcm0: chunkcount overrun\n");
+}
+
+
+static void
+fifo_recv(pcm_data *buf, int count)
+{
+ int i;
+
+ if (count > tmpbuf.size) {
+ for (i = 0; i < tmpbuf.size; i++)
+ *buf++ = tmpbuf.buff[i];
+ count -= tmpbuf.size;
+ tmpbuf.size = 0;
+ if (pcm_s.stereo)
+ fifo_recv_stereo(buf, count);
+ else
+ fifo_recv_monoral(buf, count);
+ } else {
+ for (i = 0; i < count; i++)
+ *buf++ = tmpbuf.buff[i];
+ for (i = 0; i < tmpbuf.size - count; i++)
+ tmpbuf.buff[i] = tmpbuf.buff[i + count];
+ tmpbuf.size -= count;
+ }
+
+#ifdef PCM86_DEBUG
+ printk("fifo_recv(): %d bytes received\n",
+ ((count / (pcm_s.bytes << pcm_s.stereo)) * pcm_s.chipspeed
+ / pcm_s.speed) * pcm_s.bytes * 2);
+#endif
+}
+
+
+static void
+fifo_recv_stereo(pcm_data *buf, int count)
+{
+ /* Convert format and sampling speed. */
+ switch (pcm_s.format) {
+ case AFMT_MU_LAW:
+ fifo_recv_stereo_ulaw(buf, count);
+ break;
+ case AFMT_S8:
+ fifo_recv_stereo_8(buf, count, NO);
+ break;
+ case AFMT_U8:
+ fifo_recv_stereo_8(buf, count, YES);
+ break;
+ case AFMT_S16_LE:
+ fifo_recv_stereo_16le(buf, count, NO);
+ break;
+ case AFMT_U16_LE:
+ fifo_recv_stereo_16le(buf, count, YES);
+ break;
+ case AFMT_S16_BE:
+ fifo_recv_stereo_16be(buf, count, NO);
+ break;
+ case AFMT_U16_BE:
+ fifo_recv_stereo_16be(buf, count, YES);
+ break;
+ }
+}
+
+
+static void
+fifo_recv_monoral(pcm_data *buf, int count)
+{
+ /* Convert format and sampling speed. */
+ switch (pcm_s.format) {
+ case AFMT_MU_LAW:
+ fifo_recv_mono_ulaw(buf, count);
+ break;
+ case AFMT_S8:
+ fifo_recv_mono_8(buf, count, NO);
+ break;
+ case AFMT_U8:
+ fifo_recv_mono_8(buf, count, YES);
+ break;
+ case AFMT_S16_LE:
+ fifo_recv_mono_16le(buf, count, NO);
+ break;
+ case AFMT_U16_LE:
+ fifo_recv_mono_16le(buf, count, YES);
+ break;
+ case AFMT_S16_BE:
+ fifo_recv_mono_16be(buf, count, NO);
+ break;
+ case AFMT_U16_BE:
+ fifo_recv_mono_16be(buf, count, YES);
+ break;
+ }
+}
+
+
+static void
+fifo_recv_stereo_ulaw(pcm_data *buf, int count)
+{
+ int i, cnt;
+ signed char dl, dl0, dl1, dr, dr0, dr1;
+
+ cnt = count / 2;
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < cnt; i++) {
+ *buf++ = linear2ulaw[inb(pcm_s.iobase + 12)];
+ *buf++ = linear2ulaw[inb(pcm_s.iobase + 12)];
+ }
+ if (count % 2) {
+ *buf++ = linear2ulaw[inb(pcm_s.iobase + 12)];
+ tmpbuf.buff[0] = linear2ulaw[inb(pcm_s.iobase + 12)];
+ tmpbuf.size = 1;
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ dl0 = pcm_s.last_l;
+ dr0 = pcm_s.last_r;
+ dl1 = inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12);
+ for (i = 0; i < cnt; i++) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ dl0 = dl1;
+ dr0 = dr1;
+ dl1 = inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12);
+ }
+ dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.speed;
+ dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = linear2ulaw[dl & 0xff];
+ *buf++ = linear2ulaw[dr & 0xff];
+ pcm_s.acc += pcm_s.chipspeed;
+ }
+ if (count % 2) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ dl0 = dl1;
+ dr0 = dr1;
+ dl1 = inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12);
+ }
+ dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.speed;
+ dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = linear2ulaw[dl & 0xff];
+ tmpbuf.buff[0] = linear2ulaw[dr & 0xff];
+ tmpbuf.size = 1;
+ }
+
+ pcm_s.last_l = dl0;
+ pcm_s.last_r = dr0;
+ }
+}
+
+
+static void
+fifo_recv_stereo_8(pcm_data *buf, int count, int uflag)
+{
+ int i, cnt;
+ signed char dl, dl0, dl1, dr, dr0, dr1, zlev;
+
+ zlev = uflag ? -128 : 0;
+
+ cnt = count / 2;
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < cnt; i++) {
+ *buf++ = inb(pcm_s.iobase + 12) + zlev;
+ *buf++ = inb(pcm_s.iobase + 12) + zlev;
+ }
+ if (count % 2) {
+ *buf++ = inb(pcm_s.iobase + 12) + zlev;
+ tmpbuf.buff[0] = inb(pcm_s.iobase + 12) + zlev;
+ tmpbuf.size = 1;
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ dl0 = pcm_s.last_l;
+ dr0 = pcm_s.last_r;
+ dl1 = inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12);
+ for (i = 0; i < cnt; i++) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ dl0 = dl1;
+ dr0 = dr1;
+ dl1 = inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12);
+ }
+ dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.speed;
+ dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = dl + zlev;
+ *buf++ = dr + zlev;
+ pcm_s.acc += pcm_s.chipspeed;
+ }
+ if (count % 2) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ dl0 = dl1;
+ dr0 = dr1;
+ dl1 = inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12);
+ }
+ dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.speed;
+ dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = dl + zlev;
+ tmpbuf.buff[0] = dr + zlev;
+ tmpbuf.size = 1;
+ }
+
+ pcm_s.last_l = dl0;
+ pcm_s.last_r = dr0;
+ }
+}
+
+
+static void
+fifo_recv_stereo_16le(pcm_data *buf, int count, int uflag)
+{
+ int i, cnt;
+ short dl, dl0, dl1, dr, dr0, dr1, zlev;
+ pcm_data t[4];
+
+ zlev = uflag ? -128 : 0;
+
+ cnt = count / 4;
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < cnt; i++) {
+ *(buf + 1) = inb(pcm_s.iobase + 12) + zlev;
+ *buf = inb(pcm_s.iobase + 12);
+ *(buf + 3) = inb(pcm_s.iobase + 12) + zlev;
+ *(buf + 2) = inb(pcm_s.iobase + 12);
+ buf += 4;
+ }
+ if (count % 4) {
+ t[1] = inb(pcm_s.iobase + 12) + zlev;
+ t[0] = inb(pcm_s.iobase + 12);
+ t[3] = inb(pcm_s.iobase + 12) + zlev;
+ t[2] = inb(pcm_s.iobase + 12);
+ tmpbuf.size = 0;
+ for (i = 0; i < count % 4; i++)
+ *buf++ = t[i];
+ for (i = count % 4; i < 4; i++)
+ tmpbuf.buff[tmpbuf.size++] = t[i];
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ dl0 = pcm_s.last_l;
+ dr0 = pcm_s.last_r;
+ dl1 = inb(pcm_s.iobase + 12) << 8;
+ dl1 |= inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12) << 8;
+ dr1 |= inb(pcm_s.iobase + 12);
+ for (i = 0; i < cnt; i++) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ dl0 = dl1;
+ dr0 = dr1;
+ dl1 = inb(pcm_s.iobase + 12) << 8;
+ dl1 |= inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12) << 8;
+ dr1 |= inb(pcm_s.iobase + 12);
+ }
+ dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.speed;
+ dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = dl & 0xff;
+ *buf++ = ((dl >> 8) & 0xff) + zlev;
+ *buf++ = dr & 0xff;
+ *buf++ = ((dr >> 8) & 0xff) + zlev;
+ pcm_s.acc += pcm_s.chipspeed;
+ }
+ if (count % 4) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ dl0 = dl1;
+ dr0 = dr1;
+ dl1 = inb(pcm_s.iobase + 12) << 8;
+ dl1 |= inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12) << 8;
+ dr1 |= inb(pcm_s.iobase + 12);
+ }
+ dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.speed;
+ dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.speed;
+ t[0] = dl & 0xff;
+ t[1] = ((dl >> 8) & 0xff) + zlev;
+ t[2] = dr & 0xff;
+ t[3] = ((dr >> 8) & 0xff) + zlev;
+ tmpbuf.size = 0;
+ for (i = 0; i < count % 4; i++)
+ *buf++ = t[i];
+ for (i = count % 4; i < 4; i++)
+ tmpbuf.buff[tmpbuf.size++] = t[i];
+ }
+
+ pcm_s.last_l = dl0;
+ pcm_s.last_r = dr0;
+ }
+}
+
+
+static void
+fifo_recv_stereo_16be(pcm_data *buf, int count, int uflag)
+{
+ int i, cnt;
+ short dl, dl0, dl1, dr, dr0, dr1, zlev;
+ pcm_data t[4];
+
+ zlev = uflag ? -128 : 0;
+
+ cnt = count / 4;
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < cnt; i++) {
+ *buf++ = inb(pcm_s.iobase + 12) + zlev;
+ *buf++ = inb(pcm_s.iobase + 12);
+ *buf++ = inb(pcm_s.iobase + 12) + zlev;
+ *buf++ = inb(pcm_s.iobase + 12);
+ }
+ if (count % 4) {
+ t[0] = inb(pcm_s.iobase + 12) + zlev;
+ t[1] = inb(pcm_s.iobase + 12);
+ t[2] = inb(pcm_s.iobase + 12) + zlev;
+ t[3] = inb(pcm_s.iobase + 12);
+ tmpbuf.size = 0;
+ for (i = 0; i < count % 4; i++)
+ *buf++ = t[i];
+ for (i = count % 4; i < 4; i++)
+ tmpbuf.buff[tmpbuf.size++] = t[i];
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ dl0 = pcm_s.last_l;
+ dr0 = pcm_s.last_r;
+ dl1 = inb(pcm_s.iobase + 12) << 8;
+ dl1 |= inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12) << 8;
+ dr1 |= inb(pcm_s.iobase + 12);
+ for (i = 0; i < cnt; i++) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ dl0 = dl1;
+ dr0 = dr1;
+ dl1 = inb(pcm_s.iobase + 12) << 8;
+ dl1 |= inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12) << 8;
+ dr1 |= inb(pcm_s.iobase + 12);
+ }
+ dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.speed;
+ dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = ((dl >> 8) & 0xff) + zlev;
+ *buf++ = dl & 0xff;
+ *buf++ = ((dr >> 8) & 0xff) + zlev;
+ *buf++ = dr & 0xff;
+ pcm_s.acc += pcm_s.chipspeed;
+ }
+ if (count % 4) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ dl0 = dl1;
+ dr0 = dr1;
+ dl1 = inb(pcm_s.iobase + 12) << 8;
+ dl1 |= inb(pcm_s.iobase + 12);
+ dr1 = inb(pcm_s.iobase + 12) << 8;
+ dr1 |= inb(pcm_s.iobase + 12);
+ }
+ dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc))
+ / pcm_s.speed;
+ dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc))
+ / pcm_s.speed;
+ t[0] = ((dl >> 8) & 0xff) + zlev;
+ t[1] = dl & 0xff;
+ t[2] = ((dr >> 8) & 0xff) + zlev;
+ t[3] = dr & 0xff;
+ tmpbuf.size = 0;
+ for (i = 0; i < count % 4; i++)
+ *buf++ = t[i];
+ for (i = count % 4; i < 4; i++)
+ tmpbuf.buff[tmpbuf.size++] = t[i];
+ }
+
+ pcm_s.last_l = dl0;
+ pcm_s.last_r = dr0;
+ }
+}
+
+
+static void
+fifo_recv_mono_ulaw(pcm_data *buf, int count)
+{
+ int i;
+ signed char d, d0, d1;
+
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < count; i++) {
+ d = ((signed char)inb(pcm_s.iobase + 12)
+ + (signed char)inb(pcm_s.iobase + 12)) >> 1;
+ *buf++ = linear2ulaw[d & 0xff];
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ d0 = pcm_s.last_l;
+ d1 = ((signed char)inb(pcm_s.iobase + 12)
+ + (signed char)inb(pcm_s.iobase + 12)) >> 1;
+ for (i = 0; i < count; i++) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ d0 = d1;
+ d1 = ((signed char)inb(pcm_s.iobase + 12)
+ + (signed char)inb(pcm_s.iobase + 12)) >> 1;
+ }
+ d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = linear2ulaw[d & 0xff];
+ pcm_s.acc += pcm_s.chipspeed;
+ }
+
+ pcm_s.last_l = d0;
+ }
+}
+
+
+static void
+fifo_recv_mono_8(pcm_data *buf, int count, int uflag)
+{
+ int i;
+ signed char d, d0, d1, zlev;
+
+ zlev = uflag ? -128 : 0;
+
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < count; i++) {
+ d = ((signed char)inb(pcm_s.iobase + 12)
+ + (signed char)inb(pcm_s.iobase + 12)) >> 1;
+ *buf++ = d + zlev;
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ d0 = pcm_s.last_l;
+ d1 = ((signed char)inb(pcm_s.iobase + 12)
+ + (signed char)inb(pcm_s.iobase + 12)) >> 1;
+ for (i = 0; i < count; i++) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ d0 = d1;
+ d1 = ((signed char)inb(pcm_s.iobase + 12)
+ + (signed char)inb(pcm_s.iobase + 12)) >> 1;
+ }
+ d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = d + zlev;
+ pcm_s.acc += pcm_s.chipspeed;
+ }
+
+ pcm_s.last_l = d0;
+ }
+}
+
+
+static void
+fifo_recv_mono_16le(pcm_data *buf, int count, int uflag)
+{
+ int i, cnt;
+ short d, d0, d1, el, er, zlev;
+
+ zlev = uflag ? -128 : 0;
+
+ cnt = count / 2;
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < cnt; i++) {
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d = (el + er) >> 1;
+ *buf++ = d & 0xff;
+ *buf++ = ((d >> 8) & 0xff) + zlev;
+ }
+ if (count % 2) {
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d = (el + er) >> 1;
+ *buf++ = d & 0xff;
+ tmpbuf.buff[0] = ((d >> 8) & 0xff) + zlev;
+ tmpbuf.size = 1;
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ d0 = pcm_s.last_l;
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d1 = (el + er) >> 1;
+ for (i = 0; i < cnt; i++) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ d0 = d1;
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d1 = (el + er) >> 1;
+ }
+ d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = d & 0xff;
+ *buf++ = ((d >> 8) & 0xff) + zlev;
+ pcm_s.acc += pcm_s.chipspeed;
+ }
+ if (count % 2) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ d0 = d1;
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d1 = (el + er) >> 1;
+ }
+ d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = d & 0xff;
+ tmpbuf.buff[0] = ((d >> 8) & 0xff) + zlev;
+ tmpbuf.size = 1;
+ }
+
+ pcm_s.last_l = d0;
+ }
+}
+
+
+static void
+fifo_recv_mono_16be(pcm_data *buf, int count, int uflag)
+{
+ int i, cnt;
+ short d, d0, d1, el, er, zlev;
+
+ zlev = uflag ? -128 : 0;
+
+ cnt = count / 2;
+ if (pcm_s.speed == pcm_s.chipspeed) {
+ /* No reason to convert the pcm speed. */
+ for (i = 0; i < cnt; i++) {
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d = (el + er) >> 1;
+ *buf++ = ((d >> 8) & 0xff) + zlev;
+ *buf++ = d & 0xff;
+ }
+ if (count % 2) {
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d = (el + er) >> 1;
+ *buf++ = ((d >> 8) & 0xff) + zlev;
+ tmpbuf.buff[0] = d & 0xff;
+ tmpbuf.size = 1;
+ }
+ } else {
+ /* Speed conversion with linear interpolation method. */
+ d0 = pcm_s.last_l;
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d1 = (el + er) >> 1;
+ for (i = 0; i < cnt; i++) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ d0 = d1;
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d1 = (el + er) >> 1;
+ }
+ d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = ((d >> 8) & 0xff) + zlev;
+ *buf++ = d & 0xff;
+ pcm_s.acc += pcm_s.chipspeed;
+ }
+ if (count % 2) {
+ while (pcm_s.acc >= pcm_s.speed) {
+ pcm_s.acc -= pcm_s.speed;
+ d0 = d1;
+ el = inb(pcm_s.iobase + 12) << 8;
+ el |= inb(pcm_s.iobase + 12);
+ er = inb(pcm_s.iobase + 12) << 8;
+ er |= inb(pcm_s.iobase + 12);
+ d1 = (el + er) >> 1;
+ }
+ d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc))
+ / pcm_s.speed;
+ *buf++ = ((d >> 8) & 0xff) + zlev;
+ tmpbuf.buff[0] = d & 0xff;
+ tmpbuf.size = 1;
+ }
+
+ pcm_s.last_l = d0;
+ }
+}
+
+
+static void
+pcm_stop(void)
+{
+ fifo_stop(); /* stop FIFO */
+ fifo_reset(); /* reset FIFO buffer */
+
+ /* Reset driver's status. */
+ pcm_s.intr_busy = NO;
+ pcm_s.intr_last = NO;
+ pcm_s.intr_trailer = NO;
+ pcm_s.acc = 0;
+ pcm_s.last_l = 0;
+ pcm_s.last_r = 0;
+
+ DEB(printk("pcm_stop\n"));
+}
+
+
+static void
+pcm_init(void)
+{
+ /* Initialize registers on the board. */
+ pcm_stop();
+ if (pcm_s.board_type == PC980173_FAMILY)
+ dsp73_init();
+
+ /* Set default volume. */
+ set_volume(DEFAULT_VOLUME);
+
+ /* Initialize driver's status. */
+ pcm_s.opened = NO;
+ pcm_initialized = YES;
+}
+
+
+/*
+ * Codes for global use
+ */
+
+int
+probe_pcm86(struct address_info *hw_config)
+{
+ return pcm86_detect(hw_config);
+}
+
+
+long
+attach_pcm86(long mem_start, struct address_info *hw_config)
+{
+ if (pcm_s.board_type == NO_SUPPORTED_BOARD)
+ return mem_start;
+
+ /* Initialize the board. */
+ pcm_init();
+
+ printk("pcm0: <%s>", pcm86_operations.name);
+
+ if (num_audiodevs < MAX_AUDIO_DEV) {
+ my_dev = num_audiodevs++;
+ audio_devs[my_dev] = &pcm86_operations;
+ audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT;
+ audio_devs[my_dev]->buffsize = DSP_BUFFSIZE;
+#ifdef PCM86_DEBUG
+ printk("\nbuffsize = %d", DSP_BUFFSIZE);
+#endif
+ } else
+ printk("pcm0: Too many PCM devices available");
+
+ return mem_start;
+}
+
+
+static int
+pcm86_detect(struct address_info *hw_config)
+{
+ int opna_iobase = 0x188, irq = 12, i;
+ unsigned char tmp;
+
+ if (hw_config->io_base == -1) {
+ printf("pcm0: iobase not specified. Assume default port(0x%x)\n",
+ PCM86_IOBASE);
+ hw_config->io_base = PCM86_IOBASE;
+ }
+ pcm_s.iobase = hw_config->io_base;
+
+ /* auto configuration */
+ tmp = inb(pcm_s.iobase) & 0xfc;
+ switch ((tmp & 0xf0) >> 4) {
+ case 2:
+ opna_iobase = 0x188;
+ pcm_s.board_type = PC980173_FAMILY;
+ break;
+ case 3:
+ opna_iobase = 0x288;
+ pcm_s.board_type = PC980173_FAMILY;
+ break;
+ case 4:
+ opna_iobase = 0x188;
+ pcm_s.board_type = PC980186_FAMILY;
+ break;
+ case 5:
+ opna_iobase = 0x288;
+ pcm_s.board_type = PC980186_FAMILY;
+ break;
+ default:
+ pcm_s.board_type = NO_SUPPORTED_BOARD;
+ return NO;
+ }
+
+ /* Enable OPNA(YM2608) facilities. */
+ outb(pcm_s.iobase, tmp | 0x01);
+
+ /* Wait for OPNA to be ready. */
+ i = 100000; /* Some large value */
+ while((inb(opna_iobase) & 0x80) && (i-- > 0));
+
+ /* Make IOA/IOB port ready (IOA:input, IOB:output) */
+ outb(opna_iobase, 0x07);
+ outb(0x5f, 0); /* Because OPNA ports are comparatively slow(?), */
+ outb(0x5f, 0); /* we'd better wait a moment. */
+ outb(0x5f, 0);
+ outb(0x5f, 0);
+ tmp = inb(opna_iobase + 2) & 0x3f;
+ outb(opna_iobase + 2, tmp | 0x80);
+
+ /* Wait for OPNA to be ready. */
+ i = 100000; /* Some large value */
+ while((inb(opna_iobase) & 0x80) && (i-- > 0));
+
+ /* Get irq number from IOA port. */
+ outb(opna_iobase, 0x0e);
+ outb(0x5f, 0);
+ outb(0x5f, 0);
+ outb(0x5f, 0);
+ outb(0x5f, 0);
+ tmp = inb(opna_iobase + 2) & 0xc0;
+ switch (tmp >> 6) {
+ case 0: /* INT0 (IRQ3)*/
+ irq = 3;
+ break;
+ case 1: /* INT6 (IRQ13)*/
+ irq = 13;
+ break;
+ case 2: /* INT4 (IRQ10)*/
+ irq = 10;
+ break;
+ case 3: /* INT5 (IRQ12)*/
+ irq = 12;
+ break;
+ default: /* error */
+ return NO;
+ }
+
+ /* Wait for OPNA to be ready. */
+ i = 100000; /* Some large value */
+ while((inb(opna_iobase) & 0x80) && (i-- > 0));
+
+ /* Reset OPNA timer register. */
+ outb(opna_iobase, 0x27);
+ outb(0x5f, 0);
+ outb(0x5f, 0);
+ outb(0x5f, 0);
+ outb(0x5f, 0);
+ outb(opna_iobase + 2, 0x30);
+
+ /* Ok. Detection finished. */
+ sprintf(pcm86_operations.name, board_name[pcm_s.board_type]);
+ pcm_initialized = NO;
+ pcm_s.irq = irq;
+
+ if ((hw_config->irq > 0) && (hw_config->irq != irq))
+ printf("pcm0: change irq %d -> %d\n", hw_config->irq, irq);
+ hw_config->irq = irq;
+
+ return YES;
+}
+
+
+static int
+pcm86_open(int dev, int mode)
+{
+ int err;
+
+ if (!pcm_initialized)
+ return RET_ERROR(ENXIO);
+
+ if (pcm_s.intr_busy || pcm_s.opened)
+ return RET_ERROR(EBUSY);
+
+ if ((err = snd_set_irq_handler(pcm_s.irq, pcmintr, "PC-9801-73/86")) < 0)
+ return err;
+
+ pcm_stop();
+
+ tmpbuf.size = 0;
+ pcm_s.intr_mode = IMODE_NONE;
+ pcm_s.opened = YES;
+
+ return 0;
+}
+
+
+static void
+pcm86_close(int dev)
+{
+ snd_release_irq(pcm_s.irq);
+
+ pcm_s.opened = NO;
+}
+
+
+static void
+pcm86_output_block(int dev, unsigned long buf, int count, int intrflag,
+ int dma_restart)
+{
+ unsigned long flags, cnt;
+ int maxchunksize;
+
+#ifdef PCM86_DEBUG
+ printk("pcm86_output_block():");
+ if (audio_devs[dev]->dmap->flags & DMA_BUSY)
+ printk(" DMA_BUSY");
+ if (audio_devs[dev]->dmap->flags & DMA_RESTART)
+ printk(" DMA_RESTART");
+ if (audio_devs[dev]->dmap->flags & DMA_ACTIVE)
+ printk(" DMA_ACTIVE");
+ if (audio_devs[dev]->dmap->flags & DMA_STARTED)
+ printk(" DMA_STARTED");
+ if (audio_devs[dev]->dmap->flags & DMA_ALLOC_DONE)
+ printk(" DMA_ALLOC_DONE");
+ printk("\n");
+#endif
+
+#if 0
+ DISABLE_INTR(flags);
+#endif
+
+#ifdef PCM86_DEBUG
+ printk("pcm86_output_block(): count = %d, intrsize= %d\n",
+ count, pcm_s.intr_size);
+#endif
+
+ pcm_s.pdma_buf = (pcm_data *)buf;
+ pcm_s.pdma_count = count;
+ pcm_s.pdma_chunkcount = 1;
+ maxchunksize = (((PCM86_FIFOSIZE - pcm_s.intr_size * 2)
+ / (pcm_s.bytes * 2)) * pcm_s.speed
+ / pcm_s.chipspeed) * (pcm_s.bytes << pcm_s.stereo);
+ if (count > maxchunksize)
+ pcm_s.pdma_chunkcount = 2 * count / maxchunksize;
+ /*
+ * Let chunksize = (float)count / (float)pcm_s.pdma_chunkcount.
+ * Data of size chunksize is sent to the FIFO buffer on the 86-board
+ * on every occuring of interrupt.
+ * By assuming that pcm_s.intr_size < PCM86_FIFOSIZE / 2, we can conclude
+ * that the FIFO buffer never overflows from the following lemma.
+ *
+ * Lemma:
+ * maxchunksize / 2 <= chunksize <= maxchunksize.
+ * (Though pcm_s.pdma_chunkcount is obtained through the flooring
+ * function, this inequality holds.)
+ * Proof) Omitted.
+ */
+
+ fifo_output_block();
+
+ pcm_s.intr_last = NO;
+ pcm_s.intr_mode = IMODE_OUTPUT;
+ if (!pcm_s.intr_busy)
+ fifo_start(IMODE_OUTPUT);
+ pcm_s.intr_busy = YES;
+
+#if 0
+ RESTORE_INTR(flags);
+#endif
+}
+
+
+static void
+pcm86_start_input(int dev, unsigned long buf, int count, int intrflag,
+ int dma_restart)
+{
+ unsigned long flags, cnt;
+ int maxchunksize;
+
+#ifdef PCM86_DEBUG
+ printk("pcm86_start_input():");
+ if (audio_devs[dev]->dmap->flags & DMA_BUSY)
+ printk(" DMA_BUSY");
+ if (audio_devs[dev]->dmap->flags & DMA_RESTART)
+ printk(" DMA_RESTART");
+ if (audio_devs[dev]->dmap->flags & DMA_ACTIVE)
+ printk(" DMA_ACTIVE");
+ if (audio_devs[dev]->dmap->flags & DMA_STARTED)
+ printk(" DMA_STARTED");
+ if (audio_devs[dev]->dmap->flags & DMA_ALLOC_DONE)
+ printk(" DMA_ALLOC_DONE");
+ printk("\n");
+#endif
+
+#if 0
+ DISABLE_INTR(flags);
+#endif
+
+ pcm_s.intr_size = PCM86_INTRSIZE_IN;
+
+#ifdef PCM86_DEBUG
+ printk("pcm86_start_input(): count = %d, intrsize= %d\n",
+ count, pcm_s.intr_size);
+#endif
+
+ pcm_s.pdma_buf = (pcm_data *)buf;
+ pcm_s.pdma_count = count;
+ pcm_s.pdma_chunkcount = 1;
+ maxchunksize = ((pcm_s.intr_size / (pcm_s.bytes * 2)) * pcm_s.speed
+ / pcm_s.chipspeed) * (pcm_s.bytes << pcm_s.stereo);
+ if (count > maxchunksize)
+ pcm_s.pdma_chunkcount = 2 * count / maxchunksize;
+
+ pcm_s.intr_mode = IMODE_INPUT;
+ if (!pcm_s.intr_busy)
+ fifo_start(IMODE_INPUT);
+ pcm_s.intr_busy = YES;
+
+#if 0
+ RESTORE_INTR(flags);
+#endif
+}
+
+
+static int
+pcm86_ioctl(int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd) {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return set_speed(arg);
+ return IOCTL_OUT(arg, set_speed(IOCTL_IN(arg)));
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return pcm_s.speed;
+ return IOCTL_OUT(arg, pcm_s.speed);
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return set_stereo(arg);
+ return IOCTL_OUT(arg, set_stereo(IOCTL_IN(arg)));
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return set_stereo(arg - 1) + 1;
+ return IOCTL_OUT(arg, set_stereo(IOCTL_IN(arg) - 1) + 1);
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return pcm_s.stereo + 1;
+ return IOCTL_OUT(arg, pcm_s.stereo + 1);
+
+ case SNDCTL_DSP_SETFMT:
+ if (local)
+ return set_format(arg);
+ return IOCTL_OUT(arg, set_format(IOCTL_IN(arg)));
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return pcm_s.bytes * 8;
+ return IOCTL_OUT(arg, pcm_s.bytes * 8);
+ }
+
+ /* Invalid ioctl request */
+ return RET_ERROR(EINVAL);
+}
+
+
+static int
+pcm86_prepare_for_input(int dev, int bufsize, int nbufs)
+{
+ pcm_s.intr_size = PCM86_INTRSIZE_IN;
+ pcm_s.intr_mode = IMODE_NONE;
+ pcm_s.acc = 0;
+ pcm_s.last_l = 0;
+ pcm_s.last_r = 0;
+
+ DEB(printk("pcm86_prepare_for_input\n"));
+
+ return 0;
+}
+
+
+static int
+pcm86_prepare_for_output(int dev, int bufsize, int nbufs)
+{
+ pcm_s.intr_size = PCM86_INTRSIZE_OUT;
+ pcm_s.intr_mode = IMODE_NONE;
+ pcm_s.acc = 0;
+ pcm_s.last_l = 0;
+ pcm_s.last_r = 0;
+
+ DEB(printk("pcm86_prepare_for_output\n"));
+
+ return 0;
+}
+
+
+static void
+pcm86_reset(int dev)
+{
+ pcm_stop();
+}
+
+
+static void
+pcm86_halt_xfer(int dev)
+{
+ pcm_stop();
+
+ DEB(printk("pcm86_halt_xfer\n"));
+}
+
+
+void
+pcmintr(int unit)
+{
+ unsigned char tmp;
+
+ if ((inb(pcm_s.iobase + 8) & 0x10) == 0)
+ return; /* not FIFO intr. */
+
+ switch(pcm_s.intr_mode) {
+ case IMODE_OUTPUT:
+ if (pcm_s.intr_trailer) {
+ DEB(printk("pcmintr(): fifo_reset\n"));
+ fifo_reset();
+ pcm_s.intr_trailer = NO;
+ pcm_s.intr_busy = NO;
+ }
+ if (pcm_s.pdma_count > 0)
+ fifo_output_block();
+ else
+ DMAbuf_outputintr(my_dev, 1);
+ /* Reset intr. flag. */
+ tmp = inb(pcm_s.iobase + 8);
+ outb(pcm_s.iobase + 8, tmp & ~0x10);
+ outb(pcm_s.iobase + 8, tmp | 0x10);
+ break;
+
+ case IMODE_INPUT:
+ fifo_input_block();
+ if (pcm_s.pdma_count == 0)
+ DMAbuf_inputintr(my_dev);
+ /* Reset intr. flag. */
+ tmp = inb(pcm_s.iobase + 8);
+ outb(pcm_s.iobase + 8, tmp & ~0x10);
+ outb(pcm_s.iobase + 8, tmp | 0x10);
+ break;
+
+ default:
+ pcm_stop();
+ printk("pcm0: unexpected interrupt\n");
+ }
+}
+
+
+#endif /* EXCLUDE_PCM86, EXCLUDE_AUDIO */
+
+#endif /* CONFIGURE_SOUNDCARD */
diff --git a/sys/pc98/pc98/sound/sb.h b/sys/pc98/pc98/sound/sb.h
new file mode 100644
index 0000000..e21c7c8
--- /dev/null
+++ b/sys/pc98/pc98/sound/sb.h
@@ -0,0 +1,43 @@
+#ifdef PC98
+#define DSP_RESET (sbc_base + 0x600)
+#define DSP_READ (sbc_base + 0xA00)
+#define DSP_WRITE (sbc_base + 0xC00)
+#define DSP_COMMAND (sbc_base + 0xC00)
+#define DSP_STATUS (sbc_base + 0xC00)
+#define DSP_DATA_AVAIL (sbc_base + 0xE00)
+#define DSP_DATA_AVL16 (sbc_base + 0xF00)
+#define MIXER_ADDR (sbc_base + 0x400)
+#define MIXER_DATA (sbc_base + 0x500)
+#define OPL3_LEFT (sbc_base + 0x000)
+#define OPL3_RIGHT (sbc_base + 0x200)
+#define OPL3_BOTH (sbc_base + 0x800)
+#else
+#define DSP_RESET (sbc_base + 0x6)
+#define DSP_READ (sbc_base + 0xA)
+#define DSP_WRITE (sbc_base + 0xC)
+#define DSP_COMMAND (sbc_base + 0xC)
+#define DSP_STATUS (sbc_base + 0xC)
+#define DSP_DATA_AVAIL (sbc_base + 0xE)
+#define DSP_DATA_AVL16 (sbc_base + 0xF)
+#define MIXER_ADDR (sbc_base + 0x4)
+#define MIXER_DATA (sbc_base + 0x5)
+#define OPL3_LEFT (sbc_base + 0x0)
+#define OPL3_RIGHT (sbc_base + 0x2)
+#define OPL3_BOTH (sbc_base + 0x8)
+#endif
+/* DSP Commands */
+
+#define DSP_CMD_SPKON 0xD1
+#define DSP_CMD_SPKOFF 0xD3
+#define DSP_CMD_DMAON 0xD0
+#define DSP_CMD_DMAOFF 0xD4
+
+#define IMODE_NONE 0
+#define IMODE_OUTPUT 1
+#define IMODE_INPUT 2
+#define IMODE_INIT 3
+#define IMODE_MIDI 4
+
+#define NORMAL_MIDI 0
+#define UART_MIDI 1
+
diff --git a/sys/pc98/pc98/sound/sb16_dsp.c b/sys/pc98/pc98/sound/sb16_dsp.c
new file mode 100644
index 0000000..5d9ee53
--- /dev/null
+++ b/sys/pc98/pc98/sound/sb16_dsp.c
@@ -0,0 +1,589 @@
+/*
+ * sound/sb16_dsp.c
+ *
+ * The low level driver for the SoundBlaster DSP chip.
+ *
+ * (C) 1993 J. Schubert (jsb@sth.ruhr-uni-bochum.de)
+ *
+ * based on SB-driver by (C) Hannu Savolainen
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#define DEB(x)
+#define DEB1(x)
+/*
+ * #define DEB_DMARES
+ */
+#include "sound_config.h"
+#include "sb.h"
+#include "sb_mixer.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_AUDIO) && !defined(EXCLUDE_SBPRO)
+
+extern int sbc_base;
+extern int sbc_major;
+extern int sbc_minor;
+
+static int sb16_dsp_ok = 0; /*
+
+
+ * * * * Set to 1 after successful *
+ * * initialization */
+static int dsp_16bit = 0;
+static int dsp_stereo = 0;
+static int dsp_current_speed = 8000; /*
+
+
+ * * * * DSP_DEFAULT_SPEED; */
+static int dsp_busy = 0;
+static int dma16, dma8;
+static unsigned long dsp_count = 0;
+
+static int irq_mode = IMODE_NONE; /*
+
+
+ * * * * IMODE_INPUT, IMODE_OUTPUT
+ * or * * IMODE_NONE */
+static int my_dev = 0;
+
+static volatile int intr_active = 0;
+
+static int sb16_dsp_open (int dev, int mode);
+static void sb16_dsp_close (int dev);
+static void sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static void sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static int sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local);
+static int sb16_dsp_prepare_for_input (int dev, int bsize, int bcount);
+static int sb16_dsp_prepare_for_output (int dev, int bsize, int bcount);
+static void sb16_dsp_reset (int dev);
+static void sb16_dsp_halt (int dev);
+static int dsp_set_speed (int);
+static int dsp_set_stereo (int);
+static void dsp_cleanup (void);
+
+static struct audio_operations sb16_dsp_operations =
+{
+ "SoundBlaster 16",
+ DMA_AUTOMODE,
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ sb16_dsp_open,
+ sb16_dsp_close,
+ sb16_dsp_output_block,
+ sb16_dsp_start_input,
+ sb16_dsp_ioctl,
+ sb16_dsp_prepare_for_input,
+ sb16_dsp_prepare_for_output,
+ sb16_dsp_reset,
+ sb16_dsp_halt,
+ NULL,
+ NULL
+};
+
+static int
+sb_dsp_command01 (unsigned char val)
+{
+ int i = 1 << 16;
+
+ while (--i & (!INB (DSP_STATUS) & 0x80));
+ if (!i)
+ printk ("SB16 sb_dsp_command01 Timeout\n");
+ return sb_dsp_command (val);
+}
+
+static int
+dsp_set_speed (int mode)
+{
+ DEB (printk ("dsp_set_speed(%d)\n", mode));
+ if (mode)
+ {
+ if (mode < 5000)
+ mode = 5000;
+ if (mode > 44100)
+ mode = 44100;
+ dsp_current_speed = mode;
+ }
+ return mode;
+}
+
+static int
+dsp_set_stereo (int mode)
+{
+ DEB (printk ("dsp_set_stereo(%d)\n", mode));
+
+ dsp_stereo = mode;
+
+ return mode;
+}
+
+static int
+dsp_set_bits (int arg)
+{
+ DEB (printk ("dsp_set_bits(%d)\n", arg));
+
+ if (arg)
+ switch (arg)
+ {
+ case 8:
+ dsp_16bit = 0;
+ break;
+ case 16:
+ dsp_16bit = 1;
+ break;
+ default:
+ dsp_16bit = 0;
+ }
+ return dsp_16bit ? 16 : 8;
+}
+
+static int
+sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return dsp_set_speed (arg);
+ return IOCTL_OUT (arg, dsp_set_speed (IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return dsp_current_speed;
+ return IOCTL_OUT (arg, dsp_current_speed);
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return dsp_set_stereo (arg);
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg)));
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return dsp_set_stereo (arg - 1) + 1;
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1);
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return dsp_stereo + 1;
+ return IOCTL_OUT (arg, dsp_stereo + 1);
+
+ case SNDCTL_DSP_SETFMT:
+ if (local)
+ return dsp_set_bits (arg);
+ return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return dsp_16bit ? 16 : 8;
+ return IOCTL_OUT (arg, dsp_16bit ? 16 : 8);
+
+ case SOUND_PCM_WRITE_FILTER: /*
+ * NOT YET IMPLEMENTED
+ */
+ if (IOCTL_IN (arg) > 1)
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static int
+sb16_dsp_open (int dev, int mode)
+{
+ int retval;
+
+ DEB (printk ("sb16_dsp_open()\n"));
+ if (!sb16_dsp_ok)
+ {
+ printk ("SB16 Error: SoundBlaster board not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (intr_active)
+ return RET_ERROR (EBUSY);
+
+ retval = sb_get_irq ();
+ if (retval < 0)
+ return retval;
+
+ sb_reset_dsp ();
+
+ if (ALLOC_DMA_CHN (dma8, "SB16 (8bit)"))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma8);
+ sb_free_irq ();
+ return RET_ERROR (EBUSY);
+ }
+
+ if (dma16 != dma8)
+ if (ALLOC_DMA_CHN (dma16, "SB16 (16bit)"))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma16);
+ sb_free_irq ();
+ RELEASE_DMA_CHN (dma8);
+ return RET_ERROR (EBUSY);
+ }
+
+ irq_mode = IMODE_NONE;
+ dsp_busy = 1;
+
+ return 0;
+}
+
+static void
+sb16_dsp_close (int dev)
+{
+ unsigned long flags;
+
+ DEB (printk ("sb16_dsp_close()\n"));
+ sb_dsp_command01 (0xd9);
+ sb_dsp_command01 (0xd5);
+
+ DISABLE_INTR (flags);
+ RELEASE_DMA_CHN (dma8);
+
+ if (dma16 != dma8)
+ RELEASE_DMA_CHN (dma16);
+ sb_free_irq ();
+ dsp_cleanup ();
+ dsp_busy = 0;
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+
+ cnt = count;
+ if (dsp_16bit)
+ cnt >>= 1;
+ cnt--;
+
+#ifdef DEB_DMARES
+ printk ("output_block: %x %d %d\n", buf, count, intrflag);
+ if (intrflag)
+ {
+ int pos, chan = audio_devs[dev]->dmachan;
+
+ DISABLE_INTR (flags);
+ clear_dma_ff (chan);
+ disable_dma (chan);
+ pos = get_dma_residue (chan);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+ printk ("dmapos=%d %x\n", pos, pos);
+ }
+#endif
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == dsp_count)
+ {
+ irq_mode = IMODE_OUTPUT;
+ intr_active = 1;
+ return; /*
+ * Auto mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ sb16_dsp_halt (dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+ }
+ sb_dsp_command (0x41);
+ sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_16bit ? 0xb6 : 0xc6));
+ sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) +
+ (dsp_16bit ? 0x10 : 0)));
+ sb_dsp_command01 ((unsigned char) (cnt & 0xff));
+ sb_dsp_command ((unsigned char) (cnt >> 8));
+
+ dsp_count = cnt;
+ irq_mode = IMODE_OUTPUT;
+ intr_active = 1;
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+
+ cnt = count;
+ if (dsp_16bit)
+ cnt >>= 1;
+ cnt--;
+
+#ifdef DEB_DMARES
+ printk ("start_input: %x %d %d\n", buf, count, intrflag);
+ if (intrflag)
+ {
+ int pos, chan = audio_devs[dev]->dmachan;
+
+ DISABLE_INTR (flags);
+ clear_dma_ff (chan);
+ disable_dma (chan);
+ pos = get_dma_residue (chan);
+ enable_dma (chan);
+ RESTORE_INTR (flags);
+ printk ("dmapos=%d %x\n", pos, pos);
+ }
+#endif
+ if (audio_devs[dev]->flags & DMA_AUTOMODE &&
+ intrflag &&
+ cnt == dsp_count)
+ {
+ irq_mode = IMODE_INPUT;
+ intr_active = 1;
+ return; /*
+ * Auto mode on. No need to react
+ */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ sb_reset_dsp ();
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+ }
+
+ sb_dsp_command (0x42);
+ sb_dsp_command ((unsigned char) ((dsp_current_speed >> 8) & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_current_speed & 0xff));
+ sb_dsp_command ((unsigned char) (dsp_16bit ? 0xbe : 0xce));
+ sb_dsp_command ((unsigned char) ((dsp_stereo ? 0x20 : 0) +
+ (dsp_16bit ? 0x10 : 0)));
+ sb_dsp_command01 ((unsigned char) (cnt & 0xff));
+ sb_dsp_command ((unsigned char) (cnt >> 8));
+
+ dsp_count = cnt;
+ irq_mode = IMODE_INPUT;
+ intr_active = 1;
+ RESTORE_INTR (flags);
+}
+
+static int
+sb16_dsp_prepare_for_input (int dev, int bsize, int bcount)
+{
+ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8;
+ dsp_count = 0;
+ dsp_cleanup ();
+ return 0;
+}
+
+static int
+sb16_dsp_prepare_for_output (int dev, int bsize, int bcount)
+{
+ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8;
+ dsp_count = 0;
+ dsp_cleanup ();
+ return 0;
+}
+
+static void
+dsp_cleanup (void)
+{
+ irq_mode = IMODE_NONE;
+ intr_active = 0;
+}
+
+static void
+sb16_dsp_reset (int dev)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ sb_reset_dsp ();
+ dsp_cleanup ();
+
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_halt (int dev)
+{
+ if (dsp_16bit)
+ {
+ sb_dsp_command01 (0xd9);
+ sb_dsp_command01 (0xd5);
+ }
+ else
+ {
+ sb_dsp_command01 (0xda);
+ sb_dsp_command01 (0xd0);
+ }
+ DMAbuf_reset_dma (dev);
+}
+
+static void
+set_irq_hw (int level)
+{
+ int ival;
+
+ switch (level)
+ {
+#ifdef PC98
+ case 5:
+ ival = 8;
+ break;
+ case 3:
+ ival = 1;
+ break;
+ case 10:
+ ival = 2;
+ break;
+#else
+ case 5:
+ ival = 2;
+ break;
+ case 7:
+ ival = 4;
+ break;
+ case 9:
+ ival = 1;
+ break;
+ case 10:
+ ival = 8;
+ break;
+#endif
+ default:
+ printk ("SB16_IRQ_LEVEL %d does not exist\n", level);
+ return;
+ }
+ sb_setmixer (IRQ_NR, ival);
+}
+
+long
+sb16_dsp_init (long mem_start, struct address_info *hw_config)
+{
+ if (sbc_major < 4)
+ return mem_start; /* Not a SB16 */
+
+ sprintf (sb16_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor);
+
+#if defined(__FreeBSD__)
+ printk ("sbxvo0: <%s>", sb16_dsp_operations.name);
+#else
+ printk (" <%s>", sb16_dsp_operations.name);
+#endif
+
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_dev = num_audiodevs++] = &sb16_dsp_operations;
+ audio_devs[my_dev]->dmachan = hw_config->dma;
+ audio_devs[my_dev]->buffcount = 1;
+ audio_devs[my_dev]->buffsize = DSP_BUFFSIZE;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
+ sb16_dsp_ok = 1;
+ return mem_start;
+}
+
+int
+sb16_dsp_detect (struct address_info *hw_config)
+{
+ struct address_info *sb_config;
+
+ if (sb16_dsp_ok)
+ return 1; /* Can't drive two cards */
+
+ if (!(sb_config = sound_getconf (SNDCARD_SB)))
+ {
+ printk ("SB16 Error: Plain SB not configured\n");
+ return 0;
+ }
+
+ /*
+ * sb_setmixer(OPSW,0xf); if(sb_getmixer(OPSW)!=0xf) return 0;
+ */
+
+ if (!sb_reset_dsp ())
+ return 0;
+
+ if (sbc_major < 4) /* Set by the plain SB driver */
+ return 0; /* Not a SB16 */
+
+#ifdef PC98
+ hw_config->dma = sb_config->dma;
+#else
+ if (hw_config->dma < 4)
+ if (hw_config->dma != sb_config->dma)
+ {
+ printk ("SB16 Error: Invalid DMA channel %d/%d\n",
+ sb_config->dma, hw_config->dma);
+ return 0;
+ }
+#endif
+
+ dma16 = hw_config->dma;
+ dma8 = sb_config->dma;
+ set_irq_hw (sb_config->irq);
+#ifdef PC98
+ sb_setmixer (DMA_NR, hw_config->dma == 0 ? 1 : 2);
+#else
+ sb_setmixer (DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma));
+#endif
+
+ DEB (printk ("SoundBlaster 16: IRQ %d DMA %d OK\n", sb_config->irq, hw_config->dma));
+
+ /*
+ * dsp_showmessage(0xe3,99);
+ */
+ sb16_dsp_ok = 1;
+ return 1;
+}
+
+void
+sb16_dsp_interrupt (int unused)
+{
+ int data;
+
+ data = INB (DSP_DATA_AVL16); /*
+ * Interrupt acknowledge
+ */
+
+ if (intr_active)
+ switch (irq_mode)
+ {
+ case IMODE_OUTPUT:
+ intr_active = 0;
+ DMAbuf_outputintr (my_dev, 1);
+ break;
+
+ case IMODE_INPUT:
+ intr_active = 0;
+ DMAbuf_inputintr (my_dev);
+ break;
+
+ default:
+ printk ("SoundBlaster: Unexpected interrupt\n");
+ }
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/sb16_midi.c b/sys/pc98/pc98/sound/sb16_midi.c
new file mode 100644
index 0000000..8a8ac73
--- /dev/null
+++ b/sys/pc98/pc98/sound/sb16_midi.c
@@ -0,0 +1,311 @@
+/*
+ * sound/sb16_midi.c
+ *
+ * The low level driver for the MPU-401 UART emulation of the SB16.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_MIDI)
+
+#include "sb.h"
+
+#ifdef PC98
+#define DATAPORT (sb16midi_base)
+#define COMDPORT (sb16midi_base+0x100)
+#define STATPORT (sb16midi_base+0x100)
+#else
+#define DATAPORT (sb16midi_base)
+#define COMDPORT (sb16midi_base+1)
+#define STATPORT (sb16midi_base+1)
+#endif
+
+#define sb16midi_status() INB(STATPORT)
+#define input_avail() (!(sb16midi_status()&INPUT_AVAIL))
+#define output_ready() (!(sb16midi_status()&OUTPUT_READY))
+#define sb16midi_cmd(cmd) OUTB(cmd, COMDPORT)
+#define sb16midi_read() INB(DATAPORT)
+#define sb16midi_write(byte) OUTB(byte, DATAPORT)
+
+#define OUTPUT_READY 0x40
+#define INPUT_AVAIL 0x80
+#define MPU_ACK 0xFE
+#define MPU_RESET 0xFF
+#define UART_MODE_ON 0x3F
+
+extern int sbc_major;
+
+static int sb16midi_opened = 0;
+static int sb16midi_base = 0x330;
+static int sb16midi_detected = 0;
+static int my_dev;
+extern int sbc_base;
+
+static int reset_sb16midi (void);
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static void
+sb16midi_input_loop (void)
+{
+ while (input_avail ())
+ {
+ unsigned char c = sb16midi_read ();
+
+ if (sb16midi_opened & OPEN_READ)
+ midi_input_intr (my_dev, c);
+ }
+}
+
+void
+sb16midiintr (int unit)
+{
+ if (input_avail ())
+ sb16midi_input_loop ();
+}
+
+static int
+sb16midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ if (sb16midi_opened)
+ {
+ return RET_ERROR (EBUSY);
+ }
+
+ sb16midi_input_loop ();
+
+ midi_input_intr = input;
+ sb16midi_opened = mode;
+
+ return 0;
+}
+
+static void
+sb16midi_close (int dev)
+{
+ sb16midi_opened = 0;
+}
+
+static int
+sb16midi_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ sb16midi_input_loop ();
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+
+ if (!output_ready ())
+ {
+ printk ("MPU-401: Timeout\n");
+ return 0;
+ }
+
+ sb16midi_write (midi_byte);
+ return 1;
+}
+
+static int
+sb16midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+sb16midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+sb16midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+sb16midi_kick (int dev)
+{
+}
+
+static int
+sb16midi_buffer_status (int dev)
+{
+ return 0; /*
+ * No data in buffers
+ */
+}
+
+#define MIDI_SYNTH_NAME "SoundBlaster 16 Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations sb16midi_operations =
+{
+ {"SoundBlaster 16 Midi", 0, 0, SNDCARD_SB16MIDI},
+ &std_midi_synth,
+ {0},
+ sb16midi_open,
+ sb16midi_close,
+ sb16midi_ioctl,
+ sb16midi_out,
+ sb16midi_start_read,
+ sb16midi_end_read,
+ sb16midi_kick,
+ NULL,
+ sb16midi_buffer_status,
+ NULL
+};
+
+
+long
+attach_sb16midi (long mem_start, struct address_info *hw_config)
+{
+ int ok, timeout;
+ unsigned long flags;
+
+ sb16midi_base = hw_config->io_base;
+
+ if (!sb16midi_detected)
+ return RET_ERROR (EIO);
+
+ DISABLE_INTR (flags);
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+ sb16midi_cmd (UART_MODE_ON);
+
+ ok = 0;
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (sb16midi_read () == MPU_ACK)
+ ok = 1;
+
+ RESTORE_INTR (flags);
+
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ printk (" <SoundBlaster MPU-401>");
+
+ std_midi_synth.midi_dev = my_dev = num_midis;
+ midi_devs[num_midis++] = &sb16midi_operations;
+ return mem_start;
+}
+
+static int
+reset_sb16midi (void)
+{
+ unsigned long flags;
+ int ok, timeout, n;
+
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ */
+
+ ok = 0;
+
+ DISABLE_INTR (flags);
+
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+ sb16midi_cmd (MPU_RESET); /*
+ * Send MPU-401 RESET Command
+ */
+
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (sb16midi_read () == MPU_ACK)
+ ok = 1;
+
+ }
+
+ sb16midi_opened = 0;
+ if (ok)
+ sb16midi_input_loop (); /*
+ * Flush input before enabling interrupts
+ */
+
+ RESTORE_INTR (flags);
+
+ return ok;
+}
+
+
+int
+probe_sb16midi (struct address_info *hw_config)
+{
+ int ok = 0;
+
+ if (sbc_major < 4)
+ return 0; /* Not a SB16 */
+
+ sb16midi_base = hw_config->io_base;
+
+ if (sb_get_irq () < 0)
+ return 0;
+
+ ok = reset_sb16midi ();
+
+ sb16midi_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif
diff --git a/sys/pc98/pc98/sound/sb_card.c b/sys/pc98/pc98/sound/sb_card.c
new file mode 100644
index 0000000..b8e5af2
--- /dev/null
+++ b/sys/pc98/pc98/sound/sb_card.c
@@ -0,0 +1,61 @@
+/*
+ * sound/sb_card.c
+ *
+ * Detection routine for the SoundBlaster cards.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Modified:
+ * Riccardo Facchetti 24 Mar 1995
+ * - Added the Audio Excel DSP 16 initialization routine.
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+
+long
+attach_sb_card (long mem_start, struct address_info *hw_config)
+{
+#if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_MIDI)
+ if (!sb_dsp_detect (hw_config))
+ return mem_start;
+ mem_start = sb_dsp_init (mem_start, hw_config);
+#endif
+
+ return mem_start;
+}
+
+int
+probe_sb (struct address_info *hw_config)
+{
+#if !defined(EXCLUDE_AEDSP16) && defined(AEDSP16_SBPRO)
+ /*
+ * Initialize Audio Excel DSP 16 to SBPRO.
+ */
+ InitAEDSP16_SBPRO (hw_config);
+#endif
+ return sb_dsp_detect (hw_config);
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/sb_dsp.c b/sys/pc98/pc98/sound/sb_dsp.c
new file mode 100644
index 0000000..6081585
--- /dev/null
+++ b/sys/pc98/pc98/sound/sb_dsp.c
@@ -0,0 +1,1252 @@
+/*
+ * sound/sb_dsp.c
+ *
+ * The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro).
+ *
+ * Copyright by Hannu Savolainen 1994
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added code to support Sound Galaxy NX Pro
+ *
+ * JRA Gibson April 1995
+ * Code added for MV ProSonic/Jazz 16 in 16 bit mode
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB)
+
+#include "sb.h"
+#include "sb_mixer.h"
+#undef SB_TEST_IRQ
+
+int sbc_base = 0;
+static int sbc_irq = 0;
+static int open_mode = 0; /* Read, write or both */
+int Jazz16_detected = 0;
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+int sb_dsp_ok = 0; /*
+
+
+ * * * * Set to 1 after successful
+ * initialization * */
+static int midi_disabled = 0;
+int sb_dsp_highspeed = 0;
+int sbc_major = 1, sbc_minor = 0; /*
+
+
+ * * * * DSP version */
+static int dsp_stereo = 0;
+static int dsp_current_speed = DSP_DEFAULT_SPEED;
+static int sb16 = 0;
+static int irq_verified = 0;
+
+int sb_midi_mode = NORMAL_MIDI;
+int sb_midi_busy = 0; /*
+
+
+ * * * * 1 if the process has output
+ * to * * MIDI */
+int sb_dsp_busy = 0;
+
+volatile int sb_irq_mode = IMODE_NONE; /*
+
+
+ * * * * IMODE_INPUT, *
+ * IMODE_OUTPUT * * or *
+ * IMODE_NONE */
+static volatile int irq_ok = 0;
+
+#ifdef JAZZ16
+/* 16 bit support
+ */
+
+static int dsp_16bit = 0;
+static int dma8 = 1;
+static int dma16 = 5;
+
+static int dsp_set_bits (int arg);
+static int initialize_ProSonic16 (void);
+
+/* end of 16 bit support
+ */
+#endif
+
+int sb_duplex_midi = 0;
+static int my_dev = 0;
+
+volatile int sb_intr_active = 0;
+
+static int dsp_speed (int);
+static int dsp_set_stereo (int mode);
+
+#if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO)
+
+/*
+ * Common code for the midi and pcm functions
+ */
+
+int
+sb_dsp_command (unsigned char val)
+{
+ int i;
+ unsigned long limit;
+
+ limit = GET_TIME () + HZ / 10; /*
+ * The timeout is 0.1 secods
+ */
+
+ /*
+ * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
+ * called while interrupts are disabled. This means that the timer is
+ * disabled also. However the timeout situation is a abnormal condition.
+ * Normally the DSP should be ready to accept commands after just couple of
+ * loops.
+ */
+
+ for (i = 0; i < 500000 && GET_TIME () < limit; i++)
+ {
+ if ((INB (DSP_STATUS) & 0x80) == 0)
+ {
+ OUTB (val, DSP_COMMAND);
+ return 1;
+ }
+ }
+
+ printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val);
+ printk ("IRQ conflict???\n");
+ return 0;
+}
+
+void
+sbintr (INT_HANDLER_PARMS (irq, dummy))
+{
+ int status;
+
+#ifndef EXCLUDE_SBPRO
+ if (sb16)
+ {
+ unsigned char src = sb_getmixer (IRQ_STAT); /* Interrupt source register */
+
+#ifndef EXCLUDE_SB16
+ if (src & 3)
+ sb16_dsp_interrupt (irq);
+
+#ifndef EXCLUDE_MIDI
+ if (src & 4)
+ sb16midiintr (irq); /*
+ * SB MPU401 interrupt
+ */
+#endif
+
+#endif
+
+ if (!(src & 1))
+ return; /*
+ * Not a DSP interupt
+ */
+ }
+#endif
+
+ status = INB (DSP_DATA_AVAIL); /*
+ * Clear interrupt
+ */
+
+ if (sb_intr_active)
+ switch (sb_irq_mode)
+ {
+ case IMODE_OUTPUT:
+ sb_intr_active = 0;
+ DMAbuf_outputintr (my_dev, 1);
+ break;
+
+ case IMODE_INPUT:
+ sb_intr_active = 0;
+ DMAbuf_inputintr (my_dev);
+ /*
+ * A complete buffer has been input. Let's start new one
+ */
+ break;
+
+ case IMODE_INIT:
+ sb_intr_active = 0;
+ irq_ok = 1;
+ break;
+
+ case IMODE_MIDI:
+#ifndef EXCLUDE_MIDI
+ sb_midi_interrupt (irq);
+#endif
+ break;
+
+ default:
+ printk ("SoundBlaster: Unexpected interrupt\n");
+ }
+}
+
+static int sb_irq_usecount = 0;
+
+int
+sb_get_irq (void)
+{
+ int ok;
+
+ if (!sb_irq_usecount)
+ if ((ok = snd_set_irq_handler (sbc_irq, sbintr, "SoundBlaster")) < 0)
+ return ok;
+
+ sb_irq_usecount++;
+
+ return 0;
+}
+
+void
+sb_free_irq (void)
+{
+ if (!sb_irq_usecount)
+ return;
+
+ sb_irq_usecount--;
+
+ if (!sb_irq_usecount)
+ snd_release_irq (sbc_irq);
+}
+
+int
+sb_reset_dsp (void)
+{
+ int loopc;
+
+ OUTB (1, DSP_RESET);
+ tenmicrosec ();
+ OUTB (0, DSP_RESET);
+ tenmicrosec ();
+ tenmicrosec ();
+ tenmicrosec ();
+
+ for (loopc = 0; loopc < 1000 && !(INB (DSP_DATA_AVAIL) & 0x80); loopc++); /*
+ * Wait
+ * for
+ * data
+ * *
+ * available
+ * status
+ */
+
+ if (INB (DSP_READ) != 0xAA)
+ return 0; /*
+ * Sorry
+ */
+
+ return 1;
+}
+
+#endif
+
+#ifndef EXCLUDE_AUDIO
+
+static void
+dsp_speaker (char state)
+{
+ if (state)
+ sb_dsp_command (DSP_CMD_SPKON);
+ else
+ sb_dsp_command (DSP_CMD_SPKOFF);
+}
+
+static int
+dsp_speed (int speed)
+{
+ unsigned char tconst;
+ unsigned long flags;
+ int max_speed = 44100;
+
+ if (speed < 4000)
+ speed = 4000;
+
+ /*
+ * Older SB models don't support higher speeds than 22050.
+ */
+
+ if (sbc_major < 2 ||
+ (sbc_major == 2 && sbc_minor == 0))
+ max_speed = 22050;
+
+ /*
+ * SB models earlier than SB Pro have low limit for the input speed.
+ */
+ if (open_mode != OPEN_WRITE) /* Recording is possible */
+ if (sbc_major < 3) /* Limited input speed with these cards */
+ if (sbc_major == 2 && sbc_minor > 0)
+ max_speed = 15000;
+ else
+ max_speed = 13000;
+
+ if (speed > max_speed)
+ speed = max_speed; /*
+ * Invalid speed
+ */
+
+ /* Logitech SoundMan Games and Jazz16 cards can support 44.1kHz stereo */
+#if !defined (SM_GAMES)
+ /*
+ * Max. stereo speed is 22050
+ */
+ if (dsp_stereo && speed > 22050 && Jazz16_detected == 0)
+ speed = 22050;
+#endif
+
+ if ((speed > 22050) && sb_midi_busy)
+ {
+ printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n");
+ speed = 22050;
+ }
+
+ if (dsp_stereo)
+ speed *= 2;
+
+ /*
+ * Now the speed should be valid
+ */
+
+ if (speed > 22050)
+ { /*
+ * High speed mode
+ */
+ int tmp;
+
+ tconst = (unsigned char) ((65536 -
+ ((256000000 + speed / 2) / speed)) >> 8);
+ sb_dsp_highspeed = 1;
+
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x40))
+ sb_dsp_command (tconst);
+ RESTORE_INTR (flags);
+
+ tmp = 65536 - (tconst << 8);
+ speed = (256000000 + tmp / 2) / tmp;
+ }
+ else
+ {
+ int tmp;
+
+ sb_dsp_highspeed = 0;
+ tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
+
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x40)) /*
+ * Set time constant
+ */
+ sb_dsp_command (tconst);
+ RESTORE_INTR (flags);
+
+ tmp = 256 - tconst;
+ speed = (1000000 + tmp / 2) / tmp;
+ }
+
+ if (dsp_stereo)
+ speed /= 2;
+
+ dsp_current_speed = speed;
+ return speed;
+}
+
+static int
+dsp_set_stereo (int mode)
+{
+ dsp_stereo = 0;
+
+#ifdef EXCLUDE_SBPRO
+ return 0;
+#else
+ if (sbc_major < 3 || sb16)
+ return 0; /*
+ * Sorry no stereo
+ */
+
+ if (mode && sb_midi_busy)
+ {
+ printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n");
+ return 0;
+ }
+
+ dsp_stereo = !!mode;
+ return dsp_stereo;
+#endif
+}
+
+static void
+sb_dsp_output_block (int dev, unsigned long buf, int count,
+ int intrflag, int restart_dma)
+{
+ unsigned long flags;
+
+ if (!sb_irq_mode)
+ dsp_speaker (ON);
+
+ sb_irq_mode = IMODE_OUTPUT;
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+
+ if (audio_devs[dev]->dmachan > 3)
+ count >>= 1;
+ count--;
+
+ if (sb_dsp_highspeed)
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x48)) /*
+ * High speed size
+ */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ sb_dsp_command (0x91); /*
+ * High speed 8 bit DAC
+ */
+ }
+ else
+ printk ("SB Error: Unable to start (high speed) DAC\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x14)) /*
+ * 8-bit DAC (DMA)
+ */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ }
+ else
+ printk ("SB Error: Unable to start DAC\n");
+ RESTORE_INTR (flags);
+ }
+ sb_intr_active = 1;
+}
+
+static void
+sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag,
+ int restart_dma)
+{
+ /*
+ * Start a DMA input to the buffer pointed by dmaqtail
+ */
+
+ unsigned long flags;
+
+ if (!sb_irq_mode)
+ dsp_speaker (OFF);
+
+ sb_irq_mode = IMODE_INPUT;
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+
+ if (audio_devs[dev]->dmachan > 3)
+ count >>= 1;
+ count--;
+
+ if (sb_dsp_highspeed)
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x48)) /*
+ * High speed size
+ */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ sb_dsp_command (0x99); /*
+ * High speed 8 bit ADC
+ */
+ }
+ else
+ printk ("SB Error: Unable to start (high speed) ADC\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x24)) /*
+ * 8-bit ADC (DMA)
+ */
+ {
+ sb_dsp_command ((unsigned char) (count & 0xff));
+ sb_dsp_command ((unsigned char) ((count >> 8) & 0xff));
+ }
+ else
+ printk ("SB Error: Unable to start ADC\n");
+ RESTORE_INTR (flags);
+ }
+
+ sb_intr_active = 1;
+}
+
+static void
+dsp_cleanup (void)
+{
+ sb_intr_active = 0;
+}
+
+static int
+sb_dsp_prepare_for_input (int dev, int bsize, int bcount)
+{
+ dsp_cleanup ();
+ dsp_speaker (OFF);
+
+ if (sbc_major == 3) /*
+ * SB Pro
+ */
+ {
+#ifdef JAZZ16
+ /* Select correct dma channel
+ * for 16/8 bit acccess
+ */
+ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8;
+ if (dsp_stereo)
+ sb_dsp_command (dsp_16bit ? 0xac : 0xa8);
+ else
+ sb_dsp_command (dsp_16bit ? 0xa4 : 0xa0);
+#else
+ /* 8 bit only cards use this
+ */
+ if (dsp_stereo)
+ sb_dsp_command (0xa8);
+ else
+ sb_dsp_command (0xa0);
+#endif
+ dsp_speed (dsp_current_speed); /*
+ * Speed must be recalculated if
+ * #channels * changes
+ */
+ }
+ return 0;
+}
+
+static int
+sb_dsp_prepare_for_output (int dev, int bsize, int bcount)
+{
+ dsp_cleanup ();
+ dsp_speaker (ON);
+
+#ifndef EXCLUDE_SBPRO
+ if (sbc_major == 3) /*
+ * SB Pro
+ */
+ {
+#ifdef JAZZ16
+ /* 16 bit specific instructions
+ */
+ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8;
+ if (Jazz16_detected != 2) /* SM Wave */
+ sb_mixer_set_stereo (dsp_stereo);
+ if (dsp_stereo)
+ sb_dsp_command (dsp_16bit ? 0xac : 0xa8);
+ else
+ sb_dsp_command (dsp_16bit ? 0xa4 : 0xa0);
+#else
+ sb_mixer_set_stereo (dsp_stereo);
+#endif
+ dsp_speed (dsp_current_speed); /*
+ * Speed must be recalculated if
+ * #channels * changes
+ */
+ }
+#endif
+ return 0;
+}
+
+static void
+sb_dsp_halt_xfer (int dev)
+{
+}
+
+static int
+verify_irq (void)
+{
+#if 0
+ DEFINE_WAIT_QUEUE (testq, testf);
+
+ irq_ok = 0;
+
+ if (sb_get_irq () == -1)
+ {
+ printk ("*** SB Error: Irq %d already in use\n", sbc_irq);
+ return 0;
+ }
+
+
+ sb_irq_mode = IMODE_INIT;
+
+ sb_dsp_command (0xf2); /*
+ * This should cause immediate interrupt
+ */
+
+ DO_SLEEP (testq, testf, HZ / 5);
+
+ sb_free_irq ();
+
+ if (!irq_ok)
+ {
+ printk ("SB Warning: IRQ%d test not passed!", sbc_irq);
+ irq_ok = 1;
+ }
+#else
+ irq_ok = 1;
+#endif
+ return irq_ok;
+}
+
+static int
+sb_dsp_open (int dev, int mode)
+{
+ int retval;
+
+ if (!sb_dsp_ok)
+ {
+ printk ("SB Error: SoundBlaster board not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI))
+ {
+ printk ("SB: PCM not possible during MIDI input\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (!irq_verified)
+ {
+ verify_irq ();
+ irq_verified = 1;
+ }
+ else if (!irq_ok)
+ printk ("SB Warning: Incorrect IRQ setting %d\n",
+ sbc_irq);
+
+ retval = sb_get_irq ();
+ if (retval)
+ return retval;
+
+ /* Allocate 8 bit dma
+ */
+ if (DMAbuf_open_dma (dev) < 0)
+ {
+ sb_free_irq ();
+ printk ("SB: DMA Busy\n");
+ return RET_ERROR (EBUSY);
+ }
+#ifdef JAZZ16
+ /* Allocate 16 bit dma
+ */
+ if (Jazz16_detected != 0)
+ if (dma16 != dma8)
+ {
+ if (ALLOC_DMA_CHN (dma16, "Jazz16 16 bit"))
+ {
+ sb_free_irq ();
+ RELEASE_DMA_CHN (dma8);
+ return RET_ERROR (EBUSY);
+ }
+ }
+#endif
+
+ sb_irq_mode = IMODE_NONE;
+
+ sb_dsp_busy = 1;
+ open_mode = mode;
+
+ return 0;
+}
+
+static void
+sb_dsp_close (int dev)
+{
+#ifdef JAZZ16
+ /* Release 16 bit dma channel
+ */
+ if (Jazz16_detected)
+ RELEASE_DMA_CHN (dma16);
+#endif
+
+ DMAbuf_close_dma (dev);
+ sb_free_irq ();
+ dsp_cleanup ();
+ dsp_speaker (OFF);
+ sb_dsp_busy = 0;
+ sb_dsp_highspeed = 0;
+ open_mode = 0;
+}
+
+#ifdef JAZZ16
+/* Function dsp_set_bits() only required for 16 bit cards
+ */
+static int
+dsp_set_bits (int arg)
+{
+ if (arg)
+ if (Jazz16_detected == 0)
+ dsp_16bit = 0;
+ else
+ switch (arg)
+ {
+ case 8:
+ dsp_16bit = 0;
+ break;
+ case 16:
+ dsp_16bit = 1;
+ break;
+ default:
+ dsp_16bit = 0;
+ }
+ return dsp_16bit ? 16 : 8;
+}
+
+#endif /* ifdef JAZZ16 */
+
+static int
+sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return dsp_speed (arg);
+ return IOCTL_OUT (arg, dsp_speed (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return dsp_current_speed;
+ return IOCTL_OUT (arg, dsp_current_speed);
+ break;
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return dsp_set_stereo (arg - 1) + 1;
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1);
+ break;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return dsp_stereo + 1;
+ return IOCTL_OUT (arg, dsp_stereo + 1);
+ break;
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return dsp_set_stereo (arg);
+ return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg)));
+ break;
+
+#ifdef JAZZ16
+ /* Word size specific cases here.
+ * SNDCTL_DSP_SETFMT=SOUND_PCM_WRITE_BITS
+ */
+ case SNDCTL_DSP_SETFMT:
+ if (local)
+ return dsp_set_bits (arg);
+ return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg)));
+ break;
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return dsp_16bit ? 16 : 8;
+ return IOCTL_OUT (arg, dsp_16bit ? 16 : 8);
+ break;
+#else
+ case SOUND_PCM_WRITE_BITS:
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return 8;
+ return IOCTL_OUT (arg, 8); /*
+ * Only 8 bits/sample supported
+ */
+ break;
+#endif /* ifdef JAZZ16 */
+
+ case SOUND_PCM_WRITE_FILTER:
+ case SOUND_PCM_READ_FILTER:
+ return RET_ERROR (EINVAL);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+sb_dsp_reset (int dev)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ sb_reset_dsp ();
+ dsp_speed (dsp_current_speed);
+ dsp_cleanup ();
+
+ RESTORE_INTR (flags);
+}
+
+#endif
+
+
+#ifdef JAZZ16
+
+/*
+ * Initialization of a Media Vision ProSonic 16 Soundcard.
+ * The function initializes a ProSonic 16 like PROS.EXE does for DOS. It sets
+ * the base address, the DMA-channels, interrupts and enables the joystickport.
+ *
+ * Also used by Jazz 16 (same card, different name)
+ *
+ * written 1994 by Rainer Vranken
+ * E-Mail: rvranken@polaris.informatik.uni-essen.de
+ */
+
+
+#ifndef MPU_BASE /* take default values if not specified */
+#define MPU_BASE 0x330
+#endif
+#ifndef MPU_IRQ
+#define MPU_IRQ 9
+#endif
+
+unsigned int
+get_sb_byte (void)
+{
+ int i;
+
+ for (i = 1000; i; i--)
+ if (INB (DSP_DATA_AVAIL) & 0x80)
+ {
+ return INB (DSP_READ);
+ }
+
+ return 0xffff;
+}
+
+#ifdef SM_WAVE
+/*
+ * Logitech Soundman Wave detection and initialization by Hannu Savolainen.
+ *
+ * There is a microcontroller (8031) in the SM Wave card for MIDI emulation.
+ * it's located at address MPU_BASE+4. MPU_BASE+7 is a SM Wave specific
+ * control register for MC reset, SCSI, OPL4 and DSP (future expansion)
+ * address decoding. Otherwise the SM Wave is just a ordinary MV Jazz16
+ * based soundcard.
+ */
+
+static void
+smw_putmem (int base, int addr, unsigned char val)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ OUTB (addr & 0xff, base + 1); /* Low address bits */
+ OUTB (addr >> 8, base + 2); /* High address bits */
+ OUTB (val, base); /* Data */
+
+ RESTORE_INTR (flags);
+}
+
+static unsigned char
+smw_getmem (int base, int addr)
+{
+ unsigned long flags;
+ unsigned char val;
+
+ DISABLE_INTR (flags);
+
+ OUTB (addr & 0xff, base + 1); /* Low address bits */
+ OUTB (addr >> 8, base + 2); /* High address bits */
+ val = INB (base); /* Data */
+
+ RESTORE_INTR (flags);
+ return val;
+}
+
+static int
+initialize_smw (void)
+{
+#ifdef SMW_MIDI0001_INCLUDED
+#include "smw-midi0001.h"
+#else
+ unsigned char smw_ucode[1];
+ int smw_ucodeLen = 0;
+
+#endif
+
+ int mp_base = MPU_BASE + 4; /* Microcontroller base */
+ int i;
+ unsigned char control;
+
+ /*
+ * Reset the microcontroller so that the RAM can be accessed
+ */
+
+ control = INB (MPU_BASE + 7);
+ OUTB (control | 3, MPU_BASE + 7); /* Set last two bits to 1 (?) */
+ OUTB ((control & 0xfe) | 2, MPU_BASE + 7); /* xxxxxxx0 resets the mc */
+
+ for (i = 0; i < 300; i++) /* Wait at least 1ms */
+ tenmicrosec ();
+
+ OUTB (control & 0xfc, MPU_BASE + 7); /* xxxxxx00 enables RAM */
+
+ /*
+ * Detect microcontroller by probing the 8k RAM area
+ */
+ smw_putmem (mp_base, 0, 0x00);
+ smw_putmem (mp_base, 1, 0xff);
+ tenmicrosec ();
+
+ if (smw_getmem (mp_base, 0) != 0x00 || smw_getmem (mp_base, 1) != 0xff)
+ {
+ printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n",
+ smw_getmem (mp_base, 0), smw_getmem (mp_base, 1));
+ return 0; /* No RAM */
+ }
+
+ /*
+ * There is RAM so assume it's really a SM Wave
+ */
+
+#ifdef SMW_MIDI0001_INCLUDED
+ if (smw_ucodeLen != 8192)
+ {
+ printk ("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n");
+ return 1;
+ }
+#endif
+
+ /*
+ * Download microcode
+ */
+
+ for (i = 0; i < 8192; i++)
+ smw_putmem (mp_base, i, smw_ucode[i]);
+
+ /*
+ * Verify microcode
+ */
+
+ for (i = 0; i < 8192; i++)
+ if (smw_getmem (mp_base, i) != smw_ucode[i])
+ {
+ printk ("SM Wave: Microcode verification failed\n");
+ return 0;
+ }
+
+ control = 0;
+#ifdef SMW_SCSI_IRQ
+ /*
+ * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
+ * is disabled by default.
+ *
+ * Btw the Zilog 5380 SCSI controller is located at MPU base + 0x10.
+ */
+ {
+ static unsigned char scsi_irq_bits[] =
+ {0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0};
+
+ control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
+ }
+#endif
+
+#ifdef SMW_OPL4_ENABLE
+ /*
+ * Make the OPL4 chip visible on the PC bus at 0x380.
+ *
+ * There is no need to enable this feature since VoxWare
+ * doesn't support OPL4 yet. Also there is no RAM in SM Wave so
+ * enabling OPL4 is pretty useless.
+ */
+ control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */
+ /* control |= 0x20; Uncomment this if you want to use IRQ7 */
+#endif
+
+ OUTB (control | 0x03, MPU_BASE + 7); /* xxxxxx11 restarts */
+ return 1;
+}
+
+#endif
+
+static int
+initialize_ProSonic16 (void)
+{
+ int x;
+ static unsigned char int_translat[16] =
+ {0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6}, dma_translat[8] =
+ {0, 1, 0, 2, 0, 3, 0, 4};
+
+ OUTB (0xAF, 0x201); /* ProSonic/Jazz16 wakeup */
+ for (x = 0; x < 1000; ++x) /* wait 10 milliseconds */
+ tenmicrosec ();
+ OUTB (0x50, 0x201);
+ OUTB ((sbc_base & 0x70) | ((MPU_BASE & 0x30) >> 4), 0x201);
+
+ if (sb_reset_dsp ())
+ { /* OK. We have at least a SB */
+
+ /* Check the version number of ProSonic (I guess) */
+
+ if (!sb_dsp_command (0xFA))
+ return 1;
+ if (get_sb_byte () != 0x12)
+ return 1;
+
+ if (sb_dsp_command (0xFB) && /* set DMA-channels and Interrupts */
+ sb_dsp_command ((dma_translat[JAZZ_DMA16] << 4) | dma_translat[SBC_DMA]) &&
+ sb_dsp_command ((int_translat[MPU_IRQ] << 4) | int_translat[sbc_irq]))
+ {
+ Jazz16_detected = 1;
+#ifdef SM_WAVE
+ if (initialize_smw ())
+ Jazz16_detected = 2;
+#endif
+ sb_dsp_disable_midi ();
+ }
+
+ return 1; /* There was at least a SB */
+ }
+ return 0; /* No SB or ProSonic16 detected */
+}
+
+#endif /* ifdef JAZZ16 */
+
+int
+sb_dsp_detect (struct address_info *hw_config)
+{
+ sbc_base = hw_config->io_base;
+ sbc_irq = hw_config->irq;
+
+ if (sb_dsp_ok)
+ return 0; /*
+ * Already initialized
+ */
+#ifdef JAZZ16
+ dma8 = hw_config->dma;
+ dma16 = JAZZ_DMA16;
+
+ if (!initialize_ProSonic16 ())
+ return 0;
+#else
+ if (!sb_reset_dsp ())
+ return 0;
+#endif
+
+#ifdef PC98
+ switch (sbc_irq)
+ {
+ case 3:
+ sb_setmixer (IRQ_NR, 1);
+ break;
+ case 5:
+ sb_setmixer (IRQ_NR, 8);
+ break;
+ case 10:
+ sb_setmixer (IRQ_NR, 2);
+ break;
+ }
+ switch (hw_config->dma)
+ {
+ case 0:
+ sb_setmixer (DMA_NR, 1);
+ break;
+ case 3:
+ sb_setmixer (DMA_NR, 2);
+ break;
+ }
+#endif
+
+ return 1; /*
+ * Detected
+ */
+}
+
+#ifndef EXCLUDE_AUDIO
+static struct audio_operations sb_dsp_operations =
+{
+ "SoundBlaster",
+ NOTHING_SPECIAL,
+ AFMT_U8, /* Just 8 bits. Poor old SB */
+ NULL,
+ sb_dsp_open,
+ sb_dsp_close,
+ sb_dsp_output_block,
+ sb_dsp_start_input,
+ sb_dsp_ioctl,
+ sb_dsp_prepare_for_input,
+ sb_dsp_prepare_for_output,
+ sb_dsp_reset,
+ sb_dsp_halt_xfer,
+ NULL, /* local_qlen */
+ NULL /* copy_from_user */
+};
+
+#endif
+
+long
+sb_dsp_init (long mem_start, struct address_info *hw_config)
+{
+ int i;
+ int mixer_type = 0;
+
+ sbc_major = sbc_minor = 0;
+ sb_dsp_command (0xe1); /*
+ * Get version
+ */
+
+ for (i = 1000; i; i--)
+ {
+ if (INB (DSP_DATA_AVAIL) & 0x80)
+ { /*
+ * wait for Data Ready
+ */
+ if (sbc_major == 0)
+ sbc_major = INB (DSP_READ);
+ else
+ {
+ sbc_minor = INB (DSP_READ);
+ break;
+ }
+ }
+ }
+
+ if (sbc_major == 2 || sbc_major == 3)
+ sb_duplex_midi = 1;
+
+ if (sbc_major == 4)
+ sb16 = 1;
+
+#ifndef EXCLUDE_SBPRO
+ if (sbc_major >= 3)
+ mixer_type = sb_mixer_init (sbc_major);
+#else
+ if (sbc_major >= 3)
+ printk ("\n\n\n\nNOTE! SB Pro support is required with your soundcard!\n\n\n");
+#endif
+
+#ifndef EXCLUDE_YM3812
+
+#ifdef PC98
+ if (sbc_major > 3 ||
+ (sbc_major == 3 && INB (0x28d2) == 0x00))
+#else
+ if (sbc_major > 3 ||
+ (sbc_major == 3 && INB (0x388) == 0x00)) /* Should be 0x06 if not OPL-3 */
+#endif
+ enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH);
+#endif
+
+#ifndef EXCLUDE_AUDIO
+ if (sbc_major >= 3)
+ {
+ if (Jazz16_detected)
+ {
+ if (Jazz16_detected == 2)
+ sprintf (sb_dsp_operations.name, "SoundMan Wave %d.%d", sbc_major, sbc_minor);
+ else
+ sprintf (sb_dsp_operations.name, "MV Jazz16 %d.%d", sbc_major, sbc_minor);
+ sb_dsp_operations.format_mask |= AFMT_S16_LE; /* Hurrah, 16 bits */
+ }
+ else
+#ifdef __SGNXPRO__
+ if (mixer_type == 2)
+ {
+ sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
+ }
+ else
+#endif
+
+ if (sbc_major == 4)
+ {
+ sprintf (sb_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor);
+ }
+ else
+ {
+ sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
+ }
+ }
+ else
+ {
+ sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", sbc_major, sbc_minor);
+ }
+
+#if defined(__FreeBSD__)
+ printk ("sb0: <%s>", sb_dsp_operations.name);
+#else
+ printk (" <%s>", sb_dsp_operations.name);
+#endif
+
+#if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO)
+ if (!sb16) /*
+ * There is a better driver for SB16
+ */
+#endif
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_dev = num_audiodevs++] = &sb_dsp_operations;
+ audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT;
+ audio_devs[my_dev]->buffsize = (
+ (sbc_major > 2 || sbc_major == 2 && sbc_minor > 0) ?
+ 16 : 8) * 1024;
+ audio_devs[my_dev]->dmachan = hw_config->dma;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
+#else
+ printk (" <SoundBlaster (configured without audio support)>");
+#endif
+
+#ifndef EXCLUDE_MIDI
+ if (!midi_disabled && !sb16) /*
+ * Midi don't work in the SB emulation mode *
+ * of PAS, SB16 has better midi interface
+ */
+ sb_midi_init (sbc_major);
+#endif
+
+ sb_dsp_ok = 1;
+ return mem_start;
+}
+
+void
+sb_dsp_disable_midi (void)
+{
+ midi_disabled = 1;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/sb_midi.c b/sys/pc98/pc98/sound/sb_midi.c
new file mode 100644
index 0000000..f94f2c4
--- /dev/null
+++ b/sys/pc98/pc98/sound/sb_midi.c
@@ -0,0 +1,253 @@
+/*
+ * sound/sb_dsp.c
+ *
+ * The low level driver for the SoundBlaster DS chips.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_MIDI)
+
+#include "sb.h"
+#undef SB_TEST_IRQ
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+extern int sb_dsp_ok; /* Set to 1 atfer successful initialization */
+extern int sbc_base;
+
+extern int sb_midi_mode;
+extern int sb_midi_busy; /*
+
+
+ * * * * 1 if the process has output to MIDI
+ *
+ */
+extern int sb_dsp_busy;
+extern int sb_dsp_highspeed;
+
+extern volatile int sb_irq_mode;
+extern int sb_duplex_midi;
+extern int sb_intr_active;
+static int input_opened = 0;
+static int my_dev;
+
+void (*midi_input_intr) (int dev, unsigned char data);
+
+static int
+sb_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ int ret;
+
+ if (!sb_dsp_ok)
+ {
+ printk ("SB Error: MIDI hardware not installed\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (sb_midi_busy)
+ return RET_ERROR (EBUSY);
+
+ if (mode != OPEN_WRITE && !sb_duplex_midi)
+ {
+ if (num_midis == 1)
+ printk ("SoundBlaster: Midi input not currently supported\n");
+ return RET_ERROR (EPERM);
+ }
+
+ sb_midi_mode = NORMAL_MIDI;
+ if (mode != OPEN_WRITE)
+ {
+ if (sb_dsp_busy || sb_intr_active)
+ return RET_ERROR (EBUSY);
+ sb_midi_mode = UART_MIDI;
+ }
+
+ if (sb_dsp_highspeed)
+ {
+ printk ("SB Error: Midi output not possible during stereo or high speed audio\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ if (sb_midi_mode == UART_MIDI)
+ {
+ sb_irq_mode = IMODE_MIDI;
+
+ sb_reset_dsp ();
+
+ if (!sb_dsp_command (0x35))
+ return RET_ERROR (EIO); /*
+ * Enter the UART mode
+ */
+ sb_intr_active = 1;
+
+ if ((ret = sb_get_irq ()) < 0)
+ {
+ sb_reset_dsp ();
+ return 0; /*
+ * IRQ not free
+ */
+ }
+ input_opened = 1;
+ midi_input_intr = input;
+ }
+
+ sb_midi_busy = 1;
+
+ return 0;
+}
+
+static void
+sb_midi_close (int dev)
+{
+ if (sb_midi_mode == UART_MIDI)
+ {
+ sb_reset_dsp (); /*
+ * The only way to kill the UART mode
+ */
+ sb_free_irq ();
+ }
+ sb_intr_active = 0;
+ sb_midi_busy = 0;
+ input_opened = 0;
+}
+
+static int
+sb_midi_out (int dev, unsigned char midi_byte)
+{
+ unsigned long flags;
+
+ if (sb_midi_mode == NORMAL_MIDI)
+ {
+ DISABLE_INTR (flags);
+ if (sb_dsp_command (0x38))
+ sb_dsp_command (midi_byte);
+ else
+ printk ("SB Error: Unable to send a MIDI byte\n");
+ RESTORE_INTR (flags);
+ }
+ else
+ sb_dsp_command (midi_byte); /*
+ * UART write
+ */
+
+ return 1;
+}
+
+static int
+sb_midi_start_read (int dev)
+{
+ if (sb_midi_mode != UART_MIDI)
+ {
+ printk ("SoundBlaster: MIDI input not implemented.\n");
+ return RET_ERROR (EPERM);
+ }
+ return 0;
+}
+
+static int
+sb_midi_end_read (int dev)
+{
+ if (sb_midi_mode == UART_MIDI)
+ {
+ sb_reset_dsp ();
+ sb_intr_active = 0;
+ }
+ return 0;
+}
+
+static int
+sb_midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EPERM);
+}
+
+void
+sb_midi_interrupt (int dummy)
+{
+ unsigned long flags;
+ unsigned char data;
+
+ DISABLE_INTR (flags);
+
+ data = INB (DSP_READ);
+ if (input_opened)
+ midi_input_intr (my_dev, data);
+
+ RESTORE_INTR (flags);
+}
+
+#define MIDI_SYNTH_NAME "SoundBlaster Midi"
+#define MIDI_SYNTH_CAPS 0
+#include "midi_synth.h"
+
+static struct midi_operations sb_midi_operations =
+{
+ {"SoundBlaster", 0, 0, SNDCARD_SB},
+ &std_midi_synth,
+ {0},
+ sb_midi_open,
+ sb_midi_close,
+ sb_midi_ioctl,
+ sb_midi_out,
+ sb_midi_start_read,
+ sb_midi_end_read,
+ NULL, /*
+ * Kick
+ */
+ NULL, /*
+ * command
+ */
+ NULL, /*
+ * buffer_status
+ */
+ NULL
+};
+
+void
+sb_midi_init (int model)
+{
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return;
+ }
+
+ std_midi_synth.midi_dev = num_midis;
+ my_dev = num_midis;
+ midi_devs[num_midis++] = &sb_midi_operations;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/sb_mixer.c b/sys/pc98/pc98/sound/sb_mixer.c
new file mode 100644
index 0000000..b96c24c
--- /dev/null
+++ b/sys/pc98/pc98/sound/sb_mixer.c
@@ -0,0 +1,587 @@
+
+/*
+ * sound/sb_mixer.c
+ *
+ * The low level mixer driver for the SoundBlaster Pro and SB16 cards.
+ *
+ * Copyright by Hannu Savolainen 1994
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added code to support the Sound Galaxy NX Pro mixer.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_SBPRO)
+#define __SB_MIXER_C__
+
+#include "sb.h"
+#include "sb_mixer.h"
+#undef SB_TEST_IRQ
+
+extern int sbc_base;
+extern int sbc_major;
+extern int Jazz16_detected;
+
+static int mixer_initialized = 0;
+
+static int supported_rec_devices;
+static int supported_devices;
+static int recmask = 0;
+static int mixer_model;
+static int mixer_caps;
+static mixer_tab *iomap;
+
+void
+sb_setmixer (unsigned int port, unsigned int value)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /*
+ * Select register
+ */
+ tenmicrosec ();
+ OUTB ((unsigned char) (value & 0xff), MIXER_DATA);
+ tenmicrosec ();
+ RESTORE_INTR (flags);
+}
+
+int
+sb_getmixer (unsigned int port)
+{
+ int val;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB ((unsigned char) (port & 0xff), MIXER_ADDR); /*
+ * Select register
+ */
+ tenmicrosec ();
+ val = INB (MIXER_DATA);
+ tenmicrosec ();
+ RESTORE_INTR (flags);
+
+ return val;
+}
+
+void
+sb_mixer_set_stereo (int mode)
+{
+ if (!mixer_initialized)
+ return;
+
+ sb_setmixer (OUT_FILTER, ((sb_getmixer (OUT_FILTER) & ~STEREO_DAC)
+ | (mode ? STEREO_DAC : MONO_DAC)));
+}
+
+/*
+ * Returns:
+ * 0 No mixer detected.
+ * 1 Only a plain Sound Blaster Pro style mixer detected.
+ * 2 The Sound Galaxy NX Pro mixer detected.
+ */
+static int
+detect_mixer (void)
+{
+#ifdef __SGNXPRO__
+ int oldbass, oldtreble;
+
+#endif
+ int retcode = 1;
+
+ /*
+ * Detect the mixer by changing parameters of two volume channels. If the
+ * values read back match with the values written, the mixer is there (is
+ * it?)
+ */
+ sb_setmixer (FM_VOL, 0xff);
+ sb_setmixer (VOC_VOL, 0x33);
+
+ if (sb_getmixer (FM_VOL) != 0xff)
+ return 0; /*
+ * No match
+ */
+ if (sb_getmixer (VOC_VOL) != 0x33)
+ return 0;
+
+#ifdef __SGNXPRO__
+ /* Attempt to detect the SG NX Pro by check for valid bass/treble
+ * registers.
+ */
+ oldbass = sb_getmixer (BASS_LVL);
+ oldtreble = sb_getmixer (TREBLE_LVL);
+
+ sb_setmixer (BASS_LVL, 0xaa);
+ sb_setmixer (TREBLE_LVL, 0x55);
+
+ if ((sb_getmixer (BASS_LVL) != 0xaa) ||
+ (sb_getmixer (TREBLE_LVL) != 0x55))
+ {
+ retcode = 1; /* 1 == Only SB Pro detected */
+ }
+ else
+ retcode = 2; /* 2 == SG NX Pro detected */
+ /* Restore register in either case since SG NX Pro has EEPROM with
+ * 'preferred' values stored.
+ */
+ sb_setmixer (BASS_LVL, oldbass);
+ sb_setmixer (TREBLE_LVL, oldtreble);
+
+ /*
+ * If the SB version is 3.X (SB Pro), assume we have a SG NX Pro 16.
+ * In this case it's good idea to disable the Disney Sound Source
+ * compatibility mode. It's useless and just causes noise every time the
+ * LPT-port is accessed.
+ *
+ * Also place the card into WSS mode.
+ */
+ if (sbc_major == 3)
+ {
+ OUTB (0x01, sbc_base + 0x1c);
+ OUTB (0x00, sbc_base + 0x1a);
+ }
+
+#endif
+ return retcode;
+}
+
+static void
+change_bits (unsigned char *regval, int dev, int chn, int newval)
+{
+ unsigned char mask;
+ int shift;
+
+ mask = (1 << (*iomap)[dev][chn].nbits) - 1;
+ newval = (int) ((newval * mask) + 50) / 100; /*
+ * Scale it
+ */
+
+ shift = (*iomap)[dev][chn].bitoffs - (*iomap)[dev][LEFT_CHN].nbits + 1;
+
+ *regval &= ~(mask << shift); /*
+ * Filter out the previous value
+ */
+ *regval |= (newval & mask) << shift; /*
+ * Set the new value
+ */
+}
+
+static int
+sb_mixer_get (int dev)
+{
+ if (!((1 << dev) & supported_devices))
+ return RET_ERROR (EINVAL);
+
+ return levels[dev];
+}
+
+#ifdef JAZZ16
+static char smw_mix_regs[] = /* Left mixer registers */
+{
+ 0x0b, /* SOUND_MIXER_VOLUME */
+ 0x0d, /* SOUND_MIXER_BASS */
+ 0x0d, /* SOUND_MIXER_TREBLE */
+ 0x05, /* SOUND_MIXER_SYNTH */
+ 0x09, /* SOUND_MIXER_PCM */
+ 0x00, /* SOUND_MIXER_SPEAKER */
+ 0x03, /* SOUND_MIXER_LINE */
+ 0x01, /* SOUND_MIXER_MIC */
+ 0x07, /* SOUND_MIXER_CD */
+ 0x00, /* SOUND_MIXER_IMIX */
+ 0x00, /* SOUND_MIXER_ALTPCM */
+ 0x00, /* SOUND_MIXER_RECLEV */
+ 0x00, /* SOUND_MIXER_IGAIN */
+ 0x00, /* SOUND_MIXER_OGAIN */
+ 0x00, /* SOUND_MIXER_LINE1 */
+ 0x00, /* SOUND_MIXER_LINE2 */
+ 0x00 /* SOUND_MIXER_LINE3 */
+};
+
+static void
+smw_mixer_init (void)
+{
+ int i;
+
+ sb_setmixer (0x00, 0x18); /* Mute unused (Telephone) line */
+ sb_setmixer (0x10, 0x38); /* Config register 2 */
+
+ supported_devices = 0;
+ for (i = 0; i < sizeof (smw_mix_regs); i++)
+ if (smw_mix_regs[i] != 0)
+ supported_devices |= (1 << i);
+
+ supported_rec_devices = supported_devices &
+ ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM |
+ SOUND_MASK_VOLUME);
+}
+
+static int
+smw_mixer_set (int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+ int reg, val;
+
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (dev > 31)
+ return RET_ERROR (EINVAL);
+
+ if (!(supported_devices & (1 << dev))) /* Not supported */
+ return RET_ERROR (EINVAL);
+
+ switch (dev)
+ {
+ case SOUND_MIXER_VOLUME:
+ sb_setmixer (0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */
+ sb_setmixer (0x0c, 96 - (96 * right / 100));
+ break;
+
+ case SOUND_MIXER_BASS:
+ case SOUND_MIXER_TREBLE:
+ levels[dev] = left | (right << 8);
+
+ /* Set left bass and treble values */
+ val = ((levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / 100) << 4;
+ val |= ((levels[SOUND_MIXER_BASS] & 0xff) * 16 / 100) & 0x0f;
+ sb_setmixer (0x0d, val);
+
+ /* Set right bass and treble values */
+ val = (((levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / 100) << 4;
+ val |= (((levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / 100) & 0x0f;
+ sb_setmixer (0x0e, val);
+ break;
+
+ default:
+ reg = smw_mix_regs[dev];
+ if (reg == 0)
+ return RET_ERROR (EINVAL);
+ sb_setmixer (reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */
+ sb_setmixer (reg + 1, (24 - (24 * right / 100)) | 0x40);
+ }
+
+ levels[dev] = left | (right << 8);
+ return left | (right << 8);
+}
+
+#endif
+
+static int
+sb_mixer_set (int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+
+ int regoffs;
+ unsigned char val;
+
+#ifdef JAZZ16
+ if (Jazz16_detected == 2)
+ return smw_mixer_set (dev, value);
+#endif
+
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (dev > 31)
+ return RET_ERROR (EINVAL);
+
+ if (!(supported_devices & (1 << dev))) /*
+ * Not supported
+ */
+ return RET_ERROR (EINVAL);
+
+ regoffs = (*iomap)[dev][LEFT_CHN].regno;
+
+ if (regoffs == 0)
+ return RET_ERROR (EINVAL);
+
+ val = sb_getmixer (regoffs);
+ change_bits (&val, dev, LEFT_CHN, left);
+
+ levels[dev] = left | (left << 8);
+
+ if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) /*
+ * Change register
+ */
+ {
+ sb_setmixer (regoffs, val); /*
+ * Save the old one
+ */
+ regoffs = (*iomap)[dev][RIGHT_CHN].regno;
+
+ if (regoffs == 0)
+ return left | (left << 8); /*
+ * Just left channel present
+ */
+
+ val = sb_getmixer (regoffs); /*
+ * Read the new one
+ */
+ }
+
+ change_bits (&val, dev, RIGHT_CHN, right);
+
+ sb_setmixer (regoffs, val);
+
+ levels[dev] = left | (right << 8);
+ return left | (right << 8);
+}
+
+static void
+set_recsrc (int src)
+{
+ sb_setmixer (RECORD_SRC, (sb_getmixer (RECORD_SRC) & ~7) | (src & 0x7));
+}
+
+static int
+set_recmask (int mask)
+{
+ int devmask, i;
+ unsigned char regimageL, regimageR;
+
+ devmask = mask & supported_rec_devices;
+
+ switch (mixer_model)
+ {
+ case 3:
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE &&
+ devmask != SOUND_MASK_CD)
+ { /*
+ * More than one devices selected. Drop the *
+ * previous selection
+ */
+ devmask &= ~recmask;
+ }
+
+ if (devmask != SOUND_MASK_MIC &&
+ devmask != SOUND_MASK_LINE &&
+ devmask != SOUND_MASK_CD)
+ { /*
+ * More than one devices selected. Default to
+ * * mic
+ */
+ devmask = SOUND_MASK_MIC;
+ }
+
+
+ if (devmask ^ recmask) /*
+ * Input source changed
+ */
+ {
+ switch (devmask)
+ {
+
+ case SOUND_MASK_MIC:
+ set_recsrc (SRC_MIC);
+ break;
+
+ case SOUND_MASK_LINE:
+ set_recsrc (SRC_LINE);
+ break;
+
+ case SOUND_MASK_CD:
+ set_recsrc (SRC_CD);
+ break;
+
+ default:
+ set_recsrc (SRC_MIC);
+ }
+ }
+
+ break;
+
+ case 4:
+ if (!devmask)
+ devmask = SOUND_MASK_MIC;
+
+ regimageL = regimageR = 0;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & devmask)
+ {
+ regimageL |= sb16_recmasks_L[i];
+ regimageR |= sb16_recmasks_R[i];
+ }
+ sb_setmixer (SB16_IMASK_L, regimageL);
+ sb_setmixer (SB16_IMASK_R, regimageR);
+ break;
+ }
+
+ recmask = devmask;
+ return recmask;
+}
+
+static int
+sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
+{
+ if (((cmd >> 8) & 0xff) == 'M')
+ {
+ if (cmd & IOC_IN)
+ switch (cmd & 0xff)
+ {
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, set_recmask (IOCTL_IN (arg)));
+ break;
+
+ default:
+
+ return IOCTL_OUT (arg, sb_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
+ }
+ else
+ switch (cmd & 0xff) /*
+ * Return parameters
+ */
+ {
+
+ case SOUND_MIXER_RECSRC:
+ return IOCTL_OUT (arg, recmask);
+ break;
+
+ case SOUND_MIXER_DEVMASK:
+ return IOCTL_OUT (arg, supported_devices);
+ break;
+
+ case SOUND_MIXER_STEREODEVS:
+ if (Jazz16_detected)
+ return IOCTL_OUT (arg, supported_devices);
+ else
+ return IOCTL_OUT (arg, supported_devices &
+ ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER));
+ break;
+
+ case SOUND_MIXER_RECMASK:
+ return IOCTL_OUT (arg, supported_rec_devices);
+ break;
+
+ case SOUND_MIXER_CAPS:
+ return IOCTL_OUT (arg, mixer_caps);
+ break;
+
+ default:
+ return IOCTL_OUT (arg, sb_mixer_get (cmd & 0xff));
+ }
+ }
+ else
+ return RET_ERROR (EINVAL);
+}
+
+static struct mixer_operations sb_mixer_operations =
+{
+ "SoundBlaster",
+ sb_mixer_ioctl
+};
+
+static void
+sb_mixer_reset (void)
+{
+ int i;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ sb_mixer_set (i, levels[i]);
+ set_recmask (SOUND_MASK_MIC);
+}
+
+/*
+ * Returns a code depending on whether a SG NX Pro was detected.
+ * 1 == Plain SB Pro
+ * 2 == SG NX Pro detected.
+ * 3 == SB16
+ *
+ * Used to update message.
+ */
+int
+sb_mixer_init (int major_model)
+{
+ int mixer_type = 0;
+
+ sb_setmixer (0x00, 0); /* Reset mixer */
+
+ if (!(mixer_type = detect_mixer ()))
+ return 0; /* No mixer. Why? */
+
+ mixer_initialized = 1;
+ mixer_model = major_model;
+
+ switch (major_model)
+ {
+ case 3:
+ mixer_caps = SOUND_CAP_EXCL_INPUT;
+
+#ifdef JAZZ16
+ if (Jazz16_detected == 2) /* SM Wave */
+ {
+ supported_devices = 0;
+ supported_rec_devices = 0;
+ iomap = &sbpro_mix;
+ smw_mixer_init ();
+ mixer_type = 1;
+ }
+ else
+#endif
+#ifdef __SGNXPRO__
+ if (mixer_type == 2) /* A SGNXPRO was detected */
+ {
+ supported_devices = SGNXPRO_MIXER_DEVICES;
+ supported_rec_devices = SGNXPRO_RECORDING_DEVICES;
+ iomap = &sgnxpro_mix;
+ }
+ else
+#endif
+ {
+ supported_devices = SBPRO_MIXER_DEVICES;
+ supported_rec_devices = SBPRO_RECORDING_DEVICES;
+ iomap = &sbpro_mix;
+ mixer_type = 1;
+ }
+ break;
+
+ case 4:
+ mixer_caps = 0;
+ supported_devices = SB16_MIXER_DEVICES;
+ supported_rec_devices = SB16_RECORDING_DEVICES;
+ iomap = &sb16_mix;
+ mixer_type = 3;
+ break;
+
+ default:
+ printk ("SB Warning: Unsupported mixer type\n");
+ return 0;
+ }
+
+ if (num_mixers < MAX_MIXER_DEV)
+ mixer_devs[num_mixers++] = &sb_mixer_operations;
+ sb_mixer_reset ();
+ return mixer_type;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/sb_mixer.h b/sys/pc98/pc98/sound/sb_mixer.h
new file mode 100644
index 0000000..b712eea
--- /dev/null
+++ b/sys/pc98/pc98/sound/sb_mixer.h
@@ -0,0 +1,256 @@
+/*
+ * sound/sb_mixer.h
+ *
+ * Definitions for the SB Pro and SB16 mixers
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * Modified:
+ * Hunyue Yau Jan 6 1994
+ * Added defines for the Sound Galaxy NX Pro mixer.
+ *
+ */
+
+#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
+
+/* Same as SB Pro, unless I find otherwise */
+#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
+
+#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | SOUND_MASK_VOLUME)
+
+/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
+ * channel is the COVOX/DisneySoundSource emulation volume control
+ * on the mixer. It does NOT control speaker volume. Should have own
+ * mask eventually?
+ */
+#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
+ SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
+
+#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD)
+
+#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+ SOUND_MASK_CD | \
+ SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
+ SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE)
+
+/*
+ * Mixer registers
+ *
+ * NOTE! RECORD_SRC == IN_FILTER
+ */
+
+/*
+ * Mixer registers of SB Pro
+ */
+#define VOC_VOL 0x04
+#define MIC_VOL 0x0A
+#define MIC_MIX 0x0A
+#define RECORD_SRC 0x0C
+#define IN_FILTER 0x0C
+#define OUT_FILTER 0x0E
+#define MASTER_VOL 0x22
+#define FM_VOL 0x26
+#define CD_VOL 0x28
+#define LINE_VOL 0x2E
+#define IRQ_NR 0x80
+#define DMA_NR 0x81
+#define IRQ_STAT 0x82
+#define OPSW 0x3c
+
+/*
+ * Additional registers on the SG NX Pro
+ */
+#define COVOX_VOL 0x42
+#define TREBLE_LVL 0x44
+#define BASS_LVL 0x46
+
+#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
+#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
+#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
+#define FILT_OFF (1 << 5)
+
+#define MONO_DAC 0x00
+#define STEREO_DAC 0x02
+
+/*
+ * Mixer registers of SB16
+ */
+#define SB16_IMASK_L 0x3d
+#define SB16_IMASK_R 0x3e
+
+#define LEFT_CHN 0
+#define RIGHT_CHN 1
+
+struct mixer_def {
+ unsigned int regno: 8;
+ unsigned int bitoffs:4;
+ unsigned int nbits:4;
+};
+
+
+typedef struct mixer_def mixer_tab[32][2];
+typedef struct mixer_def mixer_ent;
+
+#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \
+ {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
+
+#ifdef __SB_MIXER_C__
+static mixer_tab sbpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
+};
+
+#ifdef __SGNXPRO__
+static mixer_tab sgnxpro_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
+MIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
+MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
+MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0)
+};
+#endif
+
+static mixer_tab sb16_mix = {
+MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
+MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
+MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4),
+MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5),
+MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5),
+MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5),
+MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5),
+MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
+MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */
+MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2),
+MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2)
+};
+
+#ifdef SM_GAMES /* Master volume is lower and PCM & FM volumes
+ higher than with SB Pro. This improves the
+ sound quality */
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x2020, /* Master Volume */
+ 0x4b4b, /* Bass */
+ 0x4b4b, /* Treble */
+ 0x6464, /* FM */
+ 0x6464, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x0000, /* Mic */
+ 0x4b4b, /* CD */
+ 0x4b4b, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b, /* Recording level */
+ 0x4b4b, /* Input gain */
+ 0x4b4b}; /* Output gain */
+
+#else /* If the user selected just plain SB Pro */
+
+static unsigned short levels[SOUND_MIXER_NRDEVICES] =
+{
+ 0x5a5a, /* Master Volume */
+ 0x4b4b, /* Bass */
+ 0x4b4b, /* Treble */
+ 0x4b4b, /* FM */
+ 0x4b4b, /* PCM */
+ 0x4b4b, /* PC Speaker */
+ 0x4b4b, /* Ext Line */
+ 0x1010, /* Mic */
+ 0x4b4b, /* CD */
+ 0x4b4b, /* Recording monitor */
+ 0x4b4b, /* SB PCM */
+ 0x4b4b, /* Recording level */
+ 0x4b4b, /* Input gain */
+ 0x4b4b}; /* Output gain */
+#endif /* SM_GAMES */
+
+static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
+{
+ 0x00, /* SOUND_MIXER_VOLUME */
+ 0x00, /* SOUND_MIXER_BASS */
+ 0x00, /* SOUND_MIXER_TREBLE */
+ 0x40, /* SOUND_MIXER_SYNTH */
+ 0x00, /* SOUND_MIXER_PCM */
+ 0x00, /* SOUND_MIXER_SPEAKER */
+ 0x10, /* SOUND_MIXER_LINE */
+ 0x01, /* SOUND_MIXER_MIC */
+ 0x04, /* SOUND_MIXER_CD */
+ 0x00, /* SOUND_MIXER_IMIX */
+ 0x00, /* SOUND_MIXER_ALTPCM */
+ 0x00, /* SOUND_MIXER_RECLEV */
+ 0x00, /* SOUND_MIXER_IGAIN */
+ 0x00 /* SOUND_MIXER_OGAIN */
+};
+
+static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
+{
+ 0x00, /* SOUND_MIXER_VOLUME */
+ 0x00, /* SOUND_MIXER_BASS */
+ 0x00, /* SOUND_MIXER_TREBLE */
+ 0x20, /* SOUND_MIXER_SYNTH */
+ 0x00, /* SOUND_MIXER_PCM */
+ 0x00, /* SOUND_MIXER_SPEAKER */
+ 0x08, /* SOUND_MIXER_LINE */
+ 0x01, /* SOUND_MIXER_MIC */
+ 0x02, /* SOUND_MIXER_CD */
+ 0x00, /* SOUND_MIXER_IMIX */
+ 0x00, /* SOUND_MIXER_ALTPCM */
+ 0x00, /* SOUND_MIXER_RECLEV */
+ 0x00, /* SOUND_MIXER_IGAIN */
+ 0x00 /* SOUND_MIXER_OGAIN */
+};
+
+/*
+ * Recording sources (SB Pro)
+ */
+
+#define SRC_MIC 1 /* Select Microphone recording source */
+#define SRC_CD 3 /* Select CD recording source */
+#define SRC_LINE 7 /* Use Line-in for recording source */
+
+#endif
diff --git a/sys/pc98/pc98/sound/sequencer.c b/sys/pc98/pc98/sound/sequencer.c
new file mode 100644
index 0000000..f6356f1
--- /dev/null
+++ b/sys/pc98/pc98/sound/sequencer.c
@@ -0,0 +1,1988 @@
+/*
+ * sound/sequencer.c
+ *
+ * The sequencer personality manager.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+#include "midi_ctrl.h"
+
+extern void seq_drain_midi_queues __P((void));
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_SEQUENCER
+
+static int sequencer_ok = 0;
+static struct sound_timer_operations *tmr;
+static int tmr_no = -1; /* Currently selected timer */
+static int pending_timer = -1; /* For timer change operation */
+
+/*
+ * Local counts for number of synth and MIDI devices. These are initialized
+ * by the sequencer_open.
+ */
+static int max_mididev = 0;
+static int max_synthdev = 0;
+
+/*
+ * The seq_mode gives the operating mode of the sequencer:
+ * 1 = level1 (the default)
+ * 2 = level2 (extended capabilites)
+ */
+
+#define SEQ_1 1
+#define SEQ_2 2
+static int seq_mode = SEQ_1;
+
+DEFINE_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
+DEFINE_WAIT_QUEUE (midi_sleeper, midi_sleep_flag);
+
+static int midi_opened[MAX_MIDI_DEV] =
+{0};
+static int midi_written[MAX_MIDI_DEV] =
+{0};
+
+static unsigned long prev_input_time = 0;
+static int prev_event_time;
+static unsigned long seq_time = 0;
+
+#include "tuning.h"
+
+#define EV_SZ 8
+#define IEV_SZ 8
+static unsigned char *queue = NULL;
+static unsigned char *iqueue = NULL;
+
+static volatile int qhead = 0, qtail = 0, qlen = 0;
+static volatile int iqhead = 0, iqtail = 0, iqlen = 0;
+static volatile int seq_playing = 0;
+static int sequencer_busy = 0;
+static int output_treshold;
+static int pre_event_timeout;
+static unsigned synth_open_mask;
+
+static int seq_queue (unsigned char *note, char nonblock);
+static void seq_startplay (void);
+static int seq_sync (void);
+static void seq_reset (void);
+static int pmgr_present[MAX_SYNTH_DEV] =
+{0};
+
+#if MAX_SYNTH_DEV > 15
+#error Too many synthesizer devices enabled.
+#endif
+
+int
+sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ int c = count, p = 0;
+ int ev_len;
+ unsigned long flags;
+
+ dev = dev >> 4;
+
+ ev_len = seq_mode == SEQ_1 ? 4 : 8;
+
+ if (dev) /*
+ * Patch manager device
+ */
+ return pmgr_read (dev - 1, file, buf, count);
+
+ DISABLE_INTR (flags);
+ if (!iqlen)
+ {
+ if (ISSET_FILE_FLAG (file, O_NONBLOCK))
+ {
+ RESTORE_INTR (flags);
+ return RET_ERROR (EAGAIN);
+ }
+
+ DO_SLEEP (midi_sleeper, midi_sleep_flag, pre_event_timeout);
+
+ if (!iqlen)
+ {
+ RESTORE_INTR (flags);
+ return 0;
+ }
+ }
+
+ while (iqlen && c >= ev_len)
+ {
+
+ COPY_TO_USER (buf, p, &iqueue[iqhead * IEV_SZ], ev_len);
+ p += ev_len;
+ c -= ev_len;
+
+ iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
+ iqlen--;
+ }
+ RESTORE_INTR (flags);
+
+ return count - c;
+}
+
+static void
+sequencer_midi_output (int dev)
+{
+ /*
+ * Currently NOP
+ */
+}
+
+void
+seq_copy_to_input (unsigned char *event, int len)
+{
+ unsigned long flags;
+
+ /*
+ * Verify that the len is valid for the current mode.
+ */
+
+ if (len != 4 && len != 8)
+ return;
+ if ((seq_mode == SEQ_1) != (len == 4))
+ return;
+
+ if (iqlen >= (SEQ_MAX_QUEUE - 1))
+ return; /* Overflow */
+
+ DISABLE_INTR (flags);
+ memcpy (&iqueue[iqtail * IEV_SZ], event, len);
+ iqlen++;
+ iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
+
+ if (SOMEONE_WAITING (midi_sleeper, midi_sleep_flag))
+ {
+ WAKE_UP (midi_sleeper, midi_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+#if defined(__FreeBSD__)
+ if (selinfo[0].si_pid)
+ selwakeup(&selinfo[0]);
+#endif
+}
+
+static void
+sequencer_midi_input (int dev, unsigned char data)
+{
+ unsigned int tstamp;
+ unsigned char event[4];
+
+ if (data == 0xfe) /* Ignore active sensing */
+ return;
+
+ tstamp = GET_TIME () - seq_time;
+ if (tstamp != prev_input_time)
+ {
+ tstamp = (tstamp << 8) | SEQ_WAIT;
+
+ seq_copy_to_input ((unsigned char *) &tstamp, 4);
+ prev_input_time = tstamp;
+ }
+
+ event[0] = SEQ_MIDIPUTC;
+ event[1] = data;
+ event[2] = dev;
+ event[3] = 0;
+
+ seq_copy_to_input (event, 4);
+}
+
+void
+seq_input_event (unsigned char *event, int len)
+{
+ unsigned long this_time;
+
+ if (seq_mode == SEQ_2)
+ this_time = tmr->get_time (tmr_no);
+ else
+ this_time = GET_TIME () - seq_time;
+
+ if (this_time != prev_input_time)
+ {
+ unsigned char tmp_event[8];
+
+ tmp_event[0] = EV_TIMING;
+ tmp_event[1] = TMR_WAIT_ABS;
+ tmp_event[2] = 0;
+ tmp_event[3] = 0;
+ *(unsigned long *) &tmp_event[4] = this_time;
+
+ seq_copy_to_input (tmp_event, 8);
+ prev_input_time = this_time;
+ }
+
+ seq_copy_to_input (event, len);
+}
+
+int
+sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ unsigned char event[EV_SZ], ev_code;
+ int p = 0, c, ev_size;
+ int err;
+ int mode = file->mode & O_ACCMODE;
+
+ dev = dev >> 4;
+
+ DEB (printk ("sequencer_write(dev=%d, count=%d)\n", dev, count));
+
+ if (mode == OPEN_READ)
+ return RET_ERROR (EIO);
+
+ if (dev) /*
+ * Patch manager device
+ */
+ return pmgr_write (dev - 1, file, buf, count);
+
+ c = count;
+
+ while (c >= 4)
+ {
+ COPY_FROM_USER (event, buf, p, 4);
+ ev_code = event[0];
+
+ if (ev_code == SEQ_FULLSIZE)
+ {
+ int err;
+
+ dev = *(unsigned short *) &event[2];
+ if (dev < 0 || dev >= max_synthdev)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)))
+ return RET_ERROR (ENXIO);
+
+ err = synth_devs[dev]->load_patch (dev, *(short *) &event[0], buf, p + 4, c, 0);
+ if (err < 0)
+ return err;
+
+ return err;
+ }
+
+ if (ev_code >= 128)
+ {
+ if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
+ {
+ printk ("Sequencer: Invalid level 2 event %x\n", ev_code);
+ return RET_ERROR (EINVAL);
+ }
+
+ ev_size = 8;
+
+ if (c < ev_size)
+ {
+ if (!seq_playing)
+ seq_startplay ();
+ return count - c;
+ }
+
+ COPY_FROM_USER (&event[4], buf, p + 4, 4);
+
+ }
+ else
+ {
+ if (seq_mode == SEQ_2)
+ {
+ printk ("Sequencer: 4 byte event in level 2 mode\n");
+ return RET_ERROR (EINVAL);
+ }
+ ev_size = 4;
+ }
+
+ if (event[0] == SEQ_MIDIPUTC)
+ {
+
+ if (!midi_opened[event[2]])
+ {
+ int mode;
+ int dev = event[2];
+
+ if (dev >= max_mididev)
+ {
+ printk ("Sequencer Error: Nonexistent MIDI device %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ mode = file->mode & O_ACCMODE;
+
+ if ((err = midi_devs[dev]->open (dev, mode,
+ sequencer_midi_input, sequencer_midi_output)) < 0)
+ {
+ seq_reset ();
+ printk ("Sequencer Error: Unable to open Midi #%d\n", dev);
+ return err;
+ }
+
+ midi_opened[dev] = 1;
+ }
+
+ }
+
+ if (!seq_queue (event, ISSET_FILE_FLAG (file, O_NONBLOCK)))
+ {
+ int processed = count - c;
+
+ if (!seq_playing)
+ seq_startplay ();
+
+ if (!processed && ISSET_FILE_FLAG (file, O_NONBLOCK))
+ return RET_ERROR (EAGAIN);
+ else
+ return processed;
+ }
+
+ p += ev_size;
+ c -= ev_size;
+ }
+
+ if (!seq_playing)
+ seq_startplay ();
+
+ return count; /* This will "eat" chunks shorter than 4 bytes (if written
+ * alone) Should we really do that ?
+ */
+}
+
+static int
+seq_queue (unsigned char *note, char nonblock)
+{
+
+ /*
+ * Test if there is space in the queue
+ */
+
+ if (qlen >= SEQ_MAX_QUEUE)
+ if (!seq_playing)
+ seq_startplay (); /*
+ * Give chance to drain the queue
+ */
+
+ if (!nonblock && qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ /*
+ * Sleep until there is enough space on the queue
+ */
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
+ }
+
+ if (qlen >= SEQ_MAX_QUEUE)
+ {
+ return 0; /*
+ * To be sure
+ */
+ }
+ memcpy (&queue[qtail * EV_SZ], note, EV_SZ);
+
+ qtail = (qtail + 1) % SEQ_MAX_QUEUE;
+ qlen++;
+
+ return 1;
+}
+
+static int
+extended_event (unsigned char *q)
+{
+ int dev = q[2];
+
+ if (dev < 0 || dev >= max_synthdev)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)))
+ return RET_ERROR (ENXIO);
+
+ switch (q[1])
+ {
+ case SEQ_NOTEOFF:
+ synth_devs[dev]->kill_note (dev, q[3], q[4], q[5]);
+ break;
+
+ case SEQ_NOTEON:
+ if (q[4] > 127 && q[4] != 255)
+ return 0;
+
+ synth_devs[dev]->start_note (dev, q[3], q[4], q[5]);
+ break;
+
+ case SEQ_PGMCHANGE:
+ synth_devs[dev]->set_instr (dev, q[3], q[4]);
+ break;
+
+ case SEQ_AFTERTOUCH:
+ synth_devs[dev]->aftertouch (dev, q[3], q[4]);
+ break;
+
+ case SEQ_BALANCE:
+ synth_devs[dev]->panning (dev, q[3], (char) q[4]);
+ break;
+
+ case SEQ_CONTROLLER:
+ synth_devs[dev]->controller (dev, q[3], q[4], *(short *) &q[5]);
+ break;
+
+ case SEQ_VOLMODE:
+ if (synth_devs[dev]->volume_method != NULL)
+ synth_devs[dev]->volume_method (dev, q[3]);
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return 0;
+}
+
+static int
+find_voice (int dev, int chn, int note)
+{
+ unsigned short key;
+ int i;
+
+ key = (chn << 8) | (note + 1);
+
+ for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+ if (synth_devs[dev]->alloc.map[i] == key)
+ return i;
+
+ return -1;
+}
+
+static int
+alloc_voice (int dev, int chn, int note)
+{
+ unsigned short key;
+ int voice;
+
+ key = (chn << 8) | (note + 1);
+
+ voice = synth_devs[dev]->alloc_voice (dev, chn, note,
+ &synth_devs[dev]->alloc);
+ synth_devs[dev]->alloc.map[voice] = key;
+ synth_devs[dev]->alloc.alloc_times[voice] =
+ synth_devs[dev]->alloc.timestamp++;
+ return voice;
+}
+
+static void
+seq_chn_voice_event (unsigned char *event)
+{
+ unsigned char dev = event[1];
+ unsigned char cmd = event[2];
+ unsigned char chn = event[3];
+ unsigned char note = event[4];
+ unsigned char parm = event[5];
+ int voice = -1;
+
+ if ((int) dev > max_synthdev)
+ return;
+ if (!(synth_open_mask & (1 << dev)))
+ return;
+ if (!synth_devs[dev])
+ return;
+
+ if (seq_mode == SEQ_2)
+ {
+ if (synth_devs[dev]->alloc_voice)
+ voice = find_voice (dev, chn, note);
+
+ if (cmd == MIDI_NOTEON && parm == 0)
+ {
+ cmd = MIDI_NOTEOFF;
+ parm = 64;
+ }
+ }
+
+ switch (cmd)
+ {
+ case MIDI_NOTEON:
+ if (note > 127 && note != 255) /* Not a seq2 feature */
+ return;
+
+ if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
+ { /* Internal synthesizer (FM, GUS, etc) */
+ voice = alloc_voice (dev, chn, note);
+ }
+
+ if (voice == -1)
+ voice = chn;
+
+ if (seq_mode == SEQ_2 && dev < num_synths)
+ {
+ /*
+ * The MIDI channel 10 is a percussive channel. Use the note
+ * number to select the proper patch (128 to 255) to play.
+ */
+
+ if (chn == 9)
+ {
+ synth_devs[dev]->set_instr (dev, voice, 128 + note);
+ note = 60; /* Middle C */
+
+ }
+ }
+
+ if (seq_mode == SEQ_2)
+ {
+ synth_devs[dev]->setup_voice (dev, voice, chn);
+ }
+
+ synth_devs[dev]->start_note (dev, voice, note, parm);
+ break;
+
+ case MIDI_NOTEOFF:
+ if (voice == -1)
+ voice = chn;
+ synth_devs[dev]->kill_note (dev, voice, note, parm);
+ break;
+
+ case MIDI_KEY_PRESSURE:
+ if (voice == -1)
+ voice = chn;
+ synth_devs[dev]->aftertouch (dev, voice, parm);
+ break;
+
+ default:;
+ }
+}
+
+static void
+seq_chn_common_event (unsigned char *event)
+{
+ unsigned char dev = event[1];
+ unsigned char cmd = event[2];
+ unsigned char chn = event[3];
+ unsigned char p1 = event[4];
+
+ /* unsigned char p2 = event[5]; */
+ unsigned short w14 = *(short *) &event[6];
+
+ if ((int) dev > max_synthdev)
+ return;
+ if (!(synth_open_mask & (1 << dev)))
+ return;
+ if (!synth_devs[dev])
+ return;
+
+ switch (cmd)
+ {
+ case MIDI_PGM_CHANGE:
+ if (seq_mode == SEQ_2)
+ {
+ synth_devs[dev]->chn_info[chn].pgm_num = p1;
+ if (dev >= num_synths)
+ synth_devs[dev]->set_instr (dev, chn, p1);
+ }
+ else
+ synth_devs[dev]->set_instr (dev, chn, p1);
+
+ break;
+
+ case MIDI_CTL_CHANGE:
+
+ if (seq_mode == SEQ_2)
+ {
+ if (chn > 15 || p1 > 127)
+ break;
+
+ synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f;
+
+ if (dev < num_synths)
+ {
+ int val = w14 & 0x7f;
+ int i, key;
+
+ if (p1 < 64) /* Combine MSB and LSB */
+ {
+ val = ((synth_devs[dev]->
+ chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
+ | (synth_devs[dev]->
+ chn_info[chn].controllers[p1 | 32] & 0x7f);
+ p1 &= ~32;
+ }
+
+ /* Handle all playing notes on this channel */
+
+ key = (chn << 8);
+
+ for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+ if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+ synth_devs[dev]->controller (dev, i, p1, val);
+ }
+ else
+ synth_devs[dev]->controller (dev, chn, p1, w14);
+ }
+ else /* Mode 1 */
+ synth_devs[dev]->controller (dev, chn, p1, w14);
+ break;
+
+ case MIDI_PITCH_BEND:
+ if (seq_mode == SEQ_2)
+ {
+ synth_devs[dev]->chn_info[chn].bender_value = w14;
+
+ if (dev < num_synths)
+ { /* Handle all playing notes on this channel */
+ int i, key;
+
+ key = (chn << 8);
+
+ for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+ if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+ synth_devs[dev]->bender (dev, i, w14);
+ }
+ else
+ synth_devs[dev]->bender (dev, chn, w14);
+ }
+ else /* MODE 1 */
+ synth_devs[dev]->bender (dev, chn, w14);
+ break;
+
+ default:;
+ }
+}
+
+static int
+seq_timing_event (unsigned char *event)
+{
+ unsigned char cmd = event[1];
+ unsigned int parm = *(int *) &event[4];
+
+ if (seq_mode == SEQ_2)
+ {
+ int ret;
+
+ if ((ret = tmr->event (tmr_no, event)) == TIMER_ARMED)
+ {
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+#if defined(__FreeBSD__)
+ /* must issue a wakeup for anyone waiting (select) XXX */
+#endif
+ }
+ }
+ return ret;
+ }
+
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+
+ /*
+ * NOTE! No break here. Execution of TMR_WAIT_REL continues in the
+ * next case (TMR_WAIT_ABS)
+ */
+
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ seq_playing = 1;
+ time = parm;
+ prev_event_time = time;
+
+ request_sound_timer (time);
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+#if defined(__FreeBSD__)
+ /* must issue a wakeup for select XXX */
+#endif
+ }
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ seq_time = GET_TIME ();
+ prev_input_time = 0;
+ prev_event_time = 0;
+ break;
+
+ case TMR_STOP:
+ break;
+
+ case TMR_CONTINUE:
+ break;
+
+ case TMR_TEMPO:
+ break;
+
+ case TMR_ECHO:
+ if (seq_mode == SEQ_2)
+ seq_copy_to_input (event, 8);
+ else
+ {
+ parm = (parm << 8 | SEQ_ECHO);
+ seq_copy_to_input ((unsigned char *) &parm, 4);
+ }
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static void
+seq_local_event (unsigned char *event)
+{
+ /* unsigned char cmd = event[1]; */
+
+ printk ("seq_local_event() called. WHY????????\n");
+}
+
+static int
+play_event (unsigned char *q)
+{
+ /*
+ * NOTE! This routine returns
+ * 0 = normal event played.
+ * 1 = Timer armed. Suspend playback until timer callback.
+ * 2 = MIDI output buffer full. Restore queue and suspend until timer
+ */
+ unsigned long *delay;
+
+ switch (q[0])
+ {
+ case SEQ_NOTEOFF:
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->kill_note (0, q[1], 255, q[3]);
+ break;
+
+ case SEQ_NOTEON:
+ if (q[4] < 128 || q[4] == 255)
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->start_note (0, q[1], q[2], q[3]);
+ break;
+
+ case SEQ_WAIT:
+ delay = (unsigned long *) q; /*
+ * Bytes 1 to 3 are containing the *
+ * delay in GET_TIME()
+ */
+ *delay = (*delay >> 8) & 0xffffff;
+
+ if (*delay > 0)
+ {
+ long time;
+
+ seq_playing = 1;
+ time = *delay;
+ prev_event_time = time;
+
+ request_sound_timer (time);
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+#if defined(__FreeBSD__)
+ /* must issue a wakeup for selects XXX */
+#endif
+ }
+ /*
+ * The timer is now active and will reinvoke this function
+ * after the timer expires. Return to the caller now.
+ */
+ return 1;
+ }
+ break;
+
+ case SEQ_PGMCHANGE:
+ if (synth_open_mask & (1 << 0))
+ if (synth_devs[0])
+ synth_devs[0]->set_instr (0, q[1], q[2]);
+ break;
+
+ case SEQ_SYNCTIMER: /*
+ * Reset timer
+ */
+ seq_time = GET_TIME ();
+ prev_input_time = 0;
+ prev_event_time = 0;
+ break;
+
+ case SEQ_MIDIPUTC: /*
+ * Put a midi character
+ */
+ if (midi_opened[q[2]])
+ {
+ int dev;
+
+ dev = q[2];
+
+ if (!midi_devs[dev]->putc (dev, q[1]))
+ {
+ /*
+ * Output FIFO is full. Wait one timer cycle and try again.
+ */
+
+ seq_playing = 1;
+ request_sound_timer (-1);
+ return 2;
+ }
+ else
+ midi_written[dev] = 1;
+ }
+ break;
+
+ case SEQ_ECHO:
+ seq_copy_to_input (q, 4); /*
+ * Echo back to the process
+ */
+ break;
+
+ case SEQ_PRIVATE:
+ if ((int) q[1] < max_synthdev)
+ synth_devs[q[1]]->hw_control (q[1], q);
+ break;
+
+ case SEQ_EXTENDED:
+ extended_event (q);
+ break;
+
+ case EV_CHN_VOICE:
+ seq_chn_voice_event (q);
+ break;
+
+ case EV_CHN_COMMON:
+ seq_chn_common_event (q);
+ break;
+
+ case EV_TIMING:
+ if (seq_timing_event (q) == TIMER_ARMED)
+ {
+ return 1;
+ }
+ break;
+
+ case EV_SEQ_LOCAL:
+ seq_local_event (q);
+ break;
+
+ default:;
+ }
+
+ return 0;
+}
+
+static void
+seq_startplay (void)
+{
+ unsigned long flags;
+ int this_one, action;
+
+ while (qlen > 0)
+ {
+
+ DISABLE_INTR (flags);
+ qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
+ qlen--;
+ RESTORE_INTR (flags);
+
+ seq_playing = 1;
+
+ if ((action = play_event (&queue[this_one * EV_SZ])))
+ { /* Suspend playback. Next timer routine invokes this routine again */
+ if (action == 2)
+ {
+ qlen++;
+ qhead = this_one;
+ }
+ return;
+ }
+
+ }
+
+ seq_playing = 0;
+
+ if ((SEQ_MAX_QUEUE - qlen) >= output_treshold)
+ {
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+#if defined(__FreeBSD__)
+ /* must issue a wakeup for selects XXX */
+#endif
+ }
+}
+
+static void
+reset_controllers (int dev, unsigned char *controller, int update_dev)
+{
+
+ int i;
+
+ for (i = 0; i < 128; i++)
+ controller[i] = ctrl_def_values[i];
+}
+
+static void
+setup_mode2 (void)
+{
+ int dev;
+
+ max_synthdev = num_synths;
+
+ for (dev = 0; dev < num_midis; dev++)
+ if (midi_devs[dev]->converter != NULL)
+ {
+ synth_devs[max_synthdev++] =
+ midi_devs[dev]->converter;
+ }
+
+ for (dev = 0; dev < max_synthdev; dev++)
+ {
+ int chn;
+
+ for (chn = 0; chn < 16; chn++)
+ {
+ synth_devs[dev]->chn_info[chn].pgm_num = 0;
+ reset_controllers (dev,
+ synth_devs[dev]->chn_info[chn].controllers,
+ 0);
+ synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */
+ }
+ }
+
+ max_mididev = 0;
+ seq_mode = SEQ_2;
+}
+
+int
+sequencer_open (int dev, struct fileinfo *file)
+{
+ int retval, mode, i;
+ int level, tmp;
+
+ level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
+
+ dev = dev >> 4;
+ mode = file->mode & O_ACCMODE;
+
+ DEB (printk ("sequencer_open(dev=%d)\n", dev));
+
+ if (!sequencer_ok)
+ {
+ printk ("Soundcard: Sequencer not initialized\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (dev) /*
+ * Patch manager device
+ */
+ {
+ int err;
+
+ dev--;
+
+ if (dev >= MAX_SYNTH_DEV)
+ return RET_ERROR (ENXIO);
+ if (pmgr_present[dev])
+ return RET_ERROR (EBUSY);
+ if ((err = pmgr_open (dev)) < 0)
+ return err; /*
+ * Failed
+ */
+
+ pmgr_present[dev] = 1;
+ return err;
+ }
+
+ if (sequencer_busy)
+ {
+ printk ("Sequencer busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ max_mididev = num_midis;
+ max_synthdev = num_synths;
+ pre_event_timeout = 0;
+ seq_mode = SEQ_1;
+
+ if (pending_timer != -1)
+ {
+ tmr_no = pending_timer;
+ pending_timer = -1;
+ }
+
+ if (tmr_no == -1) /* Not selected yet */
+ {
+ int i, best;
+
+ best = -1;
+ for (i = 0; i < num_sound_timers; i++)
+ if (sound_timer_devs[i]->priority > best)
+ {
+ tmr_no = i;
+ best = sound_timer_devs[i]->priority;
+ }
+
+ if (tmr_no == -1) /* Should not be */
+ tmr_no = 0;
+ }
+
+ tmr = sound_timer_devs[tmr_no];
+
+ if (level == 2)
+ {
+ if (tmr == NULL)
+ {
+ printk ("sequencer: No timer for level 2\n");
+ return RET_ERROR (ENXIO);
+ }
+ setup_mode2 ();
+ }
+
+ if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
+ if (!max_mididev)
+ {
+ printk ("Sequencer: No Midi devices. Input not possible\n");
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!max_synthdev && !max_mididev)
+ return RET_ERROR (ENXIO);
+
+ synth_open_mask = 0;
+
+ for (i = 0; i < max_mididev; i++)
+ {
+ midi_opened[i] = 0;
+ midi_written[i] = 0;
+ }
+
+ /*
+ * if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
+ */
+ for (i = 0; i < max_synthdev; i++) /*
+ * Open synth devices
+ */
+ if ((tmp = synth_devs[i]->open (i, mode)) < 0)
+ {
+ printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
+ if (synth_devs[i]->midi_dev)
+ printk ("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
+ }
+ else
+ {
+ synth_open_mask |= (1 << i);
+ if (synth_devs[i]->midi_dev) /*
+ * Is a midi interface
+ */
+ midi_opened[synth_devs[i]->midi_dev] = 1;
+ }
+
+ seq_time = GET_TIME ();
+ prev_input_time = 0;
+ prev_event_time = 0;
+
+ if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
+ { /*
+ * Initialize midi input devices
+ */
+ for (i = 0; i < max_mididev; i++)
+ if (!midi_opened[i])
+ {
+ if ((retval = midi_devs[i]->open (i, mode,
+ sequencer_midi_input, sequencer_midi_output)) >= 0)
+ midi_opened[i] = 1;
+ }
+ }
+
+ if (seq_mode == SEQ_2)
+ {
+ tmr->open (tmr_no, seq_mode);
+ }
+
+ sequencer_busy = 1;
+ RESET_WAIT_QUEUE (seq_sleeper, seq_sleep_flag);
+ RESET_WAIT_QUEUE (midi_sleeper, midi_sleep_flag);
+ output_treshold = SEQ_MAX_QUEUE / 2;
+
+ for (i = 0; i < num_synths; i++)
+ if (pmgr_present[i])
+ pmgr_inform (i, PM_E_OPENED, 0, 0, 0, 0);
+
+ return 0;
+}
+
+void
+seq_drain_midi_queues (void)
+{
+ int i, n;
+
+ /*
+ * Give the Midi drivers time to drain their output queues
+ */
+
+ n = 1;
+
+ while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && n)
+ {
+ n = 0;
+
+ for (i = 0; i < max_mididev; i++)
+ if (midi_opened[i] && midi_written[i])
+ if (midi_devs[i]->buffer_status != NULL)
+ if (midi_devs[i]->buffer_status (i))
+ n++;
+
+ /*
+ * Let's have a delay
+ */
+ if (n)
+ {
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, HZ / 10);
+ }
+ }
+}
+
+void
+sequencer_release (int dev, struct fileinfo *file)
+{
+ int i;
+ int mode = file->mode & O_ACCMODE;
+
+ dev = dev >> 4;
+
+ DEB (printk ("sequencer_release(dev=%d)\n", dev));
+
+ if (dev) /*
+ * Patch manager device
+ */
+ {
+ dev--;
+ pmgr_release (dev);
+ pmgr_present[dev] = 0;
+ return;
+ }
+
+ /*
+ * * Wait until the queue is empty (if we don't have nonblock)
+ */
+
+ if (mode != OPEN_READ && !ISSET_FILE_FLAG (file, O_NONBLOCK))
+ while (!PROCESS_ABORTING (seq_sleeper, seq_sleep_flag) && qlen)
+ {
+ seq_sync ();
+ }
+
+ if (mode != OPEN_READ)
+ seq_drain_midi_queues (); /*
+ * Ensure the output queues are empty
+ */
+ seq_reset ();
+ if (mode != OPEN_READ)
+ seq_drain_midi_queues (); /*
+ * Flush the all notes off messages
+ */
+
+ for (i = 0; i < max_synthdev; i++)
+ if (synth_open_mask & (1 << i)) /*
+ * Actually opened
+ */
+ if (synth_devs[i])
+ {
+ synth_devs[i]->close (i);
+
+ if (synth_devs[i]->midi_dev)
+ midi_opened[synth_devs[i]->midi_dev] = 0;
+ }
+
+ for (i = 0; i < num_synths; i++)
+ if (pmgr_present[i])
+ pmgr_inform (i, PM_E_CLOSED, 0, 0, 0, 0);
+
+ for (i = 0; i < max_mididev; i++)
+ if (midi_opened[i])
+ midi_devs[i]->close (i);
+
+ if (seq_mode == SEQ_2)
+ tmr->close (tmr_no);
+
+ sequencer_busy = 0;
+}
+
+static int
+seq_sync (void)
+{
+ unsigned long flags;
+
+ if (qlen && !seq_playing && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
+ seq_startplay ();
+
+ DISABLE_INTR (flags);
+ if (qlen && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
+ }
+ RESTORE_INTR (flags);
+
+ return qlen;
+}
+
+static void
+midi_outc (int dev, unsigned char data)
+{
+ /*
+ * NOTE! Calls sleep(). Don't call this from interrupt.
+ */
+
+ int n;
+ unsigned long flags;
+
+ /*
+ * This routine sends one byte to the Midi channel.
+ * If the output Fifo is full, it waits until there
+ * is space in the queue
+ */
+
+ n = 3 * HZ; /* Timeout */
+
+ DISABLE_INTR (flags);
+ while (n && !midi_devs[dev]->putc (dev, data))
+ {
+ DO_SLEEP (seq_sleeper, seq_sleep_flag, 4);
+ n--;
+ }
+ RESTORE_INTR (flags);
+}
+
+static void
+seq_reset (void)
+{
+ /*
+ * NOTE! Calls sleep(). Don't call this from interrupt.
+ */
+
+ int i;
+ int chn;
+ unsigned long flags;
+
+ sound_stop_timer ();
+ seq_time = GET_TIME ();
+ prev_input_time = 0;
+ prev_event_time = 0;
+
+ qlen = qhead = qtail = 0;
+ iqlen = iqhead = iqtail = 0;
+
+ for (i = 0; i < max_synthdev; i++)
+ if (synth_open_mask & (1 << i))
+ if (synth_devs[i])
+ synth_devs[i]->reset (i);
+
+ if (seq_mode == SEQ_2)
+ {
+
+ for (chn = 0; chn < 16; chn++)
+ for (i = 0; i < max_synthdev; i++)
+ if (synth_open_mask & (1 << i))
+ if (synth_devs[i])
+ {
+ synth_devs[i]->controller (i, chn, 123, 0); /* All notes off */
+ synth_devs[i]->controller (i, chn, 121, 0); /* Reset all ctl */
+ synth_devs[i]->bender (i, chn, 1 << 13); /* Bender off */
+ }
+
+ }
+ else
+ /* seq_mode == SEQ_1 */
+ {
+ for (i = 0; i < max_mididev; i++)
+ if (midi_written[i]) /*
+ * Midi used. Some notes may still be playing
+ */
+ {
+ /*
+ * Sending just a ACTIVE SENSING message should be enough to stop all
+ * playing notes. Since there are devices not recognizing the
+ * active sensing, we have to send some all notes off messages also.
+ */
+ midi_outc (i, 0xfe);
+
+ for (chn = 0; chn < 16; chn++)
+ {
+ midi_outc (i,
+ (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */
+ midi_outc (i, 0x7b); /* All notes off */
+ midi_outc (i, 0); /* Dummy parameter */
+ }
+
+ midi_devs[i]->close (i);
+
+ midi_written[i] = 0;
+ midi_opened[i] = 0;
+ }
+ }
+
+ seq_playing = 0;
+
+ DISABLE_INTR (flags);
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
+ {
+ /* printk ("Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
+ WAKE_UP (seq_sleeper, seq_sleep_flag);
+ }
+ RESTORE_INTR (flags);
+
+}
+
+static void
+seq_panic (void)
+{
+ /*
+ * This routine is called by the application in case the user
+ * wants to reset the system to the default state.
+ */
+
+ seq_reset ();
+
+ /*
+ * Since some of the devices don't recognize the active sensing and
+ * all notes off messages, we have to shut all notes manually.
+ *
+ * TO BE IMPLEMENTED LATER
+ */
+
+ /*
+ * Also return the controllers to their default states
+ */
+}
+
+int
+sequencer_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ int midi_dev, orig_dev;
+ int mode = file->mode & O_ACCMODE;
+
+ orig_dev = dev = dev >> 4;
+
+ switch (cmd)
+ {
+ case SNDCTL_TMR_TIMEBASE:
+ case SNDCTL_TMR_TEMPO:
+ case SNDCTL_TMR_START:
+ case SNDCTL_TMR_STOP:
+ case SNDCTL_TMR_CONTINUE:
+ case SNDCTL_TMR_METRONOME:
+ case SNDCTL_TMR_SOURCE:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ if (seq_mode != SEQ_2)
+ return RET_ERROR (EINVAL);
+ return tmr->ioctl (tmr_no, cmd, arg);
+ break;
+
+ case SNDCTL_TMR_SELECT:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ if (seq_mode != SEQ_2)
+ return RET_ERROR (EINVAL);
+ pending_timer = IOCTL_IN (arg);
+
+ if (pending_timer < 0 || pending_timer >= num_sound_timers)
+ {
+ pending_timer = -1;
+ return RET_ERROR (EINVAL);
+ }
+
+ return IOCTL_OUT (arg, pending_timer);
+ break;
+
+ case SNDCTL_SEQ_PANIC:
+ seq_panic ();
+ break;
+
+ case SNDCTL_SEQ_SYNC:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_READ)
+ return 0;
+ while (qlen && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
+ seq_sync ();
+ if (qlen)
+ return RET_ERROR (EINTR);
+ else
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_RESET:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ seq_reset ();
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_TESTMIDI:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ midi_dev = IOCTL_IN (arg);
+ if (midi_dev >= max_mididev)
+ return RET_ERROR (ENXIO);
+
+ if (!midi_opened[midi_dev])
+ {
+ int err, mode;
+
+ mode = file->mode & O_ACCMODE;
+ if ((err = midi_devs[midi_dev]->open (midi_dev, mode,
+ sequencer_midi_input,
+ sequencer_midi_output)) < 0)
+ return err;
+ }
+
+ midi_opened[midi_dev] = 1;
+
+ return 0;
+ break;
+
+ case SNDCTL_SEQ_GETINCOUNT:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_WRITE)
+ return 0;
+ return IOCTL_OUT (arg, iqlen);
+ break;
+
+ case SNDCTL_SEQ_GETOUTCOUNT:
+
+ if (mode == OPEN_READ)
+ return 0;
+ return IOCTL_OUT (arg, SEQ_MAX_QUEUE - qlen);
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (dev) /* Patch manager */
+ return RET_ERROR (EIO);
+
+ /*
+ * If *arg == 0, just return the current rate
+ */
+ if (seq_mode == SEQ_2)
+ return tmr->ioctl (tmr_no, cmd, arg);
+
+ if (IOCTL_IN (arg) != 0)
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, HZ);
+ break;
+
+ case SNDCTL_SEQ_RESETSAMPLES:
+ dev = IOCTL_IN (arg);
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return RET_ERROR (EBUSY);
+
+ if (!orig_dev && pmgr_present[dev])
+ pmgr_inform (dev, PM_E_PATCH_RESET, 0, 0, 0, 0);
+
+ return synth_devs[dev]->ioctl (dev, cmd, arg);
+ break;
+
+ case SNDCTL_SEQ_NRSYNTHS:
+ return IOCTL_OUT (arg, max_synthdev);
+ break;
+
+ case SNDCTL_SEQ_NRMIDIS:
+ return IOCTL_OUT (arg, max_mididev);
+ break;
+
+ case SNDCTL_SYNTH_MEMAVL:
+ {
+ int dev = IOCTL_IN (arg);
+
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return RET_ERROR (EBUSY);
+
+ return IOCTL_OUT (arg, synth_devs[dev]->ioctl (dev, cmd, arg));
+ }
+ break;
+
+ case SNDCTL_FM_4OP_ENABLE:
+ {
+ int dev = IOCTL_IN (arg);
+
+ if (dev < 0 || dev >= num_synths)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)))
+ return RET_ERROR (ENXIO);
+
+ synth_devs[dev]->ioctl (dev, cmd, arg);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_SYNTH_INFO:
+ {
+ struct synth_info inf;
+ int dev;
+
+ IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf));
+ dev = inf.device;
+
+ if (dev < 0 || dev >= max_synthdev)
+ return RET_ERROR (ENXIO);
+
+ if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+ return RET_ERROR (EBUSY);
+
+ return synth_devs[dev]->ioctl (dev, cmd, arg);
+ }
+ break;
+
+ case SNDCTL_SEQ_OUTOFBAND:
+ {
+ struct seq_event_rec event;
+ unsigned long flags;
+
+ IOCTL_FROM_USER ((char *) &event, (char *) arg, 0, sizeof (event));
+
+ DISABLE_INTR (flags);
+ play_event (event.arr);
+ RESTORE_INTR (flags);
+
+ return 0;
+ }
+ break;
+
+ case SNDCTL_MIDI_INFO:
+ {
+ struct midi_info inf;
+ int dev;
+
+ IOCTL_FROM_USER ((char *) &inf, (char *) arg, 0, sizeof (inf));
+ dev = inf.device;
+
+ if (dev < 0 || dev >= max_mididev)
+ return RET_ERROR (ENXIO);
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) &(midi_devs[dev]->info), sizeof (inf));
+ return 0;
+ }
+ break;
+
+ case SNDCTL_PMGR_IFACE:
+ {
+ struct patmgr_info *inf;
+ int dev, err;
+
+ if ((inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf))) == NULL)
+ {
+ printk ("patmgr: Can't allocate memory for a message\n");
+ return RET_ERROR (EIO);
+ }
+
+ IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf));
+ dev = inf->device;
+
+ if (dev < 0 || dev >= num_synths)
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!synth_devs[dev]->pmgr_interface)
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ENXIO);
+ }
+
+ if ((err = synth_devs[dev]->pmgr_interface (dev, inf)) == -1)
+ {
+ KERNEL_FREE (inf);
+ return err;
+ }
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf));
+ KERNEL_FREE (inf);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_PMGR_ACCESS:
+ {
+ struct patmgr_info *inf;
+ int dev, err;
+
+ if ((inf = (struct patmgr_info *) KERNEL_MALLOC (sizeof (*inf))) == NULL)
+ {
+ printk ("patmgr: Can't allocate memory for a message\n");
+ return RET_ERROR (EIO);
+ }
+
+ IOCTL_FROM_USER ((char *) inf, (char *) arg, 0, sizeof (*inf));
+ dev = inf->device;
+
+ if (dev < 0 || dev >= num_synths)
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ENXIO);
+ }
+
+ if (!pmgr_present[dev])
+ {
+ KERNEL_FREE (inf);
+ return RET_ERROR (ESRCH);
+ }
+
+ if ((err = pmgr_access (dev, inf)) < 0)
+ {
+ KERNEL_FREE (inf);
+ return err;
+ }
+
+ IOCTL_TO_USER ((char *) arg, 0, (char *) inf, sizeof (*inf));
+ KERNEL_FREE (inf);
+ return 0;
+ }
+ break;
+
+ case SNDCTL_SEQ_TRESHOLD:
+ {
+ int tmp = IOCTL_IN (arg);
+
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ if (tmp < 1)
+ tmp = 1;
+ if (tmp >= SEQ_MAX_QUEUE)
+ tmp = SEQ_MAX_QUEUE - 1;
+ output_treshold = tmp;
+ return 0;
+ }
+ break;
+
+ case SNDCTL_MIDI_PRETIME:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val < 0)
+ val = 0;
+
+ val = (HZ * val) / 10;
+ pre_event_timeout = val;
+ return IOCTL_OUT (arg, val);
+ }
+ break;
+
+ default:
+ if (dev) /*
+ * Patch manager
+ */
+ return RET_ERROR (EIO);
+
+ if (mode == OPEN_READ)
+ return RET_ERROR (EIO);
+
+ if (!synth_devs[0])
+ return RET_ERROR (ENXIO);
+ if (!(synth_open_mask & (1 << 0)))
+ return RET_ERROR (ENXIO);
+ return synth_devs[0]->ioctl (0, cmd, arg);
+ break;
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+#ifdef ALLOW_SELECT
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ unsigned long flags;
+
+ dev = dev >> 4;
+
+ switch (sel_type)
+ {
+ case SEL_IN:
+ DISABLE_INTR (flags);
+ if (!iqlen)
+ {
+#if defined(__FreeBSD__)
+ selrecord(wait, &selinfo[dev]);
+#else
+ midi_sleep_flag.mode = WK_SLEEP;
+ select_wait (&midi_sleeper, wait);
+#endif
+ RESTORE_INTR (flags);
+ return 0;
+ }
+ midi_sleep_flag.mode &= ~WK_SLEEP;
+ RESTORE_INTR (flags);
+ return 1;
+ break;
+
+ case SEL_OUT:
+ DISABLE_INTR (flags);
+ if (qlen >= SEQ_MAX_QUEUE)
+ {
+#if defined(__FreeBSD__)
+ selrecord(wait, &selinfo[dev]);
+#else
+ seq_sleep_flag.mode = WK_SLEEP;
+ select_wait (&seq_sleeper, wait);
+#endif
+ RESTORE_INTR (flags);
+ return 0;
+ }
+ seq_sleep_flag.mode &= ~WK_SLEEP;
+ RESTORE_INTR (flags);
+ return 1;
+ break;
+
+ case SEL_EX:
+ return 0;
+ }
+
+ return 0;
+}
+
+#endif
+
+void
+sequencer_timer (void)
+{
+ seq_startplay ();
+}
+
+int
+note_to_freq (int note_num)
+{
+
+ /*
+ * This routine converts a midi note to a frequency (multiplied by 1000)
+ */
+
+ int note, octave, note_freq;
+ int notes[] =
+ {
+ 261632, 277189, 293671, 311132, 329632, 349232,
+ 369998, 391998, 415306, 440000, 466162, 493880
+ };
+
+#define BASE_OCTAVE 5
+
+ octave = note_num / 12;
+ note = note_num % 12;
+
+ note_freq = notes[note];
+
+ if (octave < BASE_OCTAVE)
+ note_freq >>= (BASE_OCTAVE - octave);
+ else if (octave > BASE_OCTAVE)
+ note_freq <<= (octave - BASE_OCTAVE);
+
+ /*
+ * note_freq >>= 1;
+ */
+
+ return note_freq;
+}
+
+unsigned long
+compute_finetune (unsigned long base_freq, int bend, int range)
+{
+ unsigned long amount;
+ int negative, semitones, cents, multiplier = 1;
+
+ if (!bend)
+ return base_freq;
+ if (!range)
+ return base_freq;
+
+ if (!base_freq)
+ return base_freq;
+
+ if (range >= 8192)
+ range = 8191;
+
+ bend = bend * range / 8192;
+ if (!bend)
+ return base_freq;
+
+ negative = bend < 0 ? 1 : 0;
+
+ if (bend < 0)
+ bend *= -1;
+ if (bend > range)
+ bend = range;
+
+ /*
+ if (bend > 2399)
+ bend = 2399;
+ */
+ while (bend > 2399)
+ {
+ multiplier *= 4;
+ bend -= 2400;
+ }
+
+ semitones = bend / 100;
+ cents = bend % 100;
+
+ amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents])
+ / 10000;
+
+ if (negative)
+ return (base_freq * 10000) / amount; /*
+ * Bend down
+ */
+ else
+ return (base_freq * amount) / 10000; /*
+ * Bend up
+ */
+}
+
+
+long
+sequencer_init (long mem_start)
+{
+
+ sequencer_ok = 1;
+ PERMANENT_MALLOC (unsigned char *, queue, SEQ_MAX_QUEUE * EV_SZ, mem_start);
+ PERMANENT_MALLOC (unsigned char *, iqueue, SEQ_MAX_QUEUE * IEV_SZ, mem_start);
+
+ return mem_start;
+}
+
+#else
+/*
+ * Stub version
+ */
+int
+sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+sequencer_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+sequencer_open (int dev, struct fileinfo *file)
+{
+ return RET_ERROR (ENXIO);
+}
+
+void
+sequencer_release (int dev, struct fileinfo *file)
+{
+}
+int
+sequencer_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg)
+{
+ return RET_ERROR (EIO);
+}
+
+int
+sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig)
+{
+ return RET_ERROR (EIO);
+}
+
+long
+sequencer_init (long mem_start)
+{
+ return mem_start;
+}
+
+#ifdef ALLOW_SELECT
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, select_table * wait)
+{
+ return RET_ERROR (EIO);
+}
+
+#endif
+
+#endif
+
+#endif
diff --git a/sys/pc98/pc98/sound/sound.doc b/sys/pc98/pc98/sound/sound.doc
new file mode 100644
index 0000000..002bb26
--- /dev/null
+++ b/sys/pc98/pc98/sound/sound.doc
@@ -0,0 +1,120 @@
+$Id: sound.doc,v 1.6 1996/04/11 15:34:22 smpatel Exp $
+
+Instructions on using audio on a FreeBSD 2.1 (or 2.0-current) system.
+See also /sys/i386/conf/LINT.
+
+To enable sound driver support, the controller sound code must be included
+in your config file:
+
+# SB = SoundBlaster; PAS = ProAudioSpectrum; GUS = Gravis UltraSound
+# Controls all sound devices
+controller snd0
+
+Uncomment one or more of these device entries, depending on what type of
+sound card you have:
+
+# ProAudioSpectrum PCM and Midi - for PAS
+#device pas0 at isa? port 0x388 irq 10 drq 6 vector pasintr
+
+# SoundBlaster DSP driver - for SB, SB Pro, SB16, PAS(emulating SB)
+#device sb0 at isa? port 0x220 irq 7 drq 1 vector sbintr
+
+# SoundBlaster 16 DSP driver - for SB16 - requires sb0 device
+#device sbxvi0 at isa? drq 5
+
+# SoundBlaster 16 MIDI - for SB16 - requires sb0 device
+#device sbmidi0 at isa? port 0x300
+
+# Gravis UltraSound - for GUS, GUS16, GUSMAX
+# For cards that use 2 DMA Channels:
+# drq = Write DMA Channel, flags = Read DMA Channel
+#device gus0 at isa? port 0x220 irq 11 drq 1 flags 0x3 vector gusintr
+
+# Gravis UltraSound 16 bit option - for GUS16 - requires gus0
+#device gusxvi0 at isa? port 0x530 irq 7 drq 3 vector adintr
+
+# MS Sound System (AD1848 Based Boards)
+#device mss0 at isa? port 0x530 irq 10 drq 1 vector adintr
+
+# Yamaha OPL-2/OPL-3 FM - for SB, SB Pro, SB16, PAS
+#device opl0 at isa? port 0x388
+
+# MPU-401 - for MPU-401 standalone card
+#device mpu0 at isa? port 0x330 irq 6 drq 0
+
+# 6850 UART Midi
+#device uart0 at isa? port 0x330 irq 5 vector "m6850intr"
+
+You may add one or more of the following depending on what you do and don't
+want compiled into your kernel. Note: Excluding things with EXCLUDE_...
+is NOT recommended unless you really know what you're doing.
+
+#options EXCLUDE_AUDIO # NO digital audio support
+#options EXCLUDE_SEQUENCER # NO sequencer support
+#options EXCLUDE_MIDI # NO MIDI support whatsoever
+#options EXCLUDE_SBPRO # EXCLUDE SB Pro support
+#options EXCLUDE_SB_EMULATION # NO PAS SB emulation support
+#options EXCLUDE_GUS_IODETECT # NO GUS io detection
+#options EXCLUDE_PRO_MIDI # NO PAS MIDI support
+
+Other Options:
+
+#options SYMPHONY_PAS
+ Adds some code to make pas work with Symphony chipsets. Only use
+ this if your pas doesn't work and you have a Symphony chipset.
+
+#options BROKEN_BUS_CLOCK
+ Some systems with the OPTI chipset and a PAS will require you to
+ use this option. Symptoms are that you will hear a lot of clicking and
+ popping sounds, like a geiger counter, coming out of the PAS even when
+ it is not playing anything.
+
+#options MOZART_PORT
+ Adds support for Mozart (OAK OTI-601). (Part of the MSS driver)
+
+#options OPTI_MAD16_PORT
+ Adds support for the OPTI MAD16 Chip. (Part of the MSS driver)
+ If your soundcard has a chip labeled "OPTi 82C929" then try this.
+
+#options __SGNXPRO__
+ Adds support for the SG NX Pro mixer. (Part of the SB driver)
+
+#options JAZZ16
+ Adds support for the MV Jazz16 (ProSonic etc). (Part of the SB Driver)
+
+#options SM_WAVE
+ Adds support for the SoundMan Wave (Part of the SB Driver)
+ Note: You will need to do some work to get this to work.
+ See i386/isa/sound/configure.c
+
+#options SM_GAMES
+ Adds support for the Logitech SoundMan Games (Part of the SB Driver)
+
+#options PAS_JOYSTICK_ENABLE
+ Enables the gameport on the ProAudio Spectrum
+
+NOTE: The MPU-401 driver may or may not work, and is unfortunately
+unverifiable since no one I know has one. If you can test this,
+please let me know! Also note that you will have to change these
+settings if your soundcard is set for a non-standard address or IRQ.
+Please check your documentation (or verify with any provided DOS utilities
+that may have come with your card) and set the IRQ or address fields
+accordingly.
+
+
+Also: You can configure more then one card on a single DMA using
+the conflicts keyword in your configuration file. This is useful for boards
+with more then one type of emulation.
+
+
+Probing problems: Since the SB16 uses the same IRQ and addresses for
+the different drivers, some of the snd drivers will not be probed because
+the kernel thinks there is a conflict. This can be worked-around by
+using the "conflicts" keyword on the sb16's device line.
+
+
+For further information, contact multimedia@freebsd.org
+
+ - Jordan Hubbard (jkh@freefall.cdrom.com)
+ - Steven Wallace (swallace@freefall.cdrom.com)
+ - Sujal Patel (smpatel@wam.umd.edu)
diff --git a/sys/pc98/pc98/sound/sound_calls.h b/sys/pc98/pc98/sound/sound_calls.h
new file mode 100644
index 0000000..d52da0b
--- /dev/null
+++ b/sys/pc98/pc98/sound/sound_calls.h
@@ -0,0 +1,272 @@
+/*
+ * DMA buffer calls
+ */
+
+int DMAbuf_open(int dev, int mode);
+int DMAbuf_release(int dev, int mode);
+int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock);
+int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock);
+int DMAbuf_rmchars(int dev, int buff_no, int c);
+int DMAbuf_start_output(int dev, int buff_no, int l);
+int DMAbuf_ioctl(int dev, unsigned int cmd, unsigned int arg, int local);
+long DMAbuf_init(long mem_start);
+int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
+int DMAbuf_open_dma (int dev);
+void DMAbuf_close_dma (int dev);
+void DMAbuf_reset_dma (int dev);
+void DMAbuf_inputintr(int dev);
+void DMAbuf_outputintr(int dev, int underflow_flag);
+#ifdef ALLOW_SELECT
+int DMAbuf_select(int dev, struct fileinfo *file, int sel_type, select_table * wait);
+#endif
+
+/*
+ * System calls for /dev/dsp and /dev/audio
+ */
+
+int audio_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int audio_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int audio_open (int dev, struct fileinfo *file);
+void audio_release (int dev, struct fileinfo *file);
+int audio_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int audio_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long audio_init (long mem_start);
+
+#ifdef ALLOW_SELECT
+int audio_select(int dev, struct fileinfo *file, int sel_type, select_table * wait);
+#endif
+
+/*
+ * System calls for the /dev/sequencer
+ */
+
+int sequencer_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sequencer_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sequencer_open (int dev, struct fileinfo *file);
+void sequencer_release (int dev, struct fileinfo *file);
+int sequencer_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long sequencer_init (long mem_start);
+void sequencer_timer(void);
+int note_to_freq(int note_num);
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range);
+void seq_input_event(unsigned char *event, int len);
+void seq_copy_to_input (unsigned char *event, int len);
+
+#ifdef ALLOW_SELECT
+int sequencer_select(int dev, struct fileinfo *file, int sel_type, select_table * wait);
+#endif
+
+/*
+ * System calls for the /dev/midi
+ */
+
+int MIDIbuf_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int MIDIbuf_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int MIDIbuf_open (int dev, struct fileinfo *file);
+void MIDIbuf_release (int dev, struct fileinfo *file);
+int MIDIbuf_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
+long MIDIbuf_init(long mem_start);
+
+#ifdef ALLOW_SELECT
+int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, select_table * wait);
+#endif
+
+/*
+ * System calls for the generic midi interface.
+ *
+ */
+
+long CMIDI_init (long mem_start);
+int CMIDI_open (int dev, struct fileinfo *file);
+int CMIDI_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int CMIDI_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int CMIDI_close (int dev, struct fileinfo *file);
+
+/*
+ *
+ * Misc calls from various sources
+ */
+
+/* From soundcard.c */
+long soundcard_init(long mem_start);
+void tenmicrosec(void);
+void request_sound_timer (int count);
+void sound_stop_timer(void);
+int snd_set_irq_handler (int interrupt_level, INT_HANDLER_PROTO(), char *name);
+void snd_release_irq(int vect);
+void sound_dma_malloc(int dev);
+void sound_dma_free(int dev);
+
+/* From sound_switch.c */
+int sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sound_open_sw (int dev, struct fileinfo *file);
+void sound_release_sw (int dev, struct fileinfo *file);
+int sound_ioctl_sw (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned long arg);
+
+/* From sb_dsp.c */
+int sb_dsp_detect (struct address_info *hw_config);
+long sb_dsp_init (long mem_start, struct address_info *hw_config);
+void sb_dsp_disable_midi(void);
+int sb_get_irq(void);
+void sb_free_irq(void);
+int sb_dsp_command (unsigned char val);
+int sb_reset_dsp (void);
+
+/* From sb16_dsp.c */
+void sb16_dsp_interrupt (int irq);
+long sb16_dsp_init(long mem_start, struct address_info *hw_config);
+int sb16_dsp_detect(struct address_info *hw_config);
+
+/* From sb16_midi.c */
+void sb16midiintr (int unit);
+long attach_sb16midi(long mem_start, struct address_info * hw_config);
+int probe_sb16midi(struct address_info *hw_config);
+void sb_midi_interrupt(int dummy);
+
+/* From sb_midi.c */
+void sb_midi_init(int model);
+
+/* From sb_mixer.c */
+void sb_setmixer (unsigned int port, unsigned int value);
+int sb_getmixer (unsigned int port);
+void sb_mixer_set_stereo(int mode);
+int sb_mixer_init(int major_model);
+
+/* From opl3.c */
+int opl3_detect (int ioaddr);
+long opl3_init(long mem_start);
+
+/* From sb_card.c */
+long attach_sb_card(long mem_start, struct address_info *hw_config);
+int probe_sb(struct address_info *hw_config);
+
+/* From adlib_card.c */
+long attach_adlib_card(long mem_start, struct address_info *hw_config);
+int probe_adlib(struct address_info *hw_config);
+
+/* From pas_card.c */
+long attach_pas_card(long mem_start, struct address_info *hw_config);
+int probe_pas(struct address_info *hw_config);
+int pas_set_intr(int mask);
+int pas_remove_intr(int mask);
+unsigned char pas_read(int ioaddr);
+void pas_write(unsigned char data, int ioaddr);
+
+/* From pas_audio.c */
+void pas_pcm_interrupt(unsigned char status, int cause);
+long pas_pcm_init(long mem_start, struct address_info *hw_config);
+
+/* From pas_mixer.c */
+int pas_init_mixer(void);
+
+/* From pas_midi.c */
+long pas_midi_init(long mem_start);
+void pas_midi_interrupt(void);
+
+/* From gus_card.c */
+long attach_gus_card(long mem_start, struct address_info * hw_config);
+int probe_gus(struct address_info *hw_config);
+int gus_set_midi_irq(int num);
+long attach_gus_db16(long mem_start, struct address_info * hw_config);
+int probe_gus_db16(struct address_info *hw_config);
+
+/* From gus_wave.c */
+int gus_wave_detect(int baseaddr);
+long gus_wave_init(long mem_start, int irq, int dma, int dma_read);
+void gus_voice_irq(void);
+void gus_write8(int reg, unsigned int data);
+void guswave_dma_irq(void);
+void gus_delay(void);
+int gus_default_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg);
+
+/* From gus_midi.c */
+long gus_midi_init(long mem_start);
+void gus_midi_interrupt(int dummy);
+
+/* From mpu401.c */
+long attach_mpu401(long mem_start, struct address_info * hw_config);
+int probe_mpu401(struct address_info *hw_config);
+void mpuintr(INT_HANDLER_PARMS(irq, dummy));
+
+/* From uart6850.c */
+long attach_uart6850(long mem_start, struct address_info * hw_config);
+int probe_uart6850(struct address_info *hw_config);
+
+/* From opl3.c */
+void enable_opl3_mode(int left, int right, int both);
+
+/* From patmgr.c */
+int pmgr_open(int dev);
+void pmgr_release(int dev);
+int pmgr_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
+int pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count);
+int pmgr_access(int dev, struct patmgr_info *rec);
+int pmgr_inform(int dev, int event, unsigned long parm1, unsigned long parm2,
+ unsigned long parm3, unsigned long parm4);
+
+/* From ics2101.c */
+long ics2101_mixer_init(long mem_start);
+
+/* From sound_timer.c */
+void sound_timer_init(int io_base);
+void sound_timer_interrupt(void);
+
+/* From ad1848.c */
+void ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture);
+int ad1848_detect (int io_base);
+void ad1848_interrupt (INT_HANDLER_PARMS(irq, dummy));
+long attach_ms_sound(long mem_start, struct address_info * hw_config);
+int probe_ms_sound(struct address_info *hw_config);
+
+/* From pss.c */
+int probe_pss (struct address_info *hw_config);
+long attach_pss (long mem_start, struct address_info *hw_config);
+int probe_pss_mpu (struct address_info *hw_config);
+long attach_pss_mpu (long mem_start, struct address_info *hw_config);
+int probe_pss_mss (struct address_info *hw_config);
+long attach_pss_mss (long mem_start, struct address_info *hw_config);
+
+/* From sscape.c */
+int probe_sscape (struct address_info *hw_config);
+long attach_sscape (long mem_start, struct address_info *hw_config);
+int probe_ss_ms_sound (struct address_info *hw_config);
+long attach_ss_ms_sound(long mem_start, struct address_info * hw_config);
+
+int pss_read (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int pss_write (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int pss_open (int dev, struct fileinfo *file);
+void pss_release (int dev, struct fileinfo *file);
+int pss_ioctl (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned int arg);
+int pss_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
+long pss_init(long mem_start);
+
+#ifdef PC98
+/* From pcm86.c */
+int probe_pcm86(struct address_info *hw_config);
+long attach_pcm86(long mem_start, struct address_info *hw_config);
+#endif PC98
+
+/* From aedsp16.c */
+int InitAEDSP16_SBPRO(struct address_info *hw_config);
+int InitAEDSP16_MSS(struct address_info *hw_config);
+int InitAEDSP16_MPU401(struct address_info *hw_config);
+
+/* From midi_synth.c */
+void do_midi_msg (int synthno, unsigned char *msg, int mlen);
+
+/* From trix.c */
+long attach_trix_wss (long mem_start, struct address_info *hw_config);
+int probe_trix_wss (struct address_info *hw_config);
+long attach_trix_sb (long mem_start, struct address_info *hw_config);
+int probe_trix_sb (struct address_info *hw_config);
+long attach_trix_mpu (long mem_start, struct address_info *hw_config);
+int probe_trix_mpu (struct address_info *hw_config);
diff --git a/sys/pc98/pc98/sound/sound_config.h b/sys/pc98/pc98/sound/sound_config.h
new file mode 100644
index 0000000..76b4558
--- /dev/null
+++ b/sys/pc98/pc98/sound/sound_config.h
@@ -0,0 +1,375 @@
+/* sound_config.h
+ *
+ * A driver for Soundcards, misc configuration parameters.
+ *
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "local.h"
+#include "os.h"
+#include "soundvers.h"
+
+#if !defined(PSS_MPU_BASE) && defined(EXCLUDE_SSCAPE) && defined(EXCLUDE_TRIX)
+#define EXCLUDE_MPU_EMU
+#endif
+
+#if defined(ISC) || defined(SCO) || defined(SVR42)
+#define GENERIC_SYSV
+#endif
+
+/*
+ * Disable the AD1848 driver if there are no other drivers requiring it.
+ */
+
+#if defined(EXCLUDE_GUS16) && defined(EXCLUDE_MSS) && defined(EXCLUDE_PSS) && defined(EXCLUDE_GUSMAX) && defined(EXCLUDE_SSCAPE) && defined(EXCLUDE_TRIX)
+#define EXCLUDE_AD1848
+#endif
+
+#ifdef PSS_MSS_BASE
+#undef EXCLUDE_AD1848
+#endif
+
+#undef CONFIGURE_SOUNDCARD
+#undef DYNAMIC_BUFFER
+
+#ifdef KERNEL_SOUNDCARD
+#define CONFIGURE_SOUNDCARD
+#define DYNAMIC_BUFFER
+#undef LOADABLE_SOUNDCARD
+#endif
+
+#ifdef EXCLUDE_SEQUENCER
+#define EXCLUDE_MIDI
+#define EXCLUDE_YM3812
+#define EXCLUDE_OPL3
+#endif
+
+#ifndef SND_DEFAULT_ENABLE
+#define SND_DEFAULT_ENABLE 1
+#endif
+
+#ifdef CONFIGURE_SOUNDCARD
+
+/* ****** IO-address, DMA and IRQ settings ****
+
+If your card has nonstandard I/O address or IRQ number, change defines
+ for the following settings in your kernel Makefile */
+
+#ifndef SBC_BASE
+#ifdef PC98
+#define SBC_BASE 0x20d2 /* 0x20d2 is the factory default. */
+#else
+#define SBC_BASE 0x220 /* 0x220 is the factory default. */
+#endif
+#endif
+
+#ifndef SBC_IRQ
+#ifdef PC98
+#define SBC_IRQ 10 /* IQR10 is not the factory default on PC9821. */
+#else
+#define SBC_IRQ 7 /* IQR7 is the factory default. */
+#endif
+#endif
+
+#ifndef SBC_DMA
+#ifdef PC98
+#define SBC_DMA 3
+#else
+#define SBC_DMA 1
+#endif
+#endif
+
+#ifndef SB16_DMA
+#ifdef PC98
+#define SB16_DMA 3
+#else
+#define SB16_DMA 6
+#endif
+#endif
+
+#ifndef SB16MIDI_BASE
+#ifdef PC98
+#define SB16MIDI_BASE 0x80d2
+#else
+#define SB16MIDI_BASE 0x300
+#endif
+#endif
+
+#ifndef PAS_BASE
+#define PAS_BASE 0x388
+#endif
+
+#ifndef PAS_IRQ
+#define PAS_IRQ 5
+#endif
+
+#ifndef PAS_DMA
+#define PAS_DMA 3
+#endif
+
+#ifndef GUS_BASE
+#define GUS_BASE 0x220
+#endif
+
+#ifndef GUS_IRQ
+#define GUS_IRQ 15
+#endif
+
+#ifndef GUS_MIDI_IRQ
+#define GUS_MIDI_IRQ GUS_IRQ
+#endif
+
+#ifndef GUS_DMA
+#define GUS_DMA 6
+#endif
+
+#ifndef GUS_DMA_READ
+#define GUS_DMA_READ 3
+#endif
+
+#ifndef MPU_BASE
+#define MPU_BASE 0x330
+#endif
+
+#ifndef MPU_IRQ
+#define MPU_IRQ 6
+#endif
+
+/* Echo Personal Sound System */
+#ifndef PSS_BASE
+#define PSS_BASE 0x220 /* 0x240 or */
+#endif
+
+#ifndef PSS_IRQ
+#define PSS_IRQ 7
+#endif
+
+#ifndef PSS_DMA
+#define PSS_DMA 1
+#endif
+
+#ifndef MSS_BASE
+#define MSS_BASE 0
+#endif
+
+#ifndef MSS_DMA
+#define MSS_DMA 0
+#endif
+
+#ifndef MSS_IRQ
+#define MSS_IRQ 0
+#endif
+
+#ifndef GUS16_BASE
+#define GUS16_BASE 0
+#endif
+
+#ifndef GUS16_DMA
+#define GUS16_DMA 0
+#endif
+
+#ifndef GUS16_IRQ
+#define GUS16_IRQ 0
+#endif
+
+#ifndef SSCAPE_BASE
+#define SSCAPE_BASE 0
+#endif
+
+#ifndef SSCAPE_DMA
+#define SSCAPE_DMA 0
+#endif
+
+#ifndef SSCAPE_IRQ
+#define SSCAPE_IRQ 0
+#endif
+
+#ifndef SSCAPE_MSS_BASE
+#define SSCAPE_MSS_BASE 0
+#endif
+
+#ifndef SSCAPE_MSS_DMA
+#define SSCAPE_MSS_DMA 0
+#endif
+
+#ifndef SSCAPE_MSS_IRQ
+#define SSCAPE_MSS_IRQ 0
+#endif
+
+#ifndef TRIX_BASE
+#define TRIX_BASE 0x530
+#endif
+
+#ifndef TRIX_IRQ
+#define TRIX_IRQ 10
+#endif
+
+#ifndef TRIX_DMA
+#define TRIX_DMA 1
+#endif
+
+#ifndef U6850_BASE
+#define U6850_BASE 0x330
+#endif
+
+#ifndef U6850_IRQ
+#define U6850_IRQ 5
+#endif
+
+#ifndef U6850_DMA
+#define U6850_DMA 1
+#endif
+
+#ifndef MAX_REALTIME_FACTOR
+#define MAX_REALTIME_FACTOR 4
+#endif
+
+/************* PCM DMA buffer sizes *******************/
+
+/* If you are using high playback or recording speeds, the default buffersize
+ is too small. DSP_BUFFSIZE must be 64k or less.
+
+ A rule of thumb is 64k for PAS16, 32k for PAS+, 16k for SB Pro and
+ 4k for SB.
+
+ If you change the DSP_BUFFSIZE, don't modify this file.
+ Use the make config command instead. */
+
+#ifndef DSP_BUFFSIZE
+#define DSP_BUFFSIZE (4096)
+#endif
+
+#ifndef DSP_BUFFCOUNT
+#define DSP_BUFFCOUNT 2 /* 2 is recommended. */
+#endif
+
+#define DMA_AUTOINIT 0x10
+
+#ifdef PC98
+#define FM_MONO 0x28d2 /* This is the I/O address used by AdLib */
+#else
+#define FM_MONO 0x388 /* This is the I/O address used by AdLib */
+#endif
+
+/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
+ driver. (There is no need to alter this) */
+#define SEQ_MAX_QUEUE 1024
+
+#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */
+/* 128 instruments for general MIDI setup and 16 unassigned */
+
+/*
+ * Minor numbers for the sound driver.
+ *
+ * Unfortunately Creative called the codec chip of SB as a DSP. For this
+ * reason the /dev/dsp is reserved for digitized audio use. There is a
+ * device for true DSP processors but it will be called something else.
+ * In v3.0 it's /dev/sndproc but this could be a temporary solution.
+ */
+
+#define SND_NDEVS 256 /* Number of supported devices */
+#define SND_DEV_CTL 0 /* Control port /dev/mixer */
+#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */
+#define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */
+#define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */
+#define SND_DEV_STATUS 6 /* /dev/sndstat */
+/* #7 not in use now. Was in 2.4. Free for use after v3.0. */
+#define SND_DEV_SEQ2 8 /* /dev/sequecer, level 2 interface */
+#define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */
+#define SND_DEV_PSS SND_DEV_SNDPROC
+
+#define DSP_DEFAULT_SPEED 8000
+
+#define ON 1
+#define OFF 0
+
+#define MAX_AUDIO_DEV 5
+#define MAX_MIXER_DEV 5
+#define MAX_SYNTH_DEV 3
+#define MAX_MIDI_DEV 6
+#define MAX_TIMER_DEV 3
+
+struct fileinfo {
+ int mode; /* Open mode */
+ DECLARE_FILE(); /* Reference to file-flags. OS-dependent. */
+ };
+
+struct address_info {
+ int io_base;
+ int irq;
+ int dma; /* write dma channel */
+ int dma_read; /* read dma channel */
+ int always_detect; /* 1=Trust me, it's there */
+};
+
+#define SYNTH_MAX_VOICES 32
+
+struct voice_alloc_info {
+ int max_voice;
+ int used_voices;
+ int ptr; /* For device specific use */
+ unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
+ int timestamp;
+ int alloc_times[SYNTH_MAX_VOICES];
+ };
+
+struct channel_info {
+ int pgm_num;
+ int bender_value;
+ unsigned char controllers[128];
+ };
+
+/*
+ * Process wakeup reasons
+ */
+#define WK_NONE 0x00
+#define WK_WAKEUP 0x01
+#define WK_TIMEOUT 0x02
+#define WK_SIGNAL 0x04
+#define WK_SLEEP 0x08
+
+#define OPEN_READ 1
+#define OPEN_WRITE 2
+#define OPEN_READWRITE 3
+
+#include "sound_calls.h"
+#include "dev_table.h"
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+#ifndef AUDIO_DDB
+#define AUDIO_DDB(x)
+#endif
+
+#define TIMER_ARMED 121234
+#define TIMER_NOT_ARMED 1
+
+#endif
diff --git a/sys/pc98/pc98/sound/sound_switch.c b/sys/pc98/pc98/sound/sound_switch.c
new file mode 100644
index 0000000..fb665c6
--- /dev/null
+++ b/sys/pc98/pc98/sound/sound_switch.c
@@ -0,0 +1,544 @@
+/*
+ * sound/sound_switch.c
+ *
+ * The system call switch
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+struct sbc_device
+ {
+ int usecount;
+ };
+
+static struct sbc_device sbc_devices[SND_NDEVS] =
+{
+ {0}};
+
+static int in_use = 0; /*
+
+
+ * * * * Total # of open device files
+ * (excluding * * * minor 0) */
+
+/*
+ * /dev/sndstatus -device
+ */
+static char *status_buf = NULL;
+static int status_len, status_ptr;
+static int status_busy = 0;
+
+static int
+put_status (char *s)
+{
+ int l;
+
+ for (l = 0; l < 256, s[l]; l++); /*
+ * l=strlen(s);
+ */
+
+ if (status_len + l >= 4000)
+ return 0;
+
+ memcpy (&status_buf[status_len], s, l);
+ status_len += l;
+
+ return 1;
+}
+
+static int
+put_status_int (unsigned int val, int radix)
+{
+ int l, v;
+
+ static char hx[] = "0123456789abcdef";
+ char buf[11];
+
+ if (!val)
+ return put_status ("0");
+
+ l = 0;
+ buf[10] = 0;
+
+ while (val)
+ {
+ v = val % radix;
+ val = val / radix;
+
+ buf[9 - l] = hx[v];
+ l++;
+ }
+
+ if (status_len + l >= 4000)
+ return 0;
+
+ memcpy (&status_buf[status_len], &buf[10 - l], l);
+ status_len += l;
+
+ return 1;
+}
+
+static void
+init_status (void)
+{
+ /*
+ * Write the status information to the status_buf and update status_len.
+ * There is a limit of 4000 bytes for the data.
+ */
+
+ int i;
+
+ status_ptr = 0;
+
+ put_status ("VoxWare Sound Driver:" SOUND_VERSION_STRING
+ " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
+ SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
+ "\n");
+
+ if (!put_status ("Config options: "))
+ return;
+ if (!put_status_int (SELECTED_SOUND_OPTIONS, 16))
+ return;
+
+ if (!put_status ("\n\nInstalled drivers: \n"))
+ return;
+
+ for (i = 0; i < (num_sound_drivers - 1); i++)
+ {
+ if (!put_status ("Type "))
+ return;
+ if (!put_status_int (sound_drivers[i].card_type, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (sound_drivers[i].name))
+ return;
+
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\n\nCard config: \n"))
+ return;
+
+ for (i = 0; i < (num_sound_cards - 1); i++)
+ {
+ int drv;
+
+ if (!snd_installed_cards[i].enabled)
+ if (!put_status ("("))
+ return;
+
+ /*
+ * if (!put_status_int(snd_installed_cards[i].card_type, 10)) return;
+ * if (!put_status (": ")) return;
+ */
+
+ if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1)
+ if (!put_status (sound_drivers[drv].name))
+ return;
+
+ if (!put_status (" at 0x"))
+ return;
+ if (!put_status_int (snd_installed_cards[i].config.io_base, 16))
+ return;
+ if (!put_status (" irq "))
+ return;
+ if (!put_status_int (snd_installed_cards[i].config.irq, 10))
+ return;
+#ifdef PC98
+ if (snd_installed_cards[i].config.dma >= 0) {
+#endif
+ if (!put_status (" drq "))
+ return;
+ if (!put_status_int (snd_installed_cards[i].config.dma, 10))
+ return;
+#ifdef PC98
+ }
+#endif
+
+ if (!snd_installed_cards[i].enabled)
+ if (!put_status (")"))
+ return;
+
+ if (!put_status ("\n"))
+ return;
+ }
+
+#ifdef EXCLUDE_AUDIO
+ if (!put_status ("\nAudio devices: NOT ENABLED IN CONFIG\n"))
+ return;
+#else
+ if (!put_status ("\nAudio devices:\n"))
+ return;
+
+ for (i = 0; i < num_audiodevs; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (audio_devs[i]->name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+#endif
+
+#ifdef EXCLUDE_SEQUENCER
+ if (!put_status ("\nSynth devices: NOT ENABLED IN CONFIG\n"))
+ return;
+#else
+ if (!put_status ("\nSynth devices:\n"))
+ return;
+
+ for (i = 0; i < num_synths; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (synth_devs[i]->info->name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+#endif
+
+#ifdef EXCLUDE_MIDI
+ if (!put_status ("\nMidi devices: NOT ENABLED IN CONFIG\n"))
+ return;
+#else
+ if (!put_status ("\nMidi devices:\n"))
+ return;
+
+ for (i = 0; i < num_midis; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (midi_devs[i]->info.name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+#endif
+
+ if (!put_status ("\nTimers:\n"))
+ return;
+
+ for (i = 0; i < num_sound_timers; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (sound_timer_devs[i]->info.name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nMixers:\n"))
+ return;
+
+ for (i = 0; i < num_mixers; i++)
+ {
+ if (!put_status_int (i, 10))
+ return;
+ if (!put_status (": "))
+ return;
+ if (!put_status (mixer_devs[i]->name))
+ return;
+ if (!put_status ("\n"))
+ return;
+ }
+}
+
+static int
+read_status (snd_rw_buf * buf, int count)
+{
+ /*
+ * Return at most 'count' bytes from the status_buf.
+ */
+ int l, c;
+
+ l = count;
+ c = status_len - status_ptr;
+
+ if (l > c)
+ l = c;
+ if (l <= 0)
+ return 0;
+
+ COPY_TO_USER (buf, 0, &status_buf[status_ptr], l);
+ status_ptr += l;
+
+ return l;
+}
+
+int
+sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+ DEB (printk ("sound_read_sw(dev=%d, count=%d)\n", dev, count));
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ return read_status (buf, count);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_read (dev, file, buf, count);
+ break;
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_read (dev, file, buf, count);
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ return MIDIbuf_read (dev, file, buf, count);
+#endif
+
+ default:
+ printk ("Sound: Undefined minor device %d\n", dev);
+ }
+
+ return RET_ERROR (EPERM);
+}
+
+int
+sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
+{
+
+ DEB (printk ("sound_write_sw(dev=%d, count=%d)\n", dev, count));
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_write (dev, file, buf, count);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_write (dev, file, buf, count);
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ return MIDIbuf_write (dev, file, buf, count);
+#endif
+
+ default:
+ return RET_ERROR (EPERM);
+ }
+
+ return count;
+}
+
+int
+sound_open_sw (int dev, struct fileinfo *file)
+{
+ int retval;
+
+ DEB (printk ("sound_open_sw(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));
+
+ if ((dev >= SND_NDEVS) || (dev < 0))
+ {
+ printk ("Invalid minor device %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_busy)
+ return RET_ERROR (EBUSY);
+ status_busy = 1;
+ if ((status_buf = (char *) KERNEL_MALLOC (4000)) == NULL)
+ return RET_ERROR (EIO);
+ status_len = status_ptr = 0;
+ init_status ();
+ break;
+
+ case SND_DEV_CTL:
+ return 0;
+ break;
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ if ((retval = sequencer_open (dev, file)) < 0)
+ return retval;
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ if ((retval = MIDIbuf_open (dev, file)) < 0)
+ return retval;
+ break;
+#endif
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ if ((retval = audio_open (dev, file)) < 0)
+ return retval;
+ break;
+
+ default:
+ printk ("Invalid minor device %d\n", dev);
+ return RET_ERROR (ENXIO);
+ }
+
+ sbc_devices[dev].usecount++;
+ in_use++;
+
+ return 0;
+}
+
+void
+sound_release_sw (int dev, struct fileinfo *file)
+{
+
+ DEB (printk ("sound_release_sw(dev=%d)\n", dev));
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_buf)
+ KERNEL_FREE (status_buf);
+ status_buf = NULL;
+ status_busy = 0;
+ break;
+
+ case SND_DEV_CTL:
+ break;
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ sequencer_release (dev, file);
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ MIDIbuf_release (dev, file);
+ break;
+#endif
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ audio_release (dev, file);
+ break;
+
+ default:
+ printk ("Sound error: Releasing unknown device 0x%02x\n", dev);
+ }
+
+ sbc_devices[dev].usecount--;
+ in_use--;
+}
+
+int
+sound_ioctl_sw (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned long arg)
+{
+ DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+
+ if ((cmd >> 8) & 0xff == 'M' && num_mixers > 0) /* Mixer ioctl */
+ if ((dev & 0x0f) != SND_DEV_CTL)
+ {
+ int dtype = dev & 0x0f;
+ int mixdev;
+
+ switch (dtype)
+ {
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ mixdev = audio_devs[dev >> 4]->mixer_dev;
+ if (mixdev < 0 || mixdev >= num_mixers)
+ return RET_ERROR (ENXIO);
+ return mixer_devs[mixdev]->ioctl (mixdev, cmd, arg);
+ break;
+
+ default:
+ return mixer_devs[0]->ioctl (0, cmd, arg);
+ }
+ }
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_CTL:
+
+ if (!num_mixers)
+ return RET_ERROR (ENXIO);
+
+ dev = dev >> 4;
+
+ if (dev >= num_mixers)
+ return RET_ERROR (ENXIO);
+
+ return mixer_devs[dev]->ioctl (dev, cmd, arg);
+ break;
+
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_ioctl (dev, file, cmd, arg);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_ioctl (dev, file, cmd, arg);
+ break;
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ return MIDIbuf_ioctl (dev, file, cmd, arg);
+ break;
+#endif
+
+ default:
+ return RET_ERROR (EPERM);
+ break;
+ }
+
+ return RET_ERROR (EPERM);
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/sound_timer.c b/sys/pc98/pc98/sound/sound_timer.c
new file mode 100644
index 0000000..e318779
--- /dev/null
+++ b/sys/pc98/pc98/sound/sound_timer.c
@@ -0,0 +1,406 @@
+/*
+ * sound/sound_timer.c
+ *
+ * Timer for the level 2 interface of the /dev/sequencer. Uses the
+ * 80 and 320 usec timers of OPL-3 (PAS16 only) and GUS.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_SEQUENCER) && (!defined(EXCLUDE_GUS) || (!defined(EXCLUDE_PAS) && !defined(EXCLUDE_YM3812)))
+
+static volatile int initialized = 0, opened = 0, tmr_running = 0;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+static volatile int select_addr, data_addr;
+static volatile int curr_timer = 0;
+static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
+
+
+static void
+timer_command (unsigned int addr, unsigned int val)
+{
+ int i;
+
+ OUTB ((unsigned char) (addr & 0xff), select_addr);
+
+ for (i = 0; i < 2; i++)
+ INB (select_addr);
+
+ OUTB ((unsigned char) (val & 0xff), data_addr);
+
+ for (i = 0; i < 2; i++)
+ INB (select_addr);
+}
+
+static void
+arm_timer (int timer, unsigned int interval)
+{
+
+ curr_timer = timer;
+
+ if (timer == 1)
+ {
+ gus_write8 (0x46, 256 - interval); /* Set counter for timer 1 */
+ gus_write8 (0x45, 0x04); /* Enable timer 1 IRQ */
+ timer_command (0x04, 0x01); /* Start timer 1 */
+ }
+ else
+ {
+ gus_write8 (0x47, 256 - interval); /* Set counter for timer 2 */
+ gus_write8 (0x45, 0x08); /* Enable timer 2 IRQ */
+ timer_command (0x04, 0x02); /* Start timer 2 */
+ }
+}
+
+static unsigned long
+tmr2ticks (int tmr_value)
+{
+ /*
+ * Convert timer ticks to MIDI ticks
+ */
+
+ unsigned long tmp;
+ unsigned long scale;
+
+ tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
+
+ scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
+
+ return (tmp + (scale / 2)) / scale;
+}
+
+static void
+reprogram_timer (void)
+{
+ unsigned long usecs_per_tick;
+ int timer_no, resolution;
+ int divisor;
+
+ usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
+
+ /*
+ * Don't kill the system by setting too high timer rate
+ */
+ if (usecs_per_tick < 2000)
+ usecs_per_tick = 2000;
+
+ if (usecs_per_tick > (256 * 80))
+ {
+ timer_no = 2;
+ resolution = 320; /* usec */
+ }
+ else
+ {
+ timer_no = 1;
+ resolution = 80; /* usec */
+ }
+
+ divisor = (usecs_per_tick + (resolution / 2)) / resolution;
+ usecs_per_tmr = divisor * resolution;
+
+ arm_timer (timer_no, divisor);
+}
+
+static void
+tmr_reset (void)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ tmr_offs = 0;
+ ticks_offs = 0;
+ tmr_ctr = 0;
+ next_event_time = 0xffffffff;
+ prev_event_time = 0;
+ curr_ticks = 0;
+ RESTORE_INTR (flags);
+}
+
+static int
+timer_open (int dev, int mode)
+{
+ if (opened)
+ return RET_ERROR (EBUSY);
+
+ tmr_reset ();
+ curr_tempo = 60;
+ curr_timebase = HZ;
+ opened = 1;
+ reprogram_timer ();
+
+ return 0;
+}
+
+static void
+timer_close (int dev)
+{
+ opened = tmr_running = 0;
+ gus_write8 (0x45, 0); /* Disable both timers */
+}
+
+static int
+timer_event (int dev, unsigned char *event)
+{
+ unsigned char cmd = event[1];
+ unsigned long parm = *(int *) &event[4];
+
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ reprogram_timer ();
+ break;
+
+ case TMR_STOP:
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ tmr_running = 1;
+ reprogram_timer ();
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = parm;
+ reprogram_timer ();
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input (event, 8);
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+timer_get_time (int dev)
+{
+ if (!opened)
+ return 0;
+
+ return curr_ticks;
+}
+
+static int
+timer_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ switch (cmd)
+ {
+ case SNDCTL_TMR_SOURCE:
+ return IOCTL_OUT (arg, TMR_INTERNAL);
+ break;
+
+ case SNDCTL_TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 1)
+ val = 1;
+ if (val > 1000)
+ val = 1000;
+ curr_timebase = val;
+ }
+
+ return IOCTL_OUT (arg, curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = val;
+ reprogram_timer ();
+ }
+
+ return IOCTL_OUT (arg, curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (IOCTL_IN (arg) != 0) /* Can't change */
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ /* NOP */
+ break;
+
+ default:
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+timer_arm (int dev, long time)
+{
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
+
+ next_event_time = prev_event_time = time;
+
+ return;
+}
+
+static struct sound_timer_operations sound_timer =
+{
+ {"OPL-3/GUS Timer", 0},
+ 1, /* Priority */
+ 0, /* Local device link */
+ timer_open,
+ timer_close,
+ timer_event,
+ timer_get_time,
+ timer_ioctl,
+ timer_arm
+};
+
+void
+sound_timer_interrupt (void)
+{
+ gus_write8 (0x45, 0); /* Ack IRQ */
+ timer_command (4, 0x80); /* Reset IRQ flags */
+
+ if (!opened)
+ return;
+
+ if (curr_timer == 1)
+ gus_write8 (0x45, 0x04); /* Start timer 1 again */
+ else
+ gus_write8 (0x45, 0x08); /* Start timer 2 again */
+
+ if (!tmr_running)
+ return;
+
+ tmr_ctr++;
+ curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
+
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = 0xffffffff;
+ sequencer_timer ();
+ }
+}
+
+void
+sound_timer_init (int io_base)
+{
+ int n;
+
+ if (initialized)
+ return; /* There is already a similar timer */
+
+ select_addr = io_base;
+ data_addr = io_base + 1;
+
+ initialized = 1;
+
+#if 1
+ if (num_sound_timers >= MAX_TIMER_DEV)
+ n = 0; /* Overwrite the system timer */
+ else
+ n = num_sound_timers++;
+#else
+ n = 0;
+#endif
+
+ sound_timer_devs[n] = &sound_timer;
+}
+
+#endif
+#endif
diff --git a/sys/pc98/pc98/sound/soundcard.c b/sys/pc98/pc98/sound/soundcard.c
new file mode 100644
index 0000000..bc08b2b
--- /dev/null
+++ b/sys/pc98/pc98/sound/soundcard.c
@@ -0,0 +1,621 @@
+/*
+ * sound/386bsd/soundcard.c
+ *
+ * Soundcard driver for FreeBSD.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $Id: soundcard.c,v 1.42 1996/03/28 14:31:13 scrappy Exp $
+ */
+
+#include "sound_config.h"
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#include "dev_table.h"
+#ifdef PC98
+#include <pc98/pc98/pc98_device.h>
+#else
+#include <i386/isa/isa_device.h>
+#endif
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /*DEVFS*/
+
+#define FIX_RETURN(ret) { \
+ int tmp_ret = (ret); \
+ if (tmp_ret<0) return -tmp_ret; else return 0; \
+ }
+
+static int timer_running = 0;
+
+static int soundcards_installed = 0; /* Number of installed
+ * soundcards */
+static int soundcard_configured = 0;
+
+static struct fileinfo files[SND_NDEVS];
+
+#ifdef DEVFS
+static void * snd_devfs_token[SND_NDEVS];
+static void * sndstat_devfs_token;
+#endif
+
+struct selinfo selinfo[SND_NDEVS >> 4];
+
+#ifdef PC98
+static int sndprobe (struct pc98_device *dev);
+static int sndattach (struct pc98_device *dev);
+#else
+static int sndprobe (struct isa_device *dev);
+static int sndattach (struct isa_device *dev);
+#endif
+static void sound_mem_init(void);
+
+static d_open_t sndopen;
+static d_close_t sndclose;
+static d_read_t sndread;
+static d_write_t sndwrite;
+static d_ioctl_t sndioctl;
+static d_select_t sndselect;
+
+#define CDEV_MAJOR 30
+static struct cdevsw snd_cdevsw =
+ { sndopen, sndclose, sndread, sndwrite, /*30*/
+ sndioctl, nostop, nullreset, nodevtotty,/* sound */
+ sndselect, nommap, NULL, "snd", NULL, -1 };
+
+#ifdef PC98
+struct pc98_driver opldriver = {sndprobe, sndattach, "opl"};
+struct pc98_driver sbdriver = {sndprobe, sndattach, "sb"};
+struct pc98_driver sbxvidriver = {sndprobe, sndattach, "sbxvi"};
+struct pc98_driver sbmididriver = {sndprobe, sndattach, "sbmidi"};
+struct pc98_driver pasdriver = {sndprobe, sndattach, "pas"};
+struct pc98_driver mpudriver = {sndprobe, sndattach, "mpu"};
+struct pc98_driver gusdriver = {sndprobe, sndattach, "gus"};
+struct pc98_driver gusxvidriver = {sndprobe, sndattach, "gusxvi"};
+struct pc98_driver gusmaxdriver = {sndprobe, sndattach, "gusmax"};
+struct pc98_driver uartdriver = {sndprobe, sndattach, "uart"};
+struct pc98_driver mssdriver = {sndprobe, sndattach, "mss"};
+struct pc98_driver pcmdriver = {sndprobe, sndattach, "pcm"};
+#else
+struct isa_driver opldriver = {sndprobe, sndattach, "opl"};
+struct isa_driver sbdriver = {sndprobe, sndattach, "sb"};
+struct isa_driver sbxvidriver = {sndprobe, sndattach, "sbxvi"};
+struct isa_driver sbmididriver = {sndprobe, sndattach, "sbmidi"};
+struct isa_driver pasdriver = {sndprobe, sndattach, "pas"};
+struct isa_driver mpudriver = {sndprobe, sndattach, "mpu"};
+struct isa_driver gusdriver = {sndprobe, sndattach, "gus"};
+struct isa_driver gusxvidriver = {sndprobe, sndattach, "gusxvi"};
+struct isa_driver gusmaxdriver = {sndprobe, sndattach, "gusmax"};
+struct isa_driver uartdriver = {sndprobe, sndattach, "uart"};
+struct isa_driver mssdriver = {sndprobe, sndattach, "mss"};
+#endif
+
+static unsigned short
+ipri_to_irq (unsigned short ipri);
+
+void
+adintr(INT_HANDLER_PARMS(unit,dummy))
+{
+#ifndef EXCLUDE_AD1848
+ static short unit_to_irq[4] = { -1, -1, -1, -1 };
+#ifdef PC98
+ struct pc98_device *dev;
+#else
+ struct isa_device *dev;
+#endif
+
+ if (unit_to_irq [unit] > 0)
+ ad1848_interrupt(INT_HANDLER_CALL (unit_to_irq [unit]));
+ else {
+#ifdef PC98
+ dev = find_pc98dev (pc98_devtab_null, &mssdriver, unit);
+#else
+ dev = find_isadev (isa_devtab_null, &mssdriver, unit);
+#endif
+ if (!dev)
+ printk ("ad1848: Couldn't determine unit\n");
+ else {
+ unit_to_irq [unit] = ipri_to_irq (dev->id_irq);
+ ad1848_interrupt(INT_HANDLER_CALL (unit_to_irq [unit]));
+ }
+ }
+#endif
+}
+
+unsigned
+long
+get_time(void)
+{
+struct timeval timecopy;
+int x;
+
+ x = splclock();
+ timecopy = time;
+ splx(x);
+ return timecopy.tv_usec/(1000000/HZ) +
+ (unsigned long)timecopy.tv_sec*HZ;
+}
+
+
+static int
+sndread (dev_t dev, struct uio *buf, int ioflag)
+{
+ int count = buf->uio_resid;
+
+ dev = minor (dev);
+
+ FIX_RETURN (sound_read_sw (dev, &files[dev], buf, count));
+}
+
+static int
+sndwrite (dev_t dev, struct uio *buf, int ioflag)
+{
+ int count = buf->uio_resid;
+
+ dev = minor (dev);
+
+ FIX_RETURN (sound_write_sw (dev, &files[dev], buf, count));
+}
+
+static int
+sndopen (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ dev = minor (dev);
+
+ if (!soundcard_configured && dev)
+ {
+ printk ("SoundCard Error: The soundcard system has not been configured\n");
+ FIX_RETURN (-ENODEV);
+ }
+
+ files[dev].mode = 0;
+
+ if (flags & FREAD && flags & FWRITE)
+ files[dev].mode = OPEN_READWRITE;
+ else if (flags & FREAD)
+ files[dev].mode = OPEN_READ;
+ else if (flags & FWRITE)
+ files[dev].mode = OPEN_WRITE;
+
+ selinfo[dev >> 4].si_pid = 0;
+ selinfo[dev >> 4].si_flags = 0;
+
+ FIX_RETURN(sound_open_sw (dev, &files[dev]));
+}
+
+static int
+sndclose (dev_t dev, int flags, int fmt, struct proc *p)
+{
+
+ dev = minor (dev);
+
+ sound_release_sw(dev, &files[dev]);
+ FIX_RETURN (0);
+}
+
+static int
+sndioctl (dev_t dev, int cmd, caddr_t arg, int flags, struct proc *p)
+{
+ dev = minor (dev);
+
+ FIX_RETURN (sound_ioctl_sw (dev, &files[dev], cmd, (unsigned int) arg));
+}
+
+static int
+sndselect (dev_t dev, int rw, struct proc *p)
+{
+ dev = minor (dev);
+
+ DEB (printk ("snd_select(dev=%d, rw=%d, pid=%d)\n", dev, rw, p->p_pid));
+#ifdef ALLOW_SELECT
+ switch (dev & 0x0f)
+ {
+#ifndef EXCLUDE_SEQUENCER
+ case SND_DEV_SEQ:
+ case SND_DEV_SEQ2:
+ return sequencer_select (dev, &files[dev], rw, p);
+ break;
+#endif
+
+#ifndef EXCLUDE_MIDI
+ case SND_DEV_MIDIN:
+ return MIDIbuf_select (dev, &files[dev], rw, p);
+ break;
+#endif
+
+#ifndef EXCLUDE_AUDIO
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_select (dev, &files[dev], rw, p);
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+#endif
+
+ return 0;
+}
+
+static unsigned short
+ipri_to_irq (unsigned short ipri)
+{
+ /*
+ * Converts the ipri (bitmask) to the corresponding irq number
+ */
+ int irq;
+
+ for (irq = 0; irq < 16; irq++)
+ if (ipri == (1 << irq))
+ return irq;
+
+ return -1; /* Invalid argument */
+}
+
+static int
+#ifdef PC98
+driver_to_voxunit(struct pc98_driver *driver)
+#else
+driver_to_voxunit(struct isa_driver *driver)
+#endif
+{
+ /* converts a sound driver pointer into the equivalent
+ VoxWare device unit number */
+ if(driver == &opldriver)
+ return(SNDCARD_ADLIB);
+ else if(driver == &sbdriver)
+ return(SNDCARD_SB);
+ else if(driver == &pasdriver)
+ return(SNDCARD_PAS);
+ else if(driver == &gusdriver)
+ return(SNDCARD_GUS);
+ else if(driver == &mpudriver)
+ return(SNDCARD_MPU401);
+#ifdef PC98
+ else if(driver == &sbxvidriver)
+ return(SNDCARD_SB16);
+ else if(driver == &sbmididriver)
+ return(SNDCARD_SB16MIDI);
+#else
+ else if(driver == &sbxvidriver)
+ return(SNDCARD_SB16);
+ else if(driver == &sbmididriver)
+ return(SNDCARD_SB16MIDI);
+#endif
+ else if(driver == &uartdriver)
+ return(SNDCARD_UART6850);
+ else if(driver == &gusdriver)
+ return(SNDCARD_GUS16);
+ else if(driver == &mssdriver)
+ return(SNDCARD_MSS);
+#ifdef PC98
+ else if(driver == &pcmdriver)
+ return(SNDCARD_PCM86);
+#endif
+ else
+ return(0);
+}
+
+static int
+#ifdef PC98
+sndprobe (struct pc98_device *dev)
+#else
+sndprobe (struct isa_device *dev)
+#endif
+{
+ struct address_info hw_config;
+ int unit;
+
+ unit = driver_to_voxunit(dev->id_driver);
+ hw_config.io_base = dev->id_iobase;
+ hw_config.irq = ipri_to_irq (dev->id_irq);
+ hw_config.dma = dev->id_drq;
+ hw_config.dma_read = dev->id_flags; /* misuse the flags field for read dma*/
+
+ if(unit)
+#ifdef PC98
+ if(unit == SNDCARD_PCM86) {
+ int result;
+
+ result = sndtable_probe (unit, &hw_config);
+ dev->id_iobase = hw_config.io_base;
+ dev->id_irq = (1 << hw_config.irq);
+ dev->id_drq = hw_config.dma;
+
+ return result;
+ }
+ else
+#endif
+ return sndtable_probe (unit, &hw_config);
+ else
+ return 0;
+}
+
+static int
+#ifdef PC98
+sndattach (struct pc98_device *dev)
+#else
+sndattach (struct isa_device *dev)
+#endif
+{
+ int unit;
+ static int midi_initialized = 0;
+ static int seq_initialized = 0;
+ unsigned long mem_start = 0xefffffffUL;
+ struct address_info hw_config;
+
+ unit = driver_to_voxunit(dev->id_driver);
+ hw_config.io_base = dev->id_iobase;
+ hw_config.irq = ipri_to_irq (dev->id_irq);
+ hw_config.dma = dev->id_drq;
+ hw_config.dma_read = dev->id_flags; /* misuse the flags field for read dma*/
+
+ if(!unit)
+ return FALSE;
+ if (!sndtable_init_card (unit, &hw_config))
+ {
+ printf (" <Driver not configured>");
+ return FALSE;
+ }
+
+ /*
+ * Init the high level sound driver
+ */
+
+ if (!(soundcards_installed = sndtable_get_cardcount ()))
+ {
+ printf (" <No such hardware>");
+ return FALSE; /* No cards detected */
+ }
+
+ printf("\n");
+
+#ifndef EXCLUDE_AUDIO
+ if (num_audiodevs) /* Audio devices present */
+ {
+ mem_start = DMAbuf_init (mem_start);
+ mem_start = audio_init (mem_start);
+ sound_mem_init ();
+ }
+
+ soundcard_configured = 1;
+#endif
+
+#ifndef EXCLUDE_MIDI
+ if (num_midis && !midi_initialized)
+ {
+ midi_initialized = 1;
+ mem_start = MIDIbuf_init (mem_start);
+ }
+#endif
+
+#ifndef EXCLUDE_SEQUENCER
+ if ((num_midis + num_synths) && !seq_initialized)
+ {
+ seq_initialized = 1;
+ mem_start = sequencer_init (mem_start);
+ }
+#endif
+
+#ifdef DEVFS
+/* XXX */ /* find out where to store the tokens.. */
+/* XXX */ /* should only create devices if that card has them */
+#define SND_UID 0
+#define SND_GID 13
+
+ snd_devfs_token[unit]=
+ devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_CTL, DV_CHR,
+ SND_UID, SND_GID, 0660, "mixer%d", unit);
+
+#ifndef EXCLUDE_SEQUENCER
+ snd_devfs_token[unit]=
+ devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_SEQ, DV_CHR,
+ SND_UID, SND_GID, 0660, "sequencer%d", unit);
+ snd_devfs_token[unit]=
+ devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_SEQ2, DV_CHR,
+ SND_UID, SND_GID, 0660, "music%d", unit);
+#endif
+
+#ifndef EXCLUDE_MIDI
+ snd_devfs_token[unit]=
+ devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_MIDIN,
+ DV_CHR, SND_UID, SND_GID, 0660, "midi%d",
+ unit);
+#endif
+
+#ifndef EXCLUDE_AUDIO
+ snd_devfs_token[unit]=
+ devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_DSP, DV_CHR,
+ SND_UID, SND_GID, 0660, "dsp%d", unit);
+ snd_devfs_token[unit]=
+ devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_AUDIO,
+ DV_CHR, SND_UID, SND_GID, 0660, "audio%d",
+ unit);
+ snd_devfs_token[unit]=
+ devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_DSP16,
+ DV_CHR, SND_UID, SND_GID, 0660, "dspW%d",
+ unit);
+#endif
+
+ snd_devfs_token[unit]=
+ devfs_add_devswf(&snd_cdevsw, (unit << 4)+SND_DEV_SNDPROC,
+ DV_CHR, SND_UID, SND_GID, 0660, "pss%d",
+ unit);
+
+ if ( ! sndstat_devfs_token) {
+ sndstat_devfs_token =
+ devfs_add_devswf(&snd_cdevsw, 6, DV_CHR, SND_UID, SND_GID,
+ 0660, "sndstat");
+ }
+#endif /* DEVFS */
+ return TRUE;
+}
+
+void
+tenmicrosec (void)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ inb (0x80);
+}
+
+#ifndef EXCLUDE_SEQUENCER
+void
+request_sound_timer (int count)
+{
+ static int current = 0;
+ int tmp = count;
+
+ if (count < 0)
+ timeout ((timeout_func_t)sequencer_timer, 0, -count);
+ else
+ {
+
+ if (count < current)
+ current = 0; /* Timer restarted */
+
+ count = count - current;
+
+ current = tmp;
+
+ if (!count)
+ count = 1;
+
+ timeout ((timeout_func_t)sequencer_timer, 0, count);
+ }
+ timer_running = 1;
+}
+
+void
+sound_stop_timer (void)
+{
+ if (timer_running)
+ untimeout ((timeout_func_t)sequencer_timer, 0);
+ timer_running = 0;
+}
+#endif
+
+#ifndef EXCLUDE_AUDIO
+static void
+sound_mem_init (void)
+{
+ int dev;
+ unsigned long dma_pagesize;
+ struct dma_buffparms *dmap;
+ static unsigned long dsp_init_mask = 0;
+
+ for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */
+ if (!(dsp_init_mask & (1 << dev))) /* Not already done */
+#ifdef PC98
+ if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan >= 0)
+#else
+ if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan > 0)
+#endif
+ {
+ dsp_init_mask |= (1 << dev);
+ dmap = audio_devs[dev]->dmap;
+
+ if (audio_devs[dev]->flags & DMA_AUTOMODE)
+ audio_devs[dev]->buffcount = 1;
+
+ audio_devs[dev]->buffsize &= ~0xfff; /* Truncate to n*4k */
+
+ if (audio_devs[dev]->dmachan > 3 && audio_devs[dev]->buffsize > 65536)
+ dma_pagesize = 131072; /* 128k */
+ else
+ dma_pagesize = 65536;
+
+ /* More sanity checks */
+
+ if (audio_devs[dev]->buffsize > dma_pagesize)
+ audio_devs[dev]->buffsize = dma_pagesize;
+ if (audio_devs[dev]->buffsize < 4096)
+ audio_devs[dev]->buffsize = 4096;
+
+ /* Now allocate the buffers */
+
+ for (dmap->raw_count = 0; dmap->raw_count < audio_devs[dev]->buffcount; dmap->raw_count++)
+ {
+ char *tmpbuf = (char *)vm_page_alloc_contig(audio_devs[dev]->buffsize, 0ul, 0xfffffful, dma_pagesize);
+
+ if (tmpbuf == NULL)
+ {
+ printk ("snd: Unable to allocate %ld bytes of buffer\n",
+ audio_devs[dev]->buffsize);
+ return;
+ }
+
+ dmap->raw_buf[dmap->raw_count] = tmpbuf;
+ /*
+ * Use virtual address as the physical address, since
+ * isa_dmastart performs the phys address computation.
+ */
+ dmap->raw_buf_phys[dmap->raw_count] =
+ (unsigned long) dmap->raw_buf[dmap->raw_count];
+ }
+ } /* for dev */
+
+}
+
+#endif
+
+int
+snd_set_irq_handler (int interrupt_level, INT_HANDLER_PROTO(), char *name)
+{
+ return 1;
+}
+
+
+void
+snd_release_irq(int vect)
+{
+}
+
+static snd_devsw_installed = 0;
+
+static void
+snd_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! snd_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev,&snd_cdevsw, NULL);
+ snd_devsw_installed = 1;
+ }
+}
+
+SYSINIT(snddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,snd_drvinit,NULL)
+
+#endif
+
diff --git a/sys/pc98/pc98/sound/soundvers.h b/sys/pc98/pc98/sound/soundvers.h
new file mode 100644
index 0000000..ca892e8
--- /dev/null
+++ b/sys/pc98/pc98/sound/soundvers.h
@@ -0,0 +1 @@
+#define SOUND_VERSION_STRING "3.0-beta-950506"
diff --git a/sys/pc98/pc98/sound/sscape.c b/sys/pc98/pc98/sound/sscape.c
new file mode 100644
index 0000000..9204b18
--- /dev/null
+++ b/sys/pc98/pc98/sound/sscape.c
@@ -0,0 +1,1120 @@
+/*
+ * sound/sscape.c
+ *
+ * Low level driver for Ensoniq Soundscape
+ *
+ * Copyright by Hannu Savolainen 1994
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SSCAPE)
+
+#include "coproc.h"
+
+/*
+ * I/O ports
+ */
+#define MIDI_DATA 0
+#define MIDI_CTRL 1
+#define HOST_CTRL 2
+#define TX_READY 0x02
+#define RX_READY 0x01
+#define HOST_DATA 3
+#define ODIE_ADDR 4
+#define ODIE_DATA 5
+
+/*
+ * Indirect registers
+ */
+#define GA_INTSTAT_REG 0
+#define GA_INTENA_REG 1
+#define GA_DMAA_REG 2
+#define GA_DMAB_REG 3
+#define GA_INTCFG_REG 4
+#define GA_DMACFG_REG 5
+#define GA_CDCFG_REG 6
+#define GA_SMCFGA_REG 7
+#define GA_SMCFGB_REG 8
+#define GA_HMCTL_REG 9
+
+/*
+ * DMA channel identifiers (A and B)
+ */
+#define SSCAPE_DMA_A 0
+#define SSCAPE_DMA_B 1
+
+#define PORT(name) (devc->base+name)
+
+/*
+ * Host commands recognized by the OBP microcode
+ */
+#define CMD_GEN_HOST_ACK 0x80
+#define CMD_GEN_MPU_ACK 0x81
+#define CMD_GET_BOARD_TYPE 0x82
+#define CMD_SET_CONTROL 0x88
+#define CMD_GET_CONTROL 0x89
+#define CMD_SET_MT32 0x96
+#define CMD_GET_MT32 0x97
+#define CMD_SET_EXTMIDI 0x9b
+#define CMD_GET_EXTMIDI 0x9c
+
+#define CMD_ACK 0x80
+
+typedef struct sscape_info
+ {
+ int base, irq, dma;
+ int ok; /* Properly detected */
+ int dma_allocated;
+ int my_audiodev;
+ int opened;
+ }
+
+sscape_info;
+static struct sscape_info dev_info =
+{0};
+static struct sscape_info *devc = &dev_info;
+
+DEFINE_WAIT_QUEUE (sscape_sleeper, sscape_sleep_flag);
+
+#ifdef REVEAL_SPEA
+/* Spea and Reveal have assigned interrupt bits differently than Ensoniq */
+static char valid_interrupts[] =
+{9, 7, 5, 15};
+
+#else
+static char valid_interrupts[] =
+{9, 5, 7, 10};
+
+#endif
+
+static unsigned char
+sscape_read (struct sscape_info *devc, int reg)
+{
+ unsigned long flags;
+ unsigned char val;
+
+ DISABLE_INTR (flags);
+ OUTB (reg, PORT (ODIE_ADDR));
+ val = INB (PORT (ODIE_DATA));
+ RESTORE_INTR (flags);
+ return val;
+}
+
+static void
+sscape_write (struct sscape_info *devc, int reg, int data)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ OUTB (reg, PORT (ODIE_ADDR));
+ OUTB (data, PORT (ODIE_DATA));
+ RESTORE_INTR (flags);
+}
+
+static void
+host_open (struct sscape_info *devc)
+{
+ OUTB (0x00, PORT (HOST_CTRL)); /* Put the board to the host mode */
+}
+
+static void
+host_close (struct sscape_info *devc)
+{
+ OUTB (0x03, PORT (HOST_CTRL)); /* Put the board to the MIDI mode */
+}
+
+static int
+host_write (struct sscape_info *devc, unsigned char *data, int count)
+{
+ unsigned long flags;
+ int i, timeout;
+
+ DISABLE_INTR (flags);
+
+ /*
+ * Send the command and data bytes
+ */
+
+ for (i = 0; i < count; i++)
+ {
+ for (timeout = 10000; timeout > 0; timeout--)
+ if (INB (PORT (HOST_CTRL)) & TX_READY)
+ break;
+
+ if (timeout <= 0)
+ {
+ RESTORE_INTR (flags);
+ return 0;
+ }
+
+ OUTB (data[i], PORT (HOST_DATA));
+ }
+
+
+ RESTORE_INTR (flags);
+
+ return 1;
+}
+
+static int
+host_read (struct sscape_info *devc)
+{
+ unsigned long flags;
+ int timeout;
+ unsigned char data;
+
+ DISABLE_INTR (flags);
+
+ /*
+ * Read a byte
+ */
+
+ for (timeout = 10000; timeout > 0; timeout--)
+ if (INB (PORT (HOST_CTRL)) & RX_READY)
+ break;
+
+ if (timeout <= 0)
+ {
+ RESTORE_INTR (flags);
+ return -1;
+ }
+
+ data = INB (PORT (HOST_DATA));
+
+ RESTORE_INTR (flags);
+
+ return data;
+}
+
+static int
+host_command1 (struct sscape_info *devc, int cmd)
+{
+ unsigned char buf[10];
+
+ buf[0] = (unsigned char) (cmd & 0xff);
+
+ return host_write (devc, buf, 1);
+}
+
+static int
+host_command2 (struct sscape_info *devc, int cmd, int parm1)
+{
+ unsigned char buf[10];
+
+ buf[0] = (unsigned char) (cmd & 0xff);
+ buf[1] = (unsigned char) (parm1 & 0xff);
+
+ return host_write (devc, buf, 2);
+}
+
+static int
+host_command3 (struct sscape_info *devc, int cmd, int parm1, int parm2)
+{
+ unsigned char buf[10];
+
+ buf[0] = (unsigned char) (cmd & 0xff);
+ buf[1] = (unsigned char) (parm1 & 0xff);
+ buf[2] = (unsigned char) (parm2 & 0xff);
+
+ return host_write (devc, buf, 3);
+}
+
+static void
+set_mt32 (struct sscape_info *devc, int value)
+{
+ host_open (devc);
+ host_command2 (devc, CMD_SET_MT32,
+ value ? 1 : 0);
+ if (host_read (devc) != CMD_ACK)
+ {
+ printk ("SNDSCAPE: Setting MT32 mode failed\n");
+ }
+ host_close (devc);
+}
+
+static int
+get_board_type (struct sscape_info *devc)
+{
+ int tmp;
+
+ host_open (devc);
+ if (!host_command1 (devc, CMD_GET_BOARD_TYPE))
+ tmp = -1;
+ else
+ tmp = host_read (devc);
+ host_close (devc);
+ return tmp;
+}
+
+void
+sscapeintr (INT_HANDLER_PARMS (irq, dummy))
+{
+ unsigned char bits, tmp;
+ static int debug = 0;
+
+ printk ("sscapeintr(0x%02x)\n", (bits = sscape_read (devc, GA_INTSTAT_REG)));
+ if (SOMEONE_WAITING (sscape_sleeper, sscape_sleep_flag))
+ {
+ WAKE_UP (sscape_sleeper, sscape_sleep_flag);
+ }
+
+ if (bits & 0x02) /* Host interface interrupt */
+ {
+ printk ("SSCAPE: Host interrupt, data=%02x\n", host_read (devc));
+ }
+
+#if (!defined(EXCLUDE_MPU401) || !defined(EXCLUDE_MPU_EMU)) && !defined(EXCLUDE_MIDI)
+ if (bits & 0x01)
+ {
+ mpuintr (INT_HANDLER_CALL (irq));
+ if (debug++ > 10) /* Temporary debugging hack */
+ {
+ sscape_write (devc, GA_INTENA_REG, 0x00); /* Disable all interrupts */
+ }
+ }
+#endif
+
+ /*
+ * Acknowledge interrupts (toggle the interrupt bits)
+ */
+
+ tmp = sscape_read (devc, GA_INTENA_REG);
+ sscape_write (devc, GA_INTENA_REG, (~bits & 0x0e) | (tmp & 0xf1));
+
+}
+
+static void
+sscape_enable_intr (struct sscape_info *devc, unsigned intr_bits)
+{
+ unsigned char temp, orig;
+
+ temp = orig = sscape_read (devc, GA_INTENA_REG);
+ temp |= intr_bits;
+ temp |= 0x80; /* Master IRQ enable */
+
+ if (temp == orig)
+ return; /* No change */
+
+ sscape_write (devc, GA_INTENA_REG, temp);
+}
+
+static void
+sscape_disable_intr (struct sscape_info *devc, unsigned intr_bits)
+{
+ unsigned char temp, orig;
+
+ temp = orig = sscape_read (devc, GA_INTENA_REG);
+ temp &= ~intr_bits;
+ if ((temp & ~0x80) == 0x00)
+ temp = 0x00; /* Master IRQ disable */
+ if (temp == orig)
+ return; /* No change */
+
+ sscape_write (devc, GA_INTENA_REG, temp);
+}
+
+static void
+do_dma (struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode)
+{
+ unsigned char temp;
+
+ if (dma_chan != SSCAPE_DMA_A)
+ {
+ printk ("SSCAPE: Tried to use DMA channel != A. Why?\n");
+ return;
+ }
+
+ DMAbuf_start_dma (devc->my_audiodev,
+ buf,
+ blk_size, mode);
+
+ temp = devc->dma << 4; /* Setup DMA channel select bits */
+ if (devc->dma <= 3)
+ temp |= 0x80; /* 8 bit DMA channel */
+
+ temp |= 1; /* Trigger DMA */
+ sscape_write (devc, GA_DMAA_REG, temp);
+ temp &= 0xfe; /* Clear DMA trigger */
+ sscape_write (devc, GA_DMAA_REG, temp);
+}
+
+static int
+verify_mpu (struct sscape_info *devc)
+{
+ /*
+ * The SoundScape board could be in three modes (MPU, 8250 and host).
+ * If the card is not in the MPU mode, enabling the MPU driver will
+ * cause infinite loop (the driver believes that there is always some
+ * received data in the buffer.
+ *
+ * Detect this by looking if there are more than 10 received MIDI bytes
+ * (0x00) in the buffer.
+ */
+
+ int i;
+
+ for (i = 0; i < 10; i++)
+ {
+ if (INB (devc->base + HOST_CTRL) & 0x80)
+ return 1;
+
+ if (INB (devc->base) != 0x00)
+ return 1;
+ }
+
+ printk ("SoundScape: The device is not in the MPU-401 mode\n");
+ return 0;
+}
+
+static int
+sscape_coproc_open (void *dev_info, int sub_device)
+{
+ if (sub_device == COPR_MIDI)
+ {
+ set_mt32 (devc, 0);
+ if (!verify_mpu (devc))
+ return RET_ERROR (EIO);
+ }
+
+ return 0;
+}
+
+static void
+sscape_coproc_close (void *dev_info, int sub_device)
+{
+ struct sscape_info *devc = dev_info;
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ if (devc->dma_allocated)
+ {
+ sscape_write (devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */
+#ifndef EXCLUDE_NATIVE_PCM
+ DMAbuf_close_dma (devc->my_audiodev);
+#endif
+ devc->dma_allocated = 0;
+ }
+ RESET_WAIT_QUEUE (sscape_sleeper, sscape_sleep_flag);
+ RESTORE_INTR (flags);
+
+ return;
+}
+
+static void
+sscape_coproc_reset (void *dev_info)
+{
+}
+
+static int
+sscape_download_boot (struct sscape_info *devc, unsigned char *block, int size, int flag)
+{
+ unsigned long flags;
+ unsigned char temp;
+ int done, timeout;
+
+ if (flag & CPF_FIRST)
+ {
+ /*
+ * First block. Have to allocate DMA and to reset the board
+ * before continuing.
+ */
+
+ DISABLE_INTR (flags);
+ if (devc->dma_allocated == 0)
+ {
+#ifndef EXCLUDE_NATIVE_PCM
+ if (DMAbuf_open_dma (devc->my_audiodev) < 0)
+ {
+ RESTORE_INTR (flags);
+ return 0;
+ }
+#endif
+
+ devc->dma_allocated = 1;
+ }
+ RESTORE_INTR (flags);
+
+ sscape_write (devc, GA_HMCTL_REG,
+ (temp = sscape_read (devc, GA_HMCTL_REG)) & 0x3f); /*Reset */
+
+ for (timeout = 10000; timeout > 0; timeout--)
+ sscape_read (devc, GA_HMCTL_REG); /* Delay */
+
+ /* Take board out of reset */
+ sscape_write (devc, GA_HMCTL_REG,
+ (temp = sscape_read (devc, GA_HMCTL_REG)) | 0x80);
+ }
+
+ /*
+ * Transfer one code block using DMA
+ */
+ memcpy (audio_devs[devc->my_audiodev]->dmap->raw_buf[0], block, size);
+
+ DISABLE_INTR (flags);
+/******** INTERRUPTS DISABLED NOW ********/
+ do_dma (devc, SSCAPE_DMA_A,
+ audio_devs[devc->my_audiodev]->dmap->raw_buf_phys[0],
+ size, DMA_MODE_WRITE);
+
+ /*
+ * Wait until transfer completes.
+ */
+ RESET_WAIT_QUEUE (sscape_sleeper, sscape_sleep_flag);
+ done = 0;
+ timeout = 100;
+ while (!done && timeout-- > 0)
+ {
+ int resid;
+
+ DO_SLEEP (sscape_sleeper, sscape_sleep_flag, 1);
+ clear_dma_ff (devc->dma);
+ if ((resid = get_dma_residue (devc->dma)) == 0)
+ done = 1;
+ }
+
+ RESTORE_INTR (flags);
+ if (!done)
+ return 0;
+
+ if (flag & CPF_LAST)
+ {
+ /*
+ * Take the board out of reset
+ */
+ OUTB (0x00, PORT (HOST_CTRL));
+ OUTB (0x00, PORT (MIDI_CTRL));
+
+ temp = sscape_read (devc, GA_HMCTL_REG);
+ temp |= 0x40;
+ sscape_write (devc, GA_HMCTL_REG, temp); /* Kickstart the board */
+
+ /*
+ * Wait until the ODB wakes up
+ */
+
+ DISABLE_INTR (flags);
+ done = 0;
+ timeout = 5 * HZ;
+ while (!done && timeout-- > 0)
+ {
+ DO_SLEEP (sscape_sleeper, sscape_sleep_flag, 1);
+ if (INB (PORT (HOST_DATA)) == 0xff) /* OBP startup acknowledge */
+ done = 1;
+ }
+ RESTORE_INTR (flags);
+ if (!done)
+ {
+ printk ("SoundScape: The OBP didn't respond after code download\n");
+ return 0;
+ }
+
+ DISABLE_INTR (flags);
+ done = 0;
+ timeout = 5 * HZ;
+ while (!done && timeout-- > 0)
+ {
+ DO_SLEEP (sscape_sleeper, sscape_sleep_flag, 1);
+ if (INB (PORT (HOST_DATA)) == 0xfe) /* Host startup acknowledge */
+ done = 1;
+ }
+ RESTORE_INTR (flags);
+ if (!done)
+ {
+ printk ("SoundScape: OBP Initialization failed.\n");
+ return 0;
+ }
+
+ printk ("SoundScape board of type %d initialized OK\n",
+ get_board_type (devc));
+
+#ifdef SSCAPE_DEBUG3
+ /*
+ * Temporary debugging aid. Print contents of the registers after
+ * downloading the code.
+ */
+ {
+ int i;
+
+ for (i = 0; i < 13; i++)
+ printk ("I%d = %02x (new value)\n", i, sscape_read (devc, i));
+ }
+#endif
+
+ }
+
+ return 1;
+}
+
+static int
+download_boot_block (void *dev_info, copr_buffer * buf)
+{
+ if (buf->len <= 0 || buf->len > sizeof (buf->data))
+ return RET_ERROR (EINVAL);
+
+ if (!sscape_download_boot (devc, buf->data, buf->len, buf->flags))
+ {
+ printk ("SSCAPE: Unable to load microcode block to the OBP.\n");
+ return RET_ERROR (EIO);
+ }
+
+ return 0;
+}
+
+static int
+sscape_coproc_ioctl (void *dev_info, unsigned int cmd, unsigned int arg, int local)
+{
+
+ switch (cmd)
+ {
+ case SNDCTL_COPR_RESET:
+ sscape_coproc_reset (dev_info);
+ return 0;
+ break;
+
+ case SNDCTL_COPR_LOAD:
+ {
+ copr_buffer *buf;
+ int err;
+
+ buf = (copr_buffer *) KERNEL_MALLOC (sizeof (copr_buffer));
+ IOCTL_FROM_USER ((char *) buf, (char *) arg, 0, sizeof (*buf));
+ err = download_boot_block (dev_info, buf);
+ KERNEL_FREE (buf);
+ return err;
+ }
+ break;
+
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static coproc_operations sscape_coproc_operations =
+{
+ "SoundScape M68K",
+ sscape_coproc_open,
+ sscape_coproc_close,
+ sscape_coproc_ioctl,
+ sscape_coproc_reset,
+ &dev_info
+};
+
+static int
+sscape_audio_open (int dev, int mode)
+{
+ unsigned long flags;
+ sscape_info *devc = (sscape_info *) audio_devs[dev]->devc;
+
+ DISABLE_INTR (flags);
+ if (devc->opened)
+ {
+ RESTORE_INTR (flags);
+ return RET_ERROR (EBUSY);
+ }
+
+ if (devc->dma_allocated == 0)
+ {
+ int err;
+
+ if ((err = DMAbuf_open_dma (devc->my_audiodev)) < 0)
+ {
+ RESTORE_INTR (flags);
+ return err;
+ }
+
+ devc->dma_allocated = 1;
+ }
+
+ devc->opened = 1;
+ RESTORE_INTR (flags);
+#ifdef SSCAPE_DEBUG4
+ /*
+ * Temporary debugging aid. Print contents of the registers
+ * when the device is opened.
+ */
+ {
+ int i;
+
+ for (i = 0; i < 13; i++)
+ printk ("I%d = %02x\n", i, sscape_read (devc, i));
+ }
+#endif
+
+ return 0;
+}
+
+static void
+sscape_audio_close (int dev)
+{
+ unsigned long flags;
+ sscape_info *devc = (sscape_info *) audio_devs[dev]->devc;
+
+ DEB (printk ("sscape_audio_close(void)\n"));
+
+ DISABLE_INTR (flags);
+
+ if (devc->dma_allocated)
+ {
+ sscape_write (devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */
+ DMAbuf_close_dma (dev);
+ devc->dma_allocated = 0;
+ }
+ devc->opened = 0;
+
+ RESTORE_INTR (flags);
+}
+
+static int
+set_speed (sscape_info * devc, int arg)
+{
+ return 8000;
+}
+
+static int
+set_channels (sscape_info * devc, int arg)
+{
+ return 1;
+}
+
+static int
+set_format (sscape_info * devc, int arg)
+{
+ return AFMT_U8;
+}
+
+static int
+sscape_audio_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
+{
+ sscape_info *devc = (sscape_info *) audio_devs[dev]->devc;
+
+ switch (cmd)
+ {
+ case SOUND_PCM_WRITE_RATE:
+ if (local)
+ return set_speed (devc, arg);
+ return IOCTL_OUT (arg, set_speed (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_RATE:
+ if (local)
+ return 8000;
+ return IOCTL_OUT (arg, 8000);
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return set_channels (devc, arg + 1) - 1;
+ return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg) + 1) - 1);
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return set_channels (devc, arg);
+ return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return 1;
+ return IOCTL_OUT (arg, 1);
+
+ case SNDCTL_DSP_SAMPLESIZE:
+ if (local)
+ return set_format (devc, arg);
+ return IOCTL_OUT (arg, set_format (devc, IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return 8;
+ return IOCTL_OUT (arg, 8);
+
+ default:;
+ }
+ return RET_ERROR (EINVAL);
+}
+
+static void
+sscape_audio_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+}
+
+static void
+sscape_audio_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+{
+}
+
+static int
+sscape_audio_prepare_for_input (int dev, int bsize, int bcount)
+{
+ return 0;
+}
+
+static int
+sscape_audio_prepare_for_output (int dev, int bsize, int bcount)
+{
+ return 0;
+}
+
+static void
+sscape_audio_halt (int dev)
+{
+}
+
+static void
+sscape_audio_reset (int dev)
+{
+ sscape_audio_halt (dev);
+}
+
+static struct audio_operations sscape_audio_operations =
+{
+ "Ensoniq SoundScape channel A",
+ 0,
+ AFMT_U8 | AFMT_S16_LE,
+ NULL,
+ sscape_audio_open,
+ sscape_audio_close,
+ sscape_audio_output_block,
+ sscape_audio_start_input,
+ sscape_audio_ioctl,
+ sscape_audio_prepare_for_input,
+ sscape_audio_prepare_for_output,
+ sscape_audio_reset,
+ sscape_audio_halt,
+ NULL,
+ NULL
+};
+
+long
+attach_sscape (long mem_start, struct address_info *hw_config)
+{
+ int my_dev;
+
+#ifndef SSCAPE_REGS
+ /*
+ * Config register values for Spea/V7 Media FX and Ensoniq S-2000.
+ * These values are card
+ * dependent. If you have another SoundScape based card, you have to
+ * find the correct values. Do the following:
+ * - Compile this driver with SSCAPE_DEBUG1 defined.
+ * - Shut down and power off your machine.
+ * - Boot with DOS so that the SSINIT.EXE program is run.
+ * - Warm boot to {Linux|SYSV|BSD} and write down the lines displayed
+ * when detecting the SoundScape.
+ * - Modify the following list to use the values printed during boot.
+ * Undefine the SSCAPE_DEBUG1
+ */
+#define SSCAPE_REGS { \
+/* I0 */ 0x00, \
+ 0xf0, /* Note! Ignored. Set always to 0xf0 */ \
+ 0x20, /* Note! Ignored. Set always to 0x20 */ \
+ 0x20, /* Note! Ignored. Set always to 0x20 */ \
+ 0xf5, /* Ignored */ \
+ 0x10, \
+ 0x00, \
+ 0x2e, /* I7 MEM config A. Likely to vary between models */ \
+ 0x00, /* I8 MEM config A. Likely to vary between models */ \
+/* I9 */ 0x40 /* Ignored */ \
+ }
+#endif
+
+ unsigned long flags;
+ static unsigned char regs[10] = SSCAPE_REGS;
+
+ int i, irq_bits = 0xff;
+
+ if (!probe_sscape (hw_config))
+ return mem_start;
+
+ printk (" <Ensoniq Soundscape>");
+
+ for (i = 0; i < sizeof (valid_interrupts); i++)
+ if (hw_config->irq == valid_interrupts[i])
+ {
+ irq_bits = i;
+ break;
+ }
+
+ if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff))
+ {
+ printk ("Invalid IRQ%d\n", hw_config->irq);
+ return mem_start;
+ }
+
+ DISABLE_INTR (flags);
+
+ for (i = 1; i < 10; i++)
+ switch (i)
+ {
+ case 1: /* Host interrupt enable */
+ sscape_write (devc, i, 0xf0); /* All interrupts enabled */
+ break;
+
+ case 2: /* DMA A status/trigger register */
+ case 3: /* DMA B status/trigger register */
+ sscape_write (devc, i, 0x20); /* DMA channel disabled */
+ break;
+
+ case 4: /* Host interrupt config reg */
+ sscape_write (devc, i, 0xf0 | (irq_bits << 2) | irq_bits);
+ break;
+
+ case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */
+ sscape_write (devc, i, (regs[i] & 0x3f) |
+ (sscape_read (devc, i) & 0x0c));
+ break;
+
+ case 6: /* CD-ROM config. Don't touch. */
+ break;
+
+ case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */
+ sscape_write (devc, i,
+ (sscape_read (devc, i) & 0xf0) | 0x00);
+ break;
+
+ default:
+ sscape_write (devc, i, regs[i]);
+ }
+
+ RESTORE_INTR (flags);
+
+#ifdef SSCAPE_DEBUG2
+ /*
+ * Temporary debugging aid. Print contents of the registers after
+ * changing them.
+ */
+ {
+ int i;
+
+ for (i = 0; i < 13; i++)
+ printk ("I%d = %02x (new value)\n", i, sscape_read (devc, i));
+ }
+#endif
+
+#if !defined(EXCLUDE_MIDI) && !defined(EXCLUDE_MPU_EMU)
+ hw_config->always_detect = 1;
+ if (probe_mpu401 (hw_config))
+ {
+ int prev_devs;
+
+ prev_devs = num_midis;
+ mem_start = attach_mpu401 (mem_start, hw_config);
+
+ if (num_midis == (prev_devs + 1)) /* The MPU driver installed itself */
+ midi_devs[prev_devs]->coproc = &sscape_coproc_operations;
+ }
+#endif
+
+#ifndef EXCLUDE_NATIVE_PCM
+ /* Not supported yet */
+
+#ifndef EXCLUDE_AUDIO
+ if (num_audiodevs < MAX_AUDIO_DEV)
+ {
+ audio_devs[my_dev = num_audiodevs++] = &sscape_audio_operations;
+ audio_devs[my_dev]->dmachan = hw_config->dma;
+ audio_devs[my_dev]->buffcount = 1;
+ audio_devs[my_dev]->buffsize = DSP_BUFFSIZE;
+ audio_devs[my_dev]->devc = devc;
+ devc->my_audiodev = my_dev;
+ devc->opened = 0;
+ audio_devs[my_dev]->coproc = &sscape_coproc_operations;
+ if (snd_set_irq_handler (hw_config->irq, sscapeintr, "SoundScape") < 0)
+ printk ("Error: Can't allocate IRQ for SoundScape\n");
+
+ sscape_write (devc, GA_INTENA_REG, 0x80); /* Master IRQ enable */
+ }
+ else
+ printk ("SoundScape: More than enough audio devices detected\n");
+#endif
+#endif
+ devc->ok = 1;
+ return mem_start;
+}
+
+int
+probe_sscape (struct address_info *hw_config)
+{
+ unsigned char save;
+
+ devc->base = hw_config->io_base;
+ devc->irq = hw_config->irq;
+ devc->dma = hw_config->dma;
+
+ /*
+ * First check that the address register of "ODIE" is
+ * there and that it has exactly 4 writeable bits.
+ * First 4 bits
+ */
+ if ((save = INB (PORT (ODIE_ADDR))) & 0xf0)
+ return 0;
+
+ OUTB (0x00, PORT (ODIE_ADDR));
+ if (INB (PORT (ODIE_ADDR)) != 0x00)
+ return 0;
+
+ OUTB (0xff, PORT (ODIE_ADDR));
+ if (INB (PORT (ODIE_ADDR)) != 0x0f)
+ return 0;
+
+ OUTB (save, PORT (ODIE_ADDR));
+
+ /*
+ * Now verify that some indirect registers return zero on some bits.
+ * This may break the driver with some future revisions of "ODIE" but...
+ */
+
+ if (sscape_read (devc, 0) & 0x0c)
+ return 0;
+
+ if (sscape_read (devc, 1) & 0x0f)
+ return 0;
+
+ if (sscape_read (devc, 5) & 0x0f)
+ return 0;
+
+#ifdef SSCAPE_DEBUG1
+ /*
+ * Temporary debugging aid. Print contents of the registers before
+ * changing them.
+ */
+ {
+ int i;
+
+ for (i = 0; i < 13; i++)
+ printk ("I%d = %02x (old value)\n", i, sscape_read (devc, i));
+ }
+#endif
+
+ return 1;
+}
+
+int
+probe_ss_ms_sound (struct address_info *hw_config)
+{
+ int i, irq_bits = 0xff;
+
+ if (devc->ok == 0)
+ {
+ printk ("SoundScape: Invalid initialization order.\n");
+ return 0;
+ }
+
+ for (i = 0; i < sizeof (valid_interrupts); i++)
+ if (hw_config->irq == valid_interrupts[i])
+ {
+ irq_bits = i;
+ break;
+ }
+#ifdef REVEAL_SPEA
+ {
+ int tmp, status = 0;
+ int cc;
+
+ if (!((tmp = sscape_read (devc, GA_HMCTL_REG)) & 0xc0))
+ {
+ sscape_write (devc, GA_HMCTL_REG, tmp | 0x80);
+ for (cc = 0; cc < 200000; ++cc)
+ INB (devc->base + ODIE_ADDR);
+ }
+ }
+#endif
+
+ if (hw_config->irq > 15 || irq_bits == 0xff)
+ {
+ printk ("SoundScape: Invalid MSS IRQ%d\n", hw_config->irq);
+ return 0;
+ }
+
+ return ad1848_detect (hw_config->io_base);
+}
+
+long
+attach_ss_ms_sound (long mem_start, struct address_info *hw_config)
+{
+ /*
+ * This routine configures the SoundScape card for use with the
+ * Win Sound System driver. The AD1848 codec interface uses the CD-ROM
+ * config registers of the "ODIE".
+ */
+
+ int i, irq_bits = 0xff;
+
+#ifdef EXCLUDE_NATIVE_PCM
+ int prev_devs = num_audiodevs;
+
+#endif
+
+ /*
+ * Setup the DMA polarity.
+ */
+ sscape_write (devc, GA_DMACFG_REG, 0x50);
+
+ /*
+ * Take the gate-arry off of the DMA channel.
+ */
+ sscape_write (devc, GA_DMAB_REG, 0x20);
+
+ /*
+ * Init the AD1848 (CD-ROM) config reg.
+ */
+
+ for (i = 0; i < sizeof (valid_interrupts); i++)
+ if (hw_config->irq == valid_interrupts[i])
+ {
+ irq_bits = i;
+ break;
+ }
+
+ sscape_write (devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) |
+ (irq_bits << 1));
+
+ if (hw_config->irq == devc->irq)
+ printk ("SoundScape: Warning! The WSS mode can't share IRQ with MIDI\n");
+
+ ad1848_init ("SoundScape", hw_config->io_base,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma);
+
+#ifdef EXCLUDE_NATIVE_PCM
+ if (num_audiodevs == (prev_devs + 1)) /* The AD1848 driver installed itself */
+ audio_devs[prev_devs]->coproc = &sscape_coproc_operations;
+#endif
+#ifdef SSCAPE_DEBUG5
+ /*
+ * Temporary debugging aid. Print contents of the registers
+ * after the AD1848 device has been initialized.
+ */
+ {
+ int i;
+
+ for (i = 0; i < 13; i++)
+ printk ("I%d = %02x\n", i, sscape_read (devc, i));
+ }
+#endif
+
+ return mem_start;
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/sys_timer.c b/sys/pc98/pc98/sound/sys_timer.c
new file mode 100644
index 0000000..6366c22
--- /dev/null
+++ b/sys/pc98/pc98/sound/sys_timer.c
@@ -0,0 +1,304 @@
+/*
+ * sound/sys_timer.c
+ *
+ * The default timer for the Level 2 sequencer interface
+ * Uses the (100HZ) timer of kernel.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#define SEQUENCER_C
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#ifndef EXCLUDE_SEQUENCER
+
+static volatile int opened = 0, tmr_running = 0;
+static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned long ticks_offs;
+static volatile int curr_tempo, curr_timebase;
+static volatile unsigned long curr_ticks;
+static volatile unsigned long next_event_time;
+static unsigned long prev_event_time;
+
+static void poll_def_tmr (unsigned long dummy);
+
+DEFINE_TIMER (def_tmr, poll_def_tmr);
+
+static unsigned long
+tmr2ticks (int tmr_value)
+{
+ /*
+ * Convert system timer ticks (HZ) to MIDI ticks
+ */
+
+ unsigned long tmp;
+ unsigned long scale;
+
+ tmp = (tmr_value * 1000) / HZ; /* Convert to msecs */
+
+ scale = (60 * 1000) / (curr_tempo * curr_timebase); /* msecs per MIDI tick */
+
+ return (tmp + (scale / 2)) / scale;
+}
+
+static void
+poll_def_tmr (unsigned long dummy)
+{
+
+ if (opened)
+ {
+ ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1);
+
+ if (tmr_running)
+ {
+ tmr_ctr++;
+ curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
+
+ if (curr_ticks >= next_event_time)
+ {
+ next_event_time = 0xffffffff;
+ sequencer_timer ();
+ }
+ }
+ }
+}
+
+static void
+tmr_reset (void)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+ tmr_offs = 0;
+ ticks_offs = 0;
+ tmr_ctr = 0;
+ next_event_time = 0xffffffff;
+ prev_event_time = 0;
+ curr_ticks = 0;
+ RESTORE_INTR (flags);
+}
+
+static int
+def_tmr_open (int dev, int mode)
+{
+ if (opened)
+ return RET_ERROR (EBUSY);
+
+ tmr_reset ();
+ curr_tempo = 60;
+ curr_timebase = HZ;
+ opened = 1;
+
+ ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1);
+
+ return 0;
+}
+
+static void
+def_tmr_close (int dev)
+{
+ opened = tmr_running = 0;
+}
+
+static int
+def_tmr_event (int dev, unsigned char *event)
+{
+ unsigned char cmd = event[1];
+ unsigned long parm = *(int *) &event[4];
+
+ switch (cmd)
+ {
+ case TMR_WAIT_REL:
+ parm += prev_event_time;
+ case TMR_WAIT_ABS:
+ if (parm > 0)
+ {
+ long time;
+
+ if (parm <= curr_ticks) /* It's the time */
+ return TIMER_NOT_ARMED;
+
+ time = parm;
+ next_event_time = prev_event_time = time;
+
+ return TIMER_ARMED;
+ }
+ break;
+
+ case TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ break;
+
+ case TMR_STOP:
+ tmr_running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ tmr_running = 1;
+ break;
+
+ case TMR_TEMPO:
+ if (parm)
+ {
+ if (parm < 8)
+ parm = 8;
+ if (parm > 250)
+ parm = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = parm;
+ }
+ break;
+
+ case TMR_ECHO:
+ seq_copy_to_input (event, 8);
+ break;
+
+ default:;
+ }
+
+ return TIMER_NOT_ARMED;
+}
+
+static unsigned long
+def_tmr_get_time (int dev)
+{
+ if (!opened)
+ return 0;
+
+ return curr_ticks;
+}
+
+static int
+def_tmr_ioctl (int dev,
+ unsigned int cmd, unsigned int arg)
+{
+ switch (cmd)
+ {
+ case SNDCTL_TMR_SOURCE:
+ return IOCTL_OUT (arg, TMR_INTERNAL);
+ break;
+
+ case SNDCTL_TMR_START:
+ tmr_reset ();
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_STOP:
+ tmr_running = 0;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ tmr_running = 1;
+ return 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 1)
+ val = 1;
+ if (val > 1000)
+ val = 1000;
+ curr_timebase = val;
+ }
+
+ return IOCTL_OUT (arg, curr_timebase);
+ }
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ {
+ int val = IOCTL_IN (arg);
+
+ if (val)
+ {
+ if (val < 8)
+ val = 8;
+ if (val > 250)
+ val = 250;
+ tmr_offs = tmr_ctr;
+ ticks_offs += tmr2ticks (tmr_ctr);
+ tmr_ctr = 0;
+ curr_tempo = val;
+ }
+
+ return IOCTL_OUT (arg, curr_tempo);
+ }
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ if (IOCTL_IN (arg) != 0) /* Can't change */
+ return RET_ERROR (EINVAL);
+
+ return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ /* NOP */
+ break;
+
+ default:;
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static void
+def_tmr_arm (int dev, long time)
+{
+ if (time < 0)
+ time = curr_ticks + 1;
+ else if (time <= curr_ticks) /* It's the time */
+ return;
+
+ next_event_time = prev_event_time = time;
+
+ return;
+}
+
+struct sound_timer_operations default_sound_timer =
+{
+ {"System Timer", 0},
+ 0, /* Priority */
+ 0, /* Local device link */
+ def_tmr_open,
+ def_tmr_close,
+ def_tmr_event,
+ def_tmr_get_time,
+ def_tmr_ioctl,
+ def_tmr_arm
+};
+
+#endif
+#endif
diff --git a/sys/pc98/pc98/sound/trix.c b/sys/pc98/pc98/sound/trix.c
new file mode 100644
index 0000000..6e3db0f
--- /dev/null
+++ b/sys/pc98/pc98/sound/trix.c
@@ -0,0 +1,323 @@
+/*
+ * sound/trix.c
+ *
+ * Low level driver for the MediaTriX AudioTriX Pro
+ * (MT-0002-PC Control Chip)
+ *
+ * Copyright by Hannu Savolainen 1995
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_TRIX)
+
+#ifdef INCLUDE_TRIX_BOOT
+#include "trix_boot.h"
+#endif
+
+static int kilroy_was_here = 0; /* Don't detect twice */
+static int sb_initialized = 0;
+static int mpu_initialized = 0;
+
+static unsigned char
+trix_read (int addr)
+{
+ OUTB ((unsigned char) addr, 0x390); /* MT-0002-PC ASIC address */
+ return INB (0x391); /* MT-0002-PC ASIC data */
+}
+
+static void
+trix_write (int addr, int data)
+{
+ OUTB ((unsigned char) addr, 0x390); /* MT-0002-PC ASIC address */
+ OUTB ((unsigned char) data, 0x391); /* MT-0002-PC ASIC data */
+}
+
+static void
+download_boot (int base)
+{
+ int i = 0, n = sizeof (trix_boot);
+
+ trix_write (0xf8, 0x00); /* ??????? */
+ OUTB (0x01, base + 6); /* Clear the internal data pointer */
+ OUTB (0x00, base + 6); /* Restart */
+
+ /*
+ * Write the boot code to the RAM upload/download register.
+ * Each write increments the internal data pointer.
+ */
+ OUTB (0x01, base + 6); /* Clear the internal data pointer */
+ OUTB (0x1A, 0x390); /* Select RAM download/upload port */
+
+ for (i = 0; i < n; i++)
+ OUTB (trix_boot[i], 0x391);
+ for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */
+ OUTB (0x00, 0x391);
+ OUTB (0x00, base + 6); /* Reset */
+ OUTB (0x50, 0x390); /* ?????? */
+}
+
+static int
+trix_set_wss_port (struct address_info *hw_config)
+{
+ unsigned char addr_bits;
+
+ if (kilroy_was_here) /* Already initialized */
+ return 0;
+
+ kilroy_was_here = 1;
+
+ if (trix_read (0x15) != 0x71) /* No asic signature */
+ return 0;
+
+ /*
+ * Disable separate wave playback and recording DMA channels since
+ * the driver doesn't support duplex mode yet.
+ */
+
+ trix_write (0x13, trix_read (0x13) & ~0x80);
+ trix_write (0x14, trix_read (0x14) & ~0x80);
+
+ /*
+ * Configure the ASIC to place the codec to the proper I/O location
+ */
+
+ switch (hw_config->io_base)
+ {
+ case 0x530:
+ addr_bits = 0;
+ break;
+ case 0x604:
+ addr_bits = 1;
+ break;
+ case 0xE80:
+ addr_bits = 2;
+ break;
+ case 0xF40:
+ addr_bits = 3;
+ break;
+ default:
+ return 0;
+ }
+
+ trix_write (0x19, (trix_read (0x19) & 0x03) | addr_bits);
+ return 1;
+}
+
+/*
+ * Probe and attach routines for the Windows Sound System mode of
+ * AudioTriX Pro
+ */
+
+int
+probe_trix_wss (struct address_info *hw_config)
+{
+ /*
+ * Check if the IO port returns valid signature. The original MS Sound
+ * system returns 0x04 while some cards (AudioTriX Pro for example)
+ * return 0x00.
+ */
+ if (!trix_set_wss_port (hw_config))
+ return 0;
+
+ if ((INB (hw_config->io_base + 3) & 0x3f) != 0x00)
+ {
+ DDB (printk ("No MSS signature detected on port 0x%x\n", hw_config->io_base));
+ return 0;
+ }
+
+ if (hw_config->irq > 11)
+ {
+ printk ("AudioTriX: Bad WSS IRQ %d\n", hw_config->irq);
+ return 0;
+ }
+
+ if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
+ {
+ printk ("AudioTriX: Bad WSS DMA %d\n", hw_config->dma);
+ return 0;
+ }
+
+ /*
+ * Check that DMA0 is not in use with a 8 bit board.
+ */
+
+ if (hw_config->dma == 0 && INB (hw_config->io_base + 3) & 0x80)
+ {
+ printk ("AudioTriX: Can't use DMA0 with a 8 bit card\n");
+ return 0;
+ }
+
+ if (hw_config->irq > 7 && hw_config->irq != 9 && INB (hw_config->io_base + 3) & 0x80)
+ {
+ printk ("AudioTriX: Can't use IRQ%d with a 8 bit card\n", hw_config->irq);
+ return 0;
+ }
+
+ return ad1848_detect (hw_config->io_base + 4);
+}
+
+long
+attach_trix_wss (long mem_start, struct address_info *hw_config)
+{
+ static unsigned char interrupt_bits[12] =
+ {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20};
+ char bits;
+
+ static unsigned char dma_bits[4] =
+ {1, 2, 0, 3};
+
+ int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
+
+ if (!kilroy_was_here)
+ return mem_start;
+
+ /*
+ * Set the IRQ and DMA addresses.
+ */
+
+ bits = interrupt_bits[hw_config->irq];
+ if (bits == -1)
+ return mem_start;
+
+ OUTB (bits | 0x40, config_port);
+ if ((INB (version_port) & 0x40) == 0)
+ printk ("[IRQ Conflict?]");
+
+ OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */
+
+ ad1848_init ("AudioTriX Pro", hw_config->io_base + 4,
+ hw_config->irq,
+ hw_config->dma,
+ hw_config->dma);
+ return mem_start;
+}
+
+int
+probe_trix_sb (struct address_info *hw_config)
+{
+
+ int tmp;
+ unsigned char conf;
+ static char irq_translate[] =
+ {-1, -1, -1, 0, 1, 2, -1, 3};
+
+#ifndef INCLUDE_TRIX_BOOT
+ return 0; /* No boot code -> no fun */
+#endif
+ if (!kilroy_was_here)
+ return 0; /* AudioTriX Pro has not been detected earlier */
+
+ if (sb_initialized)
+ return 0;
+
+ if (hw_config->io_base & 0xffffff8f != 0x200)
+ return 0;
+
+ tmp = hw_config->irq;
+ if (tmp > 7)
+ return 0;
+ if (irq_translate[tmp] == -1)
+ return 0;
+
+ tmp = hw_config->dma;
+ if (tmp != 1 && tmp != 3)
+ return 0;
+
+ conf = 0x84; /* DMA and IRQ enable */
+ conf |= hw_config->io_base & 0x70; /* I/O address bits */
+ conf |= irq_translate[hw_config->irq];
+ if (hw_config->dma == 3)
+ conf |= 0x08;
+ trix_write (0x1b, conf);
+
+ download_boot (hw_config->io_base);
+ sb_initialized = 1;
+
+ return 1;
+}
+
+long
+attach_trix_sb (long mem_start, struct address_info *hw_config)
+{
+ printk (" <AudioTriX>");
+ return mem_start;
+}
+
+long
+attach_trix_mpu (long mem_start, struct address_info *hw_config)
+{
+ return attach_mpu401 (mem_start, hw_config);
+}
+
+int
+probe_trix_mpu (struct address_info *hw_config)
+{
+ unsigned char conf;
+ static char irq_bits[] =
+ {-1, -1, -1, 1, 2, 3, -1, 4, -1, 5};
+
+ if (!kilroy_was_here)
+ return 0; /* AudioTriX Pro has not been detected earlier */
+
+ if (!sb_initialized)
+ return 0;
+
+ if (mpu_initialized)
+ return 0;
+
+ if (hw_config->irq > 9)
+ return 0;
+
+ if (irq_bits[hw_config->irq] == -1)
+ return 0;
+
+ switch (hw_config->io_base)
+ {
+ case 0x330:
+ conf = 0x00;
+ break;
+ case 0x370:
+ conf = 0x04;
+ break;
+ case 0x3b0:
+ conf = 0x08;
+ break;
+ case 0x3f0:
+ conf = 0x0c;
+ break;
+ default:
+ return 0; /* Invalid port */
+ }
+
+ conf |= irq_bits[hw_config->irq] << 4;
+
+ trix_write (0x19, (trix_read (0x19) & 0x83) | conf);
+
+ mpu_initialized = 1;
+
+ return probe_mpu401 (hw_config);
+}
+
+#endif
diff --git a/sys/pc98/pc98/sound/tuning.h b/sys/pc98/pc98/sound/tuning.h
new file mode 100644
index 0000000..98048cc
--- /dev/null
+++ b/sys/pc98/pc98/sound/tuning.h
@@ -0,0 +1,26 @@
+#ifdef SEQUENCER_C
+
+static unsigned short semitone_tuning[24] =
+{
+/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983,
+/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784,
+/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
+};
+
+static unsigned short cent_tuning[100] =
+{
+/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041,
+/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087,
+/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134,
+/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181,
+/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228,
+/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275,
+/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323,
+/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371,
+/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419,
+/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467,
+/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515,
+/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564,
+/* 96 */ 10570, 10576, 10582, 10589
+};
+#endif
diff --git a/sys/pc98/pc98/sound/uart6850.c b/sys/pc98/pc98/sound/uart6850.c
new file mode 100644
index 0000000..ba5aac6
--- /dev/null
+++ b/sys/pc98/pc98/sound/uart6850.c
@@ -0,0 +1,327 @@
+/*
+ * sound/uart6850.c
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
+ * added 6850 support, used with COVOX SoundMaster II and custom cards.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
+
+#define DATAPORT (uart6850_base) /*
+ * * * Midi6850 Data I/O Port on IBM
+ * */
+#define COMDPORT (uart6850_base+1) /*
+ * * * Midi6850 Command Port on IBM */
+#define STATPORT (uart6850_base+1) /*
+ * * * Midi6850 Status Port on IBM */
+
+#define uart6850_status() INB(STATPORT)
+#define input_avail() ((uart6850_status()&INPUT_AVAIL))
+#define output_ready() ((uart6850_status()&OUTPUT_READY))
+#define uart6850_cmd(cmd) OUTB(cmd, COMDPORT)
+#define uart6850_read() INB(DATAPORT)
+#define uart6850_write(byte) OUTB(byte, DATAPORT)
+
+#define OUTPUT_READY 0x02 /*
+ * * * Mask for Data Read Ready Bit */
+#define INPUT_AVAIL 0x01 /*
+ * * * Mask for Data Send Ready Bit */
+
+#define UART_RESET 0x95 /*
+ * * * 6850 Total Reset Command */
+#define UART_MODE_ON 0x03 /*
+ * * * 6850 Send/Receive UART Mode */
+
+static int uart6850_opened = 0;
+static int uart6850_base = 0x330;
+static int uart6850_irq;
+static int uart6850_detected = 0;
+static int my_dev;
+
+static int reset_uart6850 (void);
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static void
+uart6850_input_loop (void)
+{
+ int count;
+
+ count = 10;
+
+ while (count) /*
+ * Not timed out
+ */
+ if (input_avail ())
+ {
+ unsigned char c = uart6850_read ();
+
+ count = 100;
+
+ if (uart6850_opened & OPEN_READ)
+ midi_input_intr (my_dev, c);
+ }
+ else
+ while (!input_avail () && count)
+ count--;
+}
+
+void
+m6850intr (int unit)
+{
+ if (input_avail ())
+ uart6850_input_loop ();
+}
+
+/*
+ * It looks like there is no input interrupts in the UART mode. Let's try
+ * polling.
+ */
+
+static void
+poll_uart6850 (unsigned long dummy)
+{
+ unsigned long flags;
+
+ DEFINE_TIMER (uart6850_timer, poll_uart6850);
+
+ if (!(uart6850_opened & OPEN_READ))
+ return; /*
+ * No longer required
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ uart6850_input_loop ();
+
+ ACTIVATE_TIMER (uart6850_timer, poll_uart6850, 1); /*
+ * Come back later
+ */
+
+ RESTORE_INTR (flags);
+}
+
+static int
+uart6850_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ if (uart6850_opened)
+ {
+ printk ("Midi6850: Midi busy\n");
+ return RET_ERROR (EBUSY);
+ }
+
+ uart6850_cmd (UART_RESET);
+
+ uart6850_input_loop ();
+
+ midi_input_intr = input;
+ uart6850_opened = mode;
+ poll_uart6850 (0); /*
+ * Enable input polling
+ */
+
+ return 0;
+}
+
+static void
+uart6850_close (int dev)
+{
+ uart6850_cmd (UART_MODE_ON);
+
+ uart6850_opened = 0;
+}
+
+static int
+uart6850_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ uart6850_input_loop ();
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+
+ if (!output_ready ())
+ {
+ printk ("Midi6850: Timeout\n");
+ return 0;
+ }
+
+ uart6850_write (midi_byte);
+ return 1;
+}
+
+static int
+uart6850_command (int dev, unsigned char *midi_byte)
+{
+ return 1;
+}
+
+static int
+uart6850_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+uart6850_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+uart6850_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+uart6850_kick (int dev)
+{
+}
+
+static int
+uart6850_buffer_status (int dev)
+{
+ return 0; /*
+ * No data in buffers
+ */
+}
+
+#define MIDI_SYNTH_NAME "6850 UART Midi"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+#include "midi_synth.h"
+
+static struct midi_operations uart6850_operations =
+{
+ {"6850 UART", 0, 0, SNDCARD_UART6850},
+ &std_midi_synth,
+ {0},
+ uart6850_open,
+ uart6850_close,
+ uart6850_ioctl,
+ uart6850_out,
+ uart6850_start_read,
+ uart6850_end_read,
+ uart6850_kick,
+ uart6850_command,
+ uart6850_buffer_status
+};
+
+
+long
+attach_uart6850 (long mem_start, struct address_info *hw_config)
+{
+ int ok, timeout;
+ unsigned long flags;
+
+ if (num_midis >= MAX_MIDI_DEV)
+ {
+ printk ("Sound: Too many midi devices detected\n");
+ return mem_start;
+ }
+
+ uart6850_base = hw_config->io_base;
+ uart6850_irq = hw_config->irq;
+
+ if (!uart6850_detected)
+ return RET_ERROR (EIO);
+
+ DISABLE_INTR (flags);
+
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
+ * Wait
+ */
+ uart6850_cmd (UART_MODE_ON);
+
+ ok = 1;
+
+ RESTORE_INTR (flags);
+
+#if defined(__FreeBSD__)
+ printk ("uart0: <6850 Midi Interface>");
+#else
+ printk (" <6850 Midi Interface>");
+#endif
+
+ std_midi_synth.midi_dev = my_dev = num_midis;
+ midi_devs[num_midis++] = &uart6850_operations;
+ return mem_start;
+}
+
+static int
+reset_uart6850 (void)
+{
+ uart6850_read ();
+ return 1; /*
+ * OK
+ */
+}
+
+
+int
+probe_uart6850 (struct address_info *hw_config)
+{
+ int ok = 0;
+
+ uart6850_base = hw_config->io_base;
+ uart6850_irq = hw_config->irq;
+
+ if (snd_set_irq_handler (uart6850_irq, m6850intr, "MIDI6850") < 0)
+ return 0;
+
+ ok = reset_uart6850 ();
+
+ uart6850_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif
diff --git a/sys/pc98/pc98/sound/ulaw.h b/sys/pc98/pc98/sound/ulaw.h
new file mode 100644
index 0000000..be9f92d
--- /dev/null
+++ b/sys/pc98/pc98/sound/ulaw.h
@@ -0,0 +1,69 @@
+static unsigned char ulaw_dsp[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 5, 9, 13, 17, 21, 25, 29, 33,
+ 37, 41, 45, 49, 53, 57, 61, 65,
+ 68, 70, 72, 74, 76, 78, 80, 82,
+ 84, 86, 88, 90, 92, 94, 96, 98,
+ 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115,
+ 115, 116, 116, 117, 117, 118, 118, 119,
+ 119, 120, 120, 121, 121, 122, 122, 123,
+ 123, 123, 124, 124, 124, 124, 125, 125,
+ 125, 125, 126, 126, 126, 126, 127, 127,
+ 127, 127, 127, 127, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 252, 248, 244, 240, 236, 232, 228, 224,
+ 220, 216, 212, 208, 204, 200, 196, 192,
+ 189, 187, 185, 183, 181, 179, 177, 175,
+ 173, 171, 169, 167, 165, 163, 161, 159,
+ 157, 156, 155, 154, 153, 152, 151, 150,
+ 149, 148, 147, 146, 145, 144, 143, 142,
+ 142, 141, 141, 140, 140, 139, 139, 138,
+ 138, 137, 137, 136, 136, 135, 135, 134,
+ 134, 134, 133, 133, 133, 133, 132, 132,
+ 132, 132, 131, 131, 131, 131, 130, 130,
+ 130, 130, 130, 130, 129, 129, 129, 129,
+ 129, 129, 129, 129, 128, 128, 128, 128,
+};
+
+static unsigned char dsp_ulaw[] = {
+ 31, 31, 31, 32, 32, 32, 32, 33,
+ 33, 33, 33, 34, 34, 34, 34, 35,
+ 35, 35, 35, 36, 36, 36, 36, 37,
+ 37, 37, 37, 38, 38, 38, 38, 39,
+ 39, 39, 39, 40, 40, 40, 40, 41,
+ 41, 41, 41, 42, 42, 42, 42, 43,
+ 43, 43, 43, 44, 44, 44, 44, 45,
+ 45, 45, 45, 46, 46, 46, 46, 47,
+ 47, 47, 47, 48, 48, 49, 49, 50,
+ 50, 51, 51, 52, 52, 53, 53, 54,
+ 54, 55, 55, 56, 56, 57, 57, 58,
+ 58, 59, 59, 60, 60, 61, 61, 62,
+ 62, 63, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 81, 83, 85, 87, 89,
+ 91, 93, 95, 99, 103, 107, 111, 119,
+ 255, 247, 239, 235, 231, 227, 223, 221,
+ 219, 217, 215, 213, 211, 209, 207, 206,
+ 205, 204, 203, 202, 201, 200, 199, 198,
+ 197, 196, 195, 194, 193, 192, 191, 191,
+ 190, 190, 189, 189, 188, 188, 187, 187,
+ 186, 186, 185, 185, 184, 184, 183, 183,
+ 182, 182, 181, 181, 180, 180, 179, 179,
+ 178, 178, 177, 177, 176, 176, 175, 175,
+ 175, 175, 174, 174, 174, 174, 173, 173,
+ 173, 173, 172, 172, 172, 172, 171, 171,
+ 171, 171, 170, 170, 170, 170, 169, 169,
+ 169, 169, 168, 168, 168, 168, 167, 167,
+ 167, 167, 166, 166, 166, 166, 165, 165,
+ 165, 165, 164, 164, 164, 164, 163, 163,
+ 163, 163, 162, 162, 162, 162, 161, 161,
+ 161, 161, 160, 160, 160, 160, 159, 159,
+};
diff --git a/sys/pc98/pc98/spkr.c b/sys/pc98/pc98/spkr.c
new file mode 100644
index 0000000..ac1d3c3
--- /dev/null
+++ b/sys/pc98/pc98/spkr.c
@@ -0,0 +1,662 @@
+/*
+ * spkr.c -- device driver for console speaker
+ *
+ * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
+ * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
+ *
+ * $Id: spkr.c,v 1.24 1996/03/27 19:07:33 bde Exp $
+ */
+
+/*
+ * modified for PC98
+ * $Id: spkr.c,v 1.2 1994/03/14 09:53:38 kakefuda Exp $
+ */
+
+#include "speaker.h"
+
+#if NSPEAKER > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/conf.h>
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/timerreg.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/timerreg.h>
+#endif
+#include <machine/clock.h>
+#include <machine/speaker.h>
+
+
+
+#ifdef DEVFS
+#include <sys/devfsext.h>
+void *devfs_token;
+#endif
+
+static d_open_t spkropen;
+static d_close_t spkrclose;
+static d_write_t spkrwrite;
+static d_ioctl_t spkrioctl;
+
+#define CDEV_MAJOR 26
+static struct cdevsw spkr_cdevsw =
+ { spkropen, spkrclose, noread, spkrwrite, /*26*/
+ spkrioctl, nostop, nullreset, nodevtotty,/* spkr */
+ seltrue, nommap, NULL, "spkr", NULL, -1 };
+
+/**************** MACHINE DEPENDENT PART STARTS HERE *************************
+ *
+ * This section defines a function tone() which causes a tone of given
+ * frequency and duration from the 80x86's console speaker.
+ * Another function endtone() is defined to force sound off, and there is
+ * also a rest() entry point to do pauses.
+ *
+ * Audible sound is generated using the Programmable Interval Timer (PIT) and
+ * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
+ * PPI controls whether sound is passed through at all; the PIT's channel 2 is
+ * used to generate clicks (a square wave) of whatever frequency is desired.
+ */
+
+/*
+ * PIT and PPI port addresses and control values
+ *
+ * Most of the magic is hidden in the TIMER_PREP value, which selects PIT
+ * channel 2, frequency LSB first, square-wave mode and binary encoding.
+ * The encoding is as follows:
+ *
+ * +----------+----------+---------------+-----+
+ * | 1 0 | 1 1 | 0 1 1 | 0 |
+ * | SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD |
+ * +----------+----------+---------------+-----+
+ * Counter Write Mode 3 Binary
+ * Channel 2 LSB first, (Square Wave) Encoding
+ * MSB second
+ */
+#ifdef PC98
+#define PPI_SPKR 0x08 /* turn these PPI bits on to pass sound */
+#define PIT_COUNT 0x3fdb /* PIT count address */
+#define PIT_MODE 0x76 /* set timer mode for sound generation */
+#else
+#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */
+#define PIT_MODE 0xB6 /* set timer mode for sound generation */
+#endif
+
+/*
+ * Magic numbers for timer control.
+ */
+#ifdef PC98
+#ifndef AUTO_CLOCK
+#ifndef PC98_8M
+#define TIMER_CLK 2457600L /* ???? for 5MHz system */
+#else
+#define TIMER_CLK 1996800L /* ???? for 8MHz system */
+#endif
+#endif /* !AUTO_CLOCK */
+#else /* IBM_PC */
+#define TIMER_CLK 1193180L /* corresponds to 18.2 MHz tick rate */
+#endif
+
+#define SPKRPRI PSOCK
+static char endtone, endrest;
+
+static void tone __P((unsigned int thz, unsigned int ticks));
+static void rest __P((int ticks));
+static void playinit __P((void));
+static void playtone __P((int pitch, int value, int sustain));
+static int abs __P((int n));
+static void playstring __P((char *cp, size_t slen));
+
+static void tone(thz, ticks)
+/* emit tone of frequency thz for given number of ticks */
+unsigned int thz, ticks;
+{
+ unsigned int divisor;
+ int sps;
+
+ if (thz <= 0)
+ return;
+
+#if defined(PC98) && defined(AUTO_CLOCK)
+ if (pc98_machine_type & M_8M)
+ divisor = 1996800L / thz;
+ else
+ divisor = 2457600L / thz;
+#else
+ divisor = TIMER_CLK / thz;
+#endif /* PC98 */
+
+#ifdef DEBUG
+ (void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
+#endif /* DEBUG */
+
+ /* set timer to generate clicks at given frequency in Hertz */
+ sps = spltty();
+
+#ifdef PC98
+ if (acquire_timer1(PIT_MODE)) {
+#else
+ if (acquire_timer2(PIT_MODE)) {
+#endif
+ /* enter list of waiting procs ??? */
+ return;
+ }
+#ifdef PC98
+ outb(PIT_COUNT, (divisor & 0xff)); /* send lo byte */
+ outb(PIT_COUNT, (divisor >> 8)); /* send hi byte */
+#else
+ outb(TIMER_CNTR2, (divisor & 0xff)); /* send lo byte */
+ outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */
+#endif
+ splx(sps);
+
+ /* turn the speaker on */
+#ifdef PC98
+ outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
+#else
+ outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
+#endif
+
+ /*
+ * Set timeout to endtone function, then give up the timeslice.
+ * This is so other processes can execute while the tone is being
+ * emitted.
+ */
+ if (ticks > 0)
+ tsleep((caddr_t)&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
+#ifdef PC98
+ outb(IO_PPI, inb(IO_PPI) | PPI_SPKR);
+ release_timer1();
+#else
+ outb(IO_PPI, inb(IO_PPI) & ~PPI_SPKR);
+ release_timer2();
+#endif
+}
+
+static void rest(ticks)
+/* rest for given number of ticks */
+int ticks;
+{
+ /*
+ * Set timeout to endrest function, then give up the timeslice.
+ * This is so other processes can execute while the rest is being
+ * waited out.
+ */
+#ifdef DEBUG
+ (void) printf("rest: %d\n", ticks);
+#endif /* DEBUG */
+ if (ticks > 0)
+ tsleep((caddr_t)&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
+}
+
+/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
+ *
+ * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
+ * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
+ * tracking facility are added.
+ * Requires tone(), rest(), and endtone(). String play is not interruptible
+ * except possibly at physical block boundaries.
+ */
+
+typedef int bool;
+#define TRUE 1
+#define FALSE 0
+
+#define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
+#define isdigit(c) (((c) >= '0') && ((c) <= '9'))
+#define dtoi(c) ((c) - '0')
+
+static int octave; /* currently selected octave */
+static int whole; /* whole-note time at current tempo, in ticks */
+static int value; /* whole divisor for note time, quarter note = 1 */
+static int fill; /* controls spacing of notes */
+static bool octtrack; /* octave-tracking on? */
+static bool octprefix; /* override current octave-tracking state? */
+
+/*
+ * Magic number avoidance...
+ */
+#define SECS_PER_MIN 60 /* seconds per minute */
+#define WHOLE_NOTE 4 /* quarter notes per whole note */
+#define MIN_VALUE 64 /* the most we can divide a note by */
+#define DFLT_VALUE 4 /* default value (quarter-note) */
+#define FILLTIME 8 /* for articulation, break note in parts */
+#define STACCATO 6 /* 6/8 = 3/4 of note is filled */
+#define NORMAL 7 /* 7/8ths of note interval is filled */
+#define LEGATO 8 /* all of note interval is filled */
+#define DFLT_OCTAVE 4 /* default octave */
+#define MIN_TEMPO 32 /* minimum tempo */
+#define DFLT_TEMPO 120 /* default tempo */
+#define MAX_TEMPO 255 /* max tempo */
+#define NUM_MULT 3 /* numerator of dot multiplier */
+#define DENOM_MULT 2 /* denominator of dot multiplier */
+
+/* letter to half-tone: A B C D E F G */
+static int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
+
+/*
+ * This is the American Standard A440 Equal-Tempered scale with frequencies
+ * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
+ * our octave 0 is standard octave 2.
+ */
+#define OCTAVE_NOTES 12 /* semitones per octave */
+static int pitchtab[] =
+{
+/* C C# D D# E F F# G G# A A# B*/
+/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
+/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
+/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
+/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
+/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
+/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
+/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
+};
+
+static void playinit()
+{
+ octave = DFLT_OCTAVE;
+ whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
+ fill = NORMAL;
+ value = DFLT_VALUE;
+ octtrack = FALSE;
+ octprefix = TRUE; /* act as though there was an initial O(n) */
+}
+
+static void playtone(pitch, value, sustain)
+/* play tone of proper duration for current rhythm signature */
+int pitch, value, sustain;
+{
+ register int sound, silence, snum = 1, sdenom = 1;
+
+ /* this weirdness avoids floating-point arithmetic */
+ for (; sustain; sustain--)
+ {
+ /* See the BUGS section in the man page for discussion */
+ snum *= NUM_MULT;
+ sdenom *= DENOM_MULT;
+ }
+
+ if (value == 0 || sdenom == 0)
+ return;
+
+ if (pitch == -1)
+ rest(whole * snum / (value * sdenom));
+ else
+ {
+ sound = (whole * snum) / (value * sdenom)
+ - (whole * (FILLTIME - fill)) / (value * FILLTIME);
+ silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
+
+#ifdef DEBUG
+ (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
+ pitch, sound, silence);
+#endif /* DEBUG */
+
+ tone(pitchtab[pitch], sound);
+ if (fill != LEGATO)
+ rest(silence);
+ }
+}
+
+static int abs(n)
+int n;
+{
+ if (n < 0)
+ return(-n);
+ else
+ return(n);
+}
+
+static void playstring(cp, slen)
+/* interpret and play an item from a notation string */
+char *cp;
+size_t slen;
+{
+ int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
+
+#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \
+ {v = v * 10 + (*++cp - '0'); slen--;}
+ for (; slen--; cp++)
+ {
+ int sustain, timeval, tempo;
+ register char c = toupper(*cp);
+
+#ifdef DEBUG
+ (void) printf("playstring: %c (%x)\n", c, c);
+#endif /* DEBUG */
+
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+
+ /* compute pitch */
+ pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
+
+ /* this may be followed by an accidental sign */
+ if (cp[1] == '#' || cp[1] == '+')
+ {
+ ++pitch;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == '-')
+ {
+ --pitch;
+ ++cp;
+ slen--;
+ }
+
+ /*
+ * If octave-tracking mode is on, and there has been no octave-
+ * setting prefix, find the version of the current letter note
+ * closest to the last regardless of octave.
+ */
+ if (octtrack && !octprefix)
+ {
+ if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
+ {
+ ++octave;
+ pitch += OCTAVE_NOTES;
+ }
+
+ if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
+ {
+ --octave;
+ pitch -= OCTAVE_NOTES;
+ }
+ }
+ octprefix = FALSE;
+ lastpitch = pitch;
+
+ /* ...which may in turn be followed by an override time value */
+ GETNUM(cp, timeval);
+ if (timeval <= 0 || timeval > MIN_VALUE)
+ timeval = value;
+
+ /* ...and/or sustain dots */
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+
+ /* ...and/or a slur mark */
+ oldfill = fill;
+ if (cp[1] == '_')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+
+ /* time to emit the actual tone */
+ playtone(pitch, timeval, sustain);
+
+ fill = oldfill;
+ break;
+
+ case 'O':
+ if (cp[1] == 'N' || cp[1] == 'n')
+ {
+ octprefix = octtrack = FALSE;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'L' || cp[1] == 'l')
+ {
+ octtrack = TRUE;
+ ++cp;
+ slen--;
+ }
+ else
+ {
+ GETNUM(cp, octave);
+ if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
+ octave = DFLT_OCTAVE;
+ octprefix = TRUE;
+ }
+ break;
+
+ case '>':
+ if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
+ octave++;
+ octprefix = TRUE;
+ break;
+
+ case '<':
+ if (octave > 0)
+ octave--;
+ octprefix = TRUE;
+ break;
+
+ case 'N':
+ GETNUM(cp, pitch);
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+ oldfill = fill;
+ if (cp[1] == '_')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+ playtone(pitch - 1, value, sustain);
+ fill = oldfill;
+ break;
+
+ case 'L':
+ GETNUM(cp, value);
+ if (value <= 0 || value > MIN_VALUE)
+ value = DFLT_VALUE;
+ break;
+
+ case 'P':
+ case '~':
+ /* this may be followed by an override time value */
+ GETNUM(cp, timeval);
+ if (timeval <= 0 || timeval > MIN_VALUE)
+ timeval = value;
+ for (sustain = 0; cp[1] == '.'; cp++)
+ {
+ slen--;
+ sustain++;
+ }
+ playtone(-1, timeval, sustain);
+ break;
+
+ case 'T':
+ GETNUM(cp, tempo);
+ if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
+ tempo = DFLT_TEMPO;
+ whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
+ break;
+
+ case 'M':
+ if (cp[1] == 'N' || cp[1] == 'n')
+ {
+ fill = NORMAL;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'L' || cp[1] == 'l')
+ {
+ fill = LEGATO;
+ ++cp;
+ slen--;
+ }
+ else if (cp[1] == 'S' || cp[1] == 's')
+ {
+ fill = STACCATO;
+ ++cp;
+ slen--;
+ }
+ break;
+ }
+ }
+}
+
+/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
+ *
+ * This section implements driver hooks to run playstring() and the tone(),
+ * endtone(), and rest() functions defined above.
+ */
+
+static int spkr_active = FALSE; /* exclusion flag */
+static struct buf *spkr_inbuf; /* incoming buf */
+
+int spkropen(dev, flags, fmt, p)
+dev_t dev;
+int flags;
+int fmt;
+struct proc *p;
+{
+#ifdef DEBUG
+ (void) printf("spkropen: entering with dev = %x\n", dev);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (spkr_active)
+ return(EBUSY);
+ else
+ {
+#ifdef DEBUG
+ (void) printf("spkropen: about to perform play initialization\n");
+#endif /* DEBUG */
+ playinit();
+ spkr_inbuf = geteblk(DEV_BSIZE);
+ spkr_active = TRUE;
+ return(0);
+ }
+}
+
+int spkrwrite(dev, uio, ioflag)
+dev_t dev;
+struct uio *uio;
+int ioflag;
+{
+#ifdef DEBUG
+ printf("spkrwrite: entering with dev = %x, count = %d\n",
+ dev, uio->uio_resid);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (uio->uio_resid > DEV_BSIZE) /* prevent system crashes */
+ return(E2BIG);
+ else
+ {
+ unsigned n;
+ char *cp;
+ int error;
+
+ n = uio->uio_resid;
+ cp = spkr_inbuf->b_un.b_addr;
+ if (!(error = uiomove(cp, n, uio)))
+ playstring(cp, n);
+ return(error);
+ }
+}
+
+int spkrclose(dev, flags, fmt, p)
+dev_t dev;
+int flags;
+int fmt;
+struct proc *p;
+{
+#ifdef DEBUG
+ (void) printf("spkrclose: entering with dev = %x\n", dev);
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else
+ {
+ wakeup((caddr_t)&endtone);
+ wakeup((caddr_t)&endrest);
+ brelse(spkr_inbuf);
+ spkr_active = FALSE;
+ return(0);
+ }
+}
+
+int spkrioctl(dev, cmd, cmdarg, flags, p)
+dev_t dev;
+int cmd;
+caddr_t cmdarg;
+int flags;
+struct proc *p;
+{
+#ifdef DEBUG
+ (void) printf("spkrioctl: entering with dev = %x, cmd = %x\n");
+#endif /* DEBUG */
+
+ if (minor(dev) != 0)
+ return(ENXIO);
+ else if (cmd == SPKRTONE)
+ {
+ tone_t *tp = (tone_t *)cmdarg;
+
+ if (tp->frequency == 0)
+ rest(tp->duration);
+ else
+ tone(tp->frequency, tp->duration);
+ return 0;
+ }
+ else if (cmd == SPKRTUNE)
+ {
+ tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg);
+ tone_t ttp;
+ int error;
+
+ for (; ; tp++) {
+ error = copyin(tp, &ttp, sizeof(tone_t));
+ if (error)
+ return(error);
+ if (ttp.duration == 0)
+ break;
+ if (ttp.frequency == 0)
+ rest(ttp.duration);
+ else
+ tone(ttp.frequency, ttp.duration);
+ }
+ return(0);
+ }
+ return(EINVAL);
+}
+
+
+static spkr_devsw_installed = 0;
+
+static void spkr_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! spkr_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev,&spkr_cdevsw, NULL);
+ spkr_devsw_installed = 1;
+#ifdef DEVFS
+ devfs_token = devfs_add_devswf(&spkr_cdevsw, 0, DV_CHR,
+ UID_ROOT, GID_WHEEL, 0600,
+ "speaker");
+#endif
+ }
+}
+
+SYSINIT(spkrdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,spkr_drvinit,NULL)
+
+
+#endif /* NSPEAKER > 0 */
+/* spkr.c ends here */
diff --git a/sys/pc98/pc98/syscons.c b/sys/pc98/pc98/syscons.c
new file mode 100644
index 0000000..706a8e1
--- /dev/null
+++ b/sys/pc98/pc98/syscons.c
@@ -0,0 +1,4394 @@
+/*-
+ * Copyright (c) 1992-1995 Sen Schmidt
+ * 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
+ * in this position and unchanged.
+ * 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 withough 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.
+ *
+ * $Id: syscons.c,v 1.150 1996/05/27 06:02:52 peter Exp $
+ */
+
+#include "sc.h"
+#include "apm.h"
+#include "opt_ddb.h"
+
+#if NSC > 0
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/signalvar.h>
+#include <sys/tty.h>
+#include <sys/uio.h>
+#include <sys/callout.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/devconf.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif
+
+#include <machine/clock.h>
+#include <machine/cons.h>
+#include <machine/console.h>
+#include <machine/psl.h>
+#include <machine/frame.h>
+#include <machine/pc/display.h>
+#include <machine/apm_bios.h>
+#include <machine/random.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#ifdef PC98
+#define KANJI
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/timerreg.h>
+#include <pc98/pc98/kbdtables.h>
+#include <pc98/pc98/syscons.h>
+#else
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/timerreg.h>
+#include <i386/isa/kbdtables.h>
+#include <i386/isa/syscons.h>
+#endif
+
+#if defined(PC98) && defined(LINE30)
+#include "30line.h"
+#endif
+
+#if !defined(MAXCONS)
+#define MAXCONS 16
+#endif
+
+
+/* this may break on older VGA's but is usefull on real 32 bit systems */
+#define bcopyw bcopy
+
+static default_attr user_default = {
+ (FG_LIGHTGREY | BG_BLACK) << 8,
+ (FG_BLACK | BG_LIGHTGREY) << 8
+};
+
+#ifdef PC98
+static default_attr kernel_default = {
+ (FG_LIGHTGREY | BG_BLACK) << 8,
+ (FG_BLACK | BG_LIGHTGREY) << 8
+};
+#else
+static default_attr kernel_default = {
+ (FG_WHITE | BG_BLACK) << 8,
+ (FG_BLACK | BG_LIGHTGREY) << 8
+};
+#endif
+
+static scr_stat main_console;
+static scr_stat *console[MAXCONS];
+static void *sc_devfs_token[MAXCONS];
+ scr_stat *cur_console;
+static scr_stat *new_scp, *old_scp;
+static term_stat kernel_console;
+static default_attr *current_default;
+static char init_done = FALSE;
+static int configuration = 0;
+static char switch_in_progress = FALSE;
+static char blink_in_progress = FALSE;
+static char write_in_progress = FALSE;
+#ifndef PC98
+ u_int crtc_addr = MONO_BASE;
+#endif
+static char crtc_vga = FALSE;
+static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0;
+#ifdef PC98
+static u_char nlkcnt = 0, slkcnt = 0, alkcnt = 0;
+#else
+static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0;
+#endif
+static char *font_8 = NULL, *font_14 = NULL, *font_16 = NULL;
+static int fonts_loaded = 0;
+ char palette[3*256];
+static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab);
+static int delayed_next_scr = FALSE;
+static long scrn_blank_time = 0; /* screen saver timeout value */
+ int scrn_blanked = FALSE; /* screen saver active flag */
+static long scrn_time_stamp;
+ u_char scr_map[256];
+#ifndef PC98
+static char *video_mode_ptr = NULL;
+#endif
+#if ASYNCH
+static u_char kbd_reply = 0;
+#endif
+
+static u_short mouse_and_mask[16] = {
+ 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80,
+ 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000
+};
+static u_short mouse_or_mask[16] = {
+ 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800,
+ 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static void none_saver(int blank) { }
+
+void (*current_saver) __P((int blank)) = none_saver;
+
+#ifdef PC98
+static int scattach(struct pc98_device *dev);
+#else
+static int scattach(struct isa_device *dev);
+#endif
+static int scparam(struct tty *tp, struct termios *t);
+#ifdef PC98
+static int scprobe(struct pc98_device *dev);
+#else
+static int scprobe(struct isa_device *dev);
+#endif
+static void scstart(struct tty *tp);
+
+/* OS specific stuff */
+#ifdef not_yet_done
+#define VIRTUAL_TTY(x) (sccons[x] = ttymalloc(sccons[x]))
+struct CONSOLE_TTY (sccons[MAXCONS] = ttymalloc(sccons[MAXCONS]))
+struct tty *sccons[MAXCONS+1];
+#else
+#define VIRTUAL_TTY(x) &sccons[x]
+#define CONSOLE_TTY &sccons[MAXCONS]
+static struct tty sccons[MAXCONS+1];
+#endif
+
+#ifdef PC98
+static u_char default_kanji = UJIS;
+u_short *Crtat;
+u_short *Atrat;
+#else
+#define MONO_BUF pa_to_va(0xB0000)
+#define CGA_BUF pa_to_va(0xB8000)
+u_short *Crtat;
+#endif
+
+#define WRAPHIST(scp, pointer, offset)\
+ ((scp->history) + ((((pointer) - (scp->history)) + (scp->history_size)\
+ + (offset)) % (scp->history_size)))
+#ifdef PC98
+#define WRAPHIST_A(scp, pointer, offset)\
+ ((scp->his_atr) + ((((pointer) - (scp->his_atr)) + (scp->history_size)\
+ + (offset)) % (scp->history_size)))
+#endif
+
+#ifdef PC98
+struct pc98_driver scdriver = {
+#else
+struct isa_driver scdriver = {
+#endif
+ scprobe, scattach, "sc", 1
+};
+
+static d_open_t scopen;
+static d_close_t scclose;
+static d_read_t scread;
+static d_write_t scwrite;
+static d_ioctl_t scioctl;
+static d_devtotty_t scdevtotty;
+static d_mmap_t scmmap;
+
+#define CDEV_MAJOR 12
+static struct cdevsw scdevsw = {
+ scopen, scclose, scread, scwrite,
+ scioctl, nullstop, noreset, scdevtotty,
+ ttselect, scmmap, nostrategy, "sc", NULL, -1 };
+
+#ifdef PC98
+static u_char ibmpc_to_pc98[16] =
+ { 0x01,0x21,0x81,0xa1,0x41,0x61,0xc1,0xe1, 0x09,0x29,0x89,0xa9,0x49,0x69,0xc9,0xe9 };
+static u_char ibmpc_to_pc98rev[16] =
+ { 0x05,0x25,0x85,0xa5,0x45,0x65,0xc5,0xe5, 0x0d,0x2d,0x8d,0xad,0x4d,0x6d,0xcd,0xed };
+
+unsigned int at2pc98(unsigned int attr)
+{
+ unsigned char fg_at, bg_at;
+ unsigned int at;
+
+ fg_at = ((attr >> 8) & 0x0F);
+ bg_at = ((attr >> 8) & 0xF0);
+
+ if (bg_at) {
+ if (bg_at & 0x80) {
+ if (bg_at & 0x70) {
+ /* reverse & blink */
+ at = ibmpc_to_pc98rev[bg_at >> 4] | 0x02;
+ } else {
+ /* normal & blink */
+ at = ibmpc_to_pc98[fg_at] | 0x02;
+ }
+ } else {
+ /* reverse */
+ at = ibmpc_to_pc98rev[bg_at >> 4];
+ }
+ } else {
+ /* normal */
+ at = ibmpc_to_pc98[fg_at];
+ }
+ at |= ((fg_at|bg_at) << 8);
+ return (at);
+}
+#endif
+
+/*
+ * Calculate hardware attributes word using logical attributes mask and
+ * hardware colors
+ */
+
+static int
+mask2attr(struct term_stat *term)
+{
+ int attr, mask = term->attr_mask;
+
+ if (mask & REVERSE_ATTR) {
+ attr = ((mask & FOREGROUND_CHANGED) ?
+ ((term->cur_color & 0xF000) >> 4) :
+ (term->rev_color & 0x0F00)) |
+ ((mask & BACKGROUND_CHANGED) ?
+ ((term->cur_color & 0x0F00) << 4) :
+ (term->rev_color & 0xF000));
+ } else
+ attr = term->cur_color;
+
+ /* XXX: underline mapping for Hercules adapter can be better */
+ if (mask & (BOLD_ATTR | UNDERLINE_ATTR))
+ attr ^= 0x0800;
+ if (mask & BLINK_ATTR)
+ attr ^= 0x8000;
+
+ return attr;
+}
+
+/*
+ * These functions need to be before calls to them so they can be inlined.
+ */
+static inline void
+draw_cursor(scr_stat *scp, int show)
+{
+ if (show && !(scp->status & CURSOR_SHOWN)) {
+#ifdef PC98
+ int pos = scp->cursor_pos - scp->scr_buf;
+ while((inb(TEXT_GDC + 0) & 0x04) == 0) {}
+ outb(TEXT_GDC + 2, 0x49); /* CSRW */
+ outb(TEXT_GDC + 0, pos & 0xff); /* EADl */
+ outb(TEXT_GDC + 0, pos >> 8); /* EADh */
+#else
+ u_short cursor_image = *(Crtat + (scp->cursor_pos - scp->scr_buf));
+
+ scp->cursor_saveunder = cursor_image;
+ if (configuration & CHAR_CURSOR) {
+ set_destructive_cursor(scp, FALSE);
+ cursor_image = (cursor_image & 0xff00) | DEAD_CHAR;
+ }
+ else {
+ if ((cursor_image & 0x7000) == 0x7000) {
+ cursor_image &= 0x8fff;
+ if(!(cursor_image & 0x0700))
+ cursor_image |= 0x0700;
+ } else {
+ cursor_image |= 0x7000;
+ if ((cursor_image & 0x0700) == 0x0700)
+ cursor_image &= 0xf0ff;
+ }
+ }
+ *(Crtat + (scp->cursor_pos - scp->scr_buf)) = cursor_image;
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ scp->status |= CURSOR_SHOWN;
+ }
+ if (!show && (scp->status & CURSOR_SHOWN)) {
+#ifdef PC98
+ /* not supported yet */
+#else
+ *(Crtat + (scp->cursor_pos - scp->scr_buf)) = scp->cursor_saveunder;
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ scp->status &= ~CURSOR_SHOWN;
+ }
+}
+
+static inline void
+move_crsr(scr_stat *scp, int x, int y)
+{
+ if (x < 0 || y < 0 || x >= scp->xsize || y >= scp->ysize)
+ return;
+ scp->xpos = x;
+ scp->ypos = y;
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ scp->cursor_pos = scp->scr_buf + scp->ypos * scp->xsize + scp->xpos;
+#ifdef PC98
+ scp->cursor_atr = scp->atr_buf + scp->ypos * scp->xsize + scp->xpos;
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+}
+
+static int
+#ifdef PC98
+scprobe(struct pc98_device *dev)
+#else
+scprobe(struct isa_device *dev)
+#endif
+{
+ int i, j, retries = 5;
+ unsigned char val;
+
+#ifdef PC98
+ return(16);
+#else
+ /* Enable interrupts and keyboard controller */
+ kbd_wait();
+ outb(KB_STAT, KB_WRITE);
+ kbd_wait();
+ outb(KB_DATA, KB_MODE);
+
+ /* flush any noise in the buffer */
+ while (inb(KB_STAT) & KB_BUF_FULL) {
+ DELAY(10);
+ (void) inb(KB_DATA);
+ }
+
+ /* Reset keyboard hardware */
+ while (retries--) {
+ kbd_wait();
+ outb(KB_DATA, KB_RESET);
+ for (i=0; i<100000; i++) {
+ DELAY(10);
+ val = inb(KB_DATA);
+ if (val == KB_ACK || val == KB_ECHO)
+ goto gotres;
+ if (val == KB_RESEND)
+ break;
+ }
+ }
+gotres:
+ if (retries < 0)
+ printf("scprobe: keyboard won't accept RESET command\n");
+ else {
+ i = 10; /* At most 10 retries. */
+gotack:
+ DELAY(10);
+ j = 1000; /* Wait at most 10 ms (supposedly). */
+ while ((inb(KB_STAT) & KB_BUF_FULL) == 0 && --j > 0) DELAY(10);
+ DELAY(10);
+ val = inb(KB_DATA);
+ if (val == KB_ACK && --i > 0)
+ goto gotack;
+ if (val != KB_RESET_DONE)
+ printf("scprobe: keyboard RESET failed (result = 0x%02x)\n", val);
+ }
+#ifdef XT_KEYBOARD
+ kbd_wait();
+ outb(KB_DATA, 0xF0);
+ kbd_wait();
+ outb(KB_DATA, 1);
+ kbd_wait();
+#endif /* XT_KEYBOARD */
+ return (IO_KBDSIZE);
+#endif
+}
+
+static struct kern_devconf kdc_sc[NSC] = {
+ 0, 0, 0, /* filled in by dev_attach */
+#ifdef PC98
+ "sc", 0, { MDDT_PC98, 0, "tty" },
+ pc98_generic_externalize, 0, 0, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "sc", 0, { MDDT_ISA, 0, "tty" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_BUSY, /* the console is almost always busy */
+ "Graphics console",
+ DC_CLS_DISPLAY /* class */
+};
+
+static inline void
+#ifdef PC98
+sc_registerdev(struct pc98_device *id)
+#else
+sc_registerdev(struct isa_device *id)
+#endif
+{
+ if(id->id_unit)
+ kdc_sc[id->id_unit] = kdc_sc[0];
+ kdc_sc[id->id_unit].kdc_unit = id->id_unit;
+#ifdef PC98
+ kdc_sc[id->id_unit].kdc_pc98 = id;
+#else
+ kdc_sc[id->id_unit].kdc_isa = id;
+#endif
+ dev_attach(&kdc_sc[id->id_unit]);
+}
+
+#if NAPM > 0
+static int
+scresume(void *dummy)
+{
+ shfts = 0;
+ ctls = 0;
+ alts = 0;
+ agrs = 0;
+ metas = 0;
+ return 0;
+}
+#endif
+
+static int
+#ifdef PC98
+scattach(struct pc98_device *dev)
+#else
+scattach(struct isa_device *dev)
+#endif
+{
+ scr_stat *scp;
+#ifdef DEVFS
+ int vc;
+#endif
+
+ scinit();
+ configuration = dev->id_flags;
+
+ scp = console[0];
+
+#ifndef PC98
+ if (crtc_vga) {
+ font_8 = (char *)malloc(8*256, M_DEVBUF, M_NOWAIT);
+ font_14 = (char *)malloc(14*256, M_DEVBUF, M_NOWAIT);
+ font_16 = (char *)malloc(16*256, M_DEVBUF, M_NOWAIT);
+ copy_font(SAVE, FONT_16, font_16);
+ fonts_loaded = FONT_16;
+ scp->font = FONT_16;
+ save_palette();
+ }
+#endif
+
+ scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short),
+ M_DEVBUF, M_NOWAIT);
+#ifdef PC98
+ scp->atr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short),
+ M_DEVBUF, M_NOWAIT);
+#endif
+ /* copy screen to buffer */
+ bcopyw(Crtat, scp->scr_buf, scp->xsize * scp->ysize * sizeof(u_short));
+#ifdef PC98
+ bcopyw(Atrat, scp->atr_buf, scp->xsize * scp->ysize * sizeof(u_short));
+#endif
+ scp->cursor_pos = scp->scr_buf + scp->xpos + scp->ypos * scp->xsize;
+#ifdef PC98
+ scp->cursor_atr = scp->atr_buf + scp->xpos + scp->ypos * scp->xsize;
+#endif
+ scp->mouse_pos = scp->scr_buf;
+
+ /* initialize history buffer & pointers */
+ scp->history_head = scp->history_pos = scp->history =
+ (u_short *)malloc(scp->history_size*sizeof(u_short),
+ M_DEVBUF, M_NOWAIT);
+ bzero(scp->history_head, scp->history_size*sizeof(u_short));
+#ifdef PC98
+ scp->his_atr_head = scp->his_atr_pos = scp->his_atr =
+ (u_short *)malloc(scp->history_size*sizeof(u_short),
+ M_DEVBUF, M_NOWAIT);
+ bzero(scp->his_atr_head, scp->history_size*sizeof(u_short));
+#endif
+
+ /* initialize cursor stuff */
+ draw_cursor(scp, TRUE);
+ if (crtc_vga && (configuration & CHAR_CURSOR))
+ set_destructive_cursor(scp, TRUE);
+
+ /* get screen update going */
+ scrn_timer();
+
+ update_leds(scp->status);
+ sc_registerdev(dev);
+
+ printf("sc%d: ", dev->id_unit);
+#ifdef PC98
+ printf(" <text mode>");
+#else
+ if (crtc_vga)
+ if (crtc_addr == MONO_BASE)
+ printf("VGA mono");
+ else
+ printf("VGA color");
+ else
+ if (crtc_addr == MONO_BASE)
+ printf("MDA/hercules");
+ else
+ printf("CGA/EGA");
+#endif
+ printf(" <%d virtual consoles, flags=0x%x>\n", MAXCONS, configuration);
+
+#if NAPM > 0
+ scp->r_hook.ah_fun = scresume;
+ scp->r_hook.ah_arg = NULL;
+ scp->r_hook.ah_name = "system keyboard";
+ scp->r_hook.ah_order = APM_MID_ORDER;
+ apm_hook_establish(APM_HOOK_RESUME , &scp->r_hook);
+#endif
+
+ {
+ dev_t dev = makedev(CDEV_MAJOR, 0);
+
+ cdevsw_add(&dev, &scdevsw, NULL);
+ }
+#ifdef DEVFS
+ for (vc = 0; vc < MAXCONS; vc++)
+ sc_devfs_token[vc] = devfs_add_devswf(&scdevsw, vc, DV_CHR,
+ UID_ROOT, GID_WHEEL, 0600,
+ "ttyv%n", vc);
+#endif
+
+ return 0;
+}
+
+struct tty
+*scdevtotty(dev_t dev)
+{
+ int unit = minor(dev);
+
+ if (!init_done)
+ return(NULL);
+ if (unit > MAXCONS || unit < 0)
+ return(NULL);
+ if (unit == MAXCONS)
+ return CONSOLE_TTY;
+ return VIRTUAL_TTY(unit);
+}
+
+static scr_stat
+*get_scr_stat(dev_t dev)
+{
+ int unit = minor(dev);
+
+ if (unit > MAXCONS || unit < 0)
+ return(NULL);
+ if (unit == MAXCONS)
+ return console[0];
+ return console[unit];
+}
+
+static int
+get_scr_num()
+{
+ int i = 0;
+
+ while ((i < MAXCONS) && (cur_console != console[i]))
+ i++;
+ return i < MAXCONS ? i : 0;
+}
+
+int
+scopen(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct tty *tp = scdevtotty(dev);
+
+ if (!tp)
+ return(ENXIO);
+
+ tp->t_oproc = scstart;
+ tp->t_param = scparam;
+ tp->t_dev = dev;
+ if (!(tp->t_state & TS_ISOPEN)) {
+ ttychars(tp);
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_cflag = TTYDEF_CFLAG;
+ tp->t_lflag = TTYDEF_LFLAG;
+ tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+ scparam(tp, &tp->t_termios);
+ ttsetwater(tp);
+ (*linesw[tp->t_line].l_modem)(tp, 1);
+ }
+ else
+ if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0)
+ return(EBUSY);
+ if (!console[minor(dev)])
+ console[minor(dev)] = alloc_scp();
+ return((*linesw[tp->t_line].l_open)(dev, tp));
+}
+
+int
+scclose(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct tty *tp = scdevtotty(dev);
+ struct scr_stat *scp;
+
+ if (!tp)
+ return(ENXIO);
+ if (minor(dev) < MAXCONS) {
+ scp = get_scr_stat(tp->t_dev);
+ if (scp->status & SWITCH_WAIT_ACQ)
+ wakeup((caddr_t)&scp->smode);
+#if not_yet_done
+ if (scp == &main_console) {
+ scp->pid = 0;
+ scp->proc = NULL;
+ scp->smode.mode = VT_AUTO;
+ }
+ else {
+ free(scp->scr_buf, M_DEVBUF);
+#ifdef PC98
+ free(scp->atr_buf, M_DEVBUF);
+ free(scp->his_atr, M_DEVBUF);
+#endif
+ free(scp->history, M_DEVBUF);
+ free(scp, M_DEVBUF);
+ console[minor(dev)] = NULL;
+ }
+#else
+ scp->pid = 0;
+ scp->proc = NULL;
+ scp->smode.mode = VT_AUTO;
+#endif
+ }
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ ttyclose(tp);
+ return(0);
+}
+
+int
+scread(dev_t dev, struct uio *uio, int flag)
+{
+ struct tty *tp = scdevtotty(dev);
+
+ if (!tp)
+ return(ENXIO);
+ return((*linesw[tp->t_line].l_read)(tp, uio, flag));
+}
+
+int
+scwrite(dev_t dev, struct uio *uio, int flag)
+{
+ struct tty *tp = scdevtotty(dev);
+
+ if (!tp)
+ return(ENXIO);
+ return((*linesw[tp->t_line].l_write)(tp, uio, flag));
+}
+
+void
+scintr(int unit)
+{
+ static struct tty *cur_tty;
+ int c, len;
+ u_char *cp;
+
+ /* make screensaver happy */
+ scrn_time_stamp = time.tv_sec;
+ if (scrn_blanked) {
+ (*current_saver)(FALSE);
+ cur_console->start = 0;
+ cur_console->end = cur_console->xsize * cur_console->ysize;
+ }
+
+ c = scgetc(1);
+
+ cur_tty = VIRTUAL_TTY(get_scr_num());
+ if (!(cur_tty->t_state & TS_ISOPEN))
+ if (!((cur_tty = CONSOLE_TTY)->t_state & TS_ISOPEN))
+ return;
+
+ switch (c & 0xff00) {
+ case 0x0000: /* normal key */
+ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty);
+ break;
+ case NOKEY: /* nothing there */
+ break;
+ case FKEY: /* function key, return string */
+ if (cp = get_fstr((u_int)c, (u_int *)&len)) {
+ while (len-- > 0)
+ (*linesw[cur_tty->t_line].l_rint)(*cp++ & 0xFF, cur_tty);
+ }
+ break;
+ case MKEY: /* meta is active, prepend ESC */
+ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty);
+ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty);
+ break;
+ case BKEY: /* backtab fixed sequence (esc [ Z) */
+ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty);
+ (*linesw[cur_tty->t_line].l_rint)('[', cur_tty);
+ (*linesw[cur_tty->t_line].l_rint)('Z', cur_tty);
+ break;
+ }
+}
+
+static int
+scparam(struct tty *tp, struct termios *t)
+{
+ tp->t_ispeed = t->c_ispeed;
+ tp->t_ospeed = t->c_ospeed;
+ tp->t_cflag = t->c_cflag;
+ return 0;
+}
+
+int
+scioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
+{
+ int i, error;
+ struct tty *tp;
+ struct trapframe *fp;
+ scr_stat *scp;
+
+ tp = scdevtotty(dev);
+ if (!tp)
+ return ENXIO;
+ scp = get_scr_stat(tp->t_dev);
+
+ switch (cmd) { /* process console hardware related ioctl's */
+
+ case GIO_ATTR: /* get current attributes */
+ *(int*)data = scp->term.cur_attr;
+ return 0;
+
+ case GIO_COLOR: /* is this a color console ? */
+#ifdef PC98
+ *(int*)data = 0;
+#else
+ if (crtc_addr == COLOR_BASE)
+ *(int*)data = 1;
+ else
+ *(int*)data = 0;
+#endif
+ return 0;
+
+ case CONS_CURRENT: /* get current adapter type */
+#ifdef PC98
+ *(int*)data = KD_PC98;
+#else
+ if (crtc_vga)
+ *(int*)data = KD_VGA;
+ else
+ if (crtc_addr == MONO_BASE)
+ *(int*)data = KD_MONO;
+ else
+ *(int*)data = KD_CGA;
+#endif
+ return 0;
+
+ case CONS_GET: /* get current video mode */
+ *(int*)data = scp->mode;
+ return 0;
+
+ case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */
+ scrn_blank_time = *(int*)data;
+ return 0;
+
+ case CONS_CURSORTYPE: /* set cursor type blink/noblink */
+ if ((*(int*)data) & 0x01)
+ configuration |= BLINK_CURSOR;
+ else
+ configuration &= ~BLINK_CURSOR;
+ if ((*(int*)data) & 0x02) {
+ configuration |= CHAR_CURSOR;
+ set_destructive_cursor(scp, TRUE);
+ } else
+ configuration &= ~CHAR_CURSOR;
+ return 0;
+
+ case CONS_BELLTYPE: /* set bell type sound/visual */
+ if (*data)
+ configuration |= VISUAL_BELL;
+ else
+ configuration &= ~VISUAL_BELL;
+ return 0;
+
+ case CONS_HISTORY: /* set history size */
+ if (*data) {
+ free(scp->history, M_DEVBUF);
+#ifdef PC98
+ free(scp->his_atr, M_DEVBUF);
+#endif
+ scp->history_size = *(int*)data;
+ if (scp->history_size < scp->ysize)
+#ifdef PC98
+ {
+#endif
+ scp->history = NULL;
+#ifdef PC98
+ scp->his_atr = NULL; }
+#endif
+ else {
+ scp->history_size *= scp->xsize;
+ scp->history_head = scp->history_pos = scp->history =
+ (u_short *)malloc(scp->history_size*sizeof(u_short),
+ M_DEVBUF, M_WAITOK);
+ bzero(scp->history_head, scp->history_size*sizeof(u_short));
+#ifdef PC98
+ scp->his_atr_head = scp->his_atr_pos = scp->his_atr =
+ (u_short *)malloc(scp->history_size*sizeof(u_short),
+ M_DEVBUF, M_WAITOK);
+ bzero(scp->his_atr_head, scp->history_size*sizeof(u_short));
+#endif
+ }
+ return 0;
+ }
+ else
+ return EINVAL;
+
+ case CONS_MOUSECTL: /* control mouse arrow */
+ {
+ mouse_info_t *mouse = (mouse_info_t*)data;
+ int fontsize;
+
+ switch (scp->font) {
+ default:
+ case FONT_8:
+ fontsize = 8; break;
+ case FONT_14:
+ fontsize = 14; break;
+ case FONT_16:
+ fontsize = 16; break;
+ }
+ switch (mouse->operation) {
+ case MOUSE_SHOW:
+ if (!(scp->status & MOUSE_ENABLED)) {
+ scp->mouse_oldpos = Crtat + (scp->mouse_pos - scp->scr_buf);
+ scp->status |= (UPDATE_MOUSE | MOUSE_ENABLED);
+ }
+ else
+ return EINVAL;
+ break;
+
+ case MOUSE_HIDE:
+ if (scp->status & MOUSE_ENABLED) {
+ scp->status &= ~MOUSE_ENABLED;
+ scp->status |= UPDATE_MOUSE;
+ }
+ else
+ return EINVAL;
+ break;
+
+ case MOUSE_MOVEABS:
+ scp->mouse_xpos = mouse->x;
+ scp->mouse_ypos = mouse->y;
+ goto set_mouse_pos;
+
+ case MOUSE_MOVEREL:
+ scp->mouse_xpos += mouse->x;
+ scp->mouse_ypos += mouse->y;
+set_mouse_pos:
+ if (scp->mouse_xpos < 0)
+ scp->mouse_xpos = 0;
+ if (scp->mouse_ypos < 0)
+ scp->mouse_ypos = 0;
+ if (scp->mouse_xpos >= scp->xsize*8)
+ scp->mouse_xpos = (scp->xsize*8)-1;
+ if (scp->mouse_ypos >= scp->ysize*fontsize)
+ scp->mouse_ypos = (scp->ysize*fontsize)-1;
+ scp->mouse_pos = scp->scr_buf +
+ (scp->mouse_ypos/fontsize)*scp->xsize + scp->mouse_xpos/8;
+ if (scp->status & MOUSE_ENABLED)
+ scp->status |= UPDATE_MOUSE;
+ break;
+
+ case MOUSE_GETPOS:
+ mouse->x = scp->mouse_xpos;
+ mouse->y = scp->mouse_ypos;
+ return 0;
+
+ default:
+ return EINVAL;
+ }
+ /* make screensaver happy */
+ if (scp == cur_console) {
+ scrn_time_stamp = time.tv_sec;
+ if (scrn_blanked) {
+ (*current_saver)(FALSE);
+ cur_console->start = 0;
+ cur_console->end = cur_console->xsize * cur_console->ysize;
+ }
+ }
+ return 0;
+ }
+
+ case CONS_GETINFO: /* get current (virtual) console info */
+ {
+ vid_info_t *ptr = (vid_info_t*)data;
+ if (ptr->size == sizeof(struct vid_info)) {
+ ptr->m_num = get_scr_num();
+ ptr->mv_col = scp->xpos;
+ ptr->mv_row = scp->ypos;
+ ptr->mv_csz = scp->xsize;
+ ptr->mv_rsz = scp->ysize;
+ ptr->mv_norm.fore = (scp->term.std_color & 0x0f00)>>8;
+ ptr->mv_norm.back = (scp->term.std_color & 0xf000)>>12;
+ ptr->mv_rev.fore = (scp->term.rev_color & 0x0f00)>>8;
+ ptr->mv_rev.back = (scp->term.rev_color & 0xf000)>>12;
+ ptr->mv_grfc.fore = 0; /* not supported */
+ ptr->mv_grfc.back = 0; /* not supported */
+ ptr->mv_ovscan = scp->border;
+ ptr->mk_keylock = scp->status & LOCK_KEY_MASK;
+ return 0;
+ }
+ return EINVAL;
+ }
+
+ case CONS_GETVERS: /* get version number */
+ *(int*)data = 0x200; /* version 2.0 */
+ return 0;
+
+#ifdef PC98
+ case SW_PC98_80x25:
+ case SW_PC98_80x30: /* PC98 TEXT MODES */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->xsize = 80;
+ switch (cmd & 0xff) {
+ case M_PC98_80x25:
+ scp->ysize = 25;
+ break;
+#ifdef LINE30
+ case M_PC98_80x30:
+ scp->ysize = LINE30_ROW;
+ break;
+#endif
+ }
+ scp->mode = cmd & 0xff;
+ scp->status &= ~UNKNOWN_MODE; /* text mode */
+ free(scp->scr_buf, M_DEVBUF);
+ scp->scr_buf = (u_short *)malloc(scp->xsize * scp->ysize *
+ sizeof(u_short),M_DEVBUF, M_WAITOK);
+ free(scp->atr_buf, M_DEVBUF);
+ scp->atr_buf = (u_short *)malloc(scp->xsize * scp->ysize *
+ sizeof(u_short),M_DEVBUF, M_WAITOK);
+ if (scp == cur_console)
+ set_mode(scp);
+ clear_screen(scp);
+ if (tp->t_winsize.ws_col != scp->xsize
+ || tp->t_winsize.ws_row != scp->ysize) {
+ tp->t_winsize.ws_col = scp->xsize;
+ tp->t_winsize.ws_row = scp->ysize;
+ pgsignal(tp->t_pgrp, SIGWINCH, 1);
+ }
+ return 0;
+#else
+ /* VGA TEXT MODES */
+ case SW_VGA_C40x25:
+ case SW_VGA_C80x25: case SW_VGA_M80x25:
+ case SW_VGA_C80x30: case SW_VGA_M80x30:
+ case SW_VGA_C80x50: case SW_VGA_M80x50:
+ case SW_VGA_C80x60: case SW_VGA_M80x60:
+ case SW_B40x25: case SW_C40x25:
+ case SW_B80x25: case SW_C80x25:
+ case SW_ENH_B40x25: case SW_ENH_C40x25:
+ case SW_ENH_B80x25: case SW_ENH_C80x25:
+ case SW_ENH_B80x43: case SW_ENH_C80x43:
+
+ if (!crtc_vga || video_mode_ptr == NULL)
+ return ENXIO;
+ switch (cmd & 0xff) {
+ case M_VGA_C80x60: case M_VGA_M80x60:
+ if (!(fonts_loaded & FONT_8))
+ return EINVAL;
+ scp->xsize = 80;
+ scp->ysize = 60;
+ break;
+ case M_VGA_C80x50: case M_VGA_M80x50:
+ if (!(fonts_loaded & FONT_8))
+ return EINVAL;
+ scp->xsize = 80;
+ scp->ysize = 50;
+ break;
+ case M_ENH_B80x43: case M_ENH_C80x43:
+ if (!(fonts_loaded & FONT_8))
+ return EINVAL;
+ scp->xsize = 80;
+ scp->ysize = 43;
+ break;
+ case M_VGA_C80x30: case M_VGA_M80x30:
+ scp->xsize = 80;
+ scp->ysize = 30;
+ break;
+ default:
+ if ((cmd & 0xff) > M_VGA_CG320)
+ return EINVAL;
+ else
+ scp->xsize = *(video_mode_ptr+((cmd&0xff)*64));
+ scp->ysize = *(video_mode_ptr+((cmd&0xff)*64)+1)+1;
+ break;
+ }
+ scp->mode = cmd & 0xff;
+ scp->status &= ~UNKNOWN_MODE; /* text mode */
+ free(scp->scr_buf, M_DEVBUF);
+ scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short),
+ M_DEVBUF, M_WAITOK);
+ if (scp == cur_console)
+ set_mode(scp);
+ clear_screen(scp);
+ if (tp->t_winsize.ws_col != scp->xsize
+ || tp->t_winsize.ws_row != scp->ysize) {
+ tp->t_winsize.ws_col = scp->xsize;
+ tp->t_winsize.ws_row = scp->ysize;
+ pgsignal(tp->t_pgrp, SIGWINCH, 1);
+ }
+ return 0;
+
+ /* GRAPHICS MODES */
+ case SW_BG320: case SW_BG640:
+ case SW_CG320: case SW_CG320_D: case SW_CG640_E:
+ case SW_CG640x350: case SW_ENH_CG640:
+ case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320:
+
+ if (!crtc_vga || video_mode_ptr == NULL)
+ return ENXIO;
+ scp->mode = cmd & 0xFF;
+ scp->status |= UNKNOWN_MODE; /* graphics mode */
+ scp->xsize = (*(video_mode_ptr + (scp->mode*64))) * 8;
+ scp->ysize = (*(video_mode_ptr + (scp->mode*64) + 1) + 1) *
+ (*(video_mode_ptr + (scp->mode*64) + 2));
+ set_mode(scp);
+ /* clear_graphics();*/
+
+ if (tp->t_winsize.ws_xpixel != scp->xsize
+ || tp->t_winsize.ws_ypixel != scp->ysize) {
+ tp->t_winsize.ws_xpixel = scp->xsize;
+ tp->t_winsize.ws_ypixel = scp->ysize;
+ pgsignal(tp->t_pgrp, SIGWINCH, 1);
+ }
+ return 0;
+#endif /* PC98 */
+
+ case VT_SETMODE: /* set screen switcher mode */
+ bcopy(data, &scp->smode, sizeof(struct vt_mode));
+ if (scp->smode.mode == VT_PROCESS) {
+ scp->proc = p;
+ scp->pid = scp->proc->p_pid;
+ }
+ return 0;
+
+ case VT_GETMODE: /* get screen switcher mode */
+ bcopy(&scp->smode, data, sizeof(struct vt_mode));
+ return 0;
+
+ case VT_RELDISP: /* screen switcher ioctl */
+ switch(*data) {
+ case VT_FALSE: /* user refuses to release screen, abort */
+ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) {
+ old_scp->status &= ~SWITCH_WAIT_REL;
+ switch_in_progress = FALSE;
+ return 0;
+ }
+ return EINVAL;
+
+ case VT_TRUE: /* user has released screen, go on */
+ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) {
+ scp->status &= ~SWITCH_WAIT_REL;
+ exchange_scr();
+ if (new_scp->smode.mode == VT_PROCESS) {
+ new_scp->status |= SWITCH_WAIT_ACQ;
+ psignal(new_scp->proc, new_scp->smode.acqsig);
+ }
+ else
+ switch_in_progress = FALSE;
+ return 0;
+ }
+ return EINVAL;
+
+ case VT_ACKACQ: /* acquire acknowledged, switch completed */
+ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) {
+ scp->status &= ~SWITCH_WAIT_ACQ;
+ switch_in_progress = FALSE;
+ return 0;
+ }
+ return EINVAL;
+
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case VT_OPENQRY: /* return free virtual console */
+ for (i = 0; i < MAXCONS; i++) {
+ tp = VIRTUAL_TTY(i);
+ if (!(tp->t_state & TS_ISOPEN)) {
+ *data = i + 1;
+ return 0;
+ }
+ }
+ return EINVAL;
+
+ case VT_ACTIVATE: /* switch to screen *data */
+ return switch_scr(scp, (*data) - 1);
+
+ case VT_WAITACTIVE: /* wait for switch to occur */
+ if (*data > MAXCONS || *data < 0)
+ return EINVAL;
+ if (minor(dev) == (*data) - 1)
+ return 0;
+ if (*data == 0) {
+ if (scp == cur_console)
+ return 0;
+ }
+ else
+ scp = console[(*data) - 1];
+ while ((error=tsleep((caddr_t)&scp->smode, PZERO|PCATCH,
+ "waitvt", 0)) == ERESTART) ;
+ return error;
+
+ case VT_GETACTIVE:
+ *data = get_scr_num()+1;
+ return 0;
+
+ case KDENABIO: /* allow io operations */
+ error = suser(p->p_ucred, &p->p_acflag);
+ if (error != 0)
+ return error;
+ fp = (struct trapframe *)p->p_md.md_regs;
+ fp->tf_eflags |= PSL_IOPL;
+ return 0;
+
+ case KDDISABIO: /* disallow io operations (default) */
+ fp = (struct trapframe *)p->p_md.md_regs;
+ fp->tf_eflags &= ~PSL_IOPL;
+ return 0;
+
+ case KDSETMODE: /* set current mode of this (virtual) console */
+ switch (*data) {
+ case KD_TEXT: /* switch to TEXT (known) mode */
+ /* restore fonts & palette ! */
+ if (crtc_vga) {
+ if (fonts_loaded & FONT_8)
+ copy_font(LOAD, FONT_8, font_8);
+ if (fonts_loaded & FONT_14)
+ copy_font(LOAD, FONT_14, font_14);
+ if (fonts_loaded & FONT_16)
+ copy_font(LOAD, FONT_16, font_16);
+ if (configuration & CHAR_CURSOR)
+ set_destructive_cursor(scp, TRUE);
+ load_palette();
+ }
+ /* FALL THROUGH */
+
+ case KD_TEXT1: /* switch to TEXT (known) mode */
+ /* no restore fonts & palette */
+ scp->status &= ~UNKNOWN_MODE;
+#ifndef PC98
+ if (crtc_vga && video_mode_ptr)
+#endif
+ set_mode(scp);
+ clear_screen(scp);
+ return 0;
+
+ case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */
+ scp->status |= UNKNOWN_MODE;
+#ifdef PC98
+ set_mode(scp);
+#endif
+ return 0;
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case KDGETMODE: /* get current mode of this (virtual) console */
+ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT;
+ return 0;
+
+ case KDSBORDER: /* set border color of this (virtual) console */
+ if (!crtc_vga)
+ return ENXIO;
+ scp->border = *data;
+ if (scp == cur_console)
+ set_border(scp->border);
+ return 0;
+
+ case KDSKBSTATE: /* set keyboard state (locks) */
+ if (*data >= 0 && *data <= LOCK_KEY_MASK) {
+ scp->status &= ~LOCK_KEY_MASK;
+ scp->status |= *data;
+ if (scp == cur_console)
+ update_leds(scp->status);
+ return 0;
+ }
+ return EINVAL;
+
+ case KDGKBSTATE: /* get keyboard state (locks) */
+ *data = scp->status & LOCK_KEY_MASK;
+ return 0;
+
+ case KDSETRAD: /* set keyboard repeat & delay rates */
+#ifndef PC98
+ if (*data & 0x80)
+ return EINVAL;
+ i = spltty();
+ kbd_cmd(KB_SETRAD);
+ kbd_cmd(*data);
+ splx(i);
+#endif
+ return 0;
+
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*data) {
+ case K_RAW: /* switch to RAW scancode mode */
+ scp->status |= KBD_RAW_MODE;
+ return 0;
+
+ case K_XLATE: /* switch to XLT ascii mode */
+ if (scp == cur_console && scp->status == KBD_RAW_MODE)
+ shfts = ctls = alts = agrs = metas = 0;
+ scp->status &= ~KBD_RAW_MODE;
+ return 0;
+ default:
+ return EINVAL;
+ }
+ /* NOT REACHED */
+
+ case KDGKBMODE: /* get keyboard mode */
+ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE;
+ return 0;
+
+ case KDMKTONE: /* sound the bell */
+ if (*(int*)data)
+ do_bell(scp, (*(int*)data)&0xffff,
+ (((*(int*)data)>>16)&0xffff)*hz/1000);
+ else
+ do_bell(scp, scp->bell_pitch, scp->bell_duration);
+ return 0;
+
+ case KIOCSOUND: /* make tone (*data) hz */
+ if (scp == cur_console) {
+ if (*(int*)data) {
+ int pitch = TIMER_FREQ/(*(int*)data);
+
+#ifdef PC98
+ /* enable counter 1 */
+ outb(0x35, inb(0x35) & 0xf7);
+ /* set command for counter 1, 2 byte write */
+ if (acquire_timer1(TIMER_16BIT|TIMER_SQWAVE)) {
+ return EBUSY;
+ }
+ /* set pitch */
+ outb(TIMER_CNTR1, pitch);
+ outb(TIMER_CNTR1, (pitch>>8));
+#else
+ /* set command for counter 2, 2 byte write */
+ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE))
+ return EBUSY;
+
+ /* set pitch */
+ outb(TIMER_CNTR2, pitch);
+ outb(TIMER_CNTR2, (pitch>>8));
+
+ /* enable counter 2 output to speaker */
+ outb(IO_PPI, inb(IO_PPI) | 3);
+#endif
+ }
+ else {
+#ifdef PC98
+ /* disable counter 1 */
+ outb(0x35, inb(0x35) | 0x08);
+ release_timer1();
+#else
+ /* disable counter 2 output to speaker */
+ outb(IO_PPI, inb(IO_PPI) & 0xFC);
+ release_timer2();
+#endif
+ }
+ }
+ return 0;
+
+ case KDGKBTYPE: /* get keyboard type */
+ *data = 0; /* type not known (yet) */
+ return 0;
+
+ case KDSETLED: /* set keyboard LED status */
+ if (*data >= 0 && *data <= LED_MASK) {
+ scp->status &= ~LED_MASK;
+ scp->status |= *data;
+ if (scp == cur_console)
+ update_leds(scp->status);
+ return 0;
+ }
+ return EINVAL;
+
+ case KDGETLED: /* get keyboard LED status */
+ *data = scp->status & LED_MASK;
+ return 0;
+
+ case GETFKEY: /* get functionkey string */
+ if (*(u_short*)data < n_fkey_tab) {
+ fkeyarg_t *ptr = (fkeyarg_t*)data;
+ bcopy(&fkey_tab[ptr->keynum].str, ptr->keydef,
+ fkey_tab[ptr->keynum].len);
+ ptr->flen = fkey_tab[ptr->keynum].len;
+ return 0;
+ }
+ else
+ return EINVAL;
+
+ case SETFKEY: /* set functionkey string */
+ if (*(u_short*)data < n_fkey_tab) {
+ fkeyarg_t *ptr = (fkeyarg_t*)data;
+ bcopy(ptr->keydef, &fkey_tab[ptr->keynum].str,
+ min(ptr->flen, MAXFK));
+ fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK);
+ return 0;
+ }
+ else
+ return EINVAL;
+
+ case GIO_SCRNMAP: /* get output translation table */
+ bcopy(&scr_map, data, sizeof(scr_map));
+ return 0;
+
+ case PIO_SCRNMAP: /* set output translation table */
+ bcopy(data, &scr_map, sizeof(scr_map));
+ return 0;
+
+ case GIO_KEYMAP: /* get keyboard translation table */
+ bcopy(&key_map, data, sizeof(key_map));
+ return 0;
+
+ case PIO_KEYMAP: /* set keyboard translation table */
+ bcopy(data, &key_map, sizeof(key_map));
+ return 0;
+
+ case PIO_FONT8x8: /* set 8x8 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, font_8, 8*256);
+ fonts_loaded |= FONT_8;
+ copy_font(LOAD, FONT_8, font_8);
+ if (configuration & CHAR_CURSOR)
+ set_destructive_cursor(scp, TRUE);
+ return 0;
+
+ case GIO_FONT8x8: /* get 8x8 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ if (fonts_loaded & FONT_8) {
+ bcopy(font_8, data, 8*256);
+ return 0;
+ }
+ else
+ return ENXIO;
+
+ case PIO_FONT8x14: /* set 8x14 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, font_14, 14*256);
+ fonts_loaded |= FONT_14;
+ copy_font(LOAD, FONT_14, font_14);
+ if (configuration & CHAR_CURSOR)
+ set_destructive_cursor(scp, TRUE);
+ return 0;
+
+ case GIO_FONT8x14: /* get 8x14 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ if (fonts_loaded & FONT_14) {
+ bcopy(font_14, data, 14*256);
+ return 0;
+ }
+ else
+ return ENXIO;
+
+ case PIO_FONT8x16: /* set 8x16 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ bcopy(data, font_16, 16*256);
+ fonts_loaded |= FONT_16;
+ copy_font(LOAD, FONT_16, font_16);
+ if (configuration & CHAR_CURSOR)
+ set_destructive_cursor(scp, TRUE);
+ return 0;
+
+ case GIO_FONT8x16: /* get 8x16 dot font */
+ if (!crtc_vga)
+ return ENXIO;
+ if (fonts_loaded & FONT_16) {
+ bcopy(font_16, data, 16*256);
+ return 0;
+ }
+ else
+ return ENXIO;
+#ifdef PC98
+ case ADJUST_CLOCK: /* /dev/rtc for 98note resume */
+ inittodr(0);
+ return 0;
+#endif
+ default:
+ break;
+ }
+
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return(error);
+ error = ttioctl(tp, cmd, data, flag);
+ if (error >= 0)
+ return(error);
+ return(ENOTTY);
+}
+
+static void
+scstart(struct tty *tp)
+{
+ struct clist *rbp;
+ int s, len;
+ u_char buf[PCBURST];
+ scr_stat *scp = get_scr_stat(tp->t_dev);
+
+ /* XXX who repeats the call when the above flags are cleared? */
+ if (scp->status & SLKED || blink_in_progress)
+ return;
+ s = spltty();
+ if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) {
+ tp->t_state |= TS_BUSY;
+ rbp = &tp->t_outq;
+ while (rbp->c_cc) {
+ len = q_to_b(rbp, buf, PCBURST);
+ splx(s);
+ ansi_put(scp, buf, len);
+ s = spltty();
+ }
+ tp->t_state &= ~TS_BUSY;
+ ttwwakeup(tp);
+ }
+ splx(s);
+}
+
+void
+sccnprobe(struct consdev *cp)
+{
+#ifdef PC98
+ struct pc98_device *dvp;
+#else
+ struct isa_device *dvp;
+#endif
+
+ /*
+ * Take control if we are the highest priority enabled display device.
+ */
+ dvp = find_display();
+ if (dvp != NULL && dvp->id_driver != &scdriver) {
+ cp->cn_pri = CN_DEAD;
+ return;
+ }
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(CDEV_MAJOR, MAXCONS);
+ cp->cn_pri = CN_INTERNAL;
+}
+
+void
+sccninit(struct consdev *cp)
+{
+ scinit();
+}
+
+void
+sccnputc(dev_t dev, int c)
+{
+ u_char buf[1];
+ int s;
+ scr_stat *scp = console[0];
+ term_stat save = scp->term;
+
+ scp->term = kernel_console;
+ current_default = &kernel_default;
+ if (scp->scr_buf == Crtat)
+ draw_cursor(scp, FALSE);
+ buf[0] = c;
+ ansi_put(scp, buf, 1);
+ kernel_console = scp->term;
+ current_default = &user_default;
+ scp->term = save;
+ s = splclock(); /* XXX stop scrn_timer */
+ if (scp == cur_console) {
+ if (scp->scr_buf != Crtat && (scp->start <= scp->end)) {
+ bcopyw(scp->scr_buf + scp->start, Crtat + scp->start,
+ (1 + scp->end - scp->start) * sizeof(u_short));
+#ifdef PC98
+ bcopyw(scp->atr_buf + scp->start, Atrat + scp->start,
+ (1 + scp->end - scp->start) * sizeof(u_short));
+#endif
+ scp->start = scp->xsize * scp->ysize;
+ scp->end = 0;
+ scp->status &= ~CURSOR_SHOWN;
+ }
+ draw_cursor(scp, TRUE);
+ }
+ splx(s);
+}
+
+int
+sccngetc(dev_t dev)
+{
+ int s = spltty(); /* block scintr while we poll */
+ int c = scgetc(0);
+ splx(s);
+ return(c);
+}
+
+int
+sccncheckc(dev_t dev)
+{
+ return (scgetc(1) & 0xff);
+}
+
+static void
+scrn_timer()
+{
+ static int cursor_blinkrate;
+ scr_stat *scp = cur_console;
+
+ /* should we just return ? */
+ if ((scp->status&UNKNOWN_MODE) || blink_in_progress || switch_in_progress) {
+ timeout((timeout_func_t)scrn_timer, 0, hz/10);
+ return;
+ }
+
+ if (!scrn_blanked) {
+ /* update screen image */
+ if (scp->start <= scp->end) {
+ bcopyw(scp->scr_buf + scp->start, Crtat + scp->start,
+ (1 + scp->end - scp->start) * sizeof(u_short));
+#ifdef PC98
+ bcopyw(scp->atr_buf + scp->start, Atrat + scp->start,
+ (1 + scp->end - scp->start) * sizeof(u_short));
+#endif
+ scp->status &= ~CURSOR_SHOWN;
+ scp->start = scp->xsize * scp->ysize;
+ scp->end = 0;
+ }
+ /* update "pseudo" mouse arrow */
+ if ((scp->status & MOUSE_ENABLED) && (scp->status & UPDATE_MOUSE))
+ draw_mouse_image(scp);
+
+ /* update cursor image */
+ if (scp->status & CURSOR_ENABLED)
+ draw_cursor(scp,
+ !(configuration&BLINK_CURSOR) || !(cursor_blinkrate++&0x04));
+ }
+ if (scrn_blank_time && (time.tv_sec>scrn_time_stamp+scrn_blank_time))
+ (*current_saver)(TRUE);
+ timeout((timeout_func_t)scrn_timer, 0, hz/25);
+}
+
+static void
+clear_screen(scr_stat *scp)
+{
+ move_crsr(scp, 0, 0);
+#ifdef PC98
+ fillw(scr_map[0x20], scp->scr_buf,
+ scp->xsize * scp->ysize);
+ fillw(at2pc98(scp->term.cur_color), scp->atr_buf,
+ scp->xsize * scp->ysize);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf,
+ scp->xsize * scp->ysize);
+#endif
+ mark_all(scp);
+}
+
+static int
+switch_scr(scr_stat *scp, u_int next_scr)
+{
+ if (switch_in_progress && (cur_console->proc != pfind(cur_console->pid)))
+ switch_in_progress = FALSE;
+
+ if (next_scr >= MAXCONS || switch_in_progress ||
+ (cur_console->smode.mode == VT_AUTO
+ && cur_console->status & UNKNOWN_MODE)) {
+ do_bell(scp, BELL_PITCH, BELL_DURATION);
+ return EINVAL;
+ }
+
+ /* is the wanted virtual console open ? */
+ if (next_scr) {
+ struct tty *tp = VIRTUAL_TTY(next_scr);
+ if (!(tp->t_state & TS_ISOPEN)) {
+ do_bell(scp, BELL_PITCH, BELL_DURATION);
+ return EINVAL;
+ }
+ }
+ /* delay switch if actively updating screen */
+ if (write_in_progress || blink_in_progress) {
+ delayed_next_scr = next_scr+1;
+ return 0;
+ }
+ switch_in_progress = TRUE;
+ old_scp = cur_console;
+ new_scp = console[next_scr];
+ wakeup((caddr_t)&new_scp->smode);
+ if (new_scp == old_scp) {
+ switch_in_progress = FALSE;
+ delayed_next_scr = FALSE;
+ return 0;
+ }
+
+ /* has controlling process died? */
+ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid)))
+ old_scp->smode.mode = VT_AUTO;
+ if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid)))
+ new_scp->smode.mode = VT_AUTO;
+
+ /* check the modes and switch approbiatly */
+ if (old_scp->smode.mode == VT_PROCESS) {
+ old_scp->status |= SWITCH_WAIT_REL;
+ psignal(old_scp->proc, old_scp->smode.relsig);
+ }
+ else {
+ exchange_scr();
+ if (new_scp->smode.mode == VT_PROCESS) {
+ new_scp->status |= SWITCH_WAIT_ACQ;
+ psignal(new_scp->proc, new_scp->smode.acqsig);
+ }
+ else
+ switch_in_progress = FALSE;
+ }
+ return 0;
+}
+
+static void
+exchange_scr(void)
+{
+ move_crsr(old_scp, old_scp->xpos, old_scp->ypos);
+ cur_console = new_scp;
+#ifdef PC98
+ if (old_scp->mode != new_scp->mode || (old_scp->status & UNKNOWN_MODE) || (new_scp->status & UNKNOWN_MODE)){
+#else
+ if (old_scp->mode != new_scp->mode || (old_scp->status & UNKNOWN_MODE)){
+ if (crtc_vga && video_mode_ptr)
+#endif
+ set_mode(new_scp);
+ }
+ move_crsr(new_scp, new_scp->xpos, new_scp->ypos);
+#ifndef PC98
+ if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) {
+ if (fonts_loaded & FONT_8)
+ copy_font(LOAD, FONT_8, font_8);
+ if (fonts_loaded & FONT_14)
+ copy_font(LOAD, FONT_14, font_14);
+ if (fonts_loaded & FONT_16)
+ copy_font(LOAD, FONT_16, font_16);
+ if (configuration & CHAR_CURSOR)
+ set_destructive_cursor(new_scp, TRUE);
+ load_palette();
+ }
+#endif
+ if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE)
+ shfts = ctls = alts = agrs = metas = 0;
+ update_leds(new_scp->status);
+ delayed_next_scr = FALSE;
+ bcopyw(new_scp->scr_buf, Crtat,
+ (new_scp->xsize*new_scp->ysize)*sizeof(u_short));
+#ifdef PC98
+ bcopyw(new_scp->atr_buf, Atrat,
+ (new_scp->xsize*new_scp->ysize)*sizeof(u_short));
+#endif
+ new_scp->status &= ~CURSOR_SHOWN;
+}
+
+static void
+scan_esc(scr_stat *scp, u_char c)
+{
+ static u_char ansi_col[16] =
+ {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15};
+ int i, n;
+ u_short *src, *dst, count;
+#ifdef PC98
+ u_short *src_attr, *dst_attr;
+#endif
+
+ if (scp->term.esc == 1) {
+#ifdef KANJI
+ switch (scp->kanji_type) {
+ case 0x80:
+ switch (c) {
+ case 'B':
+ case '@':
+ scp->kanji_type = 0x20;
+ scp->term.esc = 0;
+ scp->kanji_1st_char = 0;
+ return;
+ default:
+ scp->kanji_type = 0;
+ scp->term.esc = 0;
+ break;
+ }
+ break;
+ case 0x40:
+ switch (c) {
+ case 'J':
+ case 'B':
+ case 'H':
+ scp->kanji_type = 0;
+ scp->term.esc = 0;
+ scp->kanji_1st_char = 0;
+ return;
+ case 'I':
+ scp->kanji_type = 0x10;
+ scp->term.esc = 0;
+ scp->kanji_1st_char = 0;
+ return;
+ default:
+ scp->kanji_type = 0;
+ scp->term.esc = 0;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+#endif
+ switch (c) {
+
+ case '[': /* Start ESC [ sequence */
+ scp->term.esc = 2;
+ scp->term.last_param = -1;
+ for (i = scp->term.num_param; i < MAX_ESC_PAR; i++)
+ scp->term.param[i] = 1;
+ scp->term.num_param = 0;
+ return;
+
+#ifdef KANJI
+ case '$': /* Kanji IN sequence */
+ scp->kanji_type = 0x80;
+ return;
+
+ case '(': /* Kanji OUT sequence */
+ scp->kanji_type = 0x40;
+ return;
+#endif
+
+ case 'M': /* Move cursor up 1 line, scroll if at top */
+ if (scp->ypos > 0)
+ move_crsr(scp, scp->xpos, scp->ypos - 1);
+ else {
+#ifdef PC98
+ bcopyw(scp->scr_buf, scp->scr_buf + scp->xsize,
+ (scp->ysize - 1) * scp->xsize * sizeof(u_short));
+ bcopyw(scp->atr_buf, scp->atr_buf + scp->xsize,
+ (scp->ysize - 1) * scp->xsize * sizeof(u_short));
+ fillw(scr_map[0x20],
+ scp->scr_buf, scp->xsize);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->atr_buf, scp->xsize);
+#else
+ bcopyw(scp->scr_buf, scp->scr_buf + scp->xsize,
+ (scp->ysize - 1) * scp->xsize * sizeof(u_short));
+ fillw(scp->term.cur_color | scr_map[0x20],
+ scp->scr_buf, scp->xsize);
+#endif
+ mark_all(scp);
+ }
+ break;
+#if notyet
+ case 'Q':
+ scp->term.esc = 4;
+ break;
+#endif
+ case 'c': /* Clear screen & home */
+ clear_screen(scp);
+ break;
+ }
+ }
+ else if (scp->term.esc == 2) {
+ if (c >= '0' && c <= '9') {
+ if (scp->term.num_param < MAX_ESC_PAR) {
+ if (scp->term.last_param != scp->term.num_param) {
+ scp->term.last_param = scp->term.num_param;
+ scp->term.param[scp->term.num_param] = 0;
+ }
+ else
+ scp->term.param[scp->term.num_param] *= 10;
+ scp->term.param[scp->term.num_param] += c - '0';
+ return;
+ }
+ }
+ scp->term.num_param = scp->term.last_param + 1;
+ switch (c) {
+
+ case ';':
+ if (scp->term.num_param < MAX_ESC_PAR)
+ return;
+ break;
+
+ case '=':
+ scp->term.esc = 3;
+ scp->term.last_param = -1;
+ for (i = scp->term.num_param; i < MAX_ESC_PAR; i++)
+ scp->term.param[i] = 1;
+ scp->term.num_param = 0;
+ return;
+
+ case 'A': /* up n rows */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos - n);
+ break;
+
+ case 'B': /* down n rows */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos + n);
+ break;
+
+ case 'C': /* right n columns */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos + n, scp->ypos);
+ break;
+
+ case 'D': /* left n columns */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos - n, scp->ypos);
+ break;
+
+ case 'E': /* cursor to start of line n lines down */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, 0, scp->ypos + n);
+ break;
+
+ case 'F': /* cursor to start of line n lines up */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, 0, scp->ypos - n);
+ break;
+
+ case 'f': /* Cursor move */
+ case 'H':
+ if (scp->term.num_param == 0)
+ move_crsr(scp, 0, 0);
+ else if (scp->term.num_param == 2)
+ move_crsr(scp, scp->term.param[1] - 1, scp->term.param[0] - 1);
+ break;
+
+ case 'J': /* Clear all or part of display */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* clear form cursor to end of display */
+#ifdef PC98
+ fillw(scr_map[0x20],
+ scp->cursor_pos,
+ scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->cursor_atr,
+ scp->atr_buf + scp->xsize * scp->ysize - scp->cursor_atr);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20],
+ scp->cursor_pos,
+ scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ mark_for_update(scp, scp->xsize * scp->ysize);
+ break;
+ case 1: /* clear from beginning of display to cursor */
+#ifdef PC98
+ fillw(scr_map[0x20],
+ scp->scr_buf,
+ scp->cursor_pos - scp->scr_buf);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->atr_buf,
+ scp->cursor_atr - scp->atr_buf);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20],
+ scp->scr_buf,
+ scp->cursor_pos - scp->scr_buf);
+#endif
+ mark_for_update(scp, 0);
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ break;
+ case 2: /* clear entire display */
+ clear_screen(scp);
+ break;
+ }
+ break;
+
+ case 'K': /* Clear all or part of line */
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* clear form cursor to end of line */
+#ifdef PC98
+ fillw(scr_map[0x20],
+ scp->cursor_pos,
+ scp->xsize - scp->xpos);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->cursor_atr,
+ scp->xsize - scp->xpos);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20],
+ scp->cursor_pos,
+ scp->xsize - scp->xpos);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf +
+ scp->xsize - scp->xpos);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf +
+ scp->xsize - scp->xpos);
+#endif
+ break;
+ case 1: /* clear from beginning of line to cursor */
+#ifdef PC98
+ fillw(scr_map[0x20],
+ scp->cursor_pos - scp->xpos,
+ scp->xpos + 1);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->cursor_atr - scp->xpos,
+ scp->xpos + 1);
+#else
+ fillw(scp->term.cur_color| scr_map[0x20],
+ scp->cursor_pos - scp->xpos,
+ scp->xpos + 1);
+#endif
+ mark_for_update(scp, scp->ypos * scp->xsize);
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ break;
+ case 2: /* clear entire line */
+#ifdef PC98
+ fillw(scr_map[0x20],
+ scp->cursor_pos - scp->xpos,
+ scp->xsize);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->cursor_atr - scp->xpos,
+ scp->xsize);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20],
+ scp->cursor_pos - scp->xpos,
+ scp->xsize);
+#endif
+ mark_for_update(scp, scp->ypos * scp->xsize);
+ mark_for_update(scp, (scp->ypos + 1) * scp->xsize);
+ break;
+ }
+ break;
+
+ case 'L': /* Insert n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ src = scp->scr_buf + scp->ypos * scp->xsize;
+ dst = src + n * scp->xsize;
+ count = scp->ysize - (scp->ypos + n);
+ bcopyw(src, dst, count * scp->xsize * sizeof(u_short));
+#ifdef PC98
+ src_attr = scp->atr_buf + scp->ypos * scp->xsize;
+ dst_attr = src_attr + n * scp->xsize;
+ bcopyw(src_attr, dst_attr, count * scp->xsize * sizeof(u_short));
+ fillw(scr_map[0x20], src,
+ n * scp->xsize);
+ fillw(at2pc98(scp->term.cur_color), src_attr,
+ n * scp->xsize);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20], src,
+ n * scp->xsize);
+#endif
+ mark_for_update(scp, scp->ypos * scp->xsize);
+ mark_for_update(scp, scp->xsize * scp->ysize);
+ break;
+
+ case 'M': /* Delete n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize - scp->ypos)
+ n = scp->ysize - scp->ypos;
+ dst = scp->scr_buf + scp->ypos * scp->xsize;
+ src = dst + n * scp->xsize;
+ count = scp->ysize - (scp->ypos + n);
+ bcopyw(src, dst, count * scp->xsize * sizeof(u_short));
+ src = dst + count * scp->xsize;
+#ifdef PC98
+ dst_attr = scp->atr_buf + scp->ypos * scp->xsize;
+ src_attr = dst_attr + n * scp->xsize;
+ bcopyw(src_attr, dst_attr, count * scp->xsize * sizeof(u_short));
+ src_attr = dst_attr + count * scp->xsize;
+ fillw(scr_map[0x20], src,
+ n * scp->xsize);
+ fillw(at2pc98(scp->term.cur_color), src_attr,
+ n * scp->xsize);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20], src,
+ n * scp->xsize);
+#endif
+ mark_for_update(scp, scp->ypos * scp->xsize);
+ mark_for_update(scp, scp->xsize * scp->ysize);
+ break;
+
+ case 'P': /* Delete n chars */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ dst = scp->cursor_pos;
+ src = dst + n;
+ count = scp->xsize - (scp->xpos + n);
+ bcopyw(src, dst, count * sizeof(u_short));
+ src = dst + count;
+#ifdef PC98
+ dst_attr = scp->cursor_atr;
+ src_attr = dst_attr + n;
+ bcopyw(src_attr, dst_attr, count * sizeof(u_short));
+ src_attr = dst_attr + count;
+ fillw(scr_map[0x20], src, n);
+ fillw(at2pc98(scp->term.cur_color), src_attr, n);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20], src, n);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf + n + count);
+#endif
+ break;
+
+ case '@': /* Insert n chars */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+ src = scp->cursor_pos;
+ dst = src + n;
+ count = scp->xsize - (scp->xpos + n);
+ bcopyw(src, dst, count * sizeof(u_short));
+#ifdef PC98
+ src_attr = scp->cursor_atr;
+ dst_attr = src_attr + n;
+ bcopyw(src_attr, dst_attr, count * sizeof(u_short));
+ fillw(scr_map[0x20], src, n);
+ fillw(at2pc98(scp->term.cur_color), src_attr, n);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20], src, n);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf + n + count);
+#endif
+ break;
+
+ case 'S': /* scroll up n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize)
+ n = scp->ysize;
+ bcopyw(scp->scr_buf + (scp->xsize * n),
+ scp->scr_buf,
+ scp->xsize * (scp->ysize - n) * sizeof(u_short));
+#ifdef PC98
+ bcopyw(scp->atr_buf + (scp->xsize * n),
+ scp->atr_buf,
+ scp->xsize * (scp->ysize - n) * sizeof(u_short));
+ fillw(scr_map[0x20],
+ scp->scr_buf + scp->xsize * (scp->ysize - n),
+ scp->xsize * n);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->atr_buf + scp->xsize * (scp->ysize - n),
+ scp->xsize * n);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20],
+ scp->scr_buf + scp->xsize * (scp->ysize - n),
+ scp->xsize * n);
+#endif
+ mark_all(scp);
+ break;
+
+ case 'T': /* scroll down n lines */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->ysize)
+ n = scp->ysize;
+ bcopyw(scp->scr_buf,
+ scp->scr_buf + (scp->xsize * n),
+ scp->xsize * (scp->ysize - n) *
+ sizeof(u_short));
+#ifdef PC98
+ bcopyw(scp->atr_buf,
+ scp->atr_buf + (scp->xsize * n),
+ scp->xsize * (scp->ysize - n) *
+ sizeof(u_short));
+ fillw(scr_map[0x20],
+ scp->scr_buf, scp->xsize * n);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->atr_buf, scp->xsize * n);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20],
+ scp->scr_buf, scp->xsize * n);
+#endif
+ mark_all(scp);
+ break;
+
+ case 'X': /* erase n characters in line */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if (n > scp->xsize - scp->xpos)
+ n = scp->xsize - scp->xpos;
+#ifdef PC98
+ fillw(scr_map[0x20],
+ scp->scr_buf + scp->xpos +
+ ((scp->xsize*scp->ypos) * sizeof(u_short)), n);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->atr_buf + scp->xpos +
+ ((scp->xsize*scp->ypos) * sizeof(u_short)), n);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20],
+ scp->scr_buf + scp->xpos +
+ ((scp->xsize*scp->ypos) * sizeof(u_short)), n);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf + n);
+#endif
+ break;
+
+ case 'Z': /* move n tabs backwards */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ if ((i = scp->xpos & 0xf8) == scp->xpos)
+ i -= 8*n;
+ else
+ i -= 8*(n-1);
+ if (i < 0)
+ i = 0;
+ move_crsr(scp, i, scp->ypos);
+ break;
+
+ case '`': /* move cursor to column n */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, n - 1, scp->ypos);
+ break;
+
+ case 'a': /* move cursor n columns to the right */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos + n, scp->ypos);
+ break;
+
+ case 'd': /* move cursor to row n */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, n - 1);
+ break;
+
+ case 'e': /* move cursor n rows down */
+ n = scp->term.param[0]; if (n < 1) n = 1;
+ move_crsr(scp, scp->xpos, scp->ypos + n);
+ break;
+
+ case 'm': /* change attribute */
+ if (scp->term.num_param == 0) {
+ scp->term.attr_mask = NORMAL_ATTR;
+ scp->term.cur_attr =
+ scp->term.cur_color = scp->term.std_color;
+ break;
+ }
+ for (i = 0; i < scp->term.num_param; i++) {
+ switch (n = scp->term.param[i]) {
+ case 0: /* back to normal */
+ scp->term.attr_mask = NORMAL_ATTR;
+ scp->term.cur_attr =
+ scp->term.cur_color = scp->term.std_color;
+ break;
+ case 1: /* bold */
+ scp->term.attr_mask |= BOLD_ATTR;
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ case 4: /* underline */
+ scp->term.attr_mask |= UNDERLINE_ATTR;
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ case 5: /* blink */
+ scp->term.attr_mask |= BLINK_ATTR;
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ case 7: /* reverse video */
+ scp->term.attr_mask |= REVERSE_ATTR;
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ case 30: case 31: /* set fg color */
+ case 32: case 33: case 34:
+ case 35: case 36: case 37:
+ scp->term.attr_mask |= FOREGROUND_CHANGED;
+ scp->term.cur_color =
+ (scp->term.cur_color&0xF000) | (ansi_col[(n-30)&7]<<8);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ case 40: case 41: /* set bg color */
+ case 42: case 43: case 44:
+ case 45: case 46: case 47:
+ scp->term.attr_mask |= BACKGROUND_CHANGED;
+ scp->term.cur_color =
+ (scp->term.cur_color&0x0F00) | (ansi_col[(n-40)&7]<<12);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ }
+ }
+ break;
+
+ case 'x':
+ if (scp->term.num_param == 0)
+ n = 0;
+ else
+ n = scp->term.param[0];
+ switch (n) {
+ case 0: /* reset attributes */
+ scp->term.attr_mask = NORMAL_ATTR;
+ scp->term.cur_attr =
+ scp->term.cur_color = scp->term.std_color =
+ current_default->std_color;
+ scp->term.rev_color = current_default->rev_color;
+ break;
+ case 1: /* set ansi background */
+ scp->term.attr_mask &= ~BACKGROUND_CHANGED;
+ scp->term.cur_color = scp->term.std_color =
+ (scp->term.std_color & 0x0F00) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<12);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ case 2: /* set ansi foreground */
+ scp->term.attr_mask &= ~FOREGROUND_CHANGED;
+ scp->term.cur_color = scp->term.std_color =
+ (scp->term.std_color & 0xF000) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<8);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ case 3: /* set ansi attribute directly */
+ scp->term.attr_mask &= ~(FOREGROUND_CHANGED|BACKGROUND_CHANGED);
+ scp->term.cur_color = scp->term.std_color =
+ (scp->term.param[1]&0xFF)<<8;
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ case 5: /* set ansi reverse video background */
+ scp->term.rev_color =
+ (scp->term.rev_color & 0x0F00) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<12);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ case 6: /* set ansi reverse video foreground */
+ scp->term.rev_color =
+ (scp->term.rev_color & 0xF000) |
+ (ansi_col[(scp->term.param[1])&0x0F]<<8);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ case 7: /* set ansi reverse video directly */
+ scp->term.rev_color =
+ (scp->term.param[1]&0xFF)<<8;
+ scp->term.cur_attr = mask2attr(&scp->term);
+ break;
+ }
+ break;
+
+ case 'z': /* switch to (virtual) console n */
+ if (scp->term.num_param == 1)
+ switch_scr(scp, scp->term.param[0]);
+ break;
+ }
+ }
+ else if (scp->term.esc == 3) {
+ if (c >= '0' && c <= '9') {
+ if (scp->term.num_param < MAX_ESC_PAR) {
+ if (scp->term.last_param != scp->term.num_param) {
+ scp->term.last_param = scp->term.num_param;
+ scp->term.param[scp->term.num_param] = 0;
+ }
+ else
+ scp->term.param[scp->term.num_param] *= 10;
+ scp->term.param[scp->term.num_param] += c - '0';
+ return;
+ }
+ }
+ scp->term.num_param = scp->term.last_param + 1;
+ switch (c) {
+
+ case ';':
+ if (scp->term.num_param < MAX_ESC_PAR)
+ return;
+ break;
+
+ case 'A': /* set display border color */
+ if (scp->term.num_param == 1)
+ scp->border=scp->term.param[0] & 0xff;
+ if (scp == cur_console)
+ set_border(scp->border);
+ break;
+
+ case 'B': /* set bell pitch and duration */
+ if (scp->term.num_param == 2) {
+ scp->bell_pitch = scp->term.param[0];
+ scp->bell_duration = scp->term.param[1]*10;
+ }
+ break;
+
+ case 'C': /* set cursor type & shape */
+ if (scp->term.num_param == 1) {
+ if (scp->term.param[0] & 0x01)
+ configuration |= BLINK_CURSOR;
+ else
+ configuration &= ~BLINK_CURSOR;
+ if (scp->term.param[0] & 0x02) {
+ configuration |= CHAR_CURSOR;
+ set_destructive_cursor(scp, TRUE);
+ } else
+ configuration &= ~CHAR_CURSOR;
+ }
+ else if (scp->term.num_param == 2) {
+ scp->cursor_start = scp->term.param[0] & 0x1F;
+ scp->cursor_end = scp->term.param[1] & 0x1F;
+ if (configuration & CHAR_CURSOR)
+ set_destructive_cursor(scp, TRUE);
+ }
+ break;
+
+ case 'F': /* set ansi foreground */
+ if (scp->term.num_param == 1) {
+ scp->term.attr_mask &= ~FOREGROUND_CHANGED;
+ scp->term.cur_color = scp->term.std_color =
+ (scp->term.std_color & 0xF000)
+ | ((scp->term.param[0] & 0x0F) << 8);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ }
+ break;
+
+ case 'G': /* set ansi background */
+ if (scp->term.num_param == 1) {
+ scp->term.attr_mask &= ~BACKGROUND_CHANGED;
+ scp->term.cur_color = scp->term.std_color =
+ (scp->term.std_color & 0x0F00)
+ | ((scp->term.param[0] & 0x0F) << 12);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ }
+ break;
+
+ case 'H': /* set ansi reverse video foreground */
+ if (scp->term.num_param == 1) {
+ scp->term.rev_color =
+ (scp->term.rev_color & 0xF000)
+ | ((scp->term.param[0] & 0x0F) << 8);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ }
+ break;
+
+ case 'I': /* set ansi reverse video background */
+ if (scp->term.num_param == 1) {
+ scp->term.rev_color =
+ (scp->term.rev_color & 0x0F00)
+ | ((scp->term.param[0] & 0x0F) << 12);
+ scp->term.cur_attr = mask2attr(&scp->term);
+ }
+ break;
+ }
+ }
+ scp->term.esc = 0;
+}
+
+#ifdef KANJI
+static u_char iskanji1(u_char mode, u_char c)
+{
+ if ((mode == 0x20) && (c >= 0x21) && (c <= 0x7e)) {
+ /* JIS */
+ return 0x20;
+ }
+
+ if ((mode == 0x10) && (c >= 0x21) && (c <= 0x5f)) {
+ /* JIS HANKAKU */
+ return 0x10;
+ }
+
+ if ((c >= 0x81) && (c <= 0x9f) && (c != 0x8e)) {
+ /* SJIS */
+ default_kanji = SJIS;
+ return 2;
+ }
+
+ if ((c >= 0xa1) && (c <= 0xdf) && (default_kanji == SJIS)) {
+ /* Sjis HANKAKU */
+ return 1;
+ }
+
+ if ((c >= 0xa1) && (c <= 0xdf) && (default_kanji == UJIS)) {
+ /* UJIS */
+ return 4;
+ }
+
+ if ((c >= 0xf0) && (c <= 0xfe)) {
+ /* UJIS */
+ default_kanji = UJIS;
+ return 4;
+ }
+
+ if ((c >= 0xe0) && (c <= 0xef)) {
+ /* SJIS or UJIS */
+ return 6;
+ }
+
+ if (c == 0x8e) {
+ /* SJIS or UJIS HANKAKU */
+ return 3;
+ }
+
+ return 0;
+}
+
+static u_char iskanji2(u_char mode, u_char c)
+{
+ switch (mode) {
+ case 0x20:
+ if ((c >= 0x21) && (c <= 0x7e)) {
+ /* JIS */
+ return 0x20;
+ }
+ break;
+ case 2:
+ if ((c >= 0x40) && (c <= 0xfc) && (c != 0x7f)) {
+ /* SJIS */
+ return 2;
+ }
+ break;
+ case 4:
+ if ((c >= 0xa1) && (c <= 0xfe)) {
+ /* UJIS */
+ return 4;
+ }
+ break;
+ case 3:
+ if ((c >= 0xa1) && (c <= 0xdf) && (default_kanji == UJIS)) {
+ /* UJIS HANKAKU */
+ return 1;
+ }
+ if ((c >= 0x40) && (c <= 0xfc) && (c != 0x7f)) {
+ /* SJIS */
+ default_kanji = SJIS;
+ return 2;
+ }
+ break;
+ case 6:
+ if ((c >= 0x40) && (c <= 0xa0) && (c != 0x7f)) {
+ /* SJIS */
+ default_kanji = SJIS;
+ return 2;
+ }
+ if ((c == 0xfd) || (c == 0xfe)) {
+ /* UJIS */
+ default_kanji = UJIS;
+ return 4;
+ }
+ if ((c >= 0xa1) && (c <= 0xfc)) {
+ if (default_kanji == SJIS)
+ return 2;
+ if (default_kanji == UJIS)
+ return 4;
+ }
+ break;
+ }
+ return 0;
+}
+
+/*
+ * JIS X0208-83 keisen conversion table
+ */
+static u_short keiConv[32] = {
+ 0x240c, 0x260c, 0x300c, 0x340c, 0x3c0c, 0x380c, 0x400c, 0x500c,
+ 0x480c, 0x580c, 0x600c, 0x250c, 0x270c, 0x330c, 0x370c, 0x3f0c,
+ 0x3b0c, 0x470c, 0x570c, 0x4f0c, 0x5f0c, 0x6f0c, 0x440c, 0x530c,
+ 0x4c0c, 0x5b0c, 0x630c, 0x410c, 0x540c, 0x490c, 0x5c0c, 0x660c
+};
+
+
+static u_short kanji_convert(u_char mode, u_char h, u_char l)
+{
+ u_short tmp, high, low, c;
+ high = (u_short) h;
+ low = (u_short) l;
+
+ switch (mode) {
+ case 2: /* SHIFT JIS */
+ if (low >= 0xe0) {
+ low -= 0x40;
+ }
+ low = (low - 0x81) * 2 + 0x21;
+ if (high > 0x7f) {
+ high--;
+ }
+ if (high >0x9d) {
+ low++;
+ high -= 0x9e - 0x21;
+ } else {
+ high -= 0x40 - 0x21;
+ }
+ high &= 0x7F;
+ low &= 0x7F;
+ tmp = ((high << 8) | low) - 0x20;
+ break;
+ case 0x20: /* JIS */
+ case 4: /* HANKAKU? */
+ high &= 0x7F;
+ low &= 0x7F;
+ tmp = ((high << 8) | low) - 0x20;
+ break;
+ default:
+ tmp = 0;
+ break;
+ }
+
+ /* keisen */
+ c = ((tmp & 0xff) << 8) | (tmp >> 8);
+ if (0x0821 <= c && c <= 0x0840)
+ tmp = keiConv[c - 0x0821];
+
+ return (tmp);
+}
+#endif
+
+static void
+ansi_put(scr_stat *scp, u_char *buf, int len)
+{
+ u_char *ptr = buf;
+#ifdef KANJI
+ u_short i, kanji_code;
+#endif
+
+ if (scp->status & UNKNOWN_MODE)
+ return;
+
+ /* make screensaver happy */
+ if (scp == cur_console) {
+ scrn_time_stamp = time.tv_sec;
+ if (scrn_blanked) {
+ (*current_saver)(FALSE);
+ cur_console->start = 0;
+ cur_console->end = cur_console->xsize * cur_console->ysize;
+ }
+ }
+ write_in_progress++;
+outloop:
+ if (scp->term.esc) {
+ scan_esc(scp, *ptr++);
+ len--;
+ }
+ else if (PRINTABLE(*ptr)) { /* Print only printables */
+ int cnt = len <= (scp->xsize-scp->xpos) ? len : (scp->xsize-scp->xpos);
+ u_short cur_attr = scp->term.cur_attr;
+ u_short *cursor_pos = scp->cursor_pos;
+#ifdef PC98
+ u_char c = *ptr;
+ u_short *cursor_atr = scp->cursor_atr;
+#ifdef KANJI
+ if (scp->kanji_1st_char == 0) {
+ scp->kanji_type = iskanji1(scp->kanji_type, c);
+ if (scp->kanji_type & 0xee) {
+ /* not Ascii & not HANKAKU */
+ scp->kanji_1st_char = c;
+ ptr++; len--;
+ goto kanji_end;
+ } else {
+ scp->kanji_1st_char = 0;
+ }
+ } else {
+ if ((scp->kanji_type = iskanji2(scp->kanji_type, c)) & 0xee) {
+ /* print kanji on TEXT VRAM */
+ kanji_code = kanji_convert(scp->kanji_type, c, scp->kanji_1st_char);
+ for (i=0; i<2; i++){
+ *cursor_pos = (kanji_code | (i*0x80));
+ *cursor_atr = (at2pc98(cur_attr));
+ cursor_pos++;
+ cursor_atr++;
+ if (++scp->xpos >= scp->xsize) {
+ scp->xpos = 0;
+ scp->ypos++;
+ }
+ }
+ scp->kanji_type &= 0xF0;
+ scp->kanji_1st_char = 0;
+ ptr++; len--;
+ goto kanji_end;
+ } else {
+ scp->kanji_1st_char = 0;
+ }
+ }
+ if ((scp->kanji_type & 0x11)) c |= 0x80;
+ scp->kanji_type &= 0xf0;
+#endif /* KANJI */
+ *cursor_pos++ = (scr_map[c]);
+ *cursor_atr++ = at2pc98(cur_attr);
+ ptr++;
+#else
+ do {
+ /*
+ * gcc-2.6.3 generates poor (un)sign extension code. Casting the
+ * pointers in the following to volatile should have no effect,
+ * but in fact speeds up this inner loop from 26 to 18 cycles
+ * (+ cache misses) on i486's.
+ */
+#define UCVP(ucp) ((u_char volatile *)(ucp))
+ *cursor_pos++ = UCVP(scr_map)[*UCVP(ptr)] | cur_attr;
+ ptr++;
+ cnt--;
+ } while (cnt && PRINTABLE(*ptr));
+#endif /* PC98 */
+ len -= (cursor_pos - scp->cursor_pos);
+ scp->xpos += (cursor_pos - scp->cursor_pos);
+#ifdef KANJI
+kanji_end:
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ mark_for_update(scp, cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, cursor_atr - scp->atr_buf);
+#endif
+ scp->cursor_pos = cursor_pos;
+#ifdef PC98
+ scp->cursor_atr = cursor_atr;
+#endif
+ if (scp->xpos >= scp->xsize) {
+ scp->xpos = 0;
+ scp->ypos++;
+ }
+ }
+ else {
+ switch(*ptr) {
+ case 0x07:
+ do_bell(scp, scp->bell_pitch, scp->bell_duration);
+ break;
+
+ case 0x08: /* non-destructive backspace */
+ if (scp->cursor_pos > scp->scr_buf) {
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ scp->cursor_pos--;
+#ifdef PC98
+ scp->cursor_atr--;
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ if (scp->xpos > 0)
+ scp->xpos--;
+ else {
+ scp->xpos += scp->xsize - 1;
+ scp->ypos--;
+ }
+ }
+ break;
+
+ case 0x09: /* non-destructive tab */
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ scp->cursor_pos += (8 - scp->xpos % 8u);
+#ifdef PC98
+ scp->cursor_atr += (8 - scp->xpos % 8u);
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ if ((scp->xpos += (8 - scp->xpos % 8u)) >= scp->xsize) {
+ scp->xpos = 0;
+ scp->ypos++;
+ }
+ break;
+
+ case 0x0a: /* newline, same pos */
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ scp->cursor_pos += scp->xsize;
+#ifdef PC98
+ scp->cursor_atr += scp->xsize;
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ scp->ypos++;
+ break;
+
+ case 0x0c: /* form feed, clears screen */
+ clear_screen(scp);
+ break;
+
+ case 0x0d: /* return, return to pos 0 */
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ scp->cursor_pos -= scp->xpos;
+#ifdef PC98
+ scp->cursor_atr -= scp->xpos;
+#endif
+ mark_for_update(scp, scp->cursor_pos - scp->scr_buf);
+#ifdef PC98
+ mark_for_update(scp, scp->cursor_atr - scp->atr_buf);
+#endif
+ scp->xpos = 0;
+ break;
+
+ case 0x1b: /* start escape sequence */
+ scp->term.esc = 1;
+ scp->term.num_param = 0;
+ break;
+ }
+ ptr++; len--;
+ }
+ /* do we have to scroll ?? */
+ if (scp->cursor_pos >= scp->scr_buf + scp->ysize * scp->xsize) {
+ if (scp->history) {
+ bcopyw(scp->scr_buf, scp->history_head,
+ scp->xsize * sizeof(u_short));
+ scp->history_head += scp->xsize;
+#ifdef PC98
+ bcopyw(scp->atr_buf, scp->his_atr_head,
+ scp->xsize * sizeof(u_short));
+ scp->his_atr_head += scp->xsize;
+#endif
+ if (scp->history_head + scp->xsize >
+ scp->history + scp->history_size)
+#ifdef PC98
+ {
+#endif
+ scp->history_head = scp->history;
+#ifdef PC98
+ scp->his_atr_head = scp->his_atr; }
+#endif
+ }
+ bcopyw(scp->scr_buf + scp->xsize, scp->scr_buf,
+ scp->xsize * (scp->ysize - 1) * sizeof(u_short));
+#ifdef PC98
+ bcopyw(scp->atr_buf + scp->xsize, scp->atr_buf,
+ scp->xsize * (scp->ysize - 1) * sizeof(u_short));
+ fillw(scr_map[0x20],
+ scp->scr_buf + scp->xsize * (scp->ysize - 1),
+ scp->xsize);
+ fillw(at2pc98(scp->term.cur_color),
+ scp->atr_buf + scp->xsize * (scp->ysize - 1),
+ scp->xsize);
+#else
+ fillw(scp->term.cur_color | scr_map[0x20],
+ scp->scr_buf + scp->xsize * (scp->ysize - 1),
+ scp->xsize);
+#endif
+
+ scp->cursor_pos -= scp->xsize;
+#ifdef PC98
+ scp->cursor_atr -= scp->xsize;
+#endif
+ scp->ypos--;
+ mark_all(scp);
+ }
+ if (len)
+ goto outloop;
+ write_in_progress--;
+ if (delayed_next_scr)
+ switch_scr(scp, delayed_next_scr - 1);
+}
+
+static void
+scinit(void)
+{
+#ifndef PC98
+ u_short volatile *cp;
+ u_short was;
+#endif
+ unsigned hw_cursor;
+ int i;
+
+ if (init_done)
+ return;
+ init_done = TRUE;
+ /*
+ * Finish defaulting crtc variables for a mono screen. Crtat is a
+ * bogus common variable so that it can be shared with pcvt, so it
+ * can't be statically initialized. XXX.
+ */
+#ifdef PC98
+ Crtat = (u_short *)TEXT_VRAM;
+ Atrat = (u_short *)TEXT_VRAM + ATTR_OFFSET;
+#else
+ Crtat = (u_short *)MONO_BUF;
+ /*
+ * If CGA memory seems to work, switch to color.
+ */
+ cp = (u_short *)CGA_BUF;
+ was = *cp;
+ *cp = (u_short) 0xA55A;
+ if (*cp == 0xA55A) {
+ Crtat = (u_short *)cp;
+ crtc_addr = COLOR_BASE;
+ }
+ *cp = was;
+#endif
+
+#ifdef PC98
+#ifdef AUTO_CLOCK
+ if (pc98_machine_type & M_8M) {
+ BELL_PITCH = 1339;
+ TIMER_FREQ = 1996800L;
+ } else {
+ BELL_PITCH = 1678;
+ TIMER_FREQ = 2457600L;
+ }
+#endif /* AUTO_CLOCK */
+ outb(0x62, 0xd);
+ outb(0xA2, 0xd);
+ /* Extract cursor location */
+ while((inb(TEXT_GDC + 0) & 0x04) == 0) {} /* GDC wait */
+ outb(TEXT_GDC + 2, 0xe0); /* CSRR */
+ while((inb(TEXT_GDC + 0) & 0x1) == 0) {} /* GDC wait */
+ hw_cursor = inb(TEXT_GDC + 2); /* EADl */
+ hw_cursor |= (inb(TEXT_GDC + 2) << 8); /* EADh */
+ inb(TEXT_GDC + 2); /* dummy */
+ inb(TEXT_GDC + 2); /* dummy */
+ inb(TEXT_GDC + 2); /* dummy */
+
+ if (hw_cursor >= ROW*COL) {
+ hw_cursor = 0;
+ }
+ crtc_vga = 1;
+#else /* IBM-PC */
+ /*
+ * Ensure a zero start address. This is mainly to recover after
+ * switching from pcvt using userconfig(). The registers are w/o
+ * for old hardware so it's too hard to relocate the active screen
+ * memory.
+ */
+ outb(crtc_addr, 12);
+ outb(crtc_addr + 1, 0);
+ outb(crtc_addr, 13);
+ outb(crtc_addr + 1, 0);
+
+ /* extract cursor location */
+ outb(crtc_addr, 14);
+ hw_cursor = inb(crtc_addr + 1) << 8;
+ outb(crtc_addr, 15);
+ hw_cursor |= inb(crtc_addr + 1);
+
+ /* move hardware cursor out of the way */
+ outb(crtc_addr, 14);
+ outb(crtc_addr + 1, 0xff);
+ outb(crtc_addr, 15);
+ outb(crtc_addr + 1, 0xff);
+
+ /* is this a VGA or higher ? */
+ outb(crtc_addr, 7);
+ if (inb(crtc_addr) == 7) {
+ u_long pa;
+ u_long segoff;
+
+ crtc_vga = TRUE;
+ /*
+ * Get the BIOS video mode pointer.
+ */
+ segoff = *(u_long *)pa_to_va(0x4a8);
+ pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff));
+ if (ISMAPPED(pa, sizeof(u_long))) {
+ segoff = *(u_long *)pa_to_va(pa);
+ pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff));
+ if (ISMAPPED(pa, 64))
+ video_mode_ptr = (char *)pa_to_va(pa);
+ }
+ }
+#endif /* IBM */
+
+ current_default = &user_default;
+ console[0] = &main_console;
+ init_scp(console[0]);
+ console[0]->scr_buf = console[0]->mouse_pos = Crtat;
+ console[0]->cursor_pos = Crtat + hw_cursor;
+#ifdef PC98
+ console[0]->atr_buf = Atrat;
+ console[0]->cursor_atr = Atrat + hw_cursor;
+#endif
+ console[0]->xpos = hw_cursor % COL;
+ console[0]->ypos = hw_cursor / COL;
+ cur_console = console[0];
+ for (i=1; i<MAXCONS; i++)
+ console[i] = NULL;
+ kernel_console.esc = 0;
+ kernel_console.attr_mask = NORMAL_ATTR;
+ kernel_console.cur_attr =
+ kernel_console.cur_color = kernel_console.std_color =
+ kernel_default.std_color;
+ kernel_console.rev_color = kernel_default.rev_color;
+ /* initialize mapscrn array to a one to one map */
+ for (i=0; i<sizeof(scr_map); i++)
+ scr_map[i] = i;
+#ifdef PC98
+ scr_map[0x5c] = (u_char)0xfc; /* for backslash */
+#endif
+}
+
+static scr_stat
+*alloc_scp()
+{
+ scr_stat *scp;
+
+ scp = (scr_stat *)malloc(sizeof(scr_stat), M_DEVBUF, M_WAITOK);
+ init_scp(scp);
+ scp->scr_buf = scp->cursor_pos = scp->scr_buf = scp->mouse_pos =
+ (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short),
+ M_DEVBUF, M_WAITOK);
+#ifdef PC98
+ scp->atr_buf = scp->cursor_atr = scp->atr_buf =
+ (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short),
+ M_DEVBUF, M_WAITOK);
+ scp->his_atr_head = scp->his_atr_pos = scp->his_atr =
+ (u_short *)malloc(scp->history_size*sizeof(u_short),
+ M_DEVBUF, M_WAITOK);
+ bzero(scp->his_atr_head, scp->history_size*sizeof(u_short));
+#endif
+ scp->history_head = scp->history_pos = scp->history =
+ (u_short *)malloc(scp->history_size*sizeof(u_short),
+ M_DEVBUF, M_WAITOK);
+ bzero(scp->history_head, scp->history_size*sizeof(u_short));
+#ifndef PC98
+ if (crtc_vga && video_mode_ptr)
+#endif
+ set_mode(scp);
+ clear_screen(scp);
+ return scp;
+}
+
+static void
+init_scp(scr_stat *scp)
+{
+#ifndef PC98
+ scp->mode = M_VGA_C80x25;
+#else
+ scp->mode = M_PC98_80x25;
+#endif
+ scp->font = FONT_16;
+ scp->xsize = COL;
+ scp->ysize = ROW;
+ scp->start = COL * ROW;
+ scp->end = 0;
+ scp->term.esc = 0;
+ scp->term.attr_mask = NORMAL_ATTR;
+ scp->term.cur_attr =
+ scp->term.cur_color = scp->term.std_color =
+ current_default->std_color;
+ scp->term.rev_color = current_default->rev_color;
+ scp->border = BG_BLACK;
+#ifdef PC98
+ scp->cursor_start = 0;
+ scp->cursor_end = 0;
+#else
+ scp->cursor_start = *(char *)pa_to_va(0x461);
+ scp->cursor_end = *(char *)pa_to_va(0x460);
+#endif
+ scp->mouse_xpos = scp->mouse_ypos = 0;
+ scp->bell_pitch = BELL_PITCH;
+ scp->bell_duration = BELL_DURATION;
+#ifdef PC98
+ scp->status = 0;
+ scp->status |= CURSOR_ENABLED;
+#else
+ scp->status = (*(char *)pa_to_va(0x417) & 0x20) ? NLKED : 0;
+ scp->status |= CURSOR_ENABLED;
+#endif
+ scp->pid = 0;
+ scp->proc = NULL;
+ scp->smode.mode = VT_AUTO;
+ scp->history_head = scp->history_pos = scp->history = NULL;
+#ifdef PC98
+ scp->his_atr_head = scp->his_atr_pos = scp->his_atr = NULL;
+#endif
+ scp->history_size = HISTORY_SIZE;
+#ifdef KANJI
+ scp->kanji_1st_char = 0;
+ scp->kanji_type = 0;
+#endif
+}
+
+static u_char
+*get_fstr(u_int c, u_int *len)
+{
+ u_int i;
+
+ if (!(c & FKEY))
+ return(NULL);
+ i = (c & 0xFF) - F_FN;
+ if (i > n_fkey_tab)
+ return(NULL);
+ *len = fkey_tab[i].len;
+ return(fkey_tab[i].str);
+}
+
+static void
+update_leds(int which)
+{
+#ifndef PC98
+ int s;
+ static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+ /* replace CAPS led with ALTGR led for ALTGR keyboards */
+ if (key_map.n_keys > ALTGR_OFFSET) {
+ if (which & ALKED)
+ which |= CLKED;
+ else
+ which &= ~CLKED;
+ }
+ s = spltty();
+ kbd_cmd(KB_SETLEDS);
+ kbd_cmd(xlate_leds[which & LED_MASK]);
+ splx(s);
+#endif
+}
+
+static void
+history_to_screen(scr_stat *scp)
+{
+ int i;
+
+ for (i=0; i<scp->ysize; i++)
+#ifdef PC98
+ {
+#endif
+ bcopyw(scp->history + (((scp->history_pos - scp->history) +
+ scp->history_size-((i+1)*scp->xsize))%scp->history_size),
+ scp->scr_buf + (scp->xsize * (scp->ysize-1 - i)),
+ scp->xsize * sizeof(u_short));
+#ifdef PC98
+ bcopyw(scp->his_atr + (((scp->his_atr_pos - scp->his_atr) +
+ scp->history_size-((i+1)*scp->xsize))%scp->history_size),
+ scp->atr_buf + (scp->xsize * (scp->ysize-1 - i)),
+ scp->xsize * sizeof(u_short)); }
+#endif
+ mark_all(scp);
+}
+
+static int
+history_up_line(scr_stat *scp)
+{
+ if (WRAPHIST(scp, scp->history_pos, -(scp->xsize*scp->ysize)) !=
+ scp->history_head) {
+ scp->history_pos = WRAPHIST(scp, scp->history_pos, -scp->xsize);
+#ifdef PC98
+ scp->his_atr_pos = WRAPHIST_A(scp, scp->his_atr_pos, -scp->xsize);
+#endif
+ history_to_screen(scp);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+static int
+history_down_line(scr_stat *scp)
+{
+ if (scp->history_pos != scp->history_head) {
+ scp->history_pos = WRAPHIST(scp, scp->history_pos, scp->xsize);
+#ifdef PC98
+ scp->his_atr_pos = WRAPHIST_A(scp, scp->his_atr_pos, scp->xsize);
+#endif
+ history_to_screen(scp);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+/*
+ * scgetc(noblock) - get character from keyboard.
+ * If noblock = 0 wait until a key is pressed.
+ * Else return NOKEY.
+ */
+u_int
+scgetc(int noblock)
+{
+ u_char scancode, keycode;
+ u_int state, action;
+ struct key_t *key;
+ static u_char esc_flag = 0, compose = 0;
+ static u_int chr = 0;
+
+next_code:
+ kbd_wait();
+ /* First see if there is something in the keyboard port */
+ if (inb(KB_STAT) & KB_BUF_FULL)
+#ifdef PC98
+ {
+ kbd_wait();
+ scancode = inb(KB_DATA);
+ } else if (noblock)
+#else
+ scancode = inb(KB_DATA);
+ else if (noblock)
+#endif
+ return(NOKEY);
+ else
+ goto next_code;
+
+ add_keyboard_randomness(scancode);
+
+ if (cur_console->status & KBD_RAW_MODE)
+ return scancode;
+#if ASYNCH
+ if (scancode == KB_ACK || scancode == KB_RESEND) {
+ kbd_reply = scancode;
+ if (noblock)
+ return(NOKEY);
+ goto next_code;
+ }
+#endif
+ keycode = scancode & 0x7F;
+ switch (esc_flag) {
+ case 0x00: /* normal scancode */
+ switch(scancode) {
+ case 0xB8: /* left alt (compose key) */
+ if (compose) {
+ compose = 0;
+ if (chr > 255) {
+ do_bell(cur_console,
+ BELL_PITCH, BELL_DURATION);
+ chr = 0;
+ }
+ }
+ break;
+ case 0x38:
+ if (!compose) {
+ compose = 1;
+ chr = 0;
+ }
+ break;
+ case 0xE0:
+ case 0xE1:
+ esc_flag = scancode;
+ goto next_code;
+ }
+ break;
+ case 0xE0: /* 0xE0 prefix */
+ esc_flag = 0;
+ switch (keycode) {
+ case 0x1C: /* right enter key */
+ keycode = 0x59;
+ break;
+ case 0x1D: /* right ctrl key */
+ keycode = 0x5A;
+ break;
+ case 0x35: /* keypad divide key */
+ keycode = 0x5B;
+ break;
+ case 0x37: /* print scrn key */
+ keycode = 0x5C;
+ break;
+ case 0x38: /* right alt key (alt gr) */
+ keycode = 0x5D;
+ break;
+ case 0x47: /* grey home key */
+ keycode = 0x5E;
+ break;
+ case 0x48: /* grey up arrow key */
+ keycode = 0x5F;
+ break;
+ case 0x49: /* grey page up key */
+ keycode = 0x60;
+ break;
+ case 0x4B: /* grey left arrow key */
+ keycode = 0x61;
+ break;
+ case 0x4D: /* grey right arrow key */
+ keycode = 0x62;
+ break;
+ case 0x4F: /* grey end key */
+ keycode = 0x63;
+ break;
+ case 0x50: /* grey down arrow key */
+ keycode = 0x64;
+ break;
+ case 0x51: /* grey page down key */
+ keycode = 0x65;
+ break;
+ case 0x52: /* grey insert key */
+ keycode = 0x66;
+ break;
+ case 0x53: /* grey delete key */
+ keycode = 0x67;
+ break;
+
+ /* the following 3 are only used on the MS "Natural" keyboard */
+ case 0x5b: /* left Window key */
+ keycode = 0x69;
+ break;
+ case 0x5c: /* right Window key */
+ keycode = 0x6a;
+ break;
+ case 0x5d: /* menu key */
+ keycode = 0x6b;
+ break;
+ default: /* ignore everything else */
+ goto next_code;
+ }
+ break;
+ case 0xE1: /* 0xE1 prefix */
+ esc_flag = 0;
+ if (keycode == 0x1D)
+ esc_flag = 0x1D;
+ goto next_code;
+ /* NOT REACHED */
+ case 0x1D: /* pause / break */
+ esc_flag = 0;
+ if (keycode != 0x45)
+ goto next_code;
+ keycode = 0x68;
+ break;
+ }
+
+ /* if scroll-lock pressed allow history browsing */
+ if (cur_console->history && cur_console->status & SLKED) {
+ int i;
+
+ cur_console->status &= ~CURSOR_ENABLED;
+ if (!(cur_console->status & BUFFER_SAVED)) {
+ cur_console->status |= BUFFER_SAVED;
+ cur_console->history_save = cur_console->history_head;
+#ifdef PC98
+ cur_console->his_atr_save = cur_console->his_atr_head;
+#endif
+
+ /* copy screen into top of history buffer */
+ for (i=0; i<cur_console->ysize; i++) {
+ bcopyw(cur_console->scr_buf + (cur_console->xsize * i),
+ cur_console->history_head,
+ cur_console->xsize * sizeof(u_short));
+ cur_console->history_head += cur_console->xsize;
+#ifdef PC98
+ bcopyw(cur_console->atr_buf + (cur_console->xsize * i),
+ cur_console->his_atr_head,
+ cur_console->xsize * sizeof(u_short));
+ cur_console->his_atr_head += cur_console->xsize;
+#endif
+ if (cur_console->history_head + cur_console->xsize >
+ cur_console->history + cur_console->history_size)
+#ifdef PC98
+ {
+#endif
+ cur_console->history_head=cur_console->history;
+#ifdef PC98
+ cur_console->his_atr_head=cur_console->his_atr; }
+#endif
+ }
+ cur_console->history_pos = cur_console->history_head;
+#ifdef PC98
+ cur_console->his_atr_pos = cur_console->his_atr_head;
+#endif
+ history_to_screen(cur_console);
+ }
+ switch (scancode) {
+#ifdef PC98
+ case 0x3E: /* home key */
+#else
+ case 0x47: /* home key */
+#endif
+ cur_console->history_pos = cur_console->history_head;
+#ifdef PC98
+ cur_console->his_atr_pos = cur_console->his_atr_head;
+#endif
+ history_to_screen(cur_console);
+ goto next_code;
+
+#ifdef PC98
+ case 0x3F: /* help key */
+#else
+ case 0x4F: /* end key */
+#endif
+ cur_console->history_pos =
+ WRAPHIST(cur_console, cur_console->history_head,
+ cur_console->xsize*cur_console->ysize);
+#ifdef PC98
+ cur_console->his_atr_pos =
+ WRAPHIST_A(cur_console, cur_console->his_atr_head,
+ cur_console->xsize*cur_console->ysize);
+#endif
+ history_to_screen(cur_console);
+ goto next_code;
+
+#ifdef PC98
+ case 0x3A: /* up arrow key */
+#else
+ case 0x48: /* up arrow key */
+#endif
+ if (history_up_line(cur_console))
+ do_bell(cur_console, BELL_PITCH, BELL_DURATION);
+ goto next_code;
+
+#ifdef PC98
+ case 0x3D: /* down arrow key */
+#else
+ case 0x50: /* down arrow key */
+#endif
+ if (history_down_line(cur_console))
+ do_bell(cur_console, BELL_PITCH, BELL_DURATION);
+ goto next_code;
+
+#ifdef PC98
+ case 0x36: /* roll up key */
+#else
+ case 0x49: /* page up key */
+#endif
+ for (i=0; i<cur_console->ysize; i++)
+ if (history_up_line(cur_console)) {
+ do_bell(cur_console, BELL_PITCH, BELL_DURATION);
+ break;
+ }
+ goto next_code;
+
+#ifdef PC98
+ case 0x37: /* roll down key */
+#else
+ case 0x51: /* page down key */
+#endif
+ for (i=0; i<cur_console->ysize; i++)
+ if (history_down_line(cur_console)) {
+ do_bell(cur_console, BELL_PITCH, BELL_DURATION);
+ break;
+ }
+ goto next_code;
+ }
+ }
+
+ if (compose) {
+ switch (scancode) {
+ /* key pressed process it */
+ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
+ chr = (scancode - 0x40) + chr*10;
+ goto next_code;
+ case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
+ chr = (scancode - 0x47) + chr*10;
+ goto next_code;
+ case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
+ chr = (scancode - 0x4E) + chr*10;
+ goto next_code;
+ case 0x52: /* keypad 0 */
+ chr *= 10;
+ goto next_code;
+
+ /* key release, no interest here */
+ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */
+ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */
+ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */
+ case 0xD2: /* keypad 0 */
+ goto next_code;
+
+ case 0x38: /* left alt key */
+ break;
+ default:
+ if (chr) {
+ compose = chr = 0;
+ do_bell(cur_console, BELL_PITCH, BELL_DURATION);
+ goto next_code;
+ }
+ break;
+ }
+ }
+
+ state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0));
+ if ((!agrs && (cur_console->status & ALKED))
+ || (agrs && !(cur_console->status & ALKED)))
+ keycode += ALTGR_OFFSET;
+ key = &key_map.key[keycode];
+ if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED))
+ || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) )
+ state ^= 1;
+
+ /* Check for make/break */
+ action = key->map[state];
+ if (scancode & 0x80) { /* key released */
+ if (key->spcl & 0x80) {
+ switch (action) {
+ case LSH:
+ shfts &= ~1;
+ break;
+ case RSH:
+ shfts &= ~2;
+ break;
+ case LCTR:
+ ctls &= ~1;
+ break;
+ case RCTR:
+ ctls &= ~2;
+ break;
+ case LALT:
+ alts &= ~1;
+ break;
+ case RALT:
+ alts &= ~2;
+ break;
+ case NLK:
+ nlkcnt = 0;
+ break;
+ case CLK:
+#ifdef PC98
+ cur_console->status &= ~CLKED;
+ update_leds(cur_console->status);
+#else
+ clkcnt = 0;
+#endif
+ break;
+ case SLK:
+ slkcnt = 0;
+ break;
+ case ASH:
+ agrs = 0;
+ break;
+ case ALK:
+ alkcnt = 0;
+ break;
+ case META:
+ metas = 0;
+ break;
+ }
+ }
+ if (chr && !compose) {
+ action = chr;
+ chr = 0;
+ return(action);
+ }
+ } else {
+ /* key pressed */
+ if (key->spcl & (0x80>>state)) {
+ switch (action) {
+ /* LOCKING KEYS */
+ case NLK:
+ if (!nlkcnt) {
+ nlkcnt++;
+ if (cur_console->status & NLKED)
+ cur_console->status &= ~NLKED;
+ else
+ cur_console->status |= NLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case CLK:
+#ifdef PC98
+ cur_console->status |= CLKED;
+ update_leds(cur_console->status);
+#else
+ if (!clkcnt) {
+ clkcnt++;
+ if (cur_console->status & CLKED)
+ cur_console->status &= ~CLKED;
+ else
+ cur_console->status |= CLKED;
+ update_leds(cur_console->status);
+ }
+#endif
+ break;
+ case SLK:
+ if (!slkcnt) {
+ slkcnt++;
+ if (cur_console->status & SLKED) {
+ cur_console->status &= ~SLKED;
+ if (cur_console->status & BUFFER_SAVED){
+ int i;
+ u_short *ptr = cur_console->history_save;
+#ifdef PC98
+ u_short *ptr_a = cur_console->his_atr_save;
+#endif
+
+ for (i=0; i<cur_console->ysize; i++) {
+ bcopyw(ptr,
+ cur_console->scr_buf +
+ (cur_console->xsize*i),
+ cur_console->xsize * sizeof(u_short));
+ ptr += cur_console->xsize;
+#ifdef PC98
+ bcopyw(ptr_a,
+ cur_console->atr_buf +
+ (cur_console->xsize*i),
+ cur_console->xsize * sizeof(u_short));
+ ptr_a += cur_console->xsize;
+#endif
+ if (ptr + cur_console->xsize >
+ cur_console->history +
+ cur_console->history_size)
+#ifdef PC98
+ {
+#endif
+ ptr = cur_console->history;
+#ifdef PC98
+ ptr_a = cur_console->his_atr; }
+#endif
+ }
+ cur_console->status &= ~BUFFER_SAVED;
+ cur_console->history_head=cur_console->history_save;
+#ifdef PC98
+ cur_console->his_atr_head=cur_console->his_atr_save;
+#endif
+ cur_console->status |= CURSOR_ENABLED;
+ mark_all(cur_console);
+ }
+ scstart(VIRTUAL_TTY(get_scr_num()));
+ }
+ else
+ cur_console->status |= SLKED;
+ update_leds(cur_console->status);
+ }
+ break;
+ case ALK:
+ if (!alkcnt) {
+ alkcnt++;
+ if (cur_console->status & ALKED)
+ cur_console->status &= ~ALKED;
+ else
+ cur_console->status |= ALKED;
+ update_leds(cur_console->status);
+ }
+ break;
+
+ /* NON-LOCKING KEYS */
+ case NOP:
+ break;
+ case RBT:
+ shutdown_nice();
+ break;
+ case SUSP:
+#if NAPM > 0
+ apm_suspend();
+#endif
+ break;
+
+ case DBG:
+#ifdef DDB /* try to switch to console 0 */
+ if (cur_console->smode.mode == VT_AUTO &&
+ console[0]->smode.mode == VT_AUTO)
+ switch_scr(cur_console, 0);
+ Debugger("manual escape to debugger");
+ return(NOKEY);
+#else
+ printf("No debugger in kernel\n");
+#endif
+ break;
+ case LSH:
+ shfts |= 1;
+ break;
+ case RSH:
+ shfts |= 2;
+ break;
+ case LCTR:
+ ctls |= 1;
+ break;
+ case RCTR:
+ ctls |= 2;
+ break;
+ case LALT:
+ alts |= 1;
+ break;
+ case RALT:
+ alts |= 2;
+ break;
+ case ASH:
+ agrs = 1;
+ break;
+ case META:
+ metas = 1;
+ break;
+ case NEXT:
+ switch_scr(cur_console, (get_scr_num() + 1) % MAXCONS);
+ break;
+ case BTAB:
+ return(BKEY);
+ default:
+ if (action >= F_SCR && action <= L_SCR) {
+ switch_scr(cur_console, action - F_SCR);
+ break;
+ }
+ if (action >= F_FN && action <= L_FN)
+ action |= FKEY;
+ return(action);
+ }
+ }
+ else {
+ if (metas)
+ action |= MKEY;
+ return(action);
+ }
+ }
+ goto next_code;
+}
+
+int
+scmmap(dev_t dev, int offset, int nprot)
+{
+#ifdef PC98
+ if (offset > 0x48000 - PAGE_SIZE)
+#else
+ if (offset > 0x20000 - PAGE_SIZE)
+#endif
+ return -1;
+ return i386_btop((VIDEOMEM + offset));
+}
+
+static void
+kbd_wait(void)
+{
+#ifdef PC98
+ DELAY(30);
+#else
+ int i = 1000;
+
+ while (i--) {
+ if ((inb(KB_STAT) & KB_READY) == 0)
+ break;
+ DELAY (10);
+ }
+#endif
+}
+
+static void
+kbd_cmd(u_char command)
+{
+#ifndef PC98
+ int retry = 5;
+ do {
+ int i = 100000;
+
+ kbd_wait();
+#if ASYNCH
+ kbd_reply = 0;
+ outb(KB_DATA, command);
+ while (i--) {
+ if (kbd_reply == KB_ACK)
+ return;
+ if (kbd_reply == KB_RESEND)
+ break;
+ }
+#else
+ outb(KB_DATA, command);
+ while (i--) {
+ if (inb(KB_STAT) & KB_BUF_FULL) {
+ int val;
+ DELAY(10);
+ val = inb(KB_DATA);
+ if (val == KB_ACK)
+ return;
+ if (val == KB_RESEND)
+ break;
+ }
+ }
+#endif
+ } while (retry--);
+#endif
+}
+
+static void
+set_mode(scr_stat *scp)
+{
+ char *modetable;
+ char special_modetable[64];
+ int font_size;
+
+ if (scp != cur_console)
+ return;
+
+ /* setup video hardware for the given mode */
+#ifdef PC98
+#ifdef LINE30
+ switch (scp->mode) {
+ case M_PC98_80x25: /* VGA TEXT MODES */
+ initialize_gdc(T25_G400);
+ break;
+ case M_PC98_80x30:
+ initialize_gdc(T30_G400);
+ break;
+ default:
+ break;
+ }
+#endif
+ if (scp->status & UNKNOWN_MODE) {
+ while (!(inb(0x60) & 0x20)) {} /* V-SYNC wait */
+ outb(0x62, 0xc); /* text off */
+ outb(0xA2, 0xd); /* graphics on */
+ } else {
+ while (!(inb(0x60) & 0x20)) {} /* V-SYNC wait */
+ outb(0x62, 0xd); /* text on */
+ outb(0xA2, 0xc); /* graphics off */
+ }
+#else
+ switch (scp->mode) {
+ case M_VGA_M80x60:
+ bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64);
+ goto special_80x60;
+
+ case M_VGA_C80x60:
+ bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64);
+special_80x60:
+ special_modetable[2] = 0x08;
+ special_modetable[19] = 0x47;
+ goto special_480l;
+
+ case M_VGA_M80x30:
+ bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64);
+ goto special_80x30;
+
+ case M_VGA_C80x30:
+ bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64);
+special_80x30:
+ special_modetable[19] = 0x4f;
+special_480l:
+ special_modetable[9] |= 0xc0;
+ special_modetable[16] = 0x08;
+ special_modetable[17] = 0x3e;
+ special_modetable[26] = 0xea;
+ special_modetable[28] = 0xdf;
+ special_modetable[31] = 0xe7;
+ special_modetable[32] = 0x04;
+ modetable = special_modetable;
+ goto setup_mode;
+
+ case M_ENH_B80x43:
+ bcopyw(video_mode_ptr+(64*M_ENH_B80x25),&special_modetable, 64);
+ goto special_80x43;
+
+ case M_ENH_C80x43:
+ bcopyw(video_mode_ptr+(64*M_ENH_C80x25),&special_modetable, 64);
+special_80x43:
+ special_modetable[28] = 87;
+ goto special_80x50;
+
+ case M_VGA_M80x50:
+ bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64);
+ goto special_80x50;
+
+ case M_VGA_C80x50:
+ bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64);
+special_80x50:
+ special_modetable[2] = 8;
+ special_modetable[19] = 7;
+ modetable = special_modetable;
+ goto setup_mode;
+
+ case M_VGA_C40x25: case M_VGA_C80x25:
+ case M_VGA_M80x25:
+ case M_B40x25: case M_C40x25:
+ case M_B80x25: case M_C80x25:
+ case M_ENH_B40x25: case M_ENH_C40x25:
+ case M_ENH_B80x25: case M_ENH_C80x25:
+
+ modetable = video_mode_ptr + (scp->mode * 64);
+setup_mode:
+ set_vgaregs(modetable);
+ font_size = *(modetable + 2);
+
+ /* set font type (size) */
+ switch (font_size) {
+ case 0x10:
+ outb(TSIDX, 0x03); outb(TSREG, 0x00); /* font 0 */
+ scp->font = FONT_16;
+ break;
+ case 0x0E:
+ outb(TSIDX, 0x03); outb(TSREG, 0x05); /* font 1 */
+ scp->font = FONT_14;
+ break;
+ default:
+ case 0x08:
+ outb(TSIDX, 0x03); outb(TSREG, 0x0A); /* font 2 */
+ scp->font = FONT_8;
+ break;
+ }
+ if (configuration & CHAR_CURSOR)
+ set_destructive_cursor(scp, TRUE);
+ break;
+
+ case M_BG320: case M_CG320: case M_BG640:
+ case M_CG320_D: case M_CG640_E:
+ case M_CG640x350: case M_ENH_CG640:
+ case M_BG640x480: case M_CG640x480: case M_VGA_CG320:
+
+ set_vgaregs(video_mode_ptr + (scp->mode * 64));
+ break;
+
+ default:
+ /* call user defined function XXX */
+ break;
+ }
+#endif
+
+ /* set border color for this (virtual) console */
+ set_border(scp->border);
+ return;
+}
+
+void
+set_border(int color)
+{
+#ifdef PC98
+ outb(0x6c, color << 4);
+#else
+ inb(crtc_addr+6); /* reset flip-flop */
+ outb(ATC, 0x11); outb(ATC, color);
+ inb(crtc_addr+6); /* reset flip-flop */
+ outb(ATC, 0x20); /* enable Palette */
+#endif
+}
+
+static void
+set_vgaregs(char *modetable)
+{
+#ifndef PC98
+ int i, s = splhigh();
+
+ outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */
+ outb(TSIDX, 0x07); outb(TSREG, 0x00); /* unlock registers */
+ for (i=0; i<4; i++) { /* program sequencer */
+ outb(TSIDX, i+1);
+ outb(TSREG, modetable[i+5]);
+ }
+ outb(MISC, modetable[9]); /* set dot-clock */
+ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */
+ outb(crtc_addr, 0x11);
+ outb(crtc_addr+1, inb(crtc_addr+1) & 0x7F);
+ for (i=0; i<25; i++) { /* program crtc */
+ outb(crtc_addr, i);
+ if (i == 14 || i == 15) /* no hardware cursor */
+ outb(crtc_addr+1, 0xff);
+ else
+ outb(crtc_addr+1, modetable[i+10]);
+ }
+ inb(crtc_addr+6); /* reset flip-flop */
+ for (i=0; i<20; i++) { /* program attribute ctrl */
+ outb(ATC, i);
+ outb(ATC, modetable[i+35]);
+ }
+ for (i=0; i<9; i++) { /* program graph data ctrl */
+ outb(GDCIDX, i);
+ outb(GDCREG, modetable[i+55]);
+ }
+ inb(crtc_addr+6); /* reset flip-flop */
+ outb(ATC ,0x20); /* enable palette */
+ splx(s);
+#endif
+}
+
+static void
+set_font_mode()
+{
+#ifndef PC98
+ /* setup vga for loading fonts (graphics plane mode) */
+ inb(crtc_addr+6);
+ outb(ATC, 0x30); outb(ATC, 0x01);
+#if SLOW_VGA
+ outb(TSIDX, 0x02); outb(TSREG, 0x04);
+ outb(TSIDX, 0x04); outb(TSREG, 0x06);
+ outb(GDCIDX, 0x04); outb(GDCREG, 0x02);
+ outb(GDCIDX, 0x05); outb(GDCREG, 0x00);
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x05);
+#else
+ outw(TSIDX, 0x0402);
+ outw(TSIDX, 0x0604);
+ outw(GDCIDX, 0x0204);
+ outw(GDCIDX, 0x0005);
+ outw(GDCIDX, 0x0506); /* addr = a0000, 64kb */
+#endif
+#endif
+}
+
+static void
+set_normal_mode()
+{
+#ifndef PC98
+ int s = splhigh();
+
+ /* setup vga for normal operation mode again */
+ inb(crtc_addr+6);
+ outb(ATC, 0x30); outb(ATC, 0x0C);
+#if SLOW_VGA
+ outb(TSIDX, 0x02); outb(TSREG, 0x03);
+ outb(TSIDX, 0x04); outb(TSREG, 0x02);
+ outb(GDCIDX, 0x04); outb(GDCREG, 0x00);
+ outb(GDCIDX, 0x05); outb(GDCREG, 0x10);
+ if (crtc_addr == MONO_BASE) {
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */
+ }
+ else {
+ outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */
+ }
+#else
+ outw(TSIDX, 0x0302);
+ outw(TSIDX, 0x0204);
+ outw(GDCIDX, 0x0004);
+ outw(GDCIDX, 0x1005);
+ if (crtc_addr == MONO_BASE)
+ outw(GDCIDX, 0x0A06); /* addr = b0000, 32kb */
+ else
+ outw(GDCIDX, 0x0E06); /* addr = b8000, 32kb */
+#endif
+ splx(s);
+#endif
+}
+
+static void
+copy_font(int operation, int font_type, char* font_image)
+{
+#ifndef PC98
+ int ch, line, segment, fontsize;
+ u_char val;
+
+ switch (font_type) {
+ default:
+ case FONT_8:
+ segment = 0x8000;
+ fontsize = 8;
+ break;
+ case FONT_14:
+ segment = 0x4000;
+ fontsize = 14;
+ break;
+ case FONT_16:
+ segment = 0x0000;
+ fontsize = 16;
+ break;
+ }
+ outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */
+ outb(TSIDX, 0x01); outb(TSREG, val | 0x20);
+ set_font_mode();
+ for (ch=0; ch < 256; ch++)
+ for (line=0; line < fontsize; line++)
+ if (operation)
+ *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line) =
+ font_image[(ch*fontsize)+line];
+ else
+ font_image[(ch*fontsize)+line] =
+ *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line);
+ set_normal_mode();
+ outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); /* enable screen */
+#endif
+}
+
+static void
+set_destructive_cursor(scr_stat *scp, int force)
+{
+#ifndef PC98
+ u_char cursor[32];
+ caddr_t address;
+ int i, font_size;
+ char *font_buffer;
+ static u_char old_saveunder = DEAD_CHAR;
+
+ if (!force && (scp->cursor_saveunder & 0xFF) == old_saveunder)
+ return;
+ old_saveunder = force ? DEAD_CHAR : scp->cursor_saveunder & 0xFF;
+ switch (scp->font) {
+ default:
+ case FONT_8:
+ font_size = 8;
+ font_buffer = font_8;
+ address = (caddr_t)VIDEOMEM + 0x8000;
+ break;
+ case FONT_14:
+ font_size = 14;
+ font_buffer = font_14;
+ address = (caddr_t)VIDEOMEM + 0x4000;
+ break;
+ case FONT_16:
+ font_size = 16;
+ font_buffer = font_16;
+ address = (caddr_t)VIDEOMEM;
+ break;
+ }
+ bcopyw(font_buffer + ((scp->cursor_saveunder & 0xff) * font_size),
+ cursor, font_size);
+ for (i=0; i<32; i++)
+ if ((i >= scp->cursor_start && i <= scp->cursor_end) ||
+ (scp->cursor_start >= font_size && i == font_size - 1))
+ cursor[i] |= 0xff;
+ while (!(inb(crtc_addr+6) & 0x08)) /* wait for vertical retrace */ ;
+ set_font_mode();
+ bcopy(cursor, (char *)pa_to_va(address) + DEAD_CHAR * 32, 32);
+ set_normal_mode();
+#endif
+}
+
+static void
+draw_mouse_image(scr_stat *scp)
+{
+#ifndef PC98
+ caddr_t address;
+ int i, font_size;
+ char *font_buffer;
+ u_short buffer[32];
+ u_short xoffset, yoffset;
+ u_short *crt_pos = Crtat + (scp->mouse_pos - scp->scr_buf);
+
+ xoffset = scp->mouse_xpos % 8;
+ switch (scp->font) {
+ default:
+ case FONT_8:
+ font_size = 8;
+ font_buffer = font_8;
+ yoffset = scp->mouse_ypos % 8;
+ address = (caddr_t)VIDEOMEM + 0x8000;
+ break;
+ case FONT_14:
+ font_size = 14;
+ font_buffer = font_14;
+ yoffset = scp->mouse_ypos % 14;
+ address = (caddr_t)VIDEOMEM + 0x4000;
+ break;
+ case FONT_16:
+ font_size = 16;
+ font_buffer = font_16;
+ yoffset = scp->mouse_ypos % 16;
+ address = (caddr_t)VIDEOMEM;
+ break;
+ }
+
+ bcopyw(font_buffer + ((*(scp->mouse_pos) & 0xff) * font_size),
+ &scp->mouse_cursor[0], font_size);
+ bcopyw(font_buffer + ((*(scp->mouse_pos+1) & 0xff) * font_size),
+ &scp->mouse_cursor[32], font_size);
+ bcopyw(font_buffer + ((*(scp->mouse_pos+scp->xsize) & 0xff) * font_size),
+ &scp->mouse_cursor[64], font_size);
+ bcopyw(font_buffer + ((*(scp->mouse_pos+scp->xsize+1) & 0xff) * font_size),
+ &scp->mouse_cursor[96], font_size);
+
+ for (i=0; i<font_size; i++) {
+ buffer[i] = scp->mouse_cursor[i]<<8 | scp->mouse_cursor[i+32];
+ buffer[i+font_size]=scp->mouse_cursor[i+64]<<8|scp->mouse_cursor[i+96];
+ }
+ for (i=0; i<16; i++) {
+ buffer[i+yoffset] =
+ ( buffer[i+yoffset] & ~(mouse_and_mask[i] >> xoffset))
+ | (mouse_or_mask[i] >> xoffset);
+ }
+ for (i=0; i<font_size; i++) {
+ scp->mouse_cursor[i] = (buffer[i] & 0xff00) >> 8;
+ scp->mouse_cursor[i+32] = buffer[i] & 0xff;
+ scp->mouse_cursor[i+64] = (buffer[i+font_size] & 0xff00) >> 8;
+ scp->mouse_cursor[i+96] = buffer[i+font_size] & 0xff;
+ }
+ if (scp->status & UPDATE_MOUSE) {
+ u_short *ptr = scp->scr_buf + (scp->mouse_oldpos - Crtat);
+
+ if (crt_pos != scp->mouse_oldpos) {
+ *(scp->mouse_oldpos) = scp->mouse_saveunder[0];
+ *(scp->mouse_oldpos+1) = scp->mouse_saveunder[1];
+ *(scp->mouse_oldpos+scp->xsize) = scp->mouse_saveunder[2];
+ *(scp->mouse_oldpos+scp->xsize+1) = scp->mouse_saveunder[3];
+ }
+ scp->mouse_saveunder[0] = *(scp->mouse_pos);
+ scp->mouse_saveunder[1] = *(scp->mouse_pos+1);
+ scp->mouse_saveunder[2] = *(scp->mouse_pos+scp->xsize);
+ scp->mouse_saveunder[3] = *(scp->mouse_pos+scp->xsize+1);
+ if ((scp->cursor_pos == (ptr)) ||
+ (scp->cursor_pos == (ptr+1)) ||
+ (scp->cursor_pos == (ptr+scp->xsize)) ||
+ (scp->cursor_pos == (ptr+scp->xsize+1)) ||
+ (scp->cursor_pos == (scp->mouse_pos)) ||
+ (scp->cursor_pos == (scp->mouse_pos+1)) ||
+ (scp->cursor_pos == (scp->mouse_pos+scp->xsize)) ||
+ (scp->cursor_pos == (scp->mouse_pos+scp->xsize+1)))
+ scp->status &= ~CURSOR_SHOWN;
+ }
+ scp->mouse_oldpos = crt_pos;
+ while (!(inb(crtc_addr+6) & 0x08)) /* wait for vertical retrace */ ;
+ *(crt_pos) = (*(scp->mouse_pos)&0xff00)|0xd0;
+ *(crt_pos+1) = (*(scp->mouse_pos+1)&0xff00)|0xd1;
+ *(crt_pos+scp->xsize) = (*(scp->mouse_pos+scp->xsize)&0xff00)|0xd2;
+ *(crt_pos+scp->xsize+1) = (*(scp->mouse_pos+scp->xsize+1)&0xff00)|0xd3;
+ set_font_mode();
+ bcopy(scp->mouse_cursor, (char *)pa_to_va(address) + 0xd0 * 32, 128);
+ set_normal_mode();
+#endif
+}
+
+static void
+save_palette(void)
+{
+#ifndef PC98
+ int i;
+
+ outb(PALRADR, 0x00);
+ for (i=0x00; i<0x300; i++)
+ palette[i] = inb(PALDATA);
+ inb(crtc_addr+6); /* reset flip/flop */
+#endif
+}
+
+void
+load_palette(void)
+{
+#ifndef PC98
+ int i;
+
+ outb(PIXMASK, 0xFF); /* no pixelmask */
+ outb(PALWADR, 0x00);
+ for (i=0x00; i<0x300; i++)
+ outb(PALDATA, palette[i]);
+ inb(crtc_addr+6); /* reset flip/flop */
+ outb(ATC, 0x20); /* enable palette */
+#endif
+}
+
+static void
+do_bell(scr_stat *scp, int pitch, int duration)
+{
+ if (configuration & VISUAL_BELL) {
+ if (blink_in_progress)
+ return;
+ blink_in_progress = 4;
+ if (scp != cur_console)
+ blink_in_progress += 2;
+ blink_screen(cur_console);
+ timeout((timeout_func_t)blink_screen, cur_console, hz/10);
+ } else {
+ if (scp != cur_console)
+ pitch *= 2;
+ sysbeep(pitch, duration);
+ }
+}
+
+static void
+blink_screen(scr_stat *scp)
+{
+ if (blink_in_progress > 1) {
+#ifdef PC98
+ if (blink_in_progress & 1){
+ fillw(scr_map[0x20],
+ Crtat, scp->xsize * scp->ysize);
+ fillw(at2pc98(kernel_default.std_color),
+ Atrat, scp->xsize * scp->ysize);
+ } else {
+ fillw(scr_map[0x20],
+ Crtat, scp->xsize * scp->ysize);
+ fillw(at2pc98(kernel_default.rev_color),
+ Atrat, scp->xsize * scp->ysize);
+ }
+#else
+ if (blink_in_progress & 1)
+ fillw(kernel_default.std_color | scr_map[0x20],
+ Crtat, scp->xsize * scp->ysize);
+ else
+ fillw(kernel_default.rev_color | scr_map[0x20],
+ Crtat, scp->xsize * scp->ysize);
+#endif
+ blink_in_progress--;
+ timeout((timeout_func_t)blink_screen, scp, hz/10);
+ }
+ else {
+ blink_in_progress = FALSE;
+ mark_all(scp);
+ if (delayed_next_scr)
+ switch_scr(scp, delayed_next_scr - 1);
+ }
+}
+
+#if defined(PC98) && defined(LINE30) /* 30line */
+
+static void master_gdc_cmd(unsigned int cmd)
+{
+ while ( (inb(0x60) & 2) != 0);
+ outb(0x62, cmd);
+}
+
+static void master_gdc_prm(unsigned int pmtr)
+{
+ while ( (inb(0x60) & 2) != 0);
+ outb(0x60, pmtr);
+}
+
+static void master_gdc_word_prm(unsigned int wpmtr)
+{
+ master_gdc_prm(wpmtr & 0x00ff);
+ master_gdc_prm((wpmtr >> 8) & 0x00ff);
+}
+
+static void master_gdc_fifo_empty(void)
+{
+ while ( (inb(0x60) & 4) == 0);
+}
+
+static void master_gdc_wait_vsync(void)
+{
+ while ( (inb(0x60) & 0x20) != 0);
+ while ( (inb(0x60) & 0x20) == 0);
+}
+
+static void gdc_cmd(unsigned int cmd)
+{
+ while ( (inb(0xa0) & 2) != 0);
+ outb( 0xa2, cmd);
+}
+
+static void gdc_prm(unsigned int pmtr)
+{
+ while ( (inb(0xa0) & 2) != 0);
+ outb( 0xa0, pmtr);
+}
+
+static void gdc_word_prm(unsigned int wpmtr)
+{
+ gdc_prm(wpmtr & 0x00ff);
+ gdc_prm((wpmtr >> 8) & 0x00ff);
+}
+
+static void gdc_fifo_empty(void)
+{
+ while ( (inb(0xa0) & 0x04) == 0);
+}
+
+static void gdc_wait_vsync(void)
+{
+ while ( (inb(0xa0) & 0x20) != 0);
+ while ( (inb(0xa0) & 0x20) == 0);
+}
+
+static int check_gdc_clock(void)
+{
+ if ((inb(0x31) & 0x80) == 0){
+ return _5MHZ;
+ } else {
+ return _2_5MHZ;
+ }
+}
+
+static void initialize_gdc(unsigned int mode)
+{
+ /* start 30line initialize */
+ int m_mode,s_mode,gdc_clock;
+ gdc_clock = check_gdc_clock();
+
+ if (mode == T25_G400){
+ m_mode = _25L;
+ }else{
+ m_mode = _30L;
+ }
+
+ s_mode = 2*mode+gdc_clock;
+
+ gdc_INFO = m_mode;
+
+ master_gdc_cmd(_GDC_RESET);
+ master_gdc_cmd(_GDC_MASTER);
+ gdc_cmd(_GDC_RESET);
+ gdc_cmd(_GDC_SLAVE);
+
+ /* GDC Master */
+ master_gdc_cmd(_GDC_SYNC);
+ master_gdc_prm(0x00); /* flush less */ /* text & graph */
+ master_gdc_prm(master_param[m_mode][GDC_CR]);
+ master_gdc_word_prm(((master_param[m_mode][GDC_HFP] << 10)
+ + (master_param[m_mode][GDC_VS] << 5)
+ + master_param[m_mode][GDC_HS]));
+ master_gdc_prm(master_param[m_mode][GDC_HBP]);
+ master_gdc_prm(master_param[m_mode][GDC_VFP]);
+ master_gdc_word_prm(((master_param[m_mode][GDC_VBP] << 10)
+ + (master_param[m_mode][GDC_LF])));
+ master_gdc_fifo_empty();
+ master_gdc_cmd(_GDC_PITCH);
+ master_gdc_prm(MasterPCH);
+ master_gdc_fifo_empty();
+
+ /* GDC slave */
+ gdc_cmd(_GDC_SYNC);
+ gdc_prm(0x06);
+ gdc_prm(slave_param[s_mode][GDC_CR]);
+ gdc_word_prm((slave_param[s_mode][GDC_HFP] << 10)
+ + (slave_param[s_mode][GDC_VS] << 5)
+ + (slave_param[s_mode][GDC_HS]));
+ gdc_prm(slave_param[s_mode][GDC_HBP]);
+ gdc_prm(slave_param[s_mode][GDC_VFP]);
+ gdc_word_prm((slave_param[s_mode][GDC_VBP] << 10)
+ + (slave_param[s_mode][GDC_LF]));
+ gdc_fifo_empty();
+ gdc_cmd(_GDC_PITCH);
+ gdc_prm(SlavePCH[gdc_clock]);
+ gdc_fifo_empty();
+
+ /* set Master GDC scroll param */
+ master_gdc_wait_vsync();
+ master_gdc_wait_vsync();
+ master_gdc_wait_vsync();
+ master_gdc_cmd(_GDC_SCROLL);
+ master_gdc_word_prm(0);
+ master_gdc_word_prm((master_param[m_mode][GDC_LF] << 4) | 0x0000);
+ master_gdc_fifo_empty();
+
+ /* set Slave GDC scroll param */
+ gdc_wait_vsync();
+ gdc_cmd(_GDC_SCROLL);
+ gdc_word_prm(0);
+ if (gdc_clock == _5MHZ){
+ gdc_word_prm((SlaveScrlLF[mode] << 4) | 0x4000);
+ }else{
+ gdc_word_prm(SlaveScrlLF[mode] << 4);
+ }
+ gdc_fifo_empty();
+
+ gdc_word_prm(0);
+ if (gdc_clock == _5MHZ){
+ gdc_word_prm((SlaveScrlLF[mode] << 4) | 0x4000);
+ }else{
+ gdc_word_prm(SlaveScrlLF[mode] << 4);
+ }
+ gdc_fifo_empty();
+
+ /* sync start */
+ gdc_cmd(_GDC_STOP);
+
+ gdc_wait_vsync();
+ gdc_wait_vsync();
+ gdc_wait_vsync();
+
+ master_gdc_cmd(_GDC_START);
+}
+#endif /* 30 line */
+
+#endif /* NSC */
diff --git a/sys/pc98/pc98/syscons.h b/sys/pc98/pc98/syscons.h
new file mode 100644
index 0000000..ee80404
--- /dev/null
+++ b/sys/pc98/pc98/syscons.h
@@ -0,0 +1,265 @@
+/*-
+ * Copyright (c) 1995 Sen Schmidt
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * $Id: syscons.h,v 1.15 1996/01/30 22:56:11 mpp Exp $
+ */
+
+#ifndef _PC98_PC98_SYSCONS_H_
+#define _PC98_PC98_SYSCONS_H_
+
+/* vm things */
+#define ISMAPPED(pa, width) \
+ (((pa) <= (u_long)0x1000 - (width)) \
+ || ((pa) >= 0xa0000 && (pa) <= 0x100000 - (width)))
+#define pa_to_va(pa) (KERNBASE + (pa)) /* works if ISMAPPED(pa...) */
+
+/* printable chars */
+#define PRINTABLE(ch) ((ch) > 0x1b || ((ch) > 0x0d && (ch) < 0x1b) \
+ || (ch) < 0x07)
+
+/* macros for "intelligent" screen update */
+#define mark_for_update(scp, x) {\
+ if ((x) < scp->start) scp->start = (x);\
+ else if ((x) > scp->end) scp->end = (x);\
+ }
+#define mark_all(scp) {\
+ scp->start = 0;\
+ scp->end = scp->xsize * scp->ysize;\
+ }
+
+/* status flags */
+#define LOCK_KEY_MASK 0x0000F
+#define LED_MASK 0x00007
+#define UNKNOWN_MODE 0x00010
+#define KBD_RAW_MODE 0x00020
+#define SWITCH_WAIT_REL 0x00040
+#define SWITCH_WAIT_ACQ 0x00080
+#define BUFFER_SAVED 0x00100
+#define CURSOR_ENABLED 0x00200
+#define CURSOR_SHOWN 0x00400
+#define MOUSE_ENABLED 0x00800
+#define UPDATE_MOUSE 0x01000
+
+/* configuration flags */
+#define VISUAL_BELL 0x00001
+#define BLINK_CURSOR 0x00002
+#define CHAR_CURSOR 0x00004
+
+/* attribute flags */
+#define NORMAL_ATTR 0x00
+#define BLINK_ATTR 0x01
+#define BOLD_ATTR 0x02
+#define UNDERLINE_ATTR 0x04
+#define REVERSE_ATTR 0x08
+#define FOREGROUND_CHANGED 0x10
+#define BACKGROUND_CHANGED 0x20
+
+/* video hardware memory addresses */
+#define VIDEOMEM 0x000A0000
+
+/* misc defines */
+#define FALSE 0
+#define TRUE 1
+#define MAX_ESC_PAR 5
+#define LOAD 1
+#define SAVE 0
+#define COL 80
+#define ROW 25
+#define BELL_DURATION 5
+#ifdef PC98
+#define UJIS 0
+#define SJIS 1
+#ifndef AUTO_CLOCK
+#ifndef PC98_8M
+#define BELL_PITCH 1678
+#define TIMER_FREQ 2457600 /* should be in pc98.h */
+#else
+#define BELL_PITCH 1339
+#define TIMER_FREQ 1996800 /* should be in pc98.h */
+#endif
+#else /* AUTO_CLOCK */
+static unsigned int BELL_PITCH = 1678;
+static unsigned int TIMER_FREQ = 2457600;
+#endif /* AUTO_CLOCK */
+#else /* IBM-PC */
+#define BELL_PITCH 800
+#define TIMER_FREQ 1193182 /* should be in isa.h */
+#endif
+
+#define CONSOLE_BUFSIZE 1024
+#define PCBURST 128
+#define FONT_8 0x001
+#define FONT_14 0x002
+#define FONT_16 0x004
+#define HISTORY_SIZE 100*80
+
+/* defines related to hardware addresses */
+#ifdef PC98
+#define TEXT_GDC IO_GDC1 /* 0x60 */
+#define TEXT_VRAM (KERNBASE+0xA0000)
+#define ATTR_OFFSET 0x1000
+#else /* IBM */
+#define MONO_BASE 0x3B4 /* crt controller base mono */
+#define COLOR_BASE 0x3D4 /* crt controller base color */
+#define MISC 0x3C2 /* misc output register */
+#define ATC IO_VGA+0x00 /* attribute controller */
+#define TSIDX IO_VGA+0x04 /* timing sequencer idx */
+#define TSREG IO_VGA+0x05 /* timing sequencer data */
+#define PIXMASK IO_VGA+0x06 /* pixel write mask */
+#define PALRADR IO_VGA+0x07 /* palette read address */
+#define PALWADR IO_VGA+0x08 /* palette write address */
+#define PALDATA IO_VGA+0x09 /* palette data register */
+#define GDCIDX IO_VGA+0x0E /* graph data controller idx */
+#define GDCREG IO_VGA+0x0F /* graph data controller data */
+#endif
+
+/* special characters */
+#define cntlc 0x03
+#define cntld 0x04
+#define bs 0x08
+#define lf 0x0a
+#define cr 0x0d
+#define del 0x7f
+
+#define DEAD_CHAR 0x07 /* char used for cursor */
+
+typedef struct term_stat {
+ int esc; /* processing escape sequence */
+ int num_param; /* # of parameters to ESC */
+ int last_param; /* last parameter # */
+ int param[MAX_ESC_PAR]; /* contains ESC parameters */
+ int cur_attr; /* current hardware attributes word */
+ int attr_mask; /* current logical attributes mask */
+ int cur_color; /* current hardware color */
+ int std_color; /* normal hardware color */
+ int rev_color; /* reverse hardware color */
+} term_stat;
+
+typedef struct scr_stat {
+ u_short *scr_buf; /* buffer when off screen */
+#ifdef PC98
+ u_short *atr_buf; /* buffer when off screen */
+ u_short *cursor_atr; /* cursor address (attribute)*/
+#endif
+ int xpos; /* current X position */
+ int ypos; /* current Y position */
+ int xsize; /* X size */
+ int ysize; /* Y size */
+ int start; /* modified area start */
+ int end; /* modified area end */
+ term_stat term; /* terminal emulation stuff */
+ int status; /* status (bitfield) */
+ u_short *cursor_pos; /* cursor buffer position */
+ u_short cursor_saveunder; /* saved chars under cursor */
+ char cursor_start; /* cursor start line # */
+ char cursor_end; /* cursor end line # */
+ u_short *mouse_pos; /* mouse buffer position */
+ u_short *mouse_oldpos; /* mouse old buffer position */
+ u_short mouse_saveunder[4]; /* saved chars under mouse */
+ short mouse_xpos; /* mouse x coordinate */
+ short mouse_ypos; /* mouse y coordinate */
+ u_char mouse_cursor[128]; /* mouse cursor bitmap store */
+ u_short bell_duration;
+ u_short bell_pitch;
+ u_char border; /* border color */
+ u_char mode; /* mode */
+ u_char font; /* font on this screen */
+ pid_t pid; /* pid of controlling proc */
+ struct proc *proc; /* proc* of controlling proc */
+ struct vt_mode smode; /* switch mode */
+ u_short *history; /* circular history buffer */
+ u_short *history_head; /* current head position */
+ u_short *history_pos; /* position shown on screen */
+ u_short *history_save; /* save area index */
+#ifdef PC98
+ u_short *his_atr; /* history buffer (attribute)*/
+ u_short *his_atr_head; /* current head position */
+ u_short *his_atr_pos; /* position shown on screen */
+ u_short *his_atr_save; /* save area index */
+#endif
+ int history_size; /* size of history buffer */
+ struct apmhook r_hook; /* reconfiguration support */
+#ifdef KANJI
+ u_char kanji_1st_char;
+ u_char kanji_type; /* 0: ASCII CODE 1: HANKAKU ? */
+ /* 2: SHIFT JIS 4: EUC */
+ /* 0x10: JIS HANKAKU 0x20: JIS */
+#endif
+} scr_stat;
+
+typedef struct default_attr {
+ int std_color; /* normal hardware color */
+ int rev_color; /* reverse hardware color */
+} default_attr;
+
+/* function prototypes */
+static void scinit(void);
+static u_int scgetc(int noblock);
+static scr_stat *get_scr_stat(dev_t dev);
+static scr_stat *alloc_scp(void);
+static void init_scp(scr_stat *scp);
+static int get_scr_num(void);
+static void scrn_timer(void);
+static void clear_screen(scr_stat *scp);
+static int switch_scr(scr_stat *scp, u_int next_scr);
+static void exchange_scr(void);
+#ifdef PC98
+static void move_crsr(scr_stat *scp, int x, int y);
+#else
+static inline void move_crsr(scr_stat *scp, int x, int y);
+#endif
+static void scan_esc(scr_stat *scp, u_char c);
+#ifdef PC98
+static void draw_cursor(scr_stat *scp, int show);
+#else
+static inline void draw_cursor(scr_stat *scp, int show);
+#endif
+static void ansi_put(scr_stat *scp, u_char *buf, int len);
+static u_char *get_fstr(u_int c, u_int *len);
+static void update_leds(int which);
+static void history_to_screen(scr_stat *scp);
+static int history_up_line(scr_stat *scp);
+static int history_down_line(scr_stat *scp);
+static void kbd_wait(void);
+static void kbd_cmd(u_char command);
+static void set_mode(scr_stat *scp);
+ void set_border(int color);
+static void set_vgaregs(char *modetable);
+static void set_font_mode(void);
+static void set_normal_mode(void);
+static void copy_font(int operation, int font_type, char* font_image);
+static void set_destructive_cursor(scr_stat *scp, int force);
+static void draw_mouse_image(scr_stat *scp);
+static void save_palette(void);
+ void load_palette(void);
+static void do_bell(scr_stat *scp, int pitch, int duration);
+static void blink_screen(scr_stat *scp);
+#ifdef PC98
+unsigned int at2pc98(unsigned int attr);
+#endif
+
+#endif /* !_I386_ISA_SYSCONS_H_ */
diff --git a/sys/pc98/pc98/timerreg.h b/sys/pc98/pc98/timerreg.h
new file mode 100644
index 0000000..3ab9ae8
--- /dev/null
+++ b/sys/pc98/pc98/timerreg.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1993 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: Header: timerreg.h,v 1.2 93/02/28 15:08:58 mccanne Exp
+ * timerreg.h,v 1.2 1993/10/16 13:46:26 rgrimes Exp
+ */
+
+/*
+ *
+ * Register definitions for the Intel 8253 Programmable Interval Timer.
+ *
+ * This chip has three independent 16-bit down counters that can be
+ * read on the fly. There are three mode registers and three countdown
+ * registers. The countdown registers are addressed directly, via the
+ * first three I/O ports. The three mode registers are accessed via
+ * the fourth I/O port, with two bits in the mode byte indicating the
+ * register. (Why are hardware interfaces always so braindead?).
+ *
+ * To write a value into the countdown register, the mode register
+ * is first programmed with a command indicating the which byte of
+ * the two byte register is to be modified. The three possibilities
+ * are load msb (TMR_MR_MSB), load lsb (TMR_MR_LSB), or load lsb then
+ * msb (TMR_MR_BOTH).
+ *
+ * To read the current value ("on the fly") from the countdown register,
+ * you write a "latch" command into the mode register, then read the stable
+ * value from the corresponding I/O port. For example, you write
+ * TMR_MR_LATCH into the corresponding mode register. Presumably,
+ * after doing this, a write operation to the I/O port would result
+ * in undefined behavior (but hopefully not fry the chip).
+ * Reading in this manner has no side effects.
+ *
+ * The outputs of the three timers are connected as follows:
+ *
+ * timer 0 -> irq 0
+ * timer 1 -> speaker (via keyboard controller)
+ * timer 2 -> RS232C
+ *
+ * Timer 0 is used to call hardclock.
+ * Timer 1 is used to generate console beeps.
+ */
+
+/*
+ * Macros for specifying values to be written into a mode register.
+ */
+#define TIMER_CNTR0 0x071 /* timer 0 counter port */
+#define TIMER_CNTR1 0x3fdb /* timer 1 counter port */
+#define TIMER_CNTR2 0x075 /* timer 2 counter port */
+#define TIMER_MODE 0x077 /* timer mode port */
+#define TIMER_SEL0 0x00 /* select counter 0 */
+#define TIMER_SEL1 0x40 /* select counter 1 */
+#define TIMER_SEL2 0x80 /* select counter 2 */
+#define TIMER_INTTC 0x00 /* mode 0, intr on terminal cnt */
+#define TIMER_ONESHOT 0x02 /* mode 1, one shot */
+#define TIMER_RATEGEN 0x04 /* mode 2, rate generator */
+#define TIMER_SQWAVE 0x06 /* mode 3, square wave */
+#define TIMER_SWSTROBE 0x08 /* mode 4, s/w triggered strobe */
+#define TIMER_HWSTROBE 0x0a /* mode 5, h/w triggered strobe */
+#define TIMER_LATCH 0x00 /* latch counter for reading */
+#define TIMER_LSB 0x10 /* r/w counter LSB */
+#define TIMER_MSB 0x20 /* r/w counter MSB */
+#define TIMER_16BIT 0x30 /* r/w counter 16 bits, LSB first */
+#define TIMER_BCD 0x01 /* count in BCD */
+
diff --git a/sys/pc98/pc98/vector.s b/sys/pc98/pc98/vector.s
new file mode 100644
index 0000000..d0d578a
--- /dev/null
+++ b/sys/pc98/pc98/vector.s
@@ -0,0 +1,306 @@
+/*
+ * from: vector.s, 386BSD 0.1 unknown origin
+ * $Id: vector.s,v 1.20 1996/05/31 01:08:08 peter Exp $
+ */
+
+#include "opt_auto_eoi.h"
+
+/*
+ * modified for PC98
+ * vector.s,v 1.2 1994/03/14 09:44:21 kakefuda Exp
+ */
+
+#ifdef PC98
+#include <pc98/pc98/icu.h>
+#include <pc98/pc98/pc98.h>
+#else
+#include <i386/isa/icu.h>
+#include <i386/isa/isa.h>
+#endif
+
+#define ICU_EOI 0x20 /* XXX - define elsewhere */
+
+#define IRQ_BIT(irq_num) (1 << ((irq_num) % 8))
+#define IRQ_BYTE(irq_num) ((irq_num) / 8)
+
+#ifdef AUTO_EOI_1
+#define ENABLE_ICU1 /* use auto-EOI to reduce i/o */
+#define OUTB_ICU1
+#else
+#define ENABLE_ICU1 \
+ movb $ICU_EOI,%al ; /* as soon as possible send EOI ... */ \
+ OUTB_ICU1 /* ... to clear in service bit */
+#define OUTB_ICU1 \
+ outb %al,$IO_ICU1
+#endif
+
+#ifdef AUTO_EOI_2
+/*
+ * The data sheet says no auto-EOI on slave, but it sometimes works.
+ */
+#define ENABLE_ICU1_AND_2 ENABLE_ICU1
+#else
+#define ENABLE_ICU1_AND_2 \
+ movb $ICU_EOI,%al ; /* as above */ \
+ outb %al,$IO_ICU2 ; /* but do second icu first ... */ \
+ OUTB_ICU1 /* ... then first icu (if !AUTO_EOI_1) */
+#endif
+
+#ifdef FAST_INTR_HANDLER_USES_ES
+#define ACTUALLY_PUSHED 1
+#define MAYBE_MOVW_AX_ES movl %ax,%es
+#define MAYBE_POPL_ES popl %es
+#define MAYBE_PUSHL_ES pushl %es
+#else
+/*
+ * We can usually skip loading %es for fastintr handlers. %es should
+ * only be used for string instructions, and fastintr handlers shouldn't
+ * do anything slow enough to justify using a string instruction.
+ */
+#define ACTUALLY_PUSHED 0
+#define MAYBE_MOVW_AX_ES
+#define MAYBE_POPL_ES
+#define MAYBE_PUSHL_ES
+#endif
+
+/*
+ * Macros for interrupt interrupt entry, call to handler, and exit.
+ *
+ * XXX - the interrupt frame is set up to look like a trap frame. This is
+ * usually a waste of time. The only interrupt handlers that want a frame
+ * are the clock handler (it wants a clock frame), the npx handler (it's
+ * easier to do right all in assembler). The interrupt return routine
+ * needs a trap frame for rare AST's (it could easily convert the frame).
+ * The direct costs of setting up a trap frame are two pushl's (error
+ * code and trap number), an addl to get rid of these, and pushing and
+ * popping the call-saved regs %esi, %edi and %ebp twice, The indirect
+ * costs are making the driver interface nonuniform so unpending of
+ * interrupts is more complicated and slower (call_driver(unit) would
+ * be easier than ensuring an interrupt frame for all handlers. Finally,
+ * there are some struct copies in the npx handler and maybe in the clock
+ * handler that could be avoided by working more with pointers to frames
+ * instead of frames.
+ *
+ * XXX - should we do a cld on every system entry to avoid the requirement
+ * for scattered cld's?
+ *
+ * Coding notes for *.s:
+ *
+ * If possible, avoid operations that involve an operand size override.
+ * Word-sized operations might be smaller, but the operand size override
+ * makes them slower on on 486's and no faster on 386's unless perhaps
+ * the instruction pipeline is depleted. E.g.,
+ *
+ * Use movl to seg regs instead of the equivalent but more descriptive
+ * movw - gas generates an irelevant (slower) operand size override.
+ *
+ * Use movl to ordinary regs in preference to movw and especially
+ * in preference to movz[bw]l. Use unsigned (long) variables with the
+ * top bits clear instead of unsigned short variables to provide more
+ * opportunities for movl.
+ *
+ * If possible, use byte-sized operations. They are smaller and no slower.
+ *
+ * Use (%reg) instead of 0(%reg) - gas generates larger code for the latter.
+ *
+ * If the interrupt frame is made more flexible, INTR can push %eax first
+ * and decide the ipending case with less overhead, e.g., by avoiding
+ * loading segregs.
+ */
+
+#define FAST_INTR(irq_num, vec_name, enable_icus) \
+ .text ; \
+ SUPERALIGN_TEXT ; \
+IDTVEC(vec_name) ; \
+ pushl %eax ; /* save only call-used registers */ \
+ pushl %ecx ; \
+ pushl %edx ; \
+ pushl %ds ; \
+ MAYBE_PUSHL_ES ; \
+ movl $KDSEL,%eax ; \
+ movl %ax,%ds ; \
+ MAYBE_MOVW_AX_ES ; \
+ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \
+ pushl _intr_unit + (irq_num) * 4 ; \
+ call *_intr_handler + (irq_num) * 4 ; /* do the work ASAP */ \
+ enable_icus ; /* (re)enable ASAP (helps edge trigger?) */ \
+ addl $4,%esp ; \
+ incl _cnt+V_INTR ; /* book-keeping can wait */ \
+ movl _intr_countp + (irq_num) * 4,%eax ; \
+ incl (%eax) ; \
+ movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \
+ notl %eax ; \
+ andl _ipending,%eax ; \
+ jne 1f ; /* yes, handle them */ \
+ MEXITCOUNT ; \
+ MAYBE_POPL_ES ; \
+ popl %ds ; \
+ popl %edx ; \
+ popl %ecx ; \
+ popl %eax ; \
+ iret ; \
+; \
+ ALIGN_TEXT ; \
+1: ; \
+ movl _cpl,%eax ; \
+ movl $HWI_MASK|SWI_MASK,_cpl ; /* limit nesting ... */ \
+ sti ; /* ... to do this as early as possible */ \
+ MAYBE_POPL_ES ; /* discard most of thin frame ... */ \
+ popl %ecx ; /* ... original %ds ... */ \
+ popl %edx ; \
+ xchgl %eax,4(%esp) ; /* orig %eax; save cpl */ \
+ pushal ; /* build fat frame (grrr) ... */ \
+ pushl %ecx ; /* ... actually %ds ... */ \
+ pushl %es ; \
+ movl $KDSEL,%eax ; \
+ movl %ax,%es ; \
+ movl (2+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \
+ movl %ecx,(2+6)*4(%esp) ; /* ... to fat frame ... */ \
+ movl (2+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \
+ pushl %eax ; \
+ subl $4,%esp ; /* junk for unit number */ \
+ incb _intr_nesting_level ; \
+ MEXITCOUNT ; \
+ jmp _doreti
+
+#define INTR(irq_num, vec_name, icu, enable_icus, reg) \
+ .text ; \
+ SUPERALIGN_TEXT ; \
+IDTVEC(vec_name) ; \
+ pushl $0 ; /* dummy error code */ \
+ pushl $0 ; /* dummy trap type */ \
+ pushal ; \
+ pushl %ds ; /* save our data and extra segments ... */ \
+ pushl %es ; \
+ movl $KDSEL,%eax ; /* ... and reload with kernel's own ... */ \
+ movl %ax,%ds ; /* ... early for obsolete reasons */ \
+ movl %ax,%es ; \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ orb $IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ outb %al,$icu+2 ; \
+ enable_icus ; \
+ incl _cnt+V_INTR ; /* tally interrupts */ \
+ movl _cpl,%eax ; \
+ testb $IRQ_BIT(irq_num),%reg ; \
+ jne 2f ; \
+ incb _intr_nesting_level ; \
+__CONCAT(Xresume,irq_num): ; \
+ FAKE_MCOUNT(12*4(%esp)) ; /* XXX late to avoid double count */ \
+ movl _intr_countp + (irq_num) * 4,%eax ; \
+ incl (%eax) ; \
+ movl _cpl,%eax ; \
+ pushl %eax ; \
+ pushl _intr_unit + (irq_num) * 4 ; \
+ orl _intr_mask + (irq_num) * 4,%eax ; \
+ movl %eax,_cpl ; \
+ sti ; \
+ call *_intr_handler + (irq_num) * 4 ; \
+ cli ; /* must unmask _imen and icu atomically */ \
+ movb _imen + IRQ_BYTE(irq_num),%al ; \
+ andb $~IRQ_BIT(irq_num),%al ; \
+ movb %al,_imen + IRQ_BYTE(irq_num) ; \
+ outb %al,$icu+2 ; \
+ sti ; /* XXX _doreti repeats the cli/sti */ \
+ MEXITCOUNT ; \
+ /* We could usually avoid the following jmp by inlining some of */ \
+ /* _doreti, but it's probably better to use less cache. */ \
+ jmp _doreti ; \
+; \
+ ALIGN_TEXT ; \
+2: ; \
+ /* XXX skip mcounting here to avoid double count */ \
+ orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \
+ popl %es ; \
+ popl %ds ; \
+ popal ; \
+ addl $4+4,%esp ; \
+ iret
+
+MCOUNT_LABEL(bintr)
+ FAST_INTR(0,fastintr0, ENABLE_ICU1)
+ FAST_INTR(1,fastintr1, ENABLE_ICU1)
+ FAST_INTR(2,fastintr2, ENABLE_ICU1)
+ FAST_INTR(3,fastintr3, ENABLE_ICU1)
+ FAST_INTR(4,fastintr4, ENABLE_ICU1)
+ FAST_INTR(5,fastintr5, ENABLE_ICU1)
+ FAST_INTR(6,fastintr6, ENABLE_ICU1)
+ FAST_INTR(7,fastintr7, ENABLE_ICU1)
+ FAST_INTR(8,fastintr8, ENABLE_ICU1_AND_2)
+ FAST_INTR(9,fastintr9, ENABLE_ICU1_AND_2)
+ FAST_INTR(10,fastintr10, ENABLE_ICU1_AND_2)
+ FAST_INTR(11,fastintr11, ENABLE_ICU1_AND_2)
+ FAST_INTR(12,fastintr12, ENABLE_ICU1_AND_2)
+ FAST_INTR(13,fastintr13, ENABLE_ICU1_AND_2)
+ FAST_INTR(14,fastintr14, ENABLE_ICU1_AND_2)
+ FAST_INTR(15,fastintr15, ENABLE_ICU1_AND_2)
+ INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al)
+ INTR(1,intr1, IO_ICU1, ENABLE_ICU1, al)
+ INTR(2,intr2, IO_ICU1, ENABLE_ICU1, al)
+ INTR(3,intr3, IO_ICU1, ENABLE_ICU1, al)
+ INTR(4,intr4, IO_ICU1, ENABLE_ICU1, al)
+ INTR(5,intr5, IO_ICU1, ENABLE_ICU1, al)
+ INTR(6,intr6, IO_ICU1, ENABLE_ICU1, al)
+ INTR(7,intr7, IO_ICU1, ENABLE_ICU1, al)
+ INTR(8,intr8, IO_ICU2, ENABLE_ICU1_AND_2, ah)
+ INTR(9,intr9, IO_ICU2, ENABLE_ICU1_AND_2, ah)
+ INTR(10,intr10, IO_ICU2, ENABLE_ICU1_AND_2, ah)
+ INTR(11,intr11, IO_ICU2, ENABLE_ICU1_AND_2, ah)
+ INTR(12,intr12, IO_ICU2, ENABLE_ICU1_AND_2, ah)
+ INTR(13,intr13, IO_ICU2, ENABLE_ICU1_AND_2, ah)
+ INTR(14,intr14, IO_ICU2, ENABLE_ICU1_AND_2, ah)
+ INTR(15,intr15, IO_ICU2, ENABLE_ICU1_AND_2, ah)
+MCOUNT_LABEL(eintr)
+
+ .data
+ihandlers: /* addresses of interrupt handlers */
+ /* actually resumption addresses for HWI's */
+ .long Xresume0, Xresume1, Xresume2, Xresume3
+ .long Xresume4, Xresume5, Xresume6, Xresume7
+ .long Xresume8, Xresume9, Xresume10, Xresume11
+ .long Xresume12, Xresume13, Xresume14, Xresume15
+ .long swi_tty, swi_net, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0, 0, 0, 0, _softclock, swi_ast
+imasks: /* masks for interrupt handlers */
+ .space NHWI*4 /* padding; HWI masks are elsewhere */
+ .long SWI_TTY_MASK, SWI_NET_MASK, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0, 0, 0, 0, SWI_CLOCK_MASK, SWI_AST_MASK
+ .globl _intr_nesting_level
+_intr_nesting_level:
+ .byte 0
+ .space 3
+
+/*
+ * Interrupt counters and names. The format of these and the label names
+ * must agree with what vmstat expects. The tables are indexed by device
+ * ids so that we don't have to move the names around as devices are
+ * attached.
+ */
+#include "vector.h"
+ .globl _intrcnt, _eintrcnt
+_intrcnt:
+ .space (NR_DEVICES + ICU_LEN) * 4
+_eintrcnt:
+
+ .globl _intrnames, _eintrnames
+_intrnames:
+ .ascii DEVICE_NAMES
+ .asciz "stray irq0"
+ .asciz "stray irq1"
+ .asciz "stray irq2"
+ .asciz "stray irq3"
+ .asciz "stray irq4"
+ .asciz "stray irq5"
+ .asciz "stray irq6"
+ .asciz "stray irq7"
+ .asciz "stray irq8"
+ .asciz "stray irq9"
+ .asciz "stray irq10"
+ .asciz "stray irq11"
+ .asciz "stray irq12"
+ .asciz "stray irq13"
+ .asciz "stray irq14"
+ .asciz "stray irq15"
+_eintrnames:
+
+ .text
diff --git a/sys/pc98/pc98/wcd.c b/sys/pc98/pc98/wcd.c
new file mode 100644
index 0000000..675fd55
--- /dev/null
+++ b/sys/pc98/pc98/wcd.c
@@ -0,0 +1,1241 @@
+/*
+ * IDE CD-ROM driver for FreeBSD.
+ * Supports ATAPI-compatible drives.
+ *
+ * Copyright (C) 1995 Cronyx Ltd.
+ * Author Serge Vakulenko, <vak@cronyx.ru>
+ *
+ * This software is distributed with NO WARRANTIES, not even the implied
+ * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Authors grant any other persons or organisations permission to use
+ * or modify this software as long as this message is kept with the software,
+ * all derivative works or modified versions.
+ *
+ * Version 1.9, Mon Oct 9 20:27:42 MSK 1995
+ */
+
+#include "wdc.h"
+#include "wcd.h"
+#if NWCD > 0 && NWDC > 0 && defined (ATAPI)
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/ioctl.h>
+#include <sys/devconf.h>
+#include <sys/disklabel.h>
+#include <sys/cdio.h>
+#include <sys/conf.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /*DEVFS*/
+
+#include <machine/cpufunc.h>
+
+#ifdef PC98
+#include <pc98/pc98/atapi.h>
+#else
+#include <i386/isa/atapi.h>
+#endif
+
+static d_open_t wcdropen;
+static d_open_t wcdbopen;
+static d_close_t wcdrclose;
+static d_close_t wcdbclose;
+static d_ioctl_t wcdioctl;
+static d_strategy_t wcdstrategy;
+
+#define CDEV_MAJOR 69
+#define BDEV_MAJOR 19
+extern struct cdevsw wcd_cdevsw;
+static struct bdevsw wcd_bdevsw =
+ { wcdbopen, wcdbclose, wcdstrategy, wcdioctl, /*19*/
+ nodump, nopsize, 0, "wcd", &wcd_cdevsw, -1 };
+
+static struct cdevsw wcd_cdevsw =
+ { wcdropen, wcdrclose, rawread, nowrite, /*69*/
+ wcdioctl, nostop, nullreset, nodevtotty,/* atapi */
+ seltrue, nommap, wcdstrategy, "wcd",
+ &wcd_bdevsw, -1 };
+
+#ifndef ATAPI_STATIC
+static
+#endif
+int wcdattach(struct atapi*, int, struct atapi_params*, int, struct kern_devconf*);
+
+#define NUNIT (NWDC*2) /* Max. number of devices */
+#define UNIT(d) ((minor(d) >> 3) & 3) /* Unit part of minor device number */
+#define SECSIZE 2048 /* CD-ROM sector size in bytes */
+
+#define F_BOPEN 0x0001 /* The block device is opened */
+#define F_MEDIA_CHANGED 0x0002 /* The media have changed since open */
+#define F_DEBUG 0x0004 /* Print debug info */
+
+/*
+ * Disc table of contents.
+ */
+#define MAXTRK 99
+struct toc {
+ struct ioc_toc_header hdr;
+ struct cd_toc_entry tab[MAXTRK+1]; /* One extra for the leadout */
+};
+
+/*
+ * Volume size info.
+ */
+struct volinfo {
+ u_long volsize; /* Volume size in blocks */
+ u_long blksize; /* Block size in bytes */
+} info;
+
+/*
+ * Current subchannel status.
+ */
+struct subchan {
+ u_char void0;
+ u_char audio_status;
+ u_short data_length;
+ u_char data_format;
+ u_char control;
+ u_char track;
+ u_char indx;
+ u_long abslba;
+ u_long rellba;
+};
+
+/*
+ * Audio Control Parameters Page
+ */
+struct audiopage {
+ /* Mode data header */
+ u_short data_length;
+ u_char medium_type;
+ u_char reserved1[5];
+
+ /* Audio control page */
+ u_char page_code;
+#define AUDIO_PAGE 0x0e
+#define AUDIO_PAGE_MASK 0x4e /* changeable values */
+ u_char param_len;
+ u_char flags;
+#define CD_PA_SOTC 0x02 /* mandatory */
+#define CD_PA_IMMED 0x04 /* always 1 */
+ u_char reserved3[3];
+ u_short lb_per_sec;
+ struct port_control {
+ u_char channels : 4;
+#define CHANNEL_0 1 /* mandatory */
+#define CHANNEL_1 2 /* mandatory */
+#define CHANNEL_2 4 /* optional */
+#define CHANNEL_3 8 /* optional */
+ u_char volume;
+ } port[4];
+};
+
+/*
+ * CD-ROM Capabilities and Mechanical Status Page
+ */
+struct cappage {
+ /* Mode data header */
+ u_short data_length;
+ u_char medium_type;
+#define MDT_UNKNOWN 0x00
+#define MDT_DATA_120 0x01
+#define MDT_AUDIO_120 0x02
+#define MDT_COMB_120 0x03
+#define MDT_PHOTO_120 0x04
+#define MDT_DATA_80 0x05
+#define MDT_AUDIO_80 0x06
+#define MDT_COMB_80 0x07
+#define MDT_PHOTO_80 0x08
+#define MDT_NO_DISC 0x70
+#define MDT_DOOR_OPEN 0x71
+#define MDT_FMT_ERROR 0x72
+ u_char reserved1[5];
+
+ /* Capabilities page */
+ u_char page_code;
+#define CAP_PAGE 0x2a
+ u_char param_len;
+ u_char reserved2[2];
+
+ u_char audio_play : 1; /* audio play supported */
+ u_char composite : 1; /* composite audio/video supported */
+ u_char dport1 : 1; /* digital audio on port 1 */
+ u_char dport2 : 1; /* digital audio on port 2 */
+ u_char mode2_form1 : 1; /* mode 2 form 1 (XA) read */
+ u_char mode2_form2 : 1; /* mode 2 form 2 format */
+ u_char multisession : 1; /* multi-session photo-CD */
+ u_char : 1;
+ u_char cd_da : 1; /* audio-CD read supported */
+ u_char cd_da_stream : 1; /* CD-DA streaming */
+ u_char rw : 1; /* combined R-W subchannels */
+ u_char rw_corr : 1; /* R-W subchannel data corrected */
+ u_char c2 : 1; /* C2 error pointers supported */
+ u_char isrc : 1; /* can return the ISRC info */
+ u_char upc : 1; /* can return the catalog number UPC */
+ u_char : 1;
+ u_char lock : 1; /* could be locked */
+ u_char locked : 1; /* current lock state */
+ u_char prevent : 1; /* prevent jumper installed */
+ u_char eject : 1; /* can eject */
+ u_char : 1;
+ u_char mech : 3; /* loading mechanism type */
+#define MECH_CADDY 0
+#define MECH_TRAY 1
+#define MECH_POPUP 2
+#define MECH_CHANGER 4
+#define MECH_CARTRIDGE 5
+ u_char sep_vol : 1; /* independent volume of channels */
+ u_char sep_mute : 1; /* independent mute of channels */
+ u_char : 6;
+
+ u_short max_speed; /* max raw data rate in bytes/1000 */
+ u_short max_vol_levels; /* number of discrete volume levels */
+ u_short buf_size; /* internal buffer size in bytes/1024 */
+ u_short cur_speed; /* current data rate in bytes/1000 */
+
+ /* Digital drive output format description (optional?) */
+ u_char reserved3;
+ u_char bckf : 1; /* data valid on failing edge of BCK */
+ u_char rch : 1; /* high LRCK indicates left channel */
+ u_char lsbf : 1; /* set if LSB first */
+ u_char dlen: 2;
+#define DLEN_32 0 /* 32 BCKs */
+#define DLEN_16 1 /* 16 BCKs */
+#define DLEN_24 2 /* 24 BCKs */
+#define DLEN_24_I2S 3 /* 24 BCKs (I2S) */
+ u_char : 3;
+ u_char reserved4[2];
+};
+
+struct wcd {
+ struct atapi *ata; /* Controller structure */
+ int unit; /* IDE bus drive unit */
+ int lun; /* Logical device unit */
+ int flags; /* Device state flags */
+ int refcnt; /* The number of raw opens */
+ struct buf_queue_head buf_queue; /* Queue of i/o requests */
+ struct atapi_params *param; /* Drive parameters table */
+ struct toc toc; /* Table of disc contents */
+ struct volinfo info; /* Volume size info */
+ struct audiopage au; /* Audio page info */
+ struct cappage cap; /* Capabilities page info */
+ struct audiopage aumask; /* Audio page mask */
+ struct subchan subchan; /* Subchannel info */
+ struct kern_devconf cf; /* Driver configuration info */
+ char description[80]; /* Device description */
+#ifdef DEVFS
+ void *ra_devfs_token;
+ void *rc_devfs_token;
+ void *a_devfs_token;
+ void *c_devfs_token;
+#endif
+};
+
+struct wcd *wcdtab[NUNIT]; /* Drive info by unit number */
+static int wcdnlun = 0; /* Number of configured drives */
+
+static void wcd_start (struct wcd *t);
+static void wcd_done (struct wcd *t, struct buf *bp, int resid,
+ struct atapires result);
+static void wcd_error (struct wcd *t, struct atapires result);
+static int wcd_read_toc (struct wcd *t);
+static int wcd_request_wait (struct wcd *t, u_char cmd, u_char a1, u_char a2,
+ u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8,
+ u_char a9, char *addr, int count);
+static int wcd_externalize (struct kern_devconf*, struct sysctl_req *);
+static int wcd_goaway (struct kern_devconf *kdc, int force);
+static void wcd_describe (struct wcd *t);
+static int wcd_open(dev_t dev, int rawflag);
+static int wcd_setchan (struct wcd *t,
+ u_char c0, u_char c1, u_char c2, u_char c3);
+static int wcd_eject (struct wcd *t);
+
+static struct kern_devconf cftemplate = {
+ 0, 0, 0, "wcd", 0, { MDDT_DISK, 0 },
+ wcd_externalize, 0, wcd_goaway, DISK_EXTERNALLEN,
+ 0, 0, DC_IDLE, "ATAPI compact disc",
+};
+
+/*
+ * Dump the array in hexadecimal format for debugging purposes.
+ */
+static void wcd_dump (int lun, char *label, void *data, int len)
+{
+ u_char *p = data;
+
+ printf ("wcd%d: %s %x", lun, label, *p++);
+ while (--len > 0)
+ printf ("-%x", *p++);
+ printf ("\n");
+}
+
+static int wcd_externalize (struct kern_devconf *kdc, struct sysctl_req *req)
+{
+ return disk_externalize (wcdtab[kdc->kdc_unit]->unit, req);
+}
+
+static int wcd_goaway (struct kern_devconf *kdc, int force)
+{
+ dev_detach (kdc);
+ return 0;
+}
+
+#ifndef ATAPI_STATIC
+static
+#endif
+int
+wcdattach (struct atapi *ata, int unit, struct atapi_params *ap, int debug,
+ struct kern_devconf *parent)
+{
+ struct wcd *t;
+ struct atapires result;
+ int lun;
+
+ if (wcdnlun >= NUNIT) {
+ printf ("wcd: too many units\n");
+ return (0);
+ }
+ if (!atapi_request_immediate) {
+ printf("wcd: configuration error, ATAPI core code not present!\n");
+ printf("wcd: check `options ATAPI_STATIC' in your kernel config file!\n");
+ return (0);
+ }
+ t = malloc (sizeof (struct wcd), M_TEMP, M_NOWAIT);
+ if (! t) {
+ printf ("wcd: out of memory\n");
+ return (0);
+ }
+ wcdtab[wcdnlun] = t;
+ bzero (t, sizeof (struct wcd));
+ t->ata = ata;
+ t->unit = unit;
+ lun = t->lun = wcdnlun++;
+ t->param = ap;
+ t->flags = F_MEDIA_CHANGED;
+ t->refcnt = 0;
+ if (debug) {
+ t->flags |= F_DEBUG;
+ /* Print params. */
+ wcd_dump (t->lun, "info", ap, sizeof *ap);
+ }
+
+ /* Get drive capabilities. */
+ result = atapi_request_immediate (ata, unit, ATAPI_MODE_SENSE,
+ 0, CAP_PAGE, 0, 0, 0, 0, sizeof (t->cap) >> 8, sizeof (t->cap),
+ 0, 0, 0, 0, 0, 0, 0, (char*) &t->cap, sizeof (t->cap));
+
+ /* Do it twice to avoid the stale media changed state. */
+ if (result.code == RES_ERR &&
+ (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION)
+ result = atapi_request_immediate (ata, unit, ATAPI_MODE_SENSE,
+ 0, CAP_PAGE, 0, 0, 0, 0, sizeof (t->cap) >> 8,
+ sizeof (t->cap), 0, 0, 0, 0, 0, 0, 0,
+ (char*) &t->cap, sizeof (t->cap));
+
+ /* Some drives have shorter capabilities page. */
+ if (result.code == RES_UNDERRUN)
+ result.code = 0;
+
+ if (result.code == 0) {
+ wcd_describe (t);
+ if (t->flags & F_DEBUG)
+ wcd_dump (t->lun, "cap", &t->cap, sizeof t->cap);
+ }
+
+ /* Register driver */
+ t->cf = cftemplate;
+ t->cf.kdc_unit = t->lun;
+ t->cf.kdc_parent = parent;
+ t->cf.kdc_description = t->description;
+ strcpy (t->description, cftemplate.kdc_description);
+ strcat (t->description, ": ");
+ strncpy (t->description + strlen(t->description),
+ ap->model, sizeof(ap->model));
+ dev_attach (&t->cf);
+
+#ifdef DEVFS
+ t->ra_devfs_token =
+ devfs_add_devswf(&wcd_cdevsw, dkmakeminor(lun, 0, 0),
+ DV_CHR, UID_ROOT, GID_OPERATOR, 0640,
+ "rwcd%da", lun);
+ t->rc_devfs_token =
+ devfs_add_devswf(&wcd_cdevsw, dkmakeminor(lun, 0, RAW_PART),
+ DV_CHR, UID_ROOT, GID_OPERATOR, 0640,
+ "rwcd%dc", lun);
+ t->a_devfs_token =
+ devfs_add_devswf(&wcd_bdevsw, dkmakeminor(lun, 0, 0),
+ DV_BLK, UID_ROOT, GID_OPERATOR, 0640,
+ "wcd%da", lun);
+ t->c_devfs_token =
+ devfs_add_devswf(&wcd_bdevsw, dkmakeminor(lun, 0, RAW_PART),
+ DV_BLK, UID_ROOT, GID_OPERATOR, 0640,
+ "wcd%dc", lun);
+#endif
+ return (1);
+}
+
+void wcd_describe (struct wcd *t)
+{
+ char *m;
+
+ t->cap.max_speed = ntohs (t->cap.max_speed);
+ t->cap.max_vol_levels = ntohs (t->cap.max_vol_levels);
+ t->cap.buf_size = ntohs (t->cap.buf_size);
+ t->cap.cur_speed = ntohs (t->cap.cur_speed);
+
+ printf ("wcd%d: ", t->lun);
+ if (t->cap.cur_speed != t->cap.max_speed)
+ printf ("%d/", t->cap.cur_speed * 1000 / 1024);
+ printf ("%dKb/sec", t->cap.max_speed * 1000 / 1024);
+ if (t->cap.buf_size)
+ printf (", %dKb cache", t->cap.buf_size);
+
+ if (t->cap.audio_play)
+ printf (", audio play");
+ if (t->cap.max_vol_levels)
+ printf (", %d volume levels", t->cap.max_vol_levels);
+
+ switch (t->cap.mech) {
+ default: m = 0; break;
+ case MECH_CADDY: m = "caddy"; break;
+ case MECH_TRAY: m = "tray"; break;
+ case MECH_POPUP: m = "popup"; break;
+ case MECH_CHANGER: m = "changer"; break;
+ case MECH_CARTRIDGE: m = "cartridge"; break;
+ }
+ if (m)
+ printf (", %s%s", t->cap.eject ? "ejectable " : "", m);
+ else if (t->cap.eject)
+ printf (", eject");
+ printf ("\n");
+
+ printf ("wcd%d: ", t->lun);
+ switch (t->cap.medium_type) {
+ case MDT_UNKNOWN: printf ("medium type unknown"); break;
+ case MDT_DATA_120: printf ("120mm data disc loaded"); break;
+ case MDT_AUDIO_120: printf ("120mm audio disc loaded"); break;
+ case MDT_COMB_120: printf ("120mm data/audio disc loaded"); break;
+ case MDT_PHOTO_120: printf ("120mm photo disc loaded"); break;
+ case MDT_DATA_80: printf ("80mm data disc loaded"); break;
+ case MDT_AUDIO_80: printf ("80mm audio disc loaded"); break;
+ case MDT_COMB_80: printf ("80mm data/audio disc loaded"); break;
+ case MDT_PHOTO_80: printf ("80mm photo disc loaded"); break;
+ case MDT_NO_DISC: printf ("no disc inside"); break;
+ case MDT_DOOR_OPEN: printf ("door open"); break;
+ case MDT_FMT_ERROR: printf ("medium format error"); break;
+ default: printf ("medium type=0x%x", t->cap.medium_type); break;
+ }
+ if (t->cap.lock)
+ printf (t->cap.locked ? ", locked" : ", unlocked");
+ if (t->cap.prevent)
+ printf (", lock protected");
+ printf ("\n");
+}
+
+static int
+wcd_open (dev_t dev, int rawflag)
+{
+ int lun = UNIT(dev);
+ struct wcd *t;
+
+ /* Check that the device number is legal
+ * and the ATAPI driver is loaded. */
+ if (lun >= wcdnlun || ! atapi_request_immediate)
+ return (ENXIO);
+ t = wcdtab[lun];
+
+ /* On the first open, read the table of contents. */
+ if (! (t->flags & F_BOPEN) && ! t->refcnt) {
+ /* Read table of contents. */
+ if (wcd_read_toc (t) < 0)
+ return (EIO);
+
+ /* Lock the media. */
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
+ }
+ if (rawflag)
+ ++t->refcnt;
+ else
+ t->flags |= F_BOPEN;
+ return (0);
+}
+
+int wcdbopen (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ return wcd_open (dev, 0);
+}
+
+int wcdropen (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ return wcd_open (dev, 1);
+}
+
+/*
+ * Close the device. Only called if we are the LAST
+ * occurence of an open device.
+ */
+int wcdbclose (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ int lun = UNIT(dev);
+ struct wcd *t = wcdtab[lun];
+
+ /* If we were the last open of the entire device, release it. */
+ if (! t->refcnt)
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ t->flags &= ~F_BOPEN;
+ return (0);
+}
+
+int wcdrclose (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ int lun = UNIT(dev);
+ struct wcd *t = wcdtab[lun];
+
+ /* If we were the last open of the entire device, release it. */
+ if (! (t->flags & F_BOPEN) && t->refcnt == 1)
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ --t->refcnt;
+ return (0);
+}
+
+/*
+ * Actually translate the requested transfer into one the physical driver can
+ * understand. The transfer is described by a buf and will include only one
+ * physical transfer.
+ */
+void wcdstrategy (struct buf *bp)
+{
+ int lun = UNIT(bp->b_dev);
+ struct wcd *t = wcdtab[lun];
+ int x;
+
+ /* Can't ever write to a CD. */
+ if (! (bp->b_flags & B_READ)) {
+ bp->b_error = EROFS;
+ bp->b_flags |= B_ERROR;
+ biodone (bp);
+ return;
+ }
+
+ /* If it's a null transfer, return immediatly. */
+ if (bp->b_bcount == 0) {
+ bp->b_resid = 0;
+ biodone (bp);
+ return;
+ }
+
+ /* Process transfer request. */
+ bp->b_pblkno = bp->b_blkno;
+ bp->b_resid = bp->b_bcount;
+ x = splbio();
+
+ /* Place it in the queue of disk activities for this disk. */
+ tqdisksort (&t->buf_queue, bp);
+
+ /* Tell the device to get going on the transfer if it's
+ * not doing anything, otherwise just wait for completion. */
+ wcd_start (t);
+ splx(x);
+}
+
+/*
+ * Look to see if there is a buf waiting for the device
+ * and that the device is not already busy. If both are true,
+ * It dequeues the buf and creates an ATAPI command to perform the
+ * transfer in the buf.
+ * The bufs are queued by the strategy routine (wcdstrategy).
+ * Must be called at the correct (splbio) level.
+ */
+static void wcd_start (struct wcd *t)
+{
+ struct buf *bp = TAILQ_FIRST(&t->buf_queue);
+ u_long blkno, nblk;
+
+ /* See if there is a buf to do and we are not already doing one. */
+ if (! bp)
+ return;
+
+ /* Unqueue the request. */
+ TAILQ_REMOVE(&t->buf_queue, bp, b_act);
+
+ /* Should reject all queued entries if media have changed. */
+ if (t->flags & F_MEDIA_CHANGED) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone (bp);
+ return;
+ }
+
+ /* We have a buf, now we should make a command
+ * First, translate the block to absolute and put it in terms of the
+ * logical blocksize of the device.
+ * What if something asks for 512 bytes not on a 2k boundary? */
+ blkno = bp->b_blkno / (SECSIZE / 512);
+ nblk = (bp->b_bcount + (SECSIZE - 1)) / SECSIZE;
+
+ atapi_request_callback (t->ata, t->unit, ATAPI_READ_BIG, 0,
+ blkno>>24, blkno>>16, blkno>>8, blkno, 0, nblk>>8, nblk, 0, 0,
+ 0, 0, 0, 0, 0, (u_char*) bp->b_un.b_addr, bp->b_bcount,
+ wcd_done, t, bp);
+ t->cf.kdc_state = DC_BUSY;
+}
+
+static void wcd_done (struct wcd *t, struct buf *bp, int resid,
+ struct atapires result)
+{
+ if (result.code) {
+ wcd_error (t, result);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ } else
+ bp->b_resid = resid;
+ biodone (bp);
+ t->cf.kdc_state = DC_IDLE;
+ wcd_start (t);
+}
+
+static void wcd_error (struct wcd *t, struct atapires result)
+{
+ if (result.code != RES_ERR)
+ return;
+ switch (result.error & AER_SKEY) {
+ case AER_SK_NOT_READY:
+ if (result.error & ~AER_SKEY) {
+ /* Audio disc. */
+ printf ("wcd%d: cannot read audio disc\n", t->lun);
+ return;
+ }
+ /* Tray open. */
+ if (! (t->flags & F_MEDIA_CHANGED))
+ printf ("wcd%d: tray open\n", t->lun);
+ t->flags |= F_MEDIA_CHANGED;
+ return;
+
+ case AER_SK_UNIT_ATTENTION:
+ /* Media changed. */
+ if (! (t->flags & F_MEDIA_CHANGED))
+ printf ("wcd%d: media changed\n", t->lun);
+ t->flags |= F_MEDIA_CHANGED;
+ return;
+
+ case AER_SK_ILLEGAL_REQUEST:
+ /* Unknown command or invalid command arguments. */
+ if (t->flags & F_DEBUG)
+ printf ("wcd%d: invalid command\n", t->lun);
+ return;
+ }
+ printf ("wcd%d: i/o error, status=%b, error=%b\n", t->lun,
+ result.status, ARS_BITS, result.error, AER_BITS);
+}
+
+static int wcd_request_wait (struct wcd *t, u_char cmd, u_char a1, u_char a2,
+ u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8,
+ u_char a9, char *addr, int count)
+{
+ struct atapires result;
+
+ t->cf.kdc_state = DC_BUSY;
+ result = atapi_request_wait (t->ata, t->unit, cmd,
+ a1, a2, a3, a4, a5, a6, a7, a8, a9, 0, 0, 0, 0, 0, 0,
+ addr, count);
+ t->cf.kdc_state = DC_IDLE;
+ if (result.code) {
+ wcd_error (t, result);
+ return (EIO);
+ }
+ return (0);
+}
+
+static inline void lba2msf (int lba, u_char *m, u_char *s, u_char *f)
+{
+ lba += 150; /* offset of first logical frame */
+ lba &= 0xffffff; /* negative lbas use only 24 bits */
+ *m = lba / (60 * 75);
+ lba %= (60 * 75);
+ *s = lba / 75;
+ *f = lba % 75;
+}
+
+/*
+ * Perform special action on behalf of the user.
+ * Knows about the internals of this device
+ */
+int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p)
+{
+ int lun = UNIT(dev);
+ struct wcd *t = wcdtab[lun];
+ int error = 0;
+
+ if (t->flags & F_MEDIA_CHANGED)
+ switch (cmd) {
+ case CDIOCSETDEBUG:
+ case CDIOCCLRDEBUG:
+ case CDIOCRESET:
+ /* These ops are media change transparent. */
+ break;
+ default:
+ /* Read table of contents. */
+ wcd_read_toc (t);
+
+ /* Lock the media. */
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
+ break;
+ }
+ switch (cmd) {
+ default:
+ return (ENOTTY);
+
+ case CDIOCSETDEBUG:
+ if (p->p_cred->pc_ucred->cr_uid)
+ return (EPERM);
+ t->flags |= F_DEBUG;
+ atapi_debug (t->ata, 1);
+ return 0;
+
+ case CDIOCCLRDEBUG:
+ if (p->p_cred->pc_ucred->cr_uid)
+ return (EPERM);
+ t->flags &= ~F_DEBUG;
+ atapi_debug (t->ata, 0);
+ return 0;
+
+ case CDIOCRESUME:
+ return wcd_request_wait (t, ATAPI_PAUSE,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0);
+
+ case CDIOCPAUSE:
+ return wcd_request_wait (t, ATAPI_PAUSE,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ case CDIOCSTART:
+ return wcd_request_wait (t, ATAPI_START_STOP,
+ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
+
+ case CDIOCSTOP:
+ return wcd_request_wait (t, ATAPI_START_STOP,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ case CDIOCALLOW:
+ return wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ case CDIOCPREVENT:
+ return wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
+
+ case CDIOCRESET:
+ if (p->p_cred->pc_ucred->cr_uid)
+ return (EPERM);
+ return wcd_request_wait (t, ATAPI_TEST_UNIT_READY,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ case CDIOCEJECT:
+ /* Don't allow eject if the device is opened
+ * by somebody (not us) in block mode. */
+ if ((t->flags & F_BOPEN) && t->refcnt)
+ return (EBUSY);
+ return wcd_eject (t);
+
+ case CDIOREADTOCHEADER:
+ if (! t->toc.hdr.ending_track)
+ return (EIO);
+ bcopy (&t->toc.hdr, addr, sizeof t->toc.hdr);
+ break;
+
+ case CDIOREADTOCENTRYS: {
+ struct ioc_read_toc_entry *te =
+ (struct ioc_read_toc_entry*) addr;
+ struct toc *toc = &t->toc;
+ struct toc buf;
+ u_long len;
+ u_char starting_track = te->starting_track;
+
+ if (! t->toc.hdr.ending_track)
+ return (EIO);
+
+ if ( te->data_len < sizeof(toc->tab[0])
+ || (te->data_len % sizeof(toc->tab[0])) != 0
+ || te->address_format != CD_MSF_FORMAT
+ && te->address_format != CD_LBA_FORMAT
+ )
+ return EINVAL;
+
+ if (starting_track == 0)
+ starting_track = toc->hdr.starting_track;
+ else if (starting_track == 170) /* Handle leadout request */
+ starting_track = toc->hdr.ending_track + 1;
+ else if (starting_track < toc->hdr.starting_track ||
+ starting_track > toc->hdr.ending_track + 1)
+ return (EINVAL);
+
+ len = ((toc->hdr.ending_track + 1 - starting_track) + 1) *
+ sizeof(toc->tab[0]);
+ if (te->data_len < len)
+ len = te->data_len;
+ if (len > sizeof(toc->tab))
+ return EINVAL;
+
+ /* Convert to MSF format, if needed. */
+ if (te->address_format == CD_MSF_FORMAT) {
+ struct cd_toc_entry *e;
+
+ buf = t->toc;
+ toc = &buf;
+ e = toc->tab + (toc->hdr.ending_track + 1 -
+ toc->hdr.starting_track) + 1;
+ while (--e >= toc->tab)
+ lba2msf (ntohl(e->addr.lba), &e->addr.msf.minute,
+ &e->addr.msf.second, &e->addr.msf.frame);
+ }
+ return copyout (toc->tab + starting_track -
+ toc->hdr.starting_track, te->data, len);
+ }
+ case CDIOCREADSUBCHANNEL: {
+ struct ioc_read_subchannel *args =
+ (struct ioc_read_subchannel*) addr;
+ struct cd_sub_channel_info data;
+ u_long len = args->data_len;
+ int abslba, rellba;
+
+ if (len > sizeof(data) ||
+ len < sizeof(struct cd_sub_channel_header))
+ return (EINVAL);
+
+ if (wcd_request_wait (t, ATAPI_READ_SUBCHANNEL, 0, 0x40, 1, 0,
+ 0, 0, sizeof (t->subchan) >> 8, sizeof (t->subchan),
+ 0, (char*)&t->subchan, sizeof (t->subchan)) != 0)
+ return (EIO);
+ if (t->flags & F_DEBUG)
+ wcd_dump (t->lun, "subchan", &t->subchan, sizeof t->subchan);
+
+ abslba = t->subchan.abslba;
+ rellba = t->subchan.rellba;
+ if (args->address_format == CD_MSF_FORMAT) {
+ lba2msf (ntohl(abslba),
+ &data.what.position.absaddr.msf.minute,
+ &data.what.position.absaddr.msf.second,
+ &data.what.position.absaddr.msf.frame);
+ lba2msf (ntohl(rellba),
+ &data.what.position.reladdr.msf.minute,
+ &data.what.position.reladdr.msf.second,
+ &data.what.position.reladdr.msf.frame);
+ } else {
+ data.what.position.absaddr.lba = abslba;
+ data.what.position.reladdr.lba = rellba;
+ }
+ data.header.audio_status = t->subchan.audio_status;
+ data.what.position.control = t->subchan.control & 0xf;
+ data.what.position.addr_type = t->subchan.control >> 4;
+ data.what.position.track_number = t->subchan.track;
+ data.what.position.index_number = t->subchan.indx;
+
+ return copyout (&data, args->data, len);
+ }
+ case CDIOCPLAYMSF: {
+ struct ioc_play_msf *args = (struct ioc_play_msf*) addr;
+
+ return wcd_request_wait (t, ATAPI_PLAY_MSF, 0, 0,
+ args->start_m, args->start_s, args->start_f,
+ args->end_m, args->end_s, args->end_f, 0, 0, 0);
+ }
+ case CDIOCPLAYBLOCKS: {
+ struct ioc_play_blocks *args = (struct ioc_play_blocks*) addr;
+
+ return wcd_request_wait (t, ATAPI_PLAY_BIG, 0,
+ args->blk >> 24 & 0xff, args->blk >> 16 & 0xff,
+ args->blk >> 8 & 0xff, args->blk & 0xff,
+ args->len >> 24 & 0xff, args->len >> 16 & 0xff,
+ args->len >> 8 & 0xff, args->len & 0xff, 0, 0);
+ }
+ case CDIOCPLAYTRACKS: {
+ struct ioc_play_track *args = (struct ioc_play_track*) addr;
+ u_long start, len;
+ int t1, t2;
+
+ if (! t->toc.hdr.ending_track)
+ return (EIO);
+
+ /* Ignore index fields,
+ * play from start_track to end_track inclusive. */
+ if (args->end_track < t->toc.hdr.ending_track+1)
+ ++args->end_track;
+ if (args->end_track > t->toc.hdr.ending_track+1)
+ args->end_track = t->toc.hdr.ending_track+1;
+ t1 = args->start_track - t->toc.hdr.starting_track;
+ t2 = args->end_track - t->toc.hdr.starting_track;
+ if (t1 < 0 || t2 < 0)
+ return (EINVAL);
+ start = ntohl(t->toc.tab[t1].addr.lba);
+ len = ntohl(t->toc.tab[t2].addr.lba) - start;
+
+ return wcd_request_wait (t, ATAPI_PLAY_BIG, 0,
+ start >> 24 & 0xff, start >> 16 & 0xff,
+ start >> 8 & 0xff, start & 0xff,
+ len >> 24 & 0xff, len >> 16 & 0xff,
+ len >> 8 & 0xff, len & 0xff, 0, 0);
+ }
+ case CDIOCGETVOL: {
+ struct ioc_vol *arg = (struct ioc_vol*) addr;
+
+ error = wcd_request_wait (t, ATAPI_MODE_SENSE, 0, AUDIO_PAGE,
+ 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au), 0,
+ (char*) &t->au, sizeof (t->au));
+ if (error)
+ return (error);
+ if (t->flags & F_DEBUG)
+ wcd_dump (t->lun, "au", &t->au, sizeof t->au);
+ if (t->au.page_code != AUDIO_PAGE)
+ return (EIO);
+ arg->vol[0] = t->au.port[0].volume;
+ arg->vol[1] = t->au.port[1].volume;
+ arg->vol[2] = t->au.port[2].volume;
+ arg->vol[3] = t->au.port[3].volume;
+ break;
+ }
+ case CDIOCSETVOL: {
+ struct ioc_vol *arg = (struct ioc_vol*) addr;
+
+ error = wcd_request_wait (t, ATAPI_MODE_SENSE, 0, AUDIO_PAGE,
+ 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au), 0,
+ (char*) &t->au, sizeof (t->au));
+ if (error)
+ return (error);
+ if (t->flags & F_DEBUG)
+ wcd_dump (t->lun, "au", &t->au, sizeof t->au);
+ if (t->au.page_code != AUDIO_PAGE)
+ return (EIO);
+
+ error = wcd_request_wait (t, ATAPI_MODE_SENSE, 0,
+ AUDIO_PAGE_MASK, 0, 0, 0, 0, sizeof (t->aumask) >> 8,
+ sizeof (t->aumask), 0, (char*) &t->aumask,
+ sizeof (t->aumask));
+ if (error)
+ return (error);
+ if (t->flags & F_DEBUG)
+ wcd_dump (t->lun, "mask", &t->aumask, sizeof t->aumask);
+
+ /* Sony-55E requires the data length field to be zeroed. */
+ t->au.data_length = 0;
+
+ t->au.port[0].channels = CHANNEL_0;
+ t->au.port[1].channels = CHANNEL_1;
+ t->au.port[0].volume = arg->vol[0] & t->aumask.port[0].volume;
+ t->au.port[1].volume = arg->vol[1] & t->aumask.port[1].volume;
+ t->au.port[2].volume = arg->vol[2] & t->aumask.port[2].volume;
+ t->au.port[3].volume = arg->vol[3] & t->aumask.port[3].volume;
+ return wcd_request_wait (t, ATAPI_MODE_SELECT_BIG, 0x10,
+ 0, 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au),
+ 0, (char*) &t->au, - sizeof (t->au));
+ }
+ case CDIOCSETPATCH: {
+ struct ioc_patch *arg = (struct ioc_patch*) addr;
+
+ return wcd_setchan (t, arg->patch[0], arg->patch[1],
+ arg->patch[2], arg->patch[3]);
+ }
+ case CDIOCSETMONO:
+ return wcd_setchan (t, CHANNEL_0 | CHANNEL_1,
+ CHANNEL_0 | CHANNEL_1, 0, 0);
+
+ case CDIOCSETSTERIO:
+ return wcd_setchan (t, CHANNEL_0, CHANNEL_1, 0, 0);
+
+ case CDIOCSETMUTE:
+ return wcd_setchan (t, 0, 0, 0, 0);
+
+ case CDIOCSETLEFT:
+ return wcd_setchan (t, CHANNEL_0, CHANNEL_0, 0, 0);
+
+ case CDIOCSETRIGHT:
+ return wcd_setchan (t, CHANNEL_1, CHANNEL_1, 0, 0);
+ }
+ return (error);
+}
+
+/*
+ * Read the entire TOC for the disc into our internal buffer.
+ */
+static int wcd_read_toc (struct wcd *t)
+{
+ int ntracks, len;
+ struct atapires result;
+
+ bzero (&t->toc, sizeof (t->toc));
+ bzero (&t->info, sizeof (t->info));
+
+ /* Check for the media.
+ * Do it twice to avoid the stale media changed state. */
+ result = atapi_request_wait (t->ata, t->unit, ATAPI_TEST_UNIT_READY,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if (result.code == RES_ERR &&
+ (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION) {
+ t->flags |= F_MEDIA_CHANGED;
+ result = atapi_request_wait (t->ata, t->unit,
+ ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+ if (result.code) {
+ wcd_error (t, result);
+ return (EIO);
+ }
+ t->flags &= ~F_MEDIA_CHANGED;
+
+ /* First read just the header, so we know how long the TOC is. */
+ len = sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry);
+ if (wcd_request_wait (t, ATAPI_READ_TOC, 0, 0, 0, 0, 0, 0,
+ len >> 8, len & 0xff, 0, (char*)&t->toc, len) != 0) {
+err: bzero (&t->toc, sizeof (t->toc));
+ return (0);
+ }
+
+ ntracks = t->toc.hdr.ending_track - t->toc.hdr.starting_track + 1;
+ if (ntracks <= 0 || ntracks > MAXTRK)
+ goto err;
+
+ /* Now read the whole schmeer. */
+ len = sizeof(struct ioc_toc_header) +
+ ntracks * sizeof(struct cd_toc_entry);
+ if (wcd_request_wait (t, ATAPI_READ_TOC, 0, 0, 0, 0, 0, 0,
+ len >> 8, len & 0xff, 0, (char*)&t->toc, len) & 0xff)
+ goto err;
+
+ NTOHS(t->toc.hdr.len);
+
+ /* Read disc capacity. */
+ if (wcd_request_wait (t, ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
+ 0, sizeof(t->info), 0, (char*)&t->info, sizeof(t->info)) != 0)
+ bzero (&t->info, sizeof (t->info));
+
+ /* make fake leadout entry */
+ t->toc.tab[ntracks].control = t->toc.tab[ntracks-1].control;
+ t->toc.tab[ntracks].addr_type = t->toc.tab[ntracks-1].addr_type;
+ t->toc.tab[ntracks].track = 170; /* magic */
+ t->toc.tab[ntracks].addr.lba = t->info.volsize;
+
+ NTOHL(t->info.volsize);
+ NTOHL(t->info.blksize);
+
+ /* Print the disc description string on every disc change.
+ * It would help to track the history of disc changes. */
+ if (t->info.volsize && t->toc.hdr.ending_track &&
+ (t->flags & F_MEDIA_CHANGED) && (t->flags & F_DEBUG)) {
+ printf ("wcd%d: ", t->lun);
+ if (t->toc.tab[0].control & 4)
+ printf ("%ldMB ", t->info.volsize / 512);
+ else
+ printf ("%ld:%ld audio ", t->info.volsize/75/60,
+ t->info.volsize/75%60);
+ printf ("(%ld sectors), %d tracks\n", t->info.volsize,
+ t->toc.hdr.ending_track - t->toc.hdr.starting_track + 1);
+ }
+ return (0);
+}
+
+/*
+ * Set up the audio channel masks.
+ */
+static int wcd_setchan (struct wcd *t,
+ u_char c0, u_char c1, u_char c2, u_char c3)
+{
+ int error;
+
+ error = wcd_request_wait (t, ATAPI_MODE_SENSE, 0, AUDIO_PAGE,
+ 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au), 0,
+ (char*) &t->au, sizeof (t->au));
+ if (error)
+ return (error);
+ if (t->flags & F_DEBUG)
+ wcd_dump (t->lun, "au", &t->au, sizeof t->au);
+ if (t->au.page_code != AUDIO_PAGE)
+ return (EIO);
+
+ /* Sony-55E requires the data length field to be zeroed. */
+ t->au.data_length = 0;
+
+ t->au.port[0].channels = c0;
+ t->au.port[1].channels = c1;
+ t->au.port[2].channels = c2;
+ t->au.port[3].channels = c3;
+ return wcd_request_wait (t, ATAPI_MODE_SELECT_BIG, 0x10,
+ 0, 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au),
+ 0, (char*) &t->au, - sizeof (t->au));
+}
+
+static int wcd_eject (struct wcd *t)
+{
+ struct atapires result;
+
+ /* Try to stop the disc. */
+ t->cf.kdc_state = DC_BUSY;
+ result = atapi_request_wait (t->ata, t->unit, ATAPI_START_STOP,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ t->cf.kdc_state = DC_IDLE;
+
+ if (result.code == RES_ERR &&
+ ((result.error & AER_SKEY) == AER_SK_NOT_READY ||
+ (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION)) {
+ /*
+ * The disc was unloaded.
+ * Load it (close tray).
+ * Read the table of contents.
+ */
+ int err = wcd_request_wait (t, ATAPI_START_STOP,
+ 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0);
+ if (err)
+ return (err);
+
+ /* Read table of contents. */
+ wcd_read_toc (t);
+
+ /* Lock the media. */
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
+
+ return (0);
+ }
+
+ if (result.code) {
+ wcd_error (t, result);
+ return (EIO);
+ }
+
+ /* Give it some time to stop spinning. */
+ tsleep ((caddr_t)&lbolt, PRIBIO, "wcdej1", 0);
+ tsleep ((caddr_t)&lbolt, PRIBIO, "wcdej2", 0);
+
+ /* Unlock. */
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ /* Eject. */
+ t->flags |= F_MEDIA_CHANGED;
+ return wcd_request_wait (t, ATAPI_START_STOP,
+ 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0);
+}
+
+#ifdef WCD_MODULE
+/*
+ * Loadable ATAPI CD-ROM driver stubs.
+ */
+#include <sys/exec.h>
+#include <sys/sysent.h>
+#include <sys/lkm.h>
+
+/*
+ * Construct lkm_dev structures (see lkm.h).
+ * Our bdevsw/cdevsw slot numbers are 19/69.
+ */
+
+
+MOD_DEV(wcd, LM_DT_BLOCK, BDEV_MAJOR, &wcd_bdevsw);
+MOD_DEV(rwcd, LM_DT_CHAR, CDEV_MAJOR, &wcd_cdevsw);
+
+/*
+ * Function called when loading the driver.
+ */
+int wcd_load (struct lkm_table *lkmtp, int cmd)
+{
+ struct atapi *ata;
+ int n, u;
+
+ if (! atapi_start)
+ /* No ATAPI driver available. */
+ return EPROTONOSUPPORT;
+ n = 0;
+ for (ata=atapi_tab; ata<atapi_tab+2; ++ata)
+ if (ata->port)
+ for (u=0; u<2; ++u)
+ /* Probing controller ata->ctrlr, unit u. */
+ if (ata->params[u] && ! ata->attached[u] &&
+ wcdattach (ata, u, ata->params[u],
+ ata->debug, ata->parent) >= 0)
+ {
+ /* Drive found. */
+ ata->attached[u] = 1;
+ ++n;
+ }
+ if (! n)
+ /* No IDE CD-ROMs found. */
+ return ENXIO;
+ return 0;
+}
+
+/*
+ * Function called when unloading the driver.
+ */
+int wcd_unload (struct lkm_table *lkmtp, int cmd)
+{
+ struct wcd **t;
+
+ for (t=wcdtab; t<wcdtab+wcdnlun; ++t)
+ if (((*t)->flags & F_BOPEN) || (*t)->refcnt)
+ /* The device is opened, cannot unload the driver. */
+ return EBUSY;
+ for (t=wcdtab; t<wcdtab+wcdnlun; ++t) {
+ (*t)->ata->attached[(*t)->unit] = 0;
+ free (*t, M_TEMP);
+ }
+ wcdnlun = 0;
+ bzero (wcdtab, sizeof(wcdtab));
+ return 0;
+}
+
+/*
+ * Dispatcher function for the module (load/unload/stat).
+ */
+int wcd_mod (struct lkm_table *lkmtp, int cmd, int ver)
+{
+ int err = 0;
+
+ if (ver != LKM_VERSION)
+ return EINVAL;
+
+ if (cmd == LKM_E_LOAD)
+ err = wcd_load (lkmtp, cmd);
+ else if (cmd == LKM_E_UNLOAD)
+ err = wcd_unload (lkmtp, cmd);
+ if (err)
+ return err;
+
+ /* Register the cdevsw entry. */
+ lkmtp->private.lkm_dev = &rwcd_module;
+ err = lkmdispatch (lkmtp, cmd);
+ if (err)
+ return err;
+
+ /* Register the bdevsw entry. */
+ lkmtp->private.lkm_dev = &wcd_module;
+ return lkmdispatch (lkmtp, cmd);
+}
+#endif /* WCD_MODULE */
+
+static wcd_devsw_installed = 0;
+
+static void wcd_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! wcd_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR, 0);
+ cdevsw_add(&dev,&wcd_cdevsw, NULL);
+ dev = makedev(BDEV_MAJOR, 0);
+ bdevsw_add(&dev,&wcd_bdevsw, NULL);
+ wcd_devsw_installed = 1;
+ }
+}
+
+SYSINIT(wcddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,wcd_drvinit,NULL)
+
+
+#endif /* NWCD && NWDC && ATAPI */
diff --git a/sys/pc98/pc98/wd.c b/sys/pc98/pc98/wd.c
new file mode 100644
index 0000000..d7db1ea
--- /dev/null
+++ b/sys/pc98/pc98/wd.c
@@ -0,0 +1,2477 @@
+/*-
+ * 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: @(#)wd.c 7.2 (Berkeley) 5/9/91
+ * $Id: wd.c,v 1.109 1996/06/08 10:03:35 bde Exp $
+ */
+
+/* TODO:
+ * o Bump error count after timeout.
+ * o Satisfy ATA timing in all cases.
+ * o Finish merging berry/sos timeout code (bump error count...).
+ * o Merge/fix TIH/NetBSD bad144 code.
+ * o Don't use polling except for initialization. Need to
+ * reorganize the state machine. Then "extra" interrupts
+ * shouldn't happen (except maybe one for initialization).
+ * o Fix disklabel, boot and driver inconsistencies with
+ * bad144 in standard versions.
+ * o Support extended DOS partitions.
+ * o Support swapping to DOS partitions.
+ * o Handle bad sectors, clustering, disklabelling, DOS
+ * partitions and swapping driver-independently. Use
+ * i386/dkbad.c for bad sectors. Swapping will need new
+ * driver entries for polled reinit and polled write).
+ */
+
+#include "wd.h"
+#ifdef NWDC
+#undef NWDC
+#endif
+
+#include "wdc.h"
+#if NWDC > 0
+
+#include <sys/param.h>
+#include <sys/dkbad.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/devconf.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif /*DEVFS*/
+#include <machine/bootinfo.h>
+#include <machine/clock.h>
+#include <machine/cons.h>
+#include <machine/md_var.h>
+#ifdef PC98
+#include <pc98/pc98/pc98.h>
+#include <pc98/pc98/pc98_device.h>
+#include <pc98/pc98/wdreg.h>
+#else
+#include <i386/i386/cons.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/wdreg.h>
+#endif
+#include <sys/syslog.h>
+#include <sys/dkstat.h>
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_prot.h>
+#include <vm/pmap.h>
+
+#ifdef ATAPI
+#ifdef PC98
+#include <pc98/pc98/atapi.h>
+#else
+#include <i386/isa/atapi.h>
+#endif
+#endif
+
+extern void wdstart(int ctrlr);
+
+#define TIMEOUT 10000
+#define RETRIES 5 /* number of retries before giving up */
+#define RECOVERYTIME 500000 /* usec for controller to recover after err */
+#define MAXTRANSFER 255 /* max size of transfer in sectors */
+ /* correct max is 256 but some controllers */
+ /* can't handle that in all cases */
+#define WDOPT_32BIT 0x8000
+#define WDOPT_SLEEPHACK 0x4000
+#define WDOPT_MULTIMASK 0x00ff
+
+
+static int wd_goaway(struct kern_devconf *, int);
+static int wdc_goaway(struct kern_devconf *, int);
+static int wd_externalize(struct kern_devconf *, struct sysctl_req *);
+
+/*
+ * Templates for the kern_devconf structures used when we attach.
+ */
+static struct kern_devconf kdc_wd[NWD] = { {
+ 0, 0, 0, /* filled in by kern_devconf.c */
+ "wd", 0, { MDDT_DISK, 0 },
+ wd_externalize, 0, wd_goaway, DISK_EXTERNALLEN,
+ 0, /* parent */
+ 0, /* parentdata */
+ DC_UNKNOWN, /* state */
+#ifdef PC98
+ "IDE disk",
+#else
+ "ST506/ESDI/IDE disk",
+#endif
+ DC_CLS_DISK /* class */
+} };
+
+static struct kern_devconf kdc_wdc[NWDC] = { {
+ 0, 0, 0, /* filled in by kern_devconf.c */
+#ifdef PC98
+ "wdc", 0, { MDDT_PC98, 0 },
+ pc98_generic_externalize, 0, wdc_goaway, PC98_EXTERNALLEN,
+ &kdc_nec0, /* parent */
+#else
+ "wdc", 0, { MDDT_ISA, 0 },
+ isa_generic_externalize, 0, wdc_goaway, ISA_EXTERNALLEN,
+ &kdc_isa0, /* parent */
+#endif
+ 0, /* parentdata */
+ DC_UNCONFIGURED, /* state */
+#ifdef PC98
+ "IDE disk controller",
+#else
+ "ST506/ESDI/IDE disk controller",
+#endif
+ DC_CLS_MISC /* just an ordinary device */
+} };
+
+static inline void
+wd_registerdev(int ctlr, int unit)
+{
+ if(unit != 0) {
+ kdc_wd[unit] = kdc_wd[0];
+ kdc_wd[unit].kdc_state = DC_IDLE;
+ }
+
+ kdc_wd[unit].kdc_unit = unit;
+ kdc_wd[unit].kdc_parent = &kdc_wdc[ctlr];
+ kdc_wdc[ctlr].kdc_state = DC_BUSY;
+ dev_attach(&kdc_wd[unit]);
+}
+
+static inline void
+#ifdef PC98
+wdc_registerdev(struct pc98_device *dvp)
+#else
+wdc_registerdev(struct isa_device *dvp)
+#endif
+{
+ int unit = dvp->id_unit;
+
+ if(unit != 0) {
+ kdc_wdc[unit] = kdc_wdc[0];
+ kdc_wdc[unit].kdc_state = DC_IDLE;
+ }
+
+ kdc_wdc[unit].kdc_unit = unit;
+ kdc_wdc[unit].kdc_parentdata = dvp;
+ dev_attach(&kdc_wdc[unit]);
+}
+
+static int
+wdc_goaway(struct kern_devconf *kdc, int force)
+{
+ if(force) {
+ dev_detach(kdc);
+ return 0;
+ } else {
+ return EBUSY; /* XXX fix */
+ }
+}
+
+static int
+wd_goaway(struct kern_devconf *kdc, int force)
+{
+ dev_detach(kdc);
+ return 0;
+}
+
+static inline u_char
+epson_errorf(int wdc)
+{
+ u_char wdc_error;
+
+ outb(wdc, inb(0x82) | 0x40);
+ wdc_error = (u_char)epson_inb(wdc);
+ outb(wdc, inb(0x82) & ~0x40);
+ return ((u_char)wdc_error);
+}
+
+/*
+ * This biotab field doubles as a field for the physical unit number on
+ * the controller.
+ */
+#define id_physid id_scsiid
+
+/*
+ * Drive states. Used to initialize drive.
+ */
+
+#define CLOSED 0 /* disk is closed. */
+#define WANTOPEN 1 /* open requested, not started */
+#define RECAL 2 /* doing restore */
+#define OPEN 3 /* done with open */
+
+/*
+ * Disk geometry. A small part of struct disklabel.
+ * XXX disklabel.5 contains an old clone of disklabel.h.
+ */
+struct diskgeom {
+ u_long d_secsize; /* # of bytes per sector */
+ u_long d_nsectors; /* # of data sectors per track */
+ u_long d_ntracks; /* # of tracks per cylinder */
+ u_long d_ncylinders; /* # of data cylinders per unit */
+ u_long d_secpercyl; /* # of data sectors per cylinder */
+ u_long d_secperunit; /* # of data sectors per unit */
+ u_long d_precompcyl; /* XXX always 0 */
+};
+
+/*
+ * The structure of a disk drive.
+ */
+struct disk {
+ long dk_bc; /* byte count left */
+ short dk_skip; /* blocks already transferred */
+ int dk_ctrlr; /* physical controller number */
+ int dk_unit; /* physical unit number */
+ int dk_lunit; /* logical unit number */
+ char dk_state; /* control state */
+ u_char dk_status; /* copy of status reg. */
+ u_char dk_error; /* copy of error reg. */
+ u_char dk_timeout; /* countdown to next timeout */
+ short dk_port; /* i/o port base */
+#ifdef DEVFS
+ void *dk_bdev; /* devfs token for whole disk */
+ void *dk_cdev; /* devfs token for raw whole disk */
+#endif
+ u_long cfg_flags; /* configured characteristics */
+ short dk_flags; /* drive characteristics found */
+#define DKFL_SINGLE 0x00004 /* sector at a time mode */
+#define DKFL_ERROR 0x00008 /* processing a disk error */
+#define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */
+#define DKFL_32BIT 0x00100 /* use 32-bit i/o mode */
+#define DKFL_MULTI 0x00200 /* use multi-i/o mode */
+#define DKFL_BADSCAN 0x00400 /* report all errors */
+ struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
+ int dk_dkunit; /* disk stats unit number */
+ int dk_multi; /* multi transfers */
+ int dk_currentiosize; /* current io size */
+ struct diskgeom dk_dd; /* device configuration data */
+ struct diskslices *dk_slices; /* virtual drives */
+#ifdef PC98
+ short single_sector;
+#endif
+};
+
+#define WD_COUNT_RETRIES
+static int wdtest = 0;
+
+static struct disk *wddrives[NWD]; /* table of units */
+static struct buf_queue_head drive_queue[NWD]; /* head of queue per drive */
+static struct {
+ int b_errcnt;
+ int b_active;
+} wdutab[NWD];
+/*
+static struct buf wdtab[NWDC];
+*/
+static struct {
+ struct buf_queue_head controller_queue;
+ int b_errcnt;
+ int b_active;
+} wdtab[NWDC];
+
+struct wddma wddma;
+
+#ifdef notyet
+static struct buf rwdbuf[NWD]; /* buffers for raw IO */
+#endif
+#ifdef PC98
+static short wd_ctlr;
+static int old_epson_note;
+#endif
+
+#ifdef PC98
+static int wdprobe(struct pc98_device *dvp);
+static int wdattach(struct pc98_device *dvp);
+#else
+static int wdprobe(struct isa_device *dvp);
+static int wdattach(struct isa_device *dvp);
+#endif
+static void wdustart(struct disk *du);
+static int wdcontrol(struct buf *bp);
+static int wdcommand(struct disk *du, u_int cylinder, u_int head,
+ u_int sector, u_int count, u_int command);
+static int wdsetctlr(struct disk *du);
+static int wdwsetctlr(struct disk *du);
+static int wdgetctlr(struct disk *du);
+static void wderror(struct buf *bp, struct disk *du, char *mesg);
+static void wdflushirq(struct disk *du, int old_ipl);
+static int wdreset(struct disk *du);
+static void wdsleep(int ctrlr, char *wmesg);
+static void wdstrategy1(struct buf *bp);
+static timeout_t wdtimeout;
+static int wdunwedge(struct disk *du);
+static int wdwait(struct disk *du, u_char bits_wanted, int timeout);
+
+static d_open_t wdopen;
+static d_close_t wdclose;
+static d_strategy_t wdstrategy;
+static d_ioctl_t wdioctl;
+static d_dump_t wddump;
+static d_psize_t wdsize;
+
+#define CDEV_MAJOR 3
+#define BDEV_MAJOR 0
+extern struct cdevsw wd_cdevsw;
+static struct bdevsw wd_bdevsw =
+ { wdopen, wdclose, wdstrategy, wdioctl, /*0*/
+ wddump, wdsize, 0, "wd", &wd_cdevsw, -1 };
+
+static struct cdevsw wd_cdevsw =
+ { wdopen, wdclose, rawread, rawwrite, /*3*/
+ wdioctl, nostop, nullreset, nodevtotty,/* wd */
+ seltrue, nommap, wdstrategy, "wd",
+ &wd_bdevsw, -1 };
+
+
+/*
+ * Provide hw.devconf information.
+ */
+static int
+wd_externalize(struct kern_devconf *kdc, struct sysctl_req *req)
+{
+ return disk_externalize(wddrives[kdc->kdc_unit]->dk_unit, req);
+}
+
+#ifdef PC98
+struct pc98_driver wdcdriver = {
+#else
+struct isa_driver wdcdriver = {
+#endif
+ wdprobe, wdattach, "wdc",
+};
+
+/*
+ * Probe for controller.
+ */
+static int
+wdprobe(struct pc98_device *dvp)
+{
+ int unit = dvp->id_unit;
+ struct disk *du;
+
+ if (unit >= NWDC)
+ return (0);
+
+ du = malloc(sizeof *du, M_TEMP, M_NOWAIT);
+ if (du == NULL)
+ return (0);
+ bzero(du, sizeof *du);
+ du->dk_ctrlr = dvp->id_unit;
+ du->dk_port = dvp->id_iobase;
+
+ wdc_registerdev(dvp);
+
+ /* check if we have registers that work */
+#ifdef PC98
+ /* XXX ATAPI support isn't imported */
+ wd_ctlr = wd_ctlr_nec; /* wdreg.h */
+ old_epson_note=0;
+
+ if (pc98_machine_type & M_EPSON_PC98 ) {
+ switch (epson_machine_id) {
+ case 0x20: case 0x22: case 0x2a: /* note A/W/WR */
+ du->dk_port = IO_WD1_EPSON; /* pc98.h */
+ dvp->id_iobase = IO_WD1_EPSON; /* pc98.h */
+ wd_ctlr = wd_ctlr_epson; /* wdreg.h */
+ old_epson_note = 1; /* for OLD EPSON NOTE */
+ break;
+ default:
+ break;
+ }
+ }
+ if ((PC98_SYSTEM_PARAMETER(0x55d) & 3) == 0) {
+ goto nodevice;
+ }
+ outb(0x432,(du->dk_unit)%2);
+#else /* IBM-PC */
+ outb(du->dk_port + wd_sdh, WDSD_IBM); /* set unit 0 */
+ outb(du->dk_port + wd_cyl_lo, 0xa5); /* wd_cyl_lo is read/write */
+ if (inb(du->dk_port + wd_cyl_lo) == 0xff) { /* XXX too weak */
+#ifdef ATAPI
+ /* There is no master, try the ATAPI slave. */
+ outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10);
+ outb(du->dk_port + wd_cyl_lo, 0xa5);
+ if (inb(du->dk_port + wd_cyl_lo) == 0xff)
+#endif
+ goto nodevice;
+ }
+#endif /* PC98 */
+
+ if (wdreset(du) == 0)
+ goto reset_ok;
+#ifdef ATAPI
+ /* test for ATAPI signature */
+ outb(du->dk_port + wd_sdh, WDSD_IBM); /* master */
+ if (inb(du->dk_port + wd_cyl_lo) == 0x14 &&
+ inb(du->dk_port + wd_cyl_hi) == 0xeb)
+ goto reset_ok;
+ du->dk_unit = 1;
+ outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); /* slave */
+ if (inb(du->dk_port + wd_cyl_lo) == 0x14 &&
+ inb(du->dk_port + wd_cyl_hi) == 0xeb)
+ goto reset_ok;
+#endif
+ DELAY(RECOVERYTIME);
+ if (wdreset(du) != 0)
+ goto nodevice;
+reset_ok:
+
+ /* execute a controller only command */
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0
+ || wdwait(du, 0, TIMEOUT) < 0)
+ goto nodevice;
+
+ /*
+ * drive(s) did not time out during diagnostic :
+ * Get error status and check that both drives are OK.
+ * Table 9-2 of ATA specs suggests that we must check for
+ * a value of 0x01
+ *
+ * Strangely, some controllers will return a status of
+ * 0x81 (drive 0 OK, drive 1 failure), and then when
+ * the DRV bit is set, return status of 0x01 (OK) for
+ * drive 2. (This seems to contradict the ATA spec.)
+ */
+ if (old_epson_note)
+ du->dk_error = epson_errorf(du->dk_port + wd_error);
+ else
+ du->dk_error = inb(du->dk_port + wd_error);
+
+ /* printf("Error : %x\n", du->dk_error); */
+ if(du->dk_error != 0x01) {
+ if(du->dk_error & 0x80) { /* drive 1 failure */
+
+ /* first set the DRV bit */
+ u_int sdh;
+ if (old_epson_note)
+ sdh = epson_inb(du->dk_port+ wd_sdh);
+ else
+ sdh = inb(du->dk_port+ wd_sdh);
+ sdh = sdh | 0x10;
+ if (old_epson_note)
+ epson_outb(du->dk_port+ wd_sdh, sdh);
+ else
+ outb(du->dk_port+ wd_sdh, sdh);
+
+ /* Wait, to make sure drv 1 has completed diags */
+ if ( wdwait(du, 0, TIMEOUT) < 0)
+ goto nodevice;
+
+ /* Get status for drive 1 */
+ if (old_epson_note)
+ du->dk_error =
+ epson_errorf(du->dk_port + wd_error);
+ else
+ du->dk_error = inb(du->dk_port + wd_error);
+ /* printf("Error (drv 1) : %x\n", du->dk_error); */
+
+ if(du->dk_error != 0x01)
+ goto nodevice;
+ } else /* drive 0 fail */
+ goto nodevice;
+ }
+
+
+ free(du, M_TEMP);
+ return (IO_WDCSIZE);
+
+nodevice:
+ free(du, M_TEMP);
+ return (0);
+}
+
+/*
+ * Attach each drive if possible.
+ */
+static int
+wdattach(struct pc98_device *dvp)
+{
+#ifdef DEVFS
+ int mynor;
+#endif
+ int unit, lunit;
+ struct pc98_device *wdup;
+ struct disk *du;
+
+ if (dvp->id_unit >= NWDC)
+ return (0);
+
+ kdc_wdc[dvp->id_unit].kdc_state = DC_UNKNOWN; /* XXX */
+ TAILQ_INIT( &wdtab[dvp->id_unit].controller_queue);
+
+ for (wdup = pc98_biotab_wdc; wdup->id_driver != 0; wdup++) {
+ if (!old_epson_note) {
+ if (wdup->id_iobase != dvp->id_iobase)
+ continue;
+ }
+ lunit = wdup->id_unit;
+ if (lunit >= NWD)
+ continue;
+
+ unit = wdup->id_physid;
+#ifdef PC98
+ if ((lunit%2)!=0) {
+ if ((PC98_SYSTEM_PARAMETER(0x457) & 0x40)==0) {
+ continue;
+ }
+ }
+#endif
+
+ du = malloc(sizeof *du, M_TEMP, M_NOWAIT);
+ if (du == NULL)
+ continue;
+ if (wddrives[lunit] != NULL)
+ panic("drive attached twice");
+ wddrives[lunit] = du;
+ TAILQ_INIT( &drive_queue[lunit]);
+ bzero(du, sizeof *du);
+ du->dk_ctrlr = dvp->id_unit;
+ du->dk_unit = unit;
+ du->dk_lunit = lunit;
+ du->dk_port = dvp->id_iobase;
+
+ /*
+ * Use the individual device flags or the controller
+ * flags.
+ */
+ du->cfg_flags = wdup->id_flags |
+ ((dvp->id_flags) >> (16 * unit));
+
+ if (wdgetctlr(du) == 0) {
+ /*
+ * Print out description of drive.
+ * wdp_model may not be null terminated.
+ */
+ printf("wdc%d: unit %d (wd%d): <%.*s>",
+ dvp->id_unit, unit, lunit,
+ sizeof du->dk_params.wdp_model,
+ du->dk_params.wdp_model);
+ if (du->dk_flags & DKFL_32BIT)
+ printf(", 32-bit");
+ if (du->dk_multi > 1)
+ printf(", multi-block-%d", du->dk_multi);
+ if (du->cfg_flags & WDOPT_SLEEPHACK)
+ printf(", sleep-hack");
+ printf("\n");
+ if (du->dk_params.wdp_heads == 0)
+ printf("wd%d: size unknown, using %s values\n",
+ lunit, du->dk_dd.d_secperunit > 17
+ ? "BIOS" : "fake");
+ printf(
+"wd%d: %luMB (%lu sectors), %lu cyls, %lu heads, %lu S/T, %lu B/S\n",
+ lunit,
+ du->dk_dd.d_secperunit
+ * du->dk_dd.d_secsize / (1024 * 1024),
+ du->dk_dd.d_secperunit,
+ du->dk_dd.d_ncylinders,
+ du->dk_dd.d_ntracks,
+ du->dk_dd.d_nsectors,
+ du->dk_dd.d_secsize);
+
+ /*
+ * Start timeout routine for this drive.
+ * XXX timeout should be per controller.
+ */
+ wdtimeout(du);
+
+ wd_registerdev(dvp->id_unit, lunit);
+#ifdef DEVFS
+ mynor = dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART);
+ du->dk_bdev = devfs_add_devswf(&wd_bdevsw, mynor,
+ DV_BLK, UID_ROOT,
+ GID_OPERATOR, 0640,
+ "wd%d", unit);
+ du->dk_cdev = devfs_add_devswf(&wd_cdevsw, mynor,
+ DV_CHR, UID_ROOT,
+ GID_OPERATOR, 0640,
+ "rwd%d", unit);
+#endif
+
+ if (dk_ndrive < DK_NDRIVE) {
+ sprintf(dk_names[dk_ndrive], "wd%d", lunit);
+ /*
+ * XXX we don't know the transfer rate of the
+ * drive. Guess the maximum ISA rate of
+ * 4MB/sec. `wpms' is words per _second_
+ * according to iostat.
+ */
+ dk_wpms[dk_ndrive] = 4 * 1024 * 1024 / 2;
+ du->dk_dkunit = dk_ndrive++;
+ } else {
+ du->dk_dkunit = -1;
+ }
+ } else {
+ free(du, M_TEMP);
+ wddrives[lunit] = NULL;
+ }
+ }
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+#ifdef ATAPI
+ /*
+ * Probe all free IDE units, searching for ATAPI drives.
+ */
+#ifdef PC98
+ for (unit=0; unit<4; ++unit) {
+ outb(0x432,unit%2);
+#else
+ for (unit=0; unit<2; ++unit) {
+#endif /* PC98 */
+ for (lunit=0; lunit<NWD && wddrives[lunit]; ++lunit)
+ if (wddrives[lunit]->dk_ctrlr == dvp->id_unit &&
+ wddrives[lunit]->dk_unit == unit)
+ goto next;
+ atapi_attach (dvp->id_unit, unit, dvp->id_iobase,
+ &kdc_wdc[dvp->id_unit]);
+next: }
+#endif
+ /*
+ * Discard any interrupts generated by wdgetctlr(). wdflushirq()
+ * doesn't work now because the ambient ipl is too high.
+ */
+ wdtab[dvp->id_unit].b_active = 2;
+
+ return (1);
+}
+
+/* Read/write routine for a buffer. Finds the proper unit, range checks
+ * arguments, and schedules the transfer. Does not wait for the transfer
+ * to complete. Multi-page transfers are supported. All I/O requests must
+ * be a multiple of a sector in length.
+ */
+void
+wdstrategy(register struct buf *bp)
+{
+ struct disk *du;
+ int lunit = dkunit(bp->b_dev);
+ int s;
+
+ /* valid unit, controller, and request? */
+ if (lunit >= NWD || bp->b_blkno < 0 || (du = wddrives[lunit]) == NULL
+ || bp->b_bcount % DEV_BSIZE != 0) {
+
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+
+ /*
+ * Do bounds checking, adjust transfer, set b_cylin and b_pbklno.
+ */
+ if (dscheck(bp, du->dk_slices) <= 0)
+ goto done;
+
+ /*
+ * Check for *any* block on this transfer being on the bad block list
+ * if it is, then flag the block as a transfer that requires
+ * bad block handling. Also, used as a hint for low level disksort
+ * clustering code to keep from coalescing a bad transfer into
+ * a normal transfer. Single block transfers for a large number of
+ * blocks associated with a cluster I/O are undesirable.
+ *
+ * XXX the old disksort() doesn't look at B_BAD. Coalescing _is_
+ * desirable. We should split the results at bad blocks just
+ * like we should split them at MAXTRANSFER boundaries.
+ */
+ if (dsgetbad(bp->b_dev, du->dk_slices) != NULL) {
+ long *badsect = dsgetbad(bp->b_dev, du->dk_slices)->bi_bad;
+ int i;
+ int nsecs = howmany(bp->b_bcount, DEV_BSIZE);
+ /* XXX pblkno is too physical. */
+ daddr_t nspblkno = bp->b_pblkno
+ - du->dk_slices->dss_slices[dkslice(bp->b_dev)].ds_offset;
+ int blkend = nspblkno + nsecs;
+
+ for (i = 0; badsect[i] != -1 && badsect[i] < blkend; i++) {
+ if (badsect[i] >= nspblkno) {
+ bp->b_flags |= B_BAD;
+ break;
+ }
+ }
+ }
+
+ /* queue transfer on drive, activate drive and controller if idle */
+ s = splbio();
+
+ tqdisksort(&drive_queue[lunit], bp);
+
+ if (wdutab[lunit].b_active == 0)
+ wdustart(du); /* start drive */
+
+ /* Pick up changes made by readdisklabel(). */
+ if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) {
+ wdsleep(du->dk_ctrlr, "wdlab");
+ du->dk_state = WANTOPEN;
+ }
+
+ if (wdtab[du->dk_ctrlr].b_active == 0)
+ wdstart(du->dk_ctrlr); /* start controller */
+
+ if (du->dk_dkunit >= 0) {
+ /*
+ * XXX perhaps we should only count successful transfers.
+ */
+ dk_xfer[du->dk_dkunit]++;
+ /*
+ * XXX we can't count seeks correctly but we can do better
+ * than this. E.g., assume that the geometry is correct
+ * and count 1 seek if the starting cylinder of this i/o
+ * differs from the starting cylinder of the previous i/o,
+ * or count 1 seek if the starting bn of this i/o doesn't
+ * immediately follow the ending bn of the previos i/o.
+ */
+ dk_seek[du->dk_dkunit]++;
+ }
+
+ splx(s);
+ return;
+
+done:
+ s = splbio();
+ /* toss transfer, we're done early */
+ biodone(bp);
+ splx(s);
+}
+
+static void
+wdstrategy1(struct buf *bp)
+{
+ /*
+ * XXX - do something to make wdstrategy() but not this block while
+ * we're doing dsinit() and dsioctl().
+ */
+ wdstrategy(bp);
+}
+
+/*
+ * Routine to queue a command to the controller. The unit's
+ * request is linked into the active list for the controller.
+ * If the controller is idle, the transfer is started.
+ */
+static void
+wdustart(register struct disk *du)
+{
+ register struct buf *bp;
+ int ctrlr = du->dk_ctrlr;
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+ /* unit already active? */
+ if (wdutab[du->dk_lunit].b_active)
+ return;
+
+
+ bp = drive_queue[du->dk_lunit].tqh_first;
+ if (bp == NULL) { /* yes, an assign */
+ return;
+ }
+ TAILQ_REMOVE( &drive_queue[du->dk_lunit], bp, b_act);
+
+ /* link onto controller queue */
+ TAILQ_INSERT_TAIL( &wdtab[ctrlr].controller_queue, bp, b_act);
+
+ /* mark the drive unit as busy */
+ wdutab[du->dk_lunit].b_active = 1;
+}
+
+/*
+ * Controller startup routine. This does the calculation, and starts
+ * a single-sector read or write operation. Called to start a transfer,
+ * or from the interrupt routine to continue a multi-sector transfer.
+ * RESTRICTIONS:
+ * 1. The transfer length must be an exact multiple of the sector size.
+ */
+
+void
+wdstart(int ctrlr)
+{
+ register struct disk *du;
+ register struct buf *bp;
+ struct diskgeom *lp; /* XXX sic */
+ long blknum;
+ long secpertrk, secpercyl;
+ int lunit;
+ int count;
+
+#ifdef ATAPI
+ if (wdtab[ctrlr].b_active == 2)
+ wdtab[ctrlr].b_active = 0;
+ if (wdtab[ctrlr].b_active)
+ return;
+#endif
+loop:
+ /* is there a drive for the controller to do a transfer with? */
+ bp = wdtab[ctrlr].controller_queue.tqh_first;
+ if (bp == NULL) {
+#ifdef ATAPI
+ if (atapi_start && atapi_start (ctrlr))
+ /* mark controller active in ATAPI mode */
+ wdtab[ctrlr].b_active = 3;
+#endif
+ return;
+ }
+
+ /* obtain controller and drive information */
+ lunit = dkunit(bp->b_dev);
+ du = wddrives[lunit];
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+ if ( du->single_sector == 1 ) {
+ du->dk_flags |= DKFL_SINGLE; /* XXX */
+ }
+#endif
+
+ /* if not really a transfer, do control operations specially */
+ if (du->dk_state < OPEN) {
+ if (du->dk_state != WANTOPEN)
+ printf("wd%d: wdstart: weird dk_state %d\n",
+ du->dk_lunit, du->dk_state);
+ if (wdcontrol(bp) != 0)
+ printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n",
+ du->dk_lunit, du->dk_state);
+ return;
+ }
+
+ /* calculate transfer details */
+ blknum = bp->b_pblkno + du->dk_skip;
+#ifdef WDDEBUG
+ if (du->dk_skip == 0)
+ printf("wd%d: wdstart: %s %d@%d; map ", lunit,
+ (bp->b_flags & B_READ) ? "read" : "write",
+ bp->b_bcount, blknum);
+ else {
+ if (old_epson_note)
+ printf(" %d)%x", du->dk_skip,
+ epson_inb(du->dk_port + wd_altsts));
+ else
+ printf(" %d)%x", du->dk_skip,
+ inb(du->dk_port + wd_altsts));
+ }
+#endif
+
+ lp = &du->dk_dd;
+ secpertrk = lp->d_nsectors;
+ secpercyl = lp->d_secpercyl;
+
+ if (du->dk_skip == 0) {
+ du->dk_bc = bp->b_bcount;
+
+ if (bp->b_flags & B_BAD
+ /*
+ * XXX handle large transfers inefficiently instead
+ * of crashing on them.
+ */
+ || howmany(du->dk_bc, DEV_BSIZE) > MAXTRANSFER)
+ du->dk_flags |= DKFL_SINGLE;
+ }
+
+ if (du->dk_flags & DKFL_SINGLE
+ && dsgetbad(bp->b_dev, du->dk_slices) != NULL) {
+ /* XXX */
+ u_long ds_offset =
+ du->dk_slices->dss_slices[dkslice(bp->b_dev)].ds_offset;
+
+ blknum = transbad144(dsgetbad(bp->b_dev, du->dk_slices),
+ blknum - ds_offset) + ds_offset;
+ }
+
+ wdtab[ctrlr].b_active = 1; /* mark controller active */
+
+ /* if starting a multisector transfer, or doing single transfers */
+ if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) {
+ u_int command;
+ u_int count;
+ long cylin, head, sector;
+
+ cylin = blknum / secpercyl;
+ head = (blknum % secpercyl) / secpertrk;
+ sector = blknum % secpertrk;
+
+ if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0)
+ du->dk_bc += DEV_BSIZE;
+ count = howmany( du->dk_bc, DEV_BSIZE);
+
+ du->dk_flags &= ~DKFL_MULTI;
+
+#ifdef B_FORMAT
+ if (bp->b_flags & B_FORMAT) {
+ command = WDCC_FORMAT;
+ count = lp->d_nsectors;
+ sector = lp->d_gap3 - 1; /* + 1 later */
+ } else
+#endif
+
+ {
+ if (du->dk_flags & DKFL_SINGLE) {
+ command = (bp->b_flags & B_READ)
+ ? WDCC_READ : WDCC_WRITE;
+ count = 1;
+ du->dk_currentiosize = 1;
+ } else {
+ if( (count > 1) && (du->dk_multi > 1)) {
+ du->dk_flags |= DKFL_MULTI;
+ if( bp->b_flags & B_READ) {
+ command = WDCC_READ_MULTI;
+ } else {
+ command = WDCC_WRITE_MULTI;
+ }
+ du->dk_currentiosize = du->dk_multi;
+ if( du->dk_currentiosize > count)
+ du->dk_currentiosize = count;
+ } else {
+ if( bp->b_flags & B_READ) {
+ command = WDCC_READ;
+ } else {
+ command = WDCC_WRITE;
+ }
+ du->dk_currentiosize = 1;
+ }
+ }
+ }
+
+ /*
+ * XXX this loop may never terminate. The code to handle
+ * counting down of retries and eventually failing the i/o
+ * is in wdintr() and we can't get there from here.
+ */
+ if (wdtest != 0) {
+ if (--wdtest == 0) {
+ wdtest = 100;
+ printf("dummy wdunwedge\n");
+ wdunwedge(du);
+ }
+ }
+ if(du->dk_dkunit >= 0) {
+ dk_busy |= 1 << du->dk_dkunit;
+ }
+ while (wdcommand(du, cylin, head, sector, count, command)
+ != 0) {
+ wderror(bp, du,
+ "wdstart: timeout waiting to give command");
+ wdunwedge(du);
+ }
+#ifdef WDDEBUG
+ printf("cylin %ld head %ld sector %ld addr %x sts ",
+ cylin, head, sector,
+ (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE)
+ if (old_epson_note)
+ printf("%x\n, epson_inb(du->dk_port + wd_altsts));
+ else
+ printf("%x\n, inb(du->dk_port + wd_altsts));
+#endif
+ }
+
+ /*
+ * Schedule wdtimeout() to wake up after a few seconds. Retrying
+ * unmarked bad blocks can take 3 seconds! Then it is not good that
+ * we retry 5 times.
+ *
+ * XXX wdtimeout() doesn't increment the error count so we may loop
+ * forever. More seriously, the loop isn't forever but causes a
+ * crash.
+ *
+ * TODO fix b_resid bug elsewhere (fd.c....). Fix short but positive
+ * counts being discarded after there is an error (in physio I
+ * think). Discarding them would be OK if the (special) file offset
+ * was not advanced.
+ */
+ du->dk_timeout = 1 + 3;
+
+ /* If this is a read operation, just go away until it's done. */
+ if (bp->b_flags & B_READ)
+ return;
+
+ /* Ready to send data? */
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) < 0) {
+ wderror(bp, du, "wdstart: timeout waiting for DRQ");
+ /*
+ * XXX what do we do now? If we've just issued the command,
+ * then we can treat this failure the same as a command
+ * failure. But if we are continuing a multi-sector write,
+ * the command was issued ages ago, so we can't simply
+ * restart it.
+ *
+ * XXX we waste a lot of time unnecessarily translating block
+ * numbers to cylin/head/sector for continued i/o's.
+ */
+ }
+
+ count = 1;
+ if( du->dk_flags & DKFL_MULTI) {
+ count = howmany(du->dk_bc, DEV_BSIZE);
+ if( count > du->dk_multi)
+ count = du->dk_multi;
+ if( du->dk_currentiosize > count)
+ du->dk_currentiosize = count;
+ }
+ if (!old_epson_note) {
+ if (du->dk_flags & DKFL_32BIT)
+ outsl(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr
+ + du->dk_skip * DEV_BSIZE),
+ (count * DEV_BSIZE) / sizeof(long));
+ else
+ outsw(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr
+ + du->dk_skip * DEV_BSIZE),
+ (count * DEV_BSIZE) / sizeof(short));
+ }
+ else
+ epson_outsw(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
+ (count * DEV_BSIZE) / sizeof(short));
+
+ du->dk_bc -= DEV_BSIZE * count;
+ if (du->dk_dkunit >= 0) {
+ /*
+ * `wd's are blocks of 32 16-bit `word's according to
+ * iostat. dk_wds[] is the one disk i/o statistic that
+ * we can record correctly.
+ * XXX perhaps we shouldn't record words for failed
+ * transfers.
+ */
+ dk_wds[du->dk_dkunit] += (count * DEV_BSIZE) >> 6;
+ }
+}
+
+/* Interrupt routine for the controller. Acknowledge the interrupt, check for
+ * errors on the current operation, mark it done if necessary, and start
+ * the next request. Also check for a partially done transfer, and
+ * continue with the next chunk if so.
+ */
+void
+wdintr(int unit)
+{
+ register struct disk *du;
+ register struct buf *bp;
+
+ if (wdtab[unit].b_active == 2)
+ return; /* intr in wdflushirq() */
+ if (!wdtab[unit].b_active) {
+#ifdef WDDEBUG
+ /*
+ * These happen mostly because the power-mgt part of the
+ * bios shuts us down, and we just manage to see the
+ * interrupt from the "SLEEP" command.
+ */
+ printf("wdc%d: extra interrupt\n", unit);
+#endif
+ return;
+ }
+#ifdef ATAPI
+ if (wdtab[unit].b_active == 3) {
+ /* process an ATAPI interrupt */
+ if (atapi_intr && atapi_intr (unit))
+ /* ATAPI op continues */
+ return;
+ /* controller is free, start new op */
+ wdtab[unit].b_active = 0;
+ wdstart (unit);
+ return;
+ }
+#endif
+ bp = wdtab[unit].controller_queue.tqh_first;
+ du = wddrives[dkunit(bp->b_dev)];
+ du->dk_timeout = 0;
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+ if (wdwait(du, 0, TIMEOUT) < 0) {
+ wderror(bp, du, "wdintr: timeout waiting for status");
+ du->dk_status |= WDCS_ERR; /* XXX */
+ }
+
+ /* is it not a transfer, but a control operation? */
+ if (du->dk_state < OPEN) {
+ wdtab[unit].b_active = 0;
+ switch (wdcontrol(bp)) {
+ case 0:
+ return;
+ case 1:
+ wdstart(unit);
+ return;
+ case 2:
+ goto done;
+ }
+ }
+
+ /* have we an error? */
+ if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) {
+oops:
+ /*
+ * XXX bogus inb() here, register 0 is assumed and intr status
+ * is reset.
+ */
+ if (old_epson_note) {
+ if( (du->dk_status & DKFL_MULTI)
+ && (epson_inb(du->dk_port) & WDERR_ABORT)) {
+ wderror(bp, du,
+ "reverting to non-multi sector mode");
+ du->dk_multi = 1;
+ }
+ }
+ else {
+ if( (du->dk_status & DKFL_MULTI)
+ && (inb(du->dk_port) & WDERR_ABORT)) {
+ wderror(bp, du,
+ "reverting to non-multi sector mode");
+ du->dk_multi = 1;
+ }
+ }
+#ifdef WDDEBUG
+ wderror(bp, du, "wdintr");
+#endif
+ if ((du->dk_flags & DKFL_SINGLE) == 0) {
+ du->dk_flags |= DKFL_ERROR;
+ goto outt;
+ }
+#ifdef B_FORMAT
+ if (bp->b_flags & B_FORMAT) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+#endif
+
+ if (du->dk_flags & DKFL_BADSCAN) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ } else if (du->dk_status & WDCS_ERR) {
+ if (++wdtab[unit].b_errcnt < RETRIES) {
+ wdtab[unit].b_active = 0;
+ } else {
+ wderror(bp, du, "hard error");
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR; /* flag the error */
+ }
+ } else
+ wderror(bp, du, "soft ecc");
+ }
+
+ /*
+ * If this was a successful read operation, fetch the data.
+ */
+ if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ)
+ && wdtab[unit].b_active) {
+ int chk, dummy, multisize;
+ multisize = chk = du->dk_currentiosize * DEV_BSIZE;
+ if( du->dk_bc < chk) {
+ chk = du->dk_bc;
+ if( ((chk + DEV_BSIZE - 1) / DEV_BSIZE) < du->dk_currentiosize) {
+ du->dk_currentiosize = (chk + DEV_BSIZE - 1) / DEV_BSIZE;
+ multisize = du->dk_currentiosize * DEV_BSIZE;
+ }
+ }
+
+ /* ready to receive data? */
+ if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
+ != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
+ wderror(bp, du, "wdintr: read intr arrived early");
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) {
+ wderror(bp, du, "wdintr: read error detected late");
+ goto oops;
+ }
+
+ /* suck in data */
+ if (!old_epson_note) {
+ if( du->dk_flags & DKFL_32BIT)
+ insl(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr
+ + du->dk_skip * DEV_BSIZE),
+ chk / sizeof(long));
+ else
+ insw(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr
+ + du->dk_skip * DEV_BSIZE),
+ chk / sizeof(short));
+ }
+ else
+ epson_insw(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr
+ + du->dk_skip * DEV_BSIZE),
+ chk / sizeof(short));
+ du->dk_bc -= chk;
+
+ /* XXX for obsolete fractional sector reads. */
+ while (chk < multisize) {
+ if (!old_epson_note)
+ insw(du->dk_port + wd_data, &dummy, 1);
+ else
+ epson_insw(du->dk_port + wd_data, &dummy, 1);
+ chk += sizeof(short);
+ }
+
+ if (du->dk_dkunit >= 0)
+ dk_wds[du->dk_dkunit] += chk >> 6;
+ }
+
+outt:
+ if (wdtab[unit].b_active) {
+ if ((bp->b_flags & B_ERROR) == 0) {
+ du->dk_skip += du->dk_currentiosize;/* add to successful sectors */
+ if (wdtab[unit].b_errcnt)
+ wderror(bp, du, "soft error");
+ wdtab[unit].b_errcnt = 0;
+
+ /* see if more to transfer */
+ if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
+ wdtab[unit].b_active = 0;
+ wdstart(unit);
+ return; /* next chunk is started */
+ } else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR))
+ == DKFL_ERROR) {
+ du->dk_skip = 0;
+ du->dk_flags &= ~DKFL_ERROR;
+ du->dk_flags |= DKFL_SINGLE;
+ wdtab[unit].b_active = 0;
+ wdstart(unit);
+ return; /* redo xfer sector by sector */
+ }
+ }
+
+done: ;
+ /* done with this transfer, with or without error */
+ du->dk_flags &= ~DKFL_SINGLE;
+ TAILQ_REMOVE(&wdtab[unit].controller_queue, bp, b_act);
+ wdtab[unit].b_errcnt = 0;
+ bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE;
+ wdutab[du->dk_lunit].b_active = 0;
+ wdutab[du->dk_lunit].b_errcnt = 0;
+ du->dk_skip = 0;
+ biodone(bp);
+ }
+
+ if(du->dk_dkunit >= 0) {
+ dk_busy &= ~(1 << du->dk_dkunit);
+ }
+
+ /* controller idle */
+ wdtab[unit].b_active = 0;
+
+ /* anything more on drive queue? */
+ wdustart(du);
+ /* anything more for controller to do? */
+#ifndef ATAPI
+ /* This is not valid in ATAPI mode. */
+ if (wdtab[unit].controller_queue.tqh_first)
+#endif
+ wdstart(unit);
+}
+
+/*
+ * Initialize a drive.
+ */
+int
+wdopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ register unsigned int lunit;
+ register struct disk *du;
+ int error;
+
+ lunit = dkunit(dev);
+ if (lunit >= NWD || dktype(dev) != 0)
+ return (ENXIO);
+ du = wddrives[lunit];
+ if (du == NULL)
+ return (ENXIO);
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+
+ /* Finish flushing IRQs left over from wdattach(). */
+ if (wdtab[du->dk_ctrlr].b_active == 2)
+ wdtab[du->dk_ctrlr].b_active = 0;
+
+ du->dk_flags &= ~DKFL_BADSCAN;
+
+ while (du->dk_flags & DKFL_LABELLING)
+ tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1);
+#if 1
+ kdc_wd[lunit].kdc_state = DC_BUSY;
+ wdsleep(du->dk_ctrlr, "wdopn1");
+ du->dk_flags |= DKFL_LABELLING;
+ du->dk_state = WANTOPEN;
+ {
+ struct disklabel label;
+
+ bzero(&label, sizeof label);
+ label.d_secsize = du->dk_dd.d_secsize;
+ label.d_nsectors = du->dk_dd.d_nsectors;
+ label.d_ntracks = du->dk_dd.d_ntracks;
+ label.d_ncylinders = du->dk_dd.d_ncylinders;
+ label.d_secpercyl = du->dk_dd.d_secpercyl;
+ label.d_secperunit = du->dk_dd.d_secperunit;
+ error = dsopen("wd", dev, fmt, &du->dk_slices, &label, wdstrategy1,
+ (ds_setgeom_t *)NULL, &wd_bdevsw, &wd_cdevsw);
+ }
+ du->dk_flags &= ~DKFL_LABELLING;
+ wdsleep(du->dk_ctrlr, "wdopn2");
+ return (error);
+#else
+ if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
+ /*
+ * wdtab[ctrlr].b_active != 0 implies XXX applicable now ??
+ * drive_queue[lunit].b_act == NULL (?) XXX applicable now ??
+ * so the following guards most things (until the next i/o).
+ * It doesn't guard against a new i/o starting and being
+ * affected by the label being changed. Sigh.
+ */
+ wdsleep(du->dk_ctrlr, "wdopn1");
+
+ du->dk_flags |= DKFL_LABELLING;
+ du->dk_state = WANTOPEN;
+
+ error = dsinit(dkmodpart(dev, RAW_PART), wdstrategy,
+ &du->dk_dd, &du->dk_slices);
+ if (error != 0) {
+ du->dk_flags &= ~DKFL_LABELLING;
+ return (error);
+ }
+ /* XXX check value returned by wdwsetctlr(). */
+ wdwsetctlr(du);
+ if (dkslice(dev) == WHOLE_DISK_SLICE) {
+ dsopen(dev, fmt, du->dk_slices);
+ return (0);
+ }
+
+ /*
+ * Read label using RAW_PART partition.
+ *
+ * If the drive has an MBR, then the current geometry (from
+ * wdgetctlr()) is used to read it; then the BIOS/DOS
+ * geometry is inferred and used to read the label off the
+ * 'c' partition. Otherwise the label is read using the
+ * current geometry. The label gives the final geometry.
+ * If bad sector handling is enabled, then this geometry
+ * is used to read the bad sector table. The geometry
+ * changes occur inside readdisklabel() and are propagated
+ * to the driver by resetting the state machine.
+ *
+ * XXX can now handle changes directly since dsinit() doesn't
+ * do too much.
+ */
+ msg = correct_readdisklabel(dkmodpart(dev, RAW_PART), wdstrategy,
+ &du->dk_dd);
+ /* XXX check value returned by wdwsetctlr(). */
+ wdwsetctlr(du);
+ if (msg == NULL && du->dk_dd.d_flags & D_BADSECT)
+ msg = readbad144(dkmodpart(dev, RAW_PART), wdstrategy,
+ &du->dk_dd, &du->dk_bad);
+ du->dk_flags &= ~DKFL_LABELLING;
+ if (msg != NULL) {
+ log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
+ lunit, msg);
+ if (part != RAW_PART)
+ return (EINVAL); /* XXX needs translation */
+ /*
+ * Soon return. This is how slices without labels
+ * are allowed. They only work on the raw partition.
+ */
+ } else {
+ unsigned long newsize, offset, size;
+#if 0
+ /*
+ * Force RAW_PART partition to be the whole disk.
+ */
+ offset = du->dk_dd.d_partitions[RAW_PART].p_offset;
+ if (offset != 0) {
+ printf(
+ "wd%d: changing offset of '%c' partition from %lu to 0\n",
+ du->dk_lunit, 'a' + RAW_PART, offset);
+ du->dk_dd.d_partitions[RAW_PART].p_offset = 0;
+ }
+ size = du->dk_dd.d_partitions[RAW_PART].p_size;
+ newsize = du->dk_dd.d_secperunit; /* XXX */
+ if (size != newsize) {
+ printf(
+ "wd%d: changing size of '%c' partition from %lu to %lu\n",
+ du->dk_lunit, 'a' + RAW_PART, size,
+ newsize);
+ du->dk_dd.d_partitions[RAW_PART].p_size
+ = newsize;
+ }
+#endif
+ }
+
+ /* Pick up changes made by readdisklabel(). */
+ wdsleep(du->dk_ctrlr, "wdopn2");
+ du->dk_state = WANTOPEN;
+ }
+
+ /*
+ * Warn if a partion is opened that overlaps another partition which
+ * is open unless one is the "raw" partition (whole disk).
+ */
+ if ((du->dk_openpart & mask) == 0 && part != RAW_PART) {
+ int start, end;
+
+ pp = &du->dk_dd.d_partitions[part];
+ start = pp->p_offset;
+ end = pp->p_offset + pp->p_size;
+ for (pp = du->dk_dd.d_partitions;
+ pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
+ pp++) {
+ if (pp->p_offset + pp->p_size <= start ||
+ pp->p_offset >= end)
+ continue;
+ if (pp - du->dk_dd.d_partitions == RAW_PART)
+ continue;
+ if (du->dk_openpart
+ & (1 << (pp - du->dk_dd.d_partitions)))
+ log(LOG_WARNING,
+ "wd%d%c: overlaps open partition (%c)\n",
+ lunit, part + 'a',
+ pp - du->dk_dd.d_partitions + 'a');
+ }
+ }
+ if (part >= du->dk_dd.d_npartitions && part != RAW_PART)
+ return (ENXIO);
+
+ dsopen(dev, fmt, du->dk_slices);
+
+ return (0);
+#endif
+}
+
+/*
+ * Implement operations other than read/write.
+ * Called from wdstart or wdintr during opens and formats.
+ * Uses finite-state-machine to track progress of operation in progress.
+ * Returns 0 if operation still in progress, 1 if completed, 2 if error.
+ */
+static int
+wdcontrol(register struct buf *bp)
+{
+ register struct disk *du;
+ int ctrlr;
+
+ du = wddrives[dkunit(bp->b_dev)];
+ ctrlr = du->dk_ctrlr;
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+
+ switch (du->dk_state) {
+ case WANTOPEN:
+tryagainrecal:
+ wdtab[ctrlr].b_active = 1;
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) {
+ wderror(bp, du, "wdcontrol: wdcommand failed");
+ goto maybe_retry;
+ }
+ du->dk_state = RECAL;
+ return (0);
+ case RECAL:
+ if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) {
+ wderror(bp, du, "wdcontrol: recal failed");
+maybe_retry:
+ if (du->dk_status & WDCS_ERR)
+ wdunwedge(du);
+ du->dk_state = WANTOPEN;
+ if (++wdtab[ctrlr].b_errcnt < RETRIES)
+ goto tryagainrecal;
+ bp->b_error = ENXIO; /* XXX needs translation */
+ bp->b_flags |= B_ERROR;
+ return (2);
+ }
+ wdtab[ctrlr].b_errcnt = 0;
+ du->dk_state = OPEN;
+ /*
+ * The rest of the initialization can be done by normal
+ * means.
+ */
+ return (1);
+ }
+ panic("wdcontrol");
+ return (2);
+}
+
+/*
+ * Wait uninterruptibly until controller is not busy, then send it a command.
+ * The wait usually terminates immediately because we waited for the previous
+ * command to terminate.
+ */
+static int
+wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
+ u_int count, u_int command)
+{
+ u_int wdc;
+#ifdef PC98
+ unsigned char u_addr;
+#endif
+
+ wdc = du->dk_port;
+ if (du->cfg_flags & WDOPT_SLEEPHACK) {
+ if (old_epson_note) {
+ if(epson_inb(wdc + wd_status) == WDCS_BUSY)
+ wdunwedge(du);
+ } else {
+ if(inb(wdc + wd_status) == WDCS_BUSY)
+ wdunwedge(du);
+ }
+ }
+
+ if (wdwait(du, 0, TIMEOUT) < 0)
+ return (1);
+#ifdef PC98
+/* u_addr = (du->dk_unit & 0xfe); */
+ u_addr = ((du->dk_unit)/2)<<4;
+#endif /* PC98 */
+ if( command == WDCC_FEATURES) {
+ if (old_epson_note)
+ epson_outb(wdc + wd_features, count);
+ else
+ outb(wdc + wd_features, count);
+ } else {
+ if (old_epson_note) {
+ epson_outb(wdc + wd_precomp, du->dk_dd.d_precompcyl/4);
+ epson_outb(wdc + wd_cyl_lo, cylinder);
+ epson_outb(wdc + wd_cyl_hi, cylinder >> 8);
+ epson_outb(wdc + wd_sdh, WDSD_IBM | u_addr | head);
+ epson_outb(wdc + wd_sector, sector + 1);
+ epson_outb(wdc + wd_seccnt, count);
+ }
+ else {
+ outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4);
+ outb(wdc + wd_cyl_lo, cylinder);
+ outb(wdc + wd_cyl_hi, cylinder >> 8);
+#ifdef PC98
+ outb(wdc + wd_sdh, WDSD_IBM | u_addr | head);
+#else
+ outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit<<4) | head);
+#endif
+ outb(wdc + wd_sector, sector + 1);
+ outb(wdc + wd_seccnt, count);
+ }
+ }
+ if (wdwait(du, command == WDCC_DIAGNOSE || command == WDCC_IDC
+ ? 0 : WDCS_READY, TIMEOUT) < 0)
+ return (1);
+ if (old_epson_note)
+ epson_outb(wdc + wd_command, command);
+ else
+ outb(wdc + wd_command, command);
+ return (0);
+}
+
+/*
+ * issue IDC to drive to tell it just what geometry it is to be.
+ */
+static int
+wdsetctlr(struct disk *du)
+{
+ int error = 0;
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+#ifdef WDDEBUG
+ printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n",
+ du->dk_ctrlr, du->dk_unit,
+ du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
+ du->dk_dd.d_nsectors);
+#endif
+ if (du->dk_dd.d_ntracks == 0 || du->dk_dd.d_ntracks > 16) {
+ struct wdparams *wp;
+
+ printf("wd%d: can't handle %lu heads from partition table ",
+ du->dk_lunit, du->dk_dd.d_ntracks);
+ /* obtain parameters */
+ wp = &du->dk_params;
+ if (wp->wdp_heads > 0 && wp->wdp_heads <= 16) {
+ printf("(controller value %u restored)\n",
+ wp->wdp_heads);
+ du->dk_dd.d_ntracks = wp->wdp_heads;
+ }
+ else {
+ printf("(truncating to 16)\n");
+ du->dk_dd.d_ntracks = 16;
+ }
+ }
+
+ if (du->dk_dd.d_nsectors == 0 || du->dk_dd.d_nsectors > 255) {
+ printf("wd%d: cannot handle %lu sectors (max 255)\n",
+ du->dk_lunit, du->dk_dd.d_nsectors);
+ error = 1;
+ }
+ if (error) {
+ wdtab[du->dk_ctrlr].b_errcnt += RETRIES;
+ return (1);
+ }
+ if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0,
+ du->dk_dd.d_nsectors, WDCC_IDC) != 0
+ || wdwait(du, WDCS_READY, TIMEOUT) < 0) {
+ wderror((struct buf *)NULL, du, "wdsetctlr failed");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Wait until driver is inactive, then set up controller.
+ */
+static int
+wdwsetctlr(struct disk *du)
+{
+ int stat;
+ int x;
+
+ wdsleep(du->dk_ctrlr, "wdwset");
+ x = splbio();
+ stat = wdsetctlr(du);
+ wdflushirq(du, x);
+ splx(x);
+ return (stat);
+}
+
+/*
+ * issue READP to drive to ask it what it is.
+ */
+static int
+wdgetctlr(struct disk *du)
+{
+ int i;
+ char tb[DEV_BSIZE], tb2[DEV_BSIZE];
+ struct wdparams *wp = NULL;
+ u_long flags = du->cfg_flags;
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+
+again:
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0
+ || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) {
+
+#ifdef PC98
+ if ( du->dk_unit > 1 )
+ return(1);
+#endif
+ /*
+ * if we failed on the second try, assume non-32bit
+ */
+ if( du->dk_flags & DKFL_32BIT)
+ goto failed;
+
+ /* XXX need to check error status after final transfer. */
+ /*
+ * Old drives don't support WDCC_READP. Try a seek to 0.
+ * Some IDE controllers return trash if there is no drive
+ * attached, so first test that the drive can be selected.
+ * This also avoids long waits for nonexistent drives.
+ */
+ if (wdwait(du, 0, TIMEOUT) < 0)
+ return (1);
+ if (old_epson_note) {
+ epson_outb(du->dk_port + wd_sdh,
+ WDSD_IBM | (du->dk_unit << 4));
+ DELAY(5000); /* usually unnecessary; drive select is fast */
+ if ((epson_inb(du->dk_port + wd_status)
+ & (WDCS_BUSY | WDCS_READY))
+ != WDCS_READY
+ || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
+ || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0)
+ return (1);
+ }
+ else {
+ outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4));
+ DELAY(5000); /* usually unnecessary; drive select is fast */
+ if ((inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY))
+ != WDCS_READY
+ || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
+ || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0)
+ return (1);
+ }
+ if (du->dk_unit == bootinfo.bi_n_bios_used) {
+ du->dk_dd.d_secsize = DEV_BSIZE;
+ du->dk_dd.d_nsectors =
+ bootinfo.bi_bios_geom[du->dk_unit] & 0xff;
+ du->dk_dd.d_ntracks =
+ ((bootinfo.bi_bios_geom[du->dk_unit] >> 8) & 0xff)
+ + 1;
+ /* XXX Why 2 ? */
+ du->dk_dd.d_ncylinders =
+ (bootinfo.bi_bios_geom[du->dk_unit] >> 16) + 2;
+ du->dk_dd.d_secpercyl =
+ du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
+ du->dk_dd.d_secperunit =
+ du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders;
+#if 0
+ du->dk_dd.d_partitions[WDRAW].p_size =
+ du->dk_dd.d_secperunit;
+ du->dk_dd.d_type = DTYPE_ST506;
+ du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
+ strncpy(du->dk_dd.d_typename, "Bios geometry",
+ sizeof du->dk_dd.d_typename);
+ strncpy(du->dk_params.wdp_model, "ST506",
+ sizeof du->dk_params.wdp_model);
+#endif
+ bootinfo.bi_n_bios_used ++;
+ return 0;
+ }
+ /*
+ * Fake minimal drive geometry for reading the MBR.
+ * readdisklabel() may enlarge it to read the label and the
+ * bad sector table.
+ */
+ du->dk_dd.d_secsize = DEV_BSIZE;
+ du->dk_dd.d_nsectors = 17;
+ du->dk_dd.d_ntracks = 1;
+ du->dk_dd.d_ncylinders = 1;
+ du->dk_dd.d_secpercyl = 17;
+ du->dk_dd.d_secperunit = 17;
+
+#if 0
+ /*
+ * Fake maximal drive size for writing the label.
+ */
+ du->dk_dd.d_partitions[RAW_PART].p_size = 64 * 16 * 1024;
+
+ /*
+ * Fake some more of the label for printing by disklabel(1)
+ * in case there is no real label.
+ */
+ du->dk_dd.d_type = DTYPE_ST506;
+ du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
+ strncpy(du->dk_dd.d_typename, "Fake geometry",
+ sizeof du->dk_dd.d_typename);
+#endif
+
+ /* Fake the model name for printing by wdattach(). */
+ strncpy(du->dk_params.wdp_model, "unknown",
+ sizeof du->dk_params.wdp_model);
+
+ return (0);
+ }
+
+ /* obtain parameters */
+ wp = &du->dk_params;
+ if (!old_epson_note) {
+ if (du->dk_flags & DKFL_32BIT)
+ insl(du->dk_port + wd_data, tb,
+ sizeof(tb) / sizeof(long));
+ else
+ insw(du->dk_port + wd_data, tb,
+ sizeof(tb) / sizeof(short));
+ }
+ else
+ epson_insw(du->dk_port + wd_data, tb,
+ sizeof(tb) / sizeof(short));
+
+ /* try 32-bit data path (VLB IDE controller) */
+ if (flags & WDOPT_32BIT) {
+ if (! (du->dk_flags & DKFL_32BIT)) {
+ bcopy(tb, tb2, sizeof(struct wdparams));
+ du->dk_flags |= DKFL_32BIT;
+ goto again;
+ }
+
+ /* check that we really have 32-bit controller */
+ if (bcmp (tb, tb2, sizeof(struct wdparams)) != 0) {
+failed:
+ /* test failed, use 16-bit i/o mode */
+ bcopy(tb2, tb, sizeof(struct wdparams));
+ du->dk_flags &= ~DKFL_32BIT;
+ }
+ }
+
+ bcopy(tb, wp, sizeof(struct wdparams));
+
+ /* shuffle string byte order */
+ for (i = 0; i < sizeof(wp->wdp_model); i += 2) {
+ u_short *p;
+
+ p = (u_short *) (wp->wdp_model + i);
+ *p = ntohs(*p);
+ }
+ /*
+ * Clean up the wdp_model by converting nulls to spaces, and
+ * then removing the trailing spaces.
+ */
+ for (i=0; i < sizeof(wp->wdp_model); i++) {
+ if (wp->wdp_model[i] == '\0') {
+ wp->wdp_model[i] = ' ';
+ }
+ }
+ for (i=sizeof(wp->wdp_model)-1; i>=0 && wp->wdp_model[i]==' '; i--) {
+ wp->wdp_model[i] = '\0';
+ }
+
+#ifdef WDDEBUG
+ printf(
+"\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
+ du->dk_ctrlr, du->dk_unit, wp->wdp_config, wp->wdp_cylinders,
+ wp->wdp_heads, wp->wdp_sectors, wp->wdp_buffertype,
+ wp->wdp_buffersize, wp->wdp_model);
+#endif
+#ifdef PC98
+ /* for larger than 40MB */
+ {
+ long cyl = wp->wdp_fixedcyl * wp->wdp_heads * wp->wdp_sectors;
+ wp->wdp_removcyl = 0; /* XXX ukai */
+#ifdef PC98
+ if ( du->dk_unit > 1 ) {
+ wp->wdp_sectors = 17;
+ wp->wdp_heads = 8;
+ } else {
+ wp->wdp_sectors = bootinfo.bi_bios_geom[du->dk_unit] & 0xff;
+ wp->wdp_heads = (bootinfo.bi_bios_geom[du->dk_unit] >> 8) & 0xff;
+ }
+#endif
+ wp->wdp_fixedcyl = cyl / (wp->wdp_heads * wp->wdp_sectors);
+ }
+#endif
+
+ /* update disklabel given drive information */
+ du->dk_dd.d_secsize = DEV_BSIZE;
+ du->dk_dd.d_ncylinders = wp->wdp_cylinders; /* +- 1 */
+ du->dk_dd.d_ntracks = wp->wdp_heads;
+ du->dk_dd.d_nsectors = wp->wdp_sectors;
+ du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
+ du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders;
+#if 0
+ du->dk_dd.d_partitions[RAW_PART].p_size = du->dk_dd.d_secperunit;
+ /* dubious ... */
+ bcopy("ESDI/IDE", du->dk_dd.d_typename, 9);
+ bcopy(wp->wdp_model + 20, du->dk_dd.d_packname, 14 - 1);
+ /* better ... */
+ du->dk_dd.d_type = DTYPE_ESDI;
+ du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
+#endif
+
+ /*
+ * find out the drives maximum multi-block transfer capability
+ */
+ du->dk_multi = wp->wdp_nsecperint & 0xff;
+
+ /*
+ * The config option flags low 8 bits define the maximum multi-block
+ * transfer size. If the user wants the maximum that the drive
+ * is capable of, just set the low bits of the config option to
+ * 0x00ff.
+ */
+ if ((flags & WDOPT_MULTIMASK) != 0 && (du->dk_multi > 1)) {
+ if (du->dk_multi > (flags & WDOPT_MULTIMASK))
+ du->dk_multi = flags & WDOPT_MULTIMASK;
+ if (wdcommand(du, 0, 0, 0, du->dk_multi, WDCC_SET_MULTI)) {
+ du->dk_multi = 1;
+ }
+ } else {
+ du->dk_multi = 1;
+ }
+
+#ifdef NOTYET
+/* set read caching and write caching */
+ wdcommand(du, 0, 0, 0, WDFEA_RCACHE, WDCC_FEATURES);
+ wdcommand(du, 0, 0, 0, WDFEA_WCACHE, WDCC_FEATURES);
+#endif
+
+ return (0);
+}
+
+int
+wdclose(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ dsclose(dev, fmt, wddrives[dkunit(dev)]->dk_slices);
+ kdc_wd[wddrives[dkunit(dev)]->dk_lunit].kdc_state = DC_IDLE;
+ return (0);
+}
+
+int
+wdioctl(dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
+{
+ int lunit = dkunit(dev);
+ register struct disk *du;
+ int error;
+#ifdef notyet
+ struct uio auio;
+ struct iovec aiov;
+ struct format_op *fop;
+#endif
+
+ du = wddrives[lunit];
+ wdsleep(du->dk_ctrlr, "wdioct");
+ error = dsioctl("wd", dev, cmd, addr, flags, &du->dk_slices,
+ wdstrategy1, (ds_setgeom_t *)NULL);
+ if (error != -1)
+ return (error);
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+ switch (cmd) {
+ case DIOCSBADSCAN:
+ if (*(int *)addr)
+ du->dk_flags |= DKFL_BADSCAN;
+ else
+ du->dk_flags &= ~DKFL_BADSCAN;
+ return (0);
+#ifdef notyet
+ case DIOCWFORMAT:
+ if (!(flag & FWRITE))
+ return (EBADF);
+ fop = (struct format_op *)addr;
+ aiov.iov_base = fop->df_buf;
+ aiov.iov_len = fop->df_count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = fop->df_count;
+ auio.uio_segflg = 0;
+ auio.uio_offset = fop->df_startblk * du->dk_dd.d_secsize;
+#error /* XXX the 386BSD interface is different */
+ error = physio(wdformat, &rwdbuf[lunit], 0, dev, B_WRITE,
+ minphys, &auio);
+ fop->df_count -= auio.uio_resid;
+ fop->df_reg[0] = du->dk_status;
+ fop->df_reg[1] = du->dk_error;
+ return (error);
+#endif
+
+ default:
+ return (ENOTTY);
+ }
+ return (error);
+}
+
+#ifdef B_FORMAT
+int
+wdformat(struct buf *bp)
+{
+
+ bp->b_flags |= B_FORMAT;
+ wdstrategy(bp);
+ /*
+ * phk put this here, better that return(wdstrategy(bp));
+ * XXX
+ */
+ return -1;
+}
+#endif
+
+int
+wdsize(dev_t dev)
+{
+ struct disk *du;
+ int lunit;
+
+ lunit = dkunit(dev);
+ if (lunit >= NWD || dktype(dev) != 0)
+ return (-1);
+ du = wddrives[lunit];
+ if (du == NULL)
+ return (-1);
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+ return (dssize(dev, &du->dk_slices, wdopen, wdclose));
+}
+
+/*
+ * Dump core after a system crash.
+ */
+int
+wddump(dev_t dev)
+{
+#ifdef PC98
+ /* do nothing */
+ printf("wddump!!\n");
+ return(0);
+#else
+ register struct disk *du;
+ struct disklabel *lp;
+ long num; /* number of sectors to write */
+ int lunit, part;
+ long blkoff, blknum;
+ long blkchk, blkcnt, blknext;
+ long cylin, head, sector;
+ long secpertrk, secpercyl, nblocks;
+ u_long ds_offset;
+ char *addr;
+ static int wddoingadump = 0;
+
+ /* Toss any characters present prior to dump. */
+ while (cncheckc())
+ ;
+
+ /* Check for acceptable device. */
+ /* XXX should reset to maybe allow du->dk_state < OPEN. */
+ lunit = dkunit(dev); /* eventually support floppies? */
+ part = dkpart(dev);
+ if (lunit >= NWD || (du = wddrives[lunit]) == NULL
+ || du->dk_state < OPEN
+ || (lp = dsgetlabel(dev, du->dk_slices)) == NULL)
+ return (ENXIO);
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+ /* Size of memory to dump, in disk sectors. */
+ num = (u_long)Maxmem * PAGE_SIZE / du->dk_dd.d_secsize;
+
+ secpertrk = du->dk_dd.d_nsectors;
+ secpercyl = du->dk_dd.d_secpercyl;
+ nblocks = lp->d_partitions[part].p_size;
+ blkoff = lp->d_partitions[part].p_offset;
+ /* XXX */
+ ds_offset = du->dk_slices->dss_slices[dkslice(dev)].ds_offset;
+ blkoff += ds_offset;
+
+#if 0
+ pg("part %x, nblocks %d, dumplo %d num %d\n",
+ part, nblocks, dumplo, num);
+#endif
+
+ /* Check transfer bounds against partition size. */
+ if (dumplo < 0 || dumplo + num > nblocks)
+ return (EINVAL);
+
+ /* Check if we are being called recursively. */
+ if (wddoingadump)
+ return (EFAULT);
+
+#if 0
+ /* Mark controller active for if we panic during the dump. */
+ wdtab[du->dk_ctrlr].b_active = 1;
+#endif
+ wddoingadump = 1;
+
+ /* Recalibrate the drive. */
+ DELAY(5); /* ATA spec XXX NOT */
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
+ || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0
+ || wdsetctlr(du) != 0) {
+ wderror((struct buf *)NULL, du, "wddump: recalibrate failed");
+ return (EIO);
+ }
+
+ du->dk_flags |= DKFL_SINGLE;
+ addr = (char *) 0;
+ blknum = dumplo + blkoff;
+ while (num > 0) {
+ blkcnt = num;
+ if (blkcnt > MAXTRANSFER)
+ blkcnt = MAXTRANSFER;
+ /* Keep transfer within current cylinder. */
+ if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
+ blkcnt = secpercyl - (blknum % secpercyl);
+ blknext = blknum + blkcnt;
+
+ /*
+ * See if one of the sectors is in the bad sector list
+ * (if we have one). If the first sector is bad, then
+ * reduce the transfer to this one bad sector; if another
+ * sector is bad, then reduce reduce the transfer to
+ * avoid any bad sectors.
+ */
+ if (du->dk_flags & DKFL_SINGLE
+ && dsgetbad(dev, du->dk_slices) != NULL) {
+ for (blkchk = blknum; blkchk < blknum + blkcnt; blkchk++) {
+ daddr_t blknew;
+ blknew = transbad144(dsgetbad(dev, du->dk_slices),
+ blkchk - ds_offset) + ds_offset;
+ if (blknew != blkchk) {
+ /* Found bad block. */
+ blkcnt = blkchk - blknum;
+ if (blkcnt > 0) {
+ blknext = blknum + blkcnt;
+ goto out;
+ }
+ blkcnt = 1;
+ blknext = blknum + blkcnt;
+#if 1 || defined(WDDEBUG)
+ printf("bad block %lu -> %lu\n",
+ blknum, blknew);
+#endif
+ break;
+ }
+ }
+ }
+out:
+
+ /* Compute disk address. */
+ cylin = blknum / secpercyl;
+ head = (blknum % secpercyl) / secpertrk;
+ sector = blknum % secpertrk;
+
+#if 0
+ /* Let's just talk about this first... */
+ pg("cylin l%d head %ld sector %ld addr 0x%x count %ld",
+ cylin, head, sector, addr, blkcnt);
+#endif
+
+ /* Do the write. */
+ if (wdcommand(du, cylin, head, sector, blkcnt, WDCC_WRITE)
+ != 0) {
+ wderror((struct buf *)NULL, du,
+ "wddump: timeout waiting to to give command");
+ return (EIO);
+ }
+ while (blkcnt != 0) {
+ pmap_enter(kernel_pmap, (vm_offset_t)CADDR1, trunc_page(addr),
+ VM_PROT_READ, TRUE);
+
+ /* Ready to send data? */
+ DELAY(5); /* ATA spec */
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT)
+ < 0) {
+ wderror((struct buf *)NULL, du,
+ "wddump: timeout waiting for DRQ");
+ return (EIO);
+ }
+ if (du->dk_flags & DKFL_32BIT)
+ outsl(du->dk_port + wd_data,
+ CADDR1 + ((int)addr & PAGE_MASK),
+ DEV_BSIZE / sizeof(long));
+ else
+ outsw(du->dk_port + wd_data,
+ CADDR1 + ((int)addr & PAGE_MASK),
+ DEV_BSIZE / sizeof(short));
+ addr += DEV_BSIZE;
+ if ((unsigned)addr % (1024 * 1024) == 0)
+ printf("%ld ", num / (1024 * 1024 / DEV_BSIZE));
+ num--;
+ blkcnt--;
+ }
+
+ /* Wait for completion. */
+ DELAY(5); /* ATA spec XXX NOT */
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) < 0) {
+ wderror((struct buf *)NULL, du,
+ "wddump: timeout waiting for status");
+ return (EIO);
+ }
+
+ /* Check final status. */
+ if (du->dk_status
+ & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ | WDCS_ERR)
+ != (WDCS_READY | WDCS_SEEKCMPLT)) {
+ wderror((struct buf *)NULL, du,
+ "wddump: extra DRQ, or error");
+ return (EIO);
+ }
+
+ /* Update block count. */
+ blknum = blknext;
+
+ /* Operator aborting dump? */
+ if (cncheckc())
+ return (EINTR);
+ }
+ return (0);
+#endif
+}
+
+static void
+wderror(struct buf *bp, struct disk *du, char *mesg)
+{
+ if (bp == NULL)
+ printf("wd%d: %s:\n", du->dk_lunit, mesg);
+ else
+ diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip,
+ dsgetlabel(bp->b_dev, du->dk_slices));
+ printf("wd%d: status %b error %b\n", du->dk_lunit,
+ du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS);
+}
+
+/*
+ * Discard any interrupts that were latched by the interrupt system while
+ * we were doing polled i/o.
+ */
+static void
+wdflushirq(struct disk *du, int old_ipl)
+{
+ wdtab[du->dk_ctrlr].b_active = 2;
+ splx(old_ipl);
+ (void)splbio();
+ wdtab[du->dk_ctrlr].b_active = 0;
+}
+
+/*
+ * Reset the controller.
+ */
+static int
+wdreset(struct disk *du)
+{
+ int wdc, err = 0;
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+ wdc = du->dk_port;
+ (void)wdwait(du, 0, TIMEOUT);
+#ifdef PC98
+ if (old_epson_note) {
+ epson_outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST);
+ DELAY(10 * 1000);
+ epson_outb(wdc + wd_ctlr, WDCTL_IDS);
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0
+ || (du->dk_error = epson_errorf(wdc + wd_error)) != 0x01)
+ return (1);
+ epson_outb(wdc + wd_ctlr, WDCTL_4BIT);
+ err = 0;
+ }
+ else {
+#endif
+ outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST);
+ DELAY(10 * 1000);
+ outb(wdc + wd_ctlr, WDCTL_IDS);
+#ifdef ATAPI
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0)
+ err = 1; /* no IDE drive found */
+ du->dk_error = inb(wdc + wd_error);
+ if (du->dk_error != 0x01)
+ err = 1; /* the drive is incompatible */
+#else
+ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0
+ || (du->dk_error = inb(wdc + wd_error)) != 0x01)
+ return (1);
+#endif
+ outb(wdc + wd_ctlr, WDCTL_4BIT);
+#ifdef PC98
+ }
+#endif
+ return (err);
+}
+
+/*
+ * Sleep until driver is inactive.
+ * This is used only for avoiding rare race conditions, so it is unimportant
+ * that the sleep may be far too short or too long.
+ */
+static void
+wdsleep(int ctrlr, char *wmesg)
+{
+ int s = splbio();
+ while (wdtab[ctrlr].b_active)
+ tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1);
+ splx(s);
+}
+
+static void
+wdtimeout(void *cdu)
+{
+ struct disk *du;
+ int x;
+ static int timeouts;
+
+ du = (struct disk *)cdu;
+ x = splbio();
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+ if (du->dk_timeout != 0 && --du->dk_timeout == 0) {
+ if(timeouts++ == 5)
+ wderror((struct buf *)NULL, du,
+ "Last time I say: interrupt timeout. Probably a portable PC.");
+ else if(timeouts++ < 5)
+ wderror((struct buf *)NULL, du, "interrupt timeout");
+ wdunwedge(du);
+ wdflushirq(du, x);
+ du->dk_skip = 0;
+ du->dk_flags |= DKFL_SINGLE;
+ wdstart(du->dk_ctrlr);
+ }
+ timeout(wdtimeout, cdu, hz);
+ splx(x);
+}
+
+/*
+ * Reset the controller after it has become wedged. This is different from
+ * wdreset() so that wdreset() can be used in the probe and so that this
+ * can restore the geometry .
+ */
+static int
+wdunwedge(struct disk *du)
+{
+ struct disk *du1;
+ int lunit;
+
+#ifdef PC98
+ outb(0x432,(du->dk_unit)%2);
+#endif
+
+ /* Schedule other drives for recalibration. */
+ for (lunit = 0; lunit < NWD; lunit++)
+ if ((du1 = wddrives[lunit]) != NULL && du1 != du
+ && du1->dk_ctrlr == du->dk_ctrlr
+ && du1->dk_state > WANTOPEN)
+ du1->dk_state = WANTOPEN;
+
+ DELAY(RECOVERYTIME);
+ if (wdreset(du) == 0) {
+ /*
+ * XXX - recalibrate current drive now because some callers
+ * aren't prepared to have its state change.
+ */
+ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0
+ && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) == 0
+ && wdsetctlr(du) == 0)
+ return (0);
+ }
+ wderror((struct buf *)NULL, du, "wdunwedge failed");
+ return (1);
+}
+
+/*
+ * Wait uninterruptibly until controller is not busy and either certain
+ * status bits are set or an error has occurred.
+ * The wait is usually short unless it is for the controller to process
+ * an entire critical command.
+ * Return 1 for (possibly stale) controller errors, -1 for timeout errors,
+ * or 0 for no errors.
+ * Return controller status in du->dk_status and, if there was a controller
+ * error, return the error code in du->dk_error.
+ */
+#ifdef WD_COUNT_RETRIES
+static int min_retries[NWDC];
+#endif
+
+static int
+wdwait(struct disk *du, u_char bits_wanted, int timeout)
+{
+ int wdc;
+ u_char status;
+
+#define POLLING 1000
+
+ wdc = du->dk_port;
+ timeout += POLLING;
+
+/*
+ * This delay is really too long, but does not impact the performance
+ * as much when using the NSECS_MULTI option. Shorter delays have
+ * caused I/O errors on some drives and system configs. This should
+ * probably be fixed if we develop a better short term delay mechanism.
+ */
+ DELAY(1);
+
+ do {
+#ifdef WD_COUNT_RETRIES
+ if (min_retries[du->dk_ctrlr] > timeout
+ || min_retries[du->dk_ctrlr] == 0)
+ min_retries[du->dk_ctrlr] = timeout;
+#endif
+#ifdef PC98
+ if (old_epson_note)
+ du->dk_status = status = epson_inb(wdc + wd_status);
+ else
+ du->dk_status = status = inb(wdc + wd_status);
+#else
+ du->dk_status = status = inb(wdc + wd_status);
+#endif
+#ifdef ATAPI
+ /*
+ * Atapi drives have a very interesting feature, when attached
+ * as a slave on the IDE bus, and there is no master.
+ * They release the bus after getting the command.
+ * We should reselect the drive here to get the status.
+ */
+ if (status == 0xff) {
+ outb(wdc + wd_sdh, WDSD_IBM | du->dk_unit << 4);
+ du->dk_status = status = inb(wdc + wd_status);
+ }
+#endif
+ if (!(status & WDCS_BUSY)) {
+ if (status & WDCS_ERR) {
+ if (old_epson_note)
+ du->dk_error = epson_errorf(wdc + wd_error);
+ else
+ du->dk_error = inb(wdc + wd_error);
+ /*
+ * We once returned here. This is wrong
+ * because the error bit is apparently only
+ * valid after the controller has interrupted
+ * (e.g., the error bit is stale when we wait
+ * for DRQ for writes). So we can't depend
+ * on the error bit at all when polling for
+ * command completion.
+ */
+ }
+ if ((status & bits_wanted) == bits_wanted)
+ return (status & WDCS_ERR);
+ }
+ if (timeout < TIMEOUT)
+ /*
+ * Switch to a polling rate of about 1 KHz so that
+ * the timeout is almost machine-independent. The
+ * controller is taking a long time to respond, so
+ * an extra msec won't matter.
+ */
+ DELAY(1000);
+ else
+ DELAY(1);
+ } while (--timeout != 0);
+ return (-1);
+}
+
+static wd_devsw_installed = 0;
+
+static void wd_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! wd_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR,0);
+ cdevsw_add(&dev,&wd_cdevsw,NULL);
+ dev = makedev(BDEV_MAJOR,0);
+ bdevsw_add(&dev,&wd_bdevsw,NULL);
+ wd_devsw_installed = 1;
+ }
+}
+
+SYSINIT(wddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,wd_drvinit,NULL)
+
+
+#endif /* NWDC > 0 */
diff --git a/sys/pc98/pc98/wdreg.h b/sys/pc98/pc98/wdreg.h
new file mode 100644
index 0000000..5edd123
--- /dev/null
+++ b/sys/pc98/pc98/wdreg.h
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 1991 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: @(#)wdreg.h 7.1 (Berkeley) 5/9/91
+ * $Id: wdreg.h,v 1.12 1996/06/08 10:03:38 bde Exp $
+ */
+
+#if 0
+static char rcsid[] = "$Id: wdreg.h,v 1.2 1992/12/06 14:05:28 ukai Exp ukai $";
+#endif
+
+/*
+ * modified for PC9801 by F.Ukai
+ * Kyoto University Microcomputer Club (KMC)
+ */
+
+/*
+ * Disk Controller register definitions.
+ */
+#ifdef PC98
+ /***** PC98 *****/
+#define wd_data 0x0 /* data register (R/W - 16 bits) */
+#define wd_error 0x2 /* error register (R) */
+#define wd_precomp wd_error /* write precompensation (W) */
+#define wd_features wd_error /* features register (W) */
+#define wd_seccnt 0x4 /* sector count (R/W) */
+#define wd_sector 0x6 /* first sector number (R/W) */
+#define wd_cyl_lo 0x8 /* cylinder address, low byte (R/W) */
+#define wd_cyl_hi 0xa /* cylinder address, high byte (R/W)*/
+#define wd_sdh 0xc /* sector size/drive/head (R/W)*/
+#define wd_command 0xe /* command register (W) */
+#define wd_status wd_command /* immediate status (R) */
+
+#define wd_altsts_nec 0x10c /*alternate fixed disk status(via 1015) (R)*/
+#define wd_ctlr_nec 0x10c /*fixed disk controller control(via 1015) (W)*/
+#define wd_altsts_epson 0x3 /*alternate fixed disk status(via 1015) (R)*/
+#define wd_ctlr_epson 0x3 /*fixed disk controller control(via 1015) (W)*/
+
+#define WDCTL_4BIT 0x8 /* use four head bits (wd1003) */
+#define WDCTL_RST 0x4 /* reset the controller */
+#define WDCTL_IDS 0x2 /* disable controller interrupts */
+#define wd_digin 0x10e /* disk controller input(via 1015) (R)*/
+#else /* IBM-PC */
+ /***** IBM-PC *****/
+#define wd_data 0x0 /* data register (R/W - 16 bits) */
+#define wd_error 0x1 /* error register (R) */
+#define wd_precomp wd_error /* write precompensation (W) */
+#define wd_seccnt 0x2 /* sector count (R/W) */
+#define wd_sector 0x3 /* first sector number (R/W) */
+#define wd_cyl_lo 0x4 /* cylinder address, low byte (R/W) */
+#define wd_cyl_hi 0x5 /* cylinder address, high byte (R/W)*/
+#define wd_sdh 0x6 /* sector size/drive/head (R/W)*/
+#define wd_command 0x7 /* command register (W) */
+#define wd_status wd_command /* immediate status (R) */
+
+#define wd_altsts 0x206 /*alternate fixed disk status(via 1015) (R)*/
+#define wd_ctlr 0x206 /*fixed disk controller control(via 1015) (W)*/
+#define WDCTL_4BIT 0x8 /* use four head bits (wd1003) */
+#define WDCTL_RST 0x4 /* reset the controller */
+#define WDCTL_IDS 0x2 /* disable controller interrupts */
+#define wd_digin 0x207 /* disk controller input(via 1015) (R)*/
+#endif /* PC98 */
+
+/*
+ * Status Bits.
+ */
+#define WDCS_BUSY 0x80 /* Controller busy bit. */
+#define WDCS_READY 0x40 /* Selected drive is ready */
+#define WDCS_WRTFLT 0x20 /* Write fault */
+#define WDCS_SEEKCMPLT 0x10 /* Seek complete */
+#define WDCS_DRQ 0x08 /* Data request bit. */
+#define WDCS_ECCCOR 0x04 /* ECC correction made in data */
+#define WDCS_INDEX 0x02 /* Index pulse from selected drive */
+#define WDCS_ERR 0x01 /* Error detect bit. */
+
+#define WDCS_BITS "\020\010busy\007rdy\006wrtflt\005seekdone\004drq\003ecc_cor\002index\001err"
+#define WDERR_ABORT 0x04
+
+#define WDERR_BITS "\020\010badblk\007uncorr\006id_crc\005no_id\003abort\002tr000\001no_dam"
+
+/*
+ * Commands for Disk Controller.
+ */
+#define WDCC_RESTORE 0x10 /* disk restore code -- resets cntlr */
+
+#define WDCC_READ 0x20 /* disk read code */
+#define WDCC_WRITE 0x30 /* disk write code */
+#define WDCC__LONG 0x02 /* modifier -- access ecc bytes */
+#define WDCC__NORETRY 0x01 /* modifier -- no retrys */
+
+#define WDCC_FORMAT 0x50 /* disk format code */
+#define WDCC_DIAGNOSE 0x90 /* controller diagnostic */
+#define WDCC_IDC 0x91 /* initialize drive command */
+#define WDCC_READ_MULTI 0xC4 /* read multiple */
+#define WDCC_WRITE_MULTI 0xC5 /* write multiple */
+#define WDCC_SET_MULTI 0xC6 /* set multiple count */
+
+
+#define WDCC_EXTDCMD 0xE0 /* send extended command */
+#define WDCC_READP 0xEC /* read parameters from controller */
+#define WDCC_FEATURES 0xEF /* features control */
+
+#define WDFEA_RCACHE 0xAA /* read cache enable */
+#define WDFEA_WCACHE 0x02 /* write cache enable */
+
+#define WD_STEP 0 /* winchester- default 35us step */
+
+#define WDSD_IBM 0xa0 /* forced to 512 byte sector, ecc */
+
+#ifdef KERNEL
+/*
+ * read parameters command returns this:
+ */
+struct wdparams {
+ /* drive info */
+ short wdp_config; /* general configuration bits */
+ short wdp_cylinders; /* number of cylinders */
+ short wdp_reserved;
+ short wdp_heads; /* number of heads */
+ short wdp_unfbytespertrk; /* number of unformatted bytes/track */
+ short wdp_unfbytes; /* number of unformatted bytes/sector */
+ short wdp_sectors; /* number of sectors per track */
+ short wdp_vendorunique[3];
+ /* controller info */
+ char wdp_serial[20]; /* serial number */
+ short wdp_buffertype; /* buffer type */
+#define WDTYPE_SINGLEPORTSECTOR 1 /* single port, single sector buffer */
+#define WDTYPE_DUALPORTMULTI 2 /* dual port, multiple sector buffer */
+#define WDTYPE_DUALPORTMULTICACHE 3 /* above plus track cache */
+ short wdp_buffersize; /* buffer size, in 512-byte units */
+ short wdp_necc; /* ecc bytes appended */
+ char wdp_rev[8]; /* firmware revision */
+ char wdp_model[40]; /* model name */
+ short wdp_nsecperint; /* sectors per interrupt */
+ short wdp_usedmovsd; /* can use double word read/write? */
+};
+
+/*
+ * wd driver entry points
+ */
+#ifdef B_FORMAT
+int wdformat(struct buf *bp);
+#endif
+
+/*
+ * IDE DMA support.
+ * This is based on what is needed for the IDE DMA function of the Intel
+ * Triton chipset; hopefully it's general enough to be used for other
+ * chipsets as well.
+ *
+ * To use this:
+ * For each drive which you might want to do DMA on, call wdd_candma()
+ * to get a cookie. If it returns a null pointer, then the drive
+ * can't do DMA.
+ *
+ * Set up the transfer be calling wdd_dmaprep(). The cookie is what
+ * you got before; vaddr is the virtual address of the buffer to be
+ * written; len is the length of the buffer; and direction is either
+ * B_READ or B_WRITE.
+ *
+ * Send a read/write DMA command to the drive.
+ *
+ * Call wdd_dmastart().
+ *
+ * Wait for an interrupt. Multi-sector transfers will only interrupt
+ * at the end of the transfer.
+ *
+ * Call wdd_dmadone(). It will return the status as defined by the
+ * WDDS_* constants below.
+ */
+struct wddma {
+ void *(*wdd_candma) /* returns a cookie if can do DMA */
+ __P((int ctlr, int drive));
+ int (*wdd_dmaprep) /* prepare DMA hardware */
+ __P((void *cookie, char *vaddr, u_long len, int direction));
+ void (*wdd_dmastart) /* begin DMA transfer */
+ __P((void *cookie));
+ int (*wdd_dmadone) /* DMA transfer completed */
+ __P((void *cookie));
+ int (*wdd_dmastatus) /* return status of DMA */
+ __P((void *cookie));
+};
+
+#define WDDS_ACTIVE 0x0001
+#define WDDS_ERROR 0x0002
+#define WDDS_INTERRUPT 0x0004
+
+extern struct wddma wddma;
+
+#endif /* KERNEL */
OpenPOWER on IntegriCloud